diff --git a/api-resources.go b/api-resources.go index a3e6012a7..7966b5014 100644 --- a/api-resources.go +++ b/api-resources.go @@ -22,7 +22,39 @@ import ( ) // Parse bucket url queries -func getBucketResources(values url.Values) (prefix, marker, delimiter string, maxkeys int, encodingType string) { +func getListObjectsV1Args(values url.Values) (prefix, marker, delimiter string, maxkeys int, encodingType string) { + prefix = values.Get("prefix") + marker = values.Get("marker") + delimiter = values.Get("delimiter") + if values.Get("max-keys") != "" { + maxkeys, _ = strconv.Atoi(values.Get("max-keys")) + } else { + maxkeys = maxObjectList + } + encodingType = values.Get("encoding-type") + return +} + +// Parse bucket url queries for ListObjects V2. +func getListObjectsV2Args(values url.Values) (prefix, token, startAfter, delimiter string, maxkeys int, encodingType string) { + prefix = values.Get("prefix") + startAfter = values.Get("start-after") + delimiter = values.Get("delimiter") + if values.Get("max-keys") != "" { + maxkeys, _ = strconv.Atoi(values.Get("max-keys")) + } else { + maxkeys = maxObjectList + } + encodingType = values.Get("encoding-type") + token = values.Get("continuation-token") + return +} + +// Parse bucket url queries +func getBucketResources(values url.Values) (listType int, prefix, marker, delimiter string, maxkeys int, encodingType string) { + if values.Get("list-type") != "" { + listType, _ = strconv.Atoi(values.Get("list-type")) + } prefix = values.Get("prefix") marker = values.Get("marker") delimiter = values.Get("delimiter") diff --git a/api-response.go b/api-response.go index 820680df7..690da75e9 100644 --- a/api-response.go +++ b/api-response.go @@ -65,6 +65,37 @@ type ListObjectsResponse struct { Prefix string } +// ListObjectsV2Response - format for list objects response. +type ListObjectsV2Response struct { + XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` + + CommonPrefixes []CommonPrefix + Contents []Object + + Delimiter string + + // Encoding type used to encode object keys in the response. + EncodingType string + + // A flag that indicates whether or not ListObjects returned all of the results + // that satisfied the search criteria. + IsTruncated bool + StartAfter string + MaxKeys int + Name string + + // When response is truncated (the IsTruncated element value in the response + // is true), you can use the key name in this field as marker in the subsequent + // request to get next set of objects. Server lists objects in alphabetical + // order Note: This element is returned only if you have delimiter request parameter + // specified. If response does not include the NextMaker and it is truncated, + // you can use the value of the last Key in the response as the marker in the + // subsequent request to get the next set of object keys. + ContinuationToken string + NextContinuationToken string + Prefix string +} + // Part container for part metadata. type Part struct { PartNumber int @@ -304,6 +335,51 @@ func generateListObjectsResponse(bucket, prefix, marker, delimiter string, maxKe return data } +// generates an ListObjects response for the said bucket with other enumerated options. +func generateListObjectsV2Response(bucket, prefix, token, startAfter, delimiter string, maxKeys int, resp ListObjectsInfo) ListObjectsV2Response { + var contents []Object + var prefixes []CommonPrefix + var owner = Owner{} + var data = ListObjectsV2Response{} + + owner.ID = "minio" + owner.DisplayName = "minio" + + for _, object := range resp.Objects { + var content = Object{} + if object.Name == "" { + continue + } + content.Key = object.Name + content.LastModified = object.ModTime.UTC().Format(timeFormatAMZ) + if object.MD5Sum != "" { + content.ETag = "\"" + object.MD5Sum + "\"" + } + content.Size = object.Size + content.StorageClass = "STANDARD" + content.Owner = owner + contents = append(contents, content) + } + // TODO - support EncodingType in xml decoding + data.Name = bucket + data.Contents = contents + + data.StartAfter = startAfter + data.Delimiter = delimiter + data.Prefix = prefix + data.MaxKeys = maxKeys + data.ContinuationToken = token + data.NextContinuationToken = resp.NextMarker + data.IsTruncated = resp.IsTruncated + for _, prefix := range resp.Prefixes { + var prefixItem = CommonPrefix{} + prefixItem.Prefix = prefix + prefixes = append(prefixes, prefixItem) + } + data.CommonPrefixes = prefixes + return data +} + // generateCopyObjectResponse func generateCopyObjectResponse(etag string, lastModified time.Time) CopyObjectResponse { return CopyObjectResponse{ diff --git a/bucket-handlers.go b/bucket-handlers.go index 645136401..afe0b192f 100644 --- a/bucket-handlers.go +++ b/bucket-handlers.go @@ -220,9 +220,22 @@ func (api objectAPIHandlers) ListObjectsHandler(w http.ResponseWriter, r *http.R return } } - + var prefix, marker, token, delimiter, startAfter string + var maxkeys int + var listV2 bool // TODO handle encoding type. - prefix, marker, delimiter, maxkeys, _ := getBucketResources(r.URL.Query()) + if r.URL.Query().Get("list-type") == "2" { + listV2 = true + prefix, token, startAfter, delimiter, maxkeys, _ = getListObjectsV2Args(r.URL.Query()) + // For ListV2 "start-after" is considered only if "continuation-token" is empty. + if token == "" { + marker = startAfter + } else { + marker = token + } + } else { + prefix, marker, delimiter, maxkeys, _ = getListObjectsV1Args(r.URL.Query()) + } if maxkeys < 0 { writeErrorResponse(w, r, ErrInvalidMaxKeys, r.URL.Path) return @@ -242,10 +255,17 @@ func (api objectAPIHandlers) ListObjectsHandler(w http.ResponseWriter, r *http.R } listObjectsInfo, err := api.ObjectAPI.ListObjects(bucket, prefix, marker, delimiter, maxkeys) + if err == nil { + var encodedSuccessResponse []byte // generate response - response := generateListObjectsResponse(bucket, prefix, marker, delimiter, maxkeys, listObjectsInfo) - encodedSuccessResponse := encodeResponse(response) + if listV2 { + response := generateListObjectsV2Response(bucket, prefix, token, startAfter, delimiter, maxkeys, listObjectsInfo) + encodedSuccessResponse = encodeResponse(response) + } else { + response := generateListObjectsResponse(bucket, prefix, marker, delimiter, maxkeys, listObjectsInfo) + encodedSuccessResponse = encodeResponse(response) + } // Write headers setCommonHeaders(w) // Write success response. diff --git a/docs/backend/README.md b/docs/backend/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/backend/fs/format.json b/docs/backend/fs/format.json new file mode 100644 index 000000000..244e25856 --- /dev/null +++ b/docs/backend/fs/format.json @@ -0,0 +1,4 @@ +{ + "format": "fs", + "version": "1" +} diff --git a/docs/backend/fs/fs.json b/docs/backend/fs/fs.json new file mode 100644 index 000000000..3fc555bd0 --- /dev/null +++ b/docs/backend/fs/fs.json @@ -0,0 +1,15 @@ +{ + "version": "1", + "format": "fs", + "minio": { + "release": "DEVELOPMENT.GOGET" + }, + "parts": [ + { + "number": 1, + "name": "object1", + "size": 29, + "eTag": "", + }, + ] +} diff --git a/docs/backend/fs/uploads.json b/docs/backend/fs/uploads.json new file mode 100644 index 000000000..339d5ecff --- /dev/null +++ b/docs/backend/fs/uploads.json @@ -0,0 +1,10 @@ +{ + "version": "1", + "format": "fs", + "uploadIds": [ + { + "uploadID": "id", + "startTime": "time", + } + ] +} diff --git a/docs/backend/xl/format.json b/docs/backend/xl/format.json new file mode 100644 index 000000000..c3acdd6cf --- /dev/null +++ b/docs/backend/xl/format.json @@ -0,0 +1,20 @@ +{ + "xl": { + "jbod": [ + "8aa2b1bc-0e5a-49e0-8221-05228336b040", + "3467a69b-0266-478a-9e10-e819447e4545", + "d4a4505b-4e4f-4864-befd-4f36adb0bc66", + "592b6583-ca26-47af-b991-ba6d097e34e8", + "c7ef69f0-dbf5-4c0e-b167-d30a441bad7e", + "f0b36ea3-fe96-4f2b-bced-22c7f33e0e0c", + "b83abf39-e39d-4e7b-8e16-6f9953455a48", + "7d63dfc9-5441-4243-bd36-de8db0691982", + "c1bbffc5-81f9-4251-9398-33a959b3ce37", + "64408f94-26e0-4277-9593-2d703f4d5a91" + ], + "disk": "8aa2b1bc-0e5a-49e0-8221-05228336b040", + "version": "1" + }, + "format": "xl", + "version": "1" +} diff --git a/docs/backend/xl/uploads.json b/docs/backend/xl/uploads.json new file mode 100644 index 000000000..301f731ec --- /dev/null +++ b/docs/backend/xl/uploads.json @@ -0,0 +1,10 @@ +{ + "version": "1", + "format": "xl", + "uploadIds": [ + { + "uploadID": "id", + "startTime": "time", + } + ] +} diff --git a/docs/backend/xl/xl.json b/docs/backend/xl/xl.json new file mode 100644 index 000000000..333b984ef --- /dev/null +++ b/docs/backend/xl/xl.json @@ -0,0 +1,57 @@ +{ + "parts": [ + { + "number": 1, + "size": 5242880, + "etag": "3565c6e741e69a007a5ac7db893a62b5", + "name": "object1" + }, + { + "number": 2, + "size": 5242880, + "etag": "d416712335c280ab1e39498552937764", + "name": "object2" + }, + { + "number": 3, + "size": 4338324, + "etag": "8a98c5c54d81c6c95ed9bdcaeb941aaf", + "name": "object3" + } + ], + "meta": { + "md5Sum": "97586a5290d4f5a41328062d6a7da593-3", + "content-type": "application\/octet-stream", + "content-encoding": "" + }, + "minio": { + "release": "DEVELOPMENT.GOGET" + }, + "erasure": { + "algorithm": "klauspost/reedsolomon/vandermonde", + "index": 2, + "distribution": [ 1, 3, 4, 2, 5, 8, 7, 6, 9 ], + "blockSize": 4194304, + "parity": 5, + "data": 5, + "checksum": [ + { + "name": "object1", + "algorithm": "sha512", + "hash": "d9910e1492446389cfae6fe979db0245f96ca97ca2c7a25cab45805882004479320d866a47ea1f7be6a62625dd4de6caf7816009ef9d62779346d01a221b335c", + }, + { + "name": "object2", + "algorithm": "sha512", + "hash": "d9910e1492446389cfae6fe979db0245f96ca97ca2c7a25cab45805882004479320d866a47ea1f7be6a62625dd4de6caf7816009ef9d62779346d01a221b335c", + }, + ], + }, + "stat": { + "version": 0, + "modTime": "2016-05-24T00:09:40.122390255Z", + "size": 14824084 + }, + "format": "xl", + "version": "1" +} diff --git a/erasure-createfile.go b/erasure-createfile.go new file mode 100644 index 000000000..c12f273b4 --- /dev/null +++ b/erasure-createfile.go @@ -0,0 +1,149 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "encoding/hex" + "hash" + "io" + "sync" + + "github.com/klauspost/reedsolomon" +) + +// erasureCreateFile - writes an entire stream by erasure coding to +// all the disks, writes also calculate individual block's checksum +// for future bit-rot protection. +func erasureCreateFile(disks []StorageAPI, volume string, path string, partName string, data io.Reader, eInfos []erasureInfo) (newEInfos []erasureInfo, size int64, err error) { + // Allocated blockSized buffer for reading. + buf := make([]byte, blockSizeV1) + hashWriters := newHashWriters(len(disks)) + + // Just pick one eInfo. + eInfo := pickValidErasureInfo(eInfos) + + // Read until io.EOF, erasure codes data and writes to all disks. + for { + var n int + n, err = io.ReadFull(data, buf) + if err == io.EOF { + break + } + if err != nil && err != io.ErrUnexpectedEOF { + return nil, 0, err + } + size += int64(n) + var blocks [][]byte + // Returns encoded blocks. + blocks, err = encodeData(buf[:n], eInfo.DataBlocks, eInfo.ParityBlocks) + if err != nil { + return nil, 0, err + } + err = appendFile(disks, volume, path, blocks, eInfo.Distribution, hashWriters) + if err != nil { + return nil, 0, err + } + } + + // Save the checksums. + checkSums := make([]checkSumInfo, len(disks)) + for index := range disks { + blockIndex := eInfo.Distribution[index] - 1 + checkSums[blockIndex] = checkSumInfo{ + Name: partName, + Algorithm: "sha512", + Hash: hex.EncodeToString(hashWriters[blockIndex].Sum(nil)), + } + } + + // Erasure info update for checksum for each disks. + newEInfos = make([]erasureInfo, len(disks)) + for index, eInfo := range eInfos { + if eInfo.IsValid() { + blockIndex := eInfo.Distribution[index] - 1 + newEInfos[index] = eInfo + newEInfos[index].Checksum = append(newEInfos[index].Checksum, checkSums[blockIndex]) + } + } + + // Return newEInfos. + return newEInfos, size, nil +} + +// encodeData - encodes incoming data buffer into +// dataBlocks+parityBlocks returns a 2 dimensional byte array. +func encodeData(dataBuffer []byte, dataBlocks, parityBlocks int) ([][]byte, error) { + rs, err := reedsolomon.New(dataBlocks, parityBlocks) + if err != nil { + return nil, err + } + // Split the input buffer into data and parity blocks. + var blocks [][]byte + blocks, err = rs.Split(dataBuffer) + if err != nil { + return nil, err + } + + // Encode parity blocks using data blocks. + err = rs.Encode(blocks) + if err != nil { + return nil, err + } + + // Return encoded blocks. + return blocks, nil +} + +// appendFile - append data buffer at path. +func appendFile(disks []StorageAPI, volume, path string, enBlocks [][]byte, distribution []int, hashWriters []hash.Hash) (err error) { + var wg = &sync.WaitGroup{} + var wErrs = make([]error, len(disks)) + // Write encoded data to quorum disks in parallel. + for index, disk := range disks { + if disk == nil { + continue + } + wg.Add(1) + // Write encoded data in routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + // Pick the block from the distribution. + blockIndex := distribution[index] - 1 + n, wErr := disk.AppendFile(volume, path, enBlocks[blockIndex]) + if wErr != nil { + wErrs[index] = wErr + return + } + if n != int64(len(enBlocks[blockIndex])) { + wErrs[index] = errUnexpected + return + } + + // Calculate hash for each blocks. + hashWriters[blockIndex].Write(enBlocks[blockIndex]) + + // Successfully wrote. + wErrs[index] = nil + }(index, disk) + } + + // Wait for all the appends to finish. + wg.Wait() + + // Return success. + return nil +} diff --git a/erasure-readfile.go b/erasure-readfile.go new file mode 100644 index 000000000..165b5c811 --- /dev/null +++ b/erasure-readfile.go @@ -0,0 +1,208 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "encoding/hex" + "errors" + + "github.com/klauspost/reedsolomon" +) + +// erasureReadFile - read an entire erasure coded file at into a byte +// array. Erasure coded parts are often few mega bytes in size and it +// is convenient to return them as byte slice. This function also +// supports bit-rot detection by verifying checksum of individual +// block's checksum. +func erasureReadFile(disks []StorageAPI, volume string, path string, partName string, size int64, eInfos []erasureInfo) ([]byte, error) { + // Return data buffer. + var buffer []byte + + // Total size left + totalSizeLeft := size + + // Starting offset for reading. + startOffset := int64(0) + + // Gather previously calculated block checksums. + blockCheckSums := metaPartBlockChecksums(disks, eInfos, partName) + + // Pick one erasure info. + eInfo := pickValidErasureInfo(eInfos) + + // Write until each parts are read and exhausted. + for totalSizeLeft > 0 { + // Calculate the proper block size. + var curBlockSize int64 + if eInfo.BlockSize < totalSizeLeft { + curBlockSize = eInfo.BlockSize + } else { + curBlockSize = totalSizeLeft + } + + // Calculate the current encoded block size. + curEncBlockSize := getEncodedBlockLen(curBlockSize, eInfo.DataBlocks) + offsetEncOffset := getEncodedBlockLen(startOffset, eInfo.DataBlocks) + + // Allocate encoded blocks up to storage disks. + enBlocks := make([][]byte, len(disks)) + + // Counter to keep success data blocks. + var successDataBlocksCount = 0 + var noReconstruct bool // Set for no reconstruction. + + // Read from all the disks. + for index, disk := range disks { + blockIndex := eInfo.Distribution[index] - 1 + if !isValidBlock(disks, volume, path, toDiskIndex(blockIndex, eInfo.Distribution), blockCheckSums) { + continue + } + if disk == nil { + continue + } + // Initialize shard slice and fill the data from each parts. + enBlocks[blockIndex] = make([]byte, curEncBlockSize) + // Read the necessary blocks. + _, err := disk.ReadFile(volume, path, offsetEncOffset, enBlocks[blockIndex]) + if err != nil { + enBlocks[blockIndex] = nil + } + // Verify if we have successfully read all the data blocks. + if blockIndex < eInfo.DataBlocks && enBlocks[blockIndex] != nil { + successDataBlocksCount++ + // Set when we have all the data blocks and no + // reconstruction is needed, so that we can avoid + // erasure reconstruction. + noReconstruct = successDataBlocksCount == eInfo.DataBlocks + if noReconstruct { + // Break out we have read all the data blocks. + break + } + } + } + + // Check blocks if they are all zero in length, we have corruption return error. + if checkBlockSize(enBlocks) == 0 { + return nil, errXLDataCorrupt + } + + // Verify if reconstruction is needed, proceed with reconstruction. + if !noReconstruct { + err := decodeData(enBlocks, eInfo.DataBlocks, eInfo.ParityBlocks) + if err != nil { + return nil, err + } + } + + // Get data blocks from encoded blocks. + dataBlocks, err := getDataBlocks(enBlocks, eInfo.DataBlocks, int(curBlockSize)) + if err != nil { + return nil, err + } + + // Copy data blocks. + buffer = append(buffer, dataBlocks...) + + // Negate the 'n' size written to client. + totalSizeLeft -= int64(len(dataBlocks)) + + // Increase the offset to move forward. + startOffset += int64(len(dataBlocks)) + + // Relenquish memory. + dataBlocks = nil + } + return buffer, nil +} + +// PartObjectChecksum - returns the checksum for the part name from the checksum slice. +func (e erasureInfo) PartObjectChecksum(partName string) checkSumInfo { + for _, checksum := range e.Checksum { + if checksum.Name == partName { + return checksum + } + } + return checkSumInfo{} +} + +// xlMetaPartBlockChecksums - get block checksums for a given part. +func metaPartBlockChecksums(disks []StorageAPI, eInfos []erasureInfo, partName string) (blockCheckSums []checkSumInfo) { + for index := range disks { + if eInfos[index].IsValid() { + // Save the read checksums for a given part. + blockCheckSums = append(blockCheckSums, eInfos[index].PartObjectChecksum(partName)) + } else { + blockCheckSums = append(blockCheckSums, checkSumInfo{}) + } + } + return blockCheckSums +} + +// Takes block index and block distribution to get the disk index. +func toDiskIndex(blockIdx int, distribution []int) (diskIndex int) { + diskIndex = -1 + // Find out the right disk index for the input block index. + for index, blockIndex := range distribution { + if blockIndex == blockIdx { + diskIndex = index + } + } + return diskIndex +} + +// isValidBlock - calculates the checksum hash for the block and +// validates if its correct returns true for valid cases, false otherwise. +func isValidBlock(disks []StorageAPI, volume, path string, diskIndex int, blockCheckSums []checkSumInfo) bool { + // Unknown block index requested, treat it as error. + if diskIndex == -1 { + return false + } + // Disk is not present, treat entire block to be non existent. + if disks[diskIndex] == nil { + return false + } + // Read everything for a given block and calculate hash. + hashWriter := newHash(blockCheckSums[diskIndex].Algorithm) + hashBytes, err := hashSum(disks[diskIndex], volume, path, hashWriter) + if err != nil { + return false + } + return hex.EncodeToString(hashBytes) == blockCheckSums[diskIndex].Hash +} + +// decodeData - decode encoded blocks. +func decodeData(enBlocks [][]byte, dataBlocks, parityBlocks int) error { + rs, err := reedsolomon.New(dataBlocks, parityBlocks) + if err != nil { + return err + } + err = rs.Reconstruct(enBlocks) + if err != nil { + return err + } + // Verify reconstructed blocks (parity). + ok, err := rs.Verify(enBlocks) + if err != nil { + return err + } + if !ok { + // Blocks cannot be reconstructed, corrupted data. + err = errors.New("Verification failed after reconstruction, data likely corrupted.") + return err + } + return nil +} diff --git a/erasure-utils.go b/erasure-utils.go new file mode 100644 index 000000000..c97195351 --- /dev/null +++ b/erasure-utils.go @@ -0,0 +1,108 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "crypto/sha512" + "hash" + "io" + + "github.com/klauspost/reedsolomon" +) + +// newHashWriters - inititialize a slice of hashes for the disk count. +func newHashWriters(diskCount int) []hash.Hash { + hashWriters := make([]hash.Hash, diskCount) + for index := range hashWriters { + hashWriters[index] = newHash("sha512") + } + return hashWriters +} + +// newHash - gives you a newly allocated hash depending on the input algorithm. +func newHash(algo string) hash.Hash { + switch algo { + case "sha512": + return sha512.New() + // Add new hashes here. + default: + return sha512.New() + } +} + +func hashSum(disk StorageAPI, volume, path string, writer hash.Hash) ([]byte, error) { + startOffset := int64(0) + // Read until io.EOF. + for { + buf := make([]byte, blockSizeV1) + n, err := disk.ReadFile(volume, path, startOffset, buf) + if err == io.EOF { + break + } + if err != nil && err != io.EOF { + return nil, err + } + writer.Write(buf[:n]) + startOffset += n + } + return writer.Sum(nil), nil +} + +// getDataBlocks - fetches the data block only part of the input encoded blocks. +func getDataBlocks(enBlocks [][]byte, dataBlocks int, curBlockSize int) (data []byte, err error) { + if len(enBlocks) < dataBlocks { + return nil, reedsolomon.ErrTooFewShards + } + size := 0 + blocks := enBlocks[:dataBlocks] + for _, block := range blocks { + size += len(block) + } + if size < curBlockSize { + return nil, reedsolomon.ErrShortData + } + + write := curBlockSize + for _, block := range blocks { + if write < len(block) { + data = append(data, block[:write]...) + return data, nil + } + data = append(data, block...) + write -= len(block) + } + return data, nil +} + +// checkBlockSize return the size of a single block. +// The first non-zero size is returned, +// or 0 if all blocks are size 0. +func checkBlockSize(blocks [][]byte) int { + for _, block := range blocks { + if len(block) != 0 { + return len(block) + } + } + return 0 +} + +// calculate the blockSize based on input length and total number of +// data blocks. +func getEncodedBlockLen(inputLen int64, dataBlocks int) (curEncBlockSize int64) { + curEncBlockSize = (inputLen + int64(dataBlocks) - 1) / int64(dataBlocks) + return curEncBlockSize +} diff --git a/xl-erasure-v1-waitcloser.go b/erasure-waitcloser.go similarity index 100% rename from xl-erasure-v1-waitcloser.go rename to erasure-waitcloser.go diff --git a/erasure.go b/erasure.go new file mode 100644 index 000000000..4e7aad742 --- /dev/null +++ b/erasure.go @@ -0,0 +1,17 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main diff --git a/format-config-v1.go b/format-config-v1.go index 823d40cff..32452b7db 100644 --- a/format-config-v1.go +++ b/format-config-v1.go @@ -20,27 +20,240 @@ import ( "encoding/json" "errors" "fmt" - "io" "strings" + "sync" "github.com/skyrings/skyring-common/tools/uuid" ) +// fsFormat - structure holding 'fs' format. type fsFormat struct { Version string `json:"version"` } +// xlFormat - structure holding 'xl' format. type xlFormat struct { - Version string `json:"version"` - Disk string `json:"disk"` - JBOD []string `json:"jbod"` + Version string `json:"version"` // Version of 'xl' format. + Disk string `json:"disk"` // Disk field carries assigned disk uuid. + // JBOD field carries the input disk order generated the first + // time when fresh disks were supplied. + JBOD []string `json:"jbod"` } +// formatConfigV1 - structure holds format config version '1'. type formatConfigV1 struct { - Version string `json:"version"` - Format string `json:"format"` - FS *fsFormat `json:"fs,omitempty"` - XL *xlFormat `json:"xl,omitempty"` + Version string `json:"version"` // Version of the format config. + // Format indicates the backend format type, supports two values 'xl' and 'fs'. + Format string `json:"format"` + FS *fsFormat `json:"fs,omitempty"` // FS field holds fs format. + XL *xlFormat `json:"xl,omitempty"` // XL field holds xl format. +} + +/* + +All disks online +----------------- +- All Unformatted - format all and return success. +- Some Unformatted - format all and return success. +- Any JBOD inconsistent - return failure // Requires deep inspection, phase2. +- Some are corrupt (missing format.json) - return failure // Requires deep inspection, phase2. +- Any unrecognized disks - return failure + +Some disks are offline and we have quorum. +----------------- +- Some unformatted - no heal, return success. +- Any JBOD inconsistent - return failure // Requires deep inspection, phase2. +- Some are corrupt (missing format.json) - return failure // Requires deep inspection, phase2. +- Any unrecognized disks - return failure + +No read quorum +----------------- +failure for all cases. + +// Pseudo code for managing `format.json`. + +// Generic checks. +if (no quorum) return error +if (any disk is corrupt) return error // phase2 +if (jbod inconsistent) return error // phase2 +if (disks not recognized) // Always error. + +// Specific checks. +if (all disks online) + if (all disks return format.json) + if (jbod consistent) + if (all disks recognized) + return + else + if (all disks return format.json not found) + (initialize format) + return + else (some disks return format.json not found) + (heal format) + return + fi + fi +else // No healing at this point forward, some disks are offline or dead. + if (some disks return format.json not found) + if (with force) + // Offline disks are marked as dead. + (heal format) // Offline disks should be marked as dead. + return success + else (without force) + // --force is necessary to heal few drives, because some drives + // are offline. Offline disks will be marked as dead. + return error + fi +fi +*/ + +var errSomeDiskUnformatted = errors.New("some disks are found to be unformatted") +var errSomeDiskOffline = errors.New("some disks are offline") + +// Returns error slice into understandable errors. +func reduceFormatErrs(errs []error, diskCount int) error { + var errUnformattedDiskCount = 0 + var errDiskNotFoundCount = 0 + for _, err := range errs { + if err == errUnformattedDisk { + errUnformattedDiskCount++ + } else if err == errDiskNotFound { + errDiskNotFoundCount++ + } + } + // Returns errUnformattedDisk if all disks report unFormattedDisk. + if errUnformattedDiskCount == diskCount { + return errUnformattedDisk + } else if errUnformattedDiskCount < diskCount && errDiskNotFoundCount == 0 { + // Only some disks return unFormattedDisk and all disks are online. + return errSomeDiskUnformatted + } else if errUnformattedDiskCount < diskCount && errDiskNotFoundCount > 0 { + // Only some disks return unFormattedDisk and some disks are + // offline as well. + return errSomeDiskOffline + } + return nil +} + +// loadAllFormats - load all format config from all input disks in parallel. +func loadAllFormats(bootstrapDisks []StorageAPI) ([]*formatConfigV1, []error) { + // Initialize sync waitgroup. + var wg = &sync.WaitGroup{} + + // Initialize list of errors. + var sErrs = make([]error, len(bootstrapDisks)) + + // Initialize format configs. + var formatConfigs = make([]*formatConfigV1, len(bootstrapDisks)) + + // Make a volume entry on all underlying storage disks. + for index, disk := range bootstrapDisks { + wg.Add(1) + // Make a volume inside a go-routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + formatConfig, lErr := loadFormat(disk) + if lErr != nil { + sErrs[index] = lErr + return + } + formatConfigs[index] = formatConfig + }(index, disk) + } + + // Wait for all make vol to finish. + wg.Wait() + + for _, err := range sErrs { + if err != nil { + // Return all formats and errors. + return formatConfigs, sErrs + } + } + // Return all formats and nil + return formatConfigs, nil +} + +// genericFormatCheck - validates and returns error. +// if (no quorum) return error +// if (any disk is corrupt) return error // phase2 +// if (jbod inconsistent) return error // phase2 +// if (disks not recognized) // Always error. +func genericFormatCheck(formatConfigs []*formatConfigV1, sErrs []error) (err error) { + // Calculate the errors. + var ( + errCorruptFormatCount = 0 + errCount = 0 + ) + + // Through all errors calculate the actual errors. + for _, lErr := range sErrs { + if lErr == nil { + continue + } + // These errors are good conditions, means disk is online. + if lErr == errUnformattedDisk || lErr == errVolumeNotFound { + continue + } + if lErr == errCorruptedFormat { + errCorruptFormatCount++ + } else { + errCount++ + } + } + + // Calculate read quorum. + readQuorum := len(formatConfigs)/2 + 1 + + // Validate the err count under tolerant limit. + if errCount > len(formatConfigs)-readQuorum { + return errXLReadQuorum + } + + // One of the disk has corrupt format, return error. + if errCorruptFormatCount > 0 { + return errCorruptedFormat + } + + // Validates if format and JBOD are consistent across all disks. + if err = checkFormatXL(formatConfigs); err != nil { + return err + } + + // Success.. + return nil +} + +// checkDisksConsistency - checks if all disks are consistent with all JBOD entries on all disks. +func checkDisksConsistency(formatConfigs []*formatConfigV1) error { + var disks = make([]string, len(formatConfigs)) + var disksFound = make(map[string]bool) + // Collect currently available disk uuids. + for index, formatConfig := range formatConfigs { + if formatConfig == nil { + continue + } + disks[index] = formatConfig.XL.Disk + } + // Validate collected uuids and verify JBOD. + for index, uuid := range disks { + if uuid == "" { + continue + } + var formatConfig = formatConfigs[index] + for _, savedUUID := range formatConfig.XL.JBOD { + if savedUUID == uuid { + disksFound[uuid] = true + } + } + } + // Check if all disks are found. + for _, value := range disksFound { + if !value { + return errors.New("Some disks not found in JBOD.") + } + } + return nil } // checkJBODConsistency - validate xl jbod order if they are consistent. @@ -61,7 +274,7 @@ func checkJBODConsistency(formatConfigs []*formatConfigV1) error { } savedJBODStr := strings.Join(format.XL.JBOD, ".") if jbodStr != savedJBODStr { - return errors.New("Inconsistent disks.") + return errors.New("Inconsistent JBOD found.") } } return nil @@ -88,10 +301,8 @@ func reorderDisks(bootstrapDisks []StorageAPI, formatConfigs []*formatConfigV1) } // Pick the first JBOD list to verify the order and construct new set of disk slice. var newDisks = make([]StorageAPI, len(bootstrapDisks)) - var unclaimedJBODIndex = make(map[int]struct{}) for fIndex, format := range formatConfigs { if format == nil { - unclaimedJBODIndex[fIndex] = struct{}{} continue } jIndex := findIndex(format.XL.Disk, savedJBOD) @@ -100,24 +311,13 @@ func reorderDisks(bootstrapDisks []StorageAPI, formatConfigs []*formatConfigV1) } newDisks[jIndex] = bootstrapDisks[fIndex] } - // Save the unclaimed jbods as well. - for index, disk := range newDisks { - if disk == nil { - for fIndex := range unclaimedJBODIndex { - newDisks[index] = bootstrapDisks[fIndex] - delete(unclaimedJBODIndex, fIndex) - break - } - continue - } - } return newDisks, nil } // loadFormat - load format from disk. func loadFormat(disk StorageAPI) (format *formatConfigV1, err error) { - offset := int64(0) - r, err := disk.ReadFile(minioMetaBucket, formatConfigFile, offset) + var buffer []byte + buffer, err = readAll(disk, minioMetaBucket, formatConfigFile) if err != nil { // 'file not found' and 'volume not found' as // same. 'volume not found' usually means its a fresh disk. @@ -136,23 +336,139 @@ func loadFormat(disk StorageAPI) (format *formatConfigV1, err error) { } return nil, err } - decoder := json.NewDecoder(r) format = &formatConfigV1{} - err = decoder.Decode(&format) + err = json.Unmarshal(buffer, format) if err != nil { return nil, err } - if err = r.Close(); err != nil { - return nil, err - } return format, nil } -// loadFormatXL - load XL format.json. +// Heals any missing format.json on the drives. Returns error only for unexpected errors +// as regular errors can be ignored since there might be enough quorum to be operational. +func healFormatXL(bootstrapDisks []StorageAPI) error { + needHeal := make([]bool, len(bootstrapDisks)) // Slice indicating which drives needs healing. + + formatConfigs := make([]*formatConfigV1, len(bootstrapDisks)) + var referenceConfig *formatConfigV1 + successCount := 0 // Tracks if we have successfully loaded all `format.json` from all disks. + formatNotFoundCount := 0 // Tracks if we `format.json` is not found on all disks. + // Loads `format.json` from all disks. + for index, disk := range bootstrapDisks { + formatXL, err := loadFormat(disk) + if err != nil { + if err == errUnformattedDisk { + // format.json is missing, should be healed. + needHeal[index] = true + formatNotFoundCount++ + continue + } else if err == errDiskNotFound { // Is a valid case we + // can proceed without healing. + return nil + } + // Return error for unsupported errors. + return err + } // Success. + formatConfigs[index] = formatXL + successCount++ + } + // All `format.json` has been read successfully, previously completed. + if successCount == len(bootstrapDisks) { + // Return success. + return nil + } + // All disks are fresh, format.json will be written by initFormatXL() + if formatNotFoundCount == len(bootstrapDisks) { + return initFormatXL(bootstrapDisks) + } + // Validate format configs for consistency in JBOD and disks. + if err := checkFormatXL(formatConfigs); err != nil { + return err + } + + if referenceConfig == nil { + // This config will be used to update the drives missing format.json. + for _, formatConfig := range formatConfigs { + if formatConfig == nil { + continue + } + referenceConfig = formatConfig + break + } + } + + uuidUsage := make([]struct { + uuid string // Disk uuid + inUse bool // indicates if the uuid is used by + // any disk + }, len(bootstrapDisks)) + + // Returns any unused drive UUID. + getUnusedUUID := func() string { + for index := range uuidUsage { + if !uuidUsage[index].inUse { + uuidUsage[index].inUse = true + return uuidUsage[index].uuid + } + } + return "" + } + + // From reference config update UUID's not be in use. + for index, diskUUID := range referenceConfig.XL.JBOD { + uuidUsage[index].uuid = diskUUID + uuidUsage[index].inUse = false + } + + // For all config formats validate if they are in use and + // update the uuidUsage values. + for _, config := range formatConfigs { + if config == nil { + continue + } + for index := range uuidUsage { + if config.XL.Disk == uuidUsage[index].uuid { + uuidUsage[index].inUse = true + break + } + } + } + // This section heals the format.json and updates the fresh disks + // by apply a new UUID for all the fresh disks. + for index, heal := range needHeal { + if !heal { + continue + } + config := &formatConfigV1{} + *config = *referenceConfig + config.XL.Disk = getUnusedUUID() + if config.XL.Disk == "" { + // getUnusedUUID() should have + // returned an unused uuid, it + // is an unexpected error. + return errUnexpected + } + + formatBytes, err := json.Marshal(config) + if err != nil { + return err + } + // Fresh disk without format.json + _, _ = bootstrapDisks[index].AppendFile(minioMetaBucket, formatConfigFile, formatBytes) + // Ignore any error from AppendFile() as + // quorum might still be there to be operational. + } + return nil +} + +// loadFormatXL - loads XL `format.json` and returns back properly +// ordered storage slice based on `format.json`. func loadFormatXL(bootstrapDisks []StorageAPI) (disks []StorageAPI, err error) { var unformattedDisksFoundCnt = 0 var diskNotFoundCount = 0 formatConfigs := make([]*formatConfigV1, len(bootstrapDisks)) + + // Try to load `format.json` bootstrap disks. for index, disk := range bootstrapDisks { var formatXL *formatConfigV1 formatXL, err = loadFormat(disk) @@ -169,6 +485,7 @@ func loadFormatXL(bootstrapDisks []StorageAPI) (disks []StorageAPI, err error) { // Save valid formats. formatConfigs[index] = formatXL } + // If all disks indicate that 'format.json' is not available // return 'errUnformattedDisk'. if unformattedDisksFoundCnt == len(bootstrapDisks) { @@ -176,11 +493,12 @@ func loadFormatXL(bootstrapDisks []StorageAPI) (disks []StorageAPI, err error) { } else if diskNotFoundCount == len(bootstrapDisks) { return nil, errDiskNotFound } else if diskNotFoundCount > len(bootstrapDisks)-(len(bootstrapDisks)/2+1) { - return nil, errReadQuorum + return nil, errXLReadQuorum } else if unformattedDisksFoundCnt > len(bootstrapDisks)-(len(bootstrapDisks)/2+1) { - return nil, errReadQuorum + return nil, errXLReadQuorum } + // Validate the format configs read are correct. if err = checkFormatXL(formatConfigs); err != nil { return nil, err } @@ -208,14 +526,16 @@ func checkFormatXL(formatConfigs []*formatConfigV1) error { return fmt.Errorf("Number of disks %d did not match the backend format %d", len(formatConfigs), len(formatXL.XL.JBOD)) } } - return checkJBODConsistency(formatConfigs) + if err := checkJBODConsistency(formatConfigs); err != nil { + return err + } + return checkDisksConsistency(formatConfigs) } // initFormatXL - save XL format configuration on all disks. func initFormatXL(storageDisks []StorageAPI) (err error) { var ( jbod = make([]string, len(storageDisks)) - formatWriters = make([]io.WriteCloser, len(storageDisks)) formats = make([]*formatConfigV1, len(storageDisks)) saveFormatErrCnt = 0 ) @@ -227,19 +547,9 @@ func initFormatXL(storageDisks []StorageAPI) (err error) { if saveFormatErrCnt <= len(storageDisks)-(len(storageDisks)/2+3) { continue } - return errWriteQuorum + return errXLWriteQuorum } } - var w io.WriteCloser - w, err = disk.CreateFile(minioMetaBucket, formatConfigFile) - if err != nil { - saveFormatErrCnt++ - // Check for write quorum. - if saveFormatErrCnt <= len(storageDisks)-(len(storageDisks)/2+3) { - continue - } - return err - } var u *uuid.UUID u, err = uuid.New() if err != nil { @@ -250,7 +560,6 @@ func initFormatXL(storageDisks []StorageAPI) (err error) { } return err } - formatWriters[index] = w formats[index] = &formatConfigV1{ Version: "1", Format: "xl", @@ -261,24 +570,19 @@ func initFormatXL(storageDisks []StorageAPI) (err error) { } jbod[index] = formats[index].XL.Disk } - for index, w := range formatWriters { - if formats[index] == nil { - continue - } + for index, disk := range storageDisks { formats[index].XL.JBOD = jbod - encoder := json.NewEncoder(w) - err = encoder.Encode(&formats[index]) + formatBytes, err := json.Marshal(formats[index]) if err != nil { return err } - } - for _, w := range formatWriters { - if w == nil { - continue - } - if err = w.Close(); err != nil { + n, err := disk.AppendFile(minioMetaBucket, formatConfigFile, formatBytes) + if err != nil { return err } + if n != int64(len(formatBytes)) { + return errUnexpected + } } return nil } diff --git a/fs-objects-multipart.go b/fs-objects-multipart.go deleted file mode 100644 index 99cadfef8..000000000 --- a/fs-objects-multipart.go +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "fmt" - "io" - "path" -) - -// ListMultipartUploads - list multipart uploads. -func (fs fsObjects) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { - return listMultipartUploadsCommon(fs, bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) -} - -// NewMultipartUpload - initialize a new multipart upload, returns a unique id. -func (fs fsObjects) NewMultipartUpload(bucket, object string, meta map[string]string) (string, error) { - meta = make(map[string]string) // Reset the meta value, we are not going to save headers for fs. - return newMultipartUploadCommon(fs.storage, bucket, object, meta) -} - -// PutObjectPart - writes the multipart upload chunks. -func (fs fsObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) { - return putObjectPartCommon(fs.storage, bucket, object, uploadID, partID, size, data, md5Hex) -} - -func (fs fsObjects) ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { - return listObjectPartsCommon(fs.storage, bucket, object, uploadID, partNumberMarker, maxParts) -} - -func (fs fsObjects) CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (string, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return "", BucketNameInvalid{Bucket: bucket} - } - // Verify whether the bucket exists. - if !isBucketExist(fs.storage, bucket) { - return "", BucketNotFound{Bucket: bucket} - } - if !IsValidObjectName(object) { - return "", ObjectNameInvalid{ - Bucket: bucket, - Object: object, - } - } - if !isUploadIDExists(fs.storage, bucket, object, uploadID) { - return "", InvalidUploadID{UploadID: uploadID} - } - - // Calculate s3 compatible md5sum for complete multipart. - s3MD5, err := completeMultipartMD5(parts...) - if err != nil { - return "", err - } - - tempObj := path.Join(tmpMetaPrefix, bucket, object, uploadID, incompleteFile) - fileWriter, err := fs.storage.CreateFile(minioMetaBucket, tempObj) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Loop through all parts, validate them and then commit to disk. - for i, part := range parts { - // Construct part suffix. - partSuffix := fmt.Sprintf("%.5d.%s", part.PartNumber, part.ETag) - multipartPartFile := path.Join(mpartMetaPrefix, bucket, object, uploadID, partSuffix) - var fi FileInfo - fi, err = fs.storage.StatFile(minioMetaBucket, multipartPartFile) - if err != nil { - if err == errFileNotFound { - return "", InvalidPart{} - } - return "", err - } - // All parts except the last part has to be atleast 5MB. - if (i < len(parts)-1) && !isMinAllowedPartSize(fi.Size) { - return "", PartTooSmall{} - } - var fileReader io.ReadCloser - fileReader, err = fs.storage.ReadFile(minioMetaBucket, multipartPartFile, 0) - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - if err == errFileNotFound { - return "", InvalidPart{} - } - return "", err - } - _, err = io.Copy(fileWriter, fileReader) - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - return "", err - } - err = fileReader.Close() - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - return "", err - } - } - - err = fileWriter.Close() - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - return "", err - } - - // Rename the file back to original location, if not delete the - // temporary object. - err = fs.storage.RenameFile(minioMetaBucket, tempObj, bucket, object) - if err != nil { - if derr := fs.storage.DeleteFile(minioMetaBucket, tempObj); derr != nil { - return "", toObjectErr(derr, minioMetaBucket, tempObj) - } - return "", toObjectErr(err, bucket, object) - } - - // Cleanup all the parts if everything else has been safely committed. - if err = cleanupUploadedParts(fs.storage, bucket, object, uploadID); err != nil { - return "", err - } - - // Return md5sum. - return s3MD5, nil -} - -// AbortMultipartUpload - aborts a multipart upload. -func (fs fsObjects) AbortMultipartUpload(bucket, object, uploadID string) error { - return abortMultipartUploadCommon(fs.storage, bucket, object, uploadID) -} diff --git a/fs-objects.go b/fs-objects.go deleted file mode 100644 index d1f7b89f2..000000000 --- a/fs-objects.go +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "crypto/md5" - "encoding/hex" - "io" - "path/filepath" - "strings" - "sync" - - "github.com/minio/minio/pkg/mimedb" -) - -// fsObjects - Implements fs object layer. -type fsObjects struct { - storage StorageAPI - listObjectMap map[listParams][]*treeWalker - listObjectMapMutex *sync.Mutex -} - -// newFSObjects - initialize new fs object layer. -func newFSObjects(exportPath string) (ObjectLayer, error) { - var storage StorageAPI - var err error - if !strings.ContainsRune(exportPath, ':') || filepath.VolumeName(exportPath) != "" { - // Initialize filesystem storage API. - storage, err = newPosix(exportPath) - if err != nil { - return nil, err - } - } else { - // Initialize rpc client storage API. - storage, err = newRPCClient(exportPath) - if err != nil { - return nil, err - } - } - - // Initialize object layer - like creating minioMetaBucket, - // cleaning up tmp files etc. - initObjectLayer(storage) - - // Return successfully initialized object layer. - return fsObjects{ - storage: storage, - listObjectMap: make(map[listParams][]*treeWalker), - listObjectMapMutex: &sync.Mutex{}, - }, nil -} - -/// Bucket operations - -// MakeBucket - make a bucket. -func (fs fsObjects) MakeBucket(bucket string) error { - return makeBucket(fs.storage, bucket) -} - -// GetBucketInfo - get bucket info. -func (fs fsObjects) GetBucketInfo(bucket string) (BucketInfo, error) { - return getBucketInfo(fs.storage, bucket) -} - -// ListBuckets - list buckets. -func (fs fsObjects) ListBuckets() ([]BucketInfo, error) { - return listBuckets(fs.storage) -} - -// DeleteBucket - delete a bucket. -func (fs fsObjects) DeleteBucket(bucket string) error { - return deleteBucket(fs.storage, bucket) -} - -/// Object Operations - -// GetObject - get an object. -func (fs fsObjects) GetObject(bucket, object string, startOffset int64) (io.ReadCloser, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return nil, BucketNameInvalid{Bucket: bucket} - } - // Verify if object is valid. - if !IsValidObjectName(object) { - return nil, ObjectNameInvalid{Bucket: bucket, Object: object} - } - fileReader, err := fs.storage.ReadFile(bucket, object, startOffset) - if err != nil { - return nil, toObjectErr(err, bucket, object) - } - return fileReader, nil -} - -// GetObjectInfo - get object info. -func (fs fsObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return ObjectInfo{}, (BucketNameInvalid{Bucket: bucket}) - } - // Verify if object is valid. - if !IsValidObjectName(object) { - return ObjectInfo{}, (ObjectNameInvalid{Bucket: bucket, Object: object}) - } - fi, err := fs.storage.StatFile(bucket, object) - if err != nil { - return ObjectInfo{}, toObjectErr(err, bucket, object) - } - contentType := "application/octet-stream" - if objectExt := filepath.Ext(object); objectExt != "" { - content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))] - if ok { - contentType = content.ContentType - } - } - return ObjectInfo{ - Bucket: bucket, - Name: object, - ModTime: fi.ModTime, - Size: fi.Size, - IsDir: fi.Mode.IsDir(), - ContentType: contentType, - MD5Sum: "", // Read from metadata. - }, nil -} - -// PutObject - create an object. -func (fs fsObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return "", BucketNameInvalid{Bucket: bucket} - } - if !IsValidObjectName(object) { - return "", ObjectNameInvalid{ - Bucket: bucket, - Object: object, - } - } - - fileWriter, err := fs.storage.CreateFile(bucket, object) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Initialize md5 writer. - md5Writer := md5.New() - - // Instantiate a new multi writer. - multiWriter := io.MultiWriter(md5Writer, fileWriter) - - // Instantiate checksum hashers and create a multiwriter. - if size > 0 { - if _, err = io.CopyN(multiWriter, data, size); err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - return "", toObjectErr(err, bucket, object) - } - } else { - if _, err = io.Copy(multiWriter, data); err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - return "", toObjectErr(err, bucket, object) - } - } - - newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) - // md5Hex representation. - var md5Hex string - if len(metadata) != 0 { - md5Hex = metadata["md5Sum"] - } - if md5Hex != "" { - if newMD5Hex != md5Hex { - if err = safeCloseAndRemove(fileWriter); err != nil { - return "", err - } - return "", BadDigest{md5Hex, newMD5Hex} - } - } - err = fileWriter.Close() - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", clErr - } - return "", err - } - - // Return md5sum, successfully wrote object. - return newMD5Hex, nil -} - -func (fs fsObjects) DeleteObject(bucket, object string) error { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return BucketNameInvalid{Bucket: bucket} - } - if !IsValidObjectName(object) { - return ObjectNameInvalid{Bucket: bucket, Object: object} - } - if err := fs.storage.DeleteFile(bucket, object); err != nil { - return toObjectErr(err, bucket, object) - } - return nil -} - -// ListObjects - list all objects. -func (fs fsObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { - return listObjectsCommon(fs, bucket, prefix, marker, delimiter, maxKeys) -} diff --git a/fs-v1-metadata.go b/fs-v1-metadata.go new file mode 100644 index 000000000..94e7c3610 --- /dev/null +++ b/fs-v1-metadata.go @@ -0,0 +1,95 @@ +package main + +import ( + "encoding/json" + "path" + "sort" +) + +const ( + fsMetaJSONFile = "fs.json" +) + +// A fsMetaV1 represents a metadata header mapping keys to sets of values. +type fsMetaV1 struct { + Version string `json:"version"` + Format string `json:"format"` + Minio struct { + Release string `json:"release"` + } `json:"minio"` + Parts []objectPartInfo `json:"parts,omitempty"` +} + +// ObjectPartIndex - returns the index of matching object part number. +func (m fsMetaV1) ObjectPartIndex(partNumber int) (partIndex int) { + for i, part := range m.Parts { + if partNumber == part.Number { + partIndex = i + return partIndex + } + } + return -1 +} + +// AddObjectPart - add a new object part in order. +func (m *fsMetaV1) AddObjectPart(partNumber int, partName string, partETag string, partSize int64) { + partInfo := objectPartInfo{ + Number: partNumber, + Name: partName, + ETag: partETag, + Size: partSize, + } + + // Update part info if it already exists. + for i, part := range m.Parts { + if partNumber == part.Number { + m.Parts[i] = partInfo + return + } + } + + // Proceed to include new part info. + m.Parts = append(m.Parts, partInfo) + + // Parts in fsMeta should be in sorted order by part number. + sort.Sort(byObjectPartNumber(m.Parts)) +} + +// readFSMetadata - returns the object metadata `fs.json` content. +func (fs fsObjects) readFSMetadata(bucket, object string) (fsMeta fsMetaV1, err error) { + var buffer []byte + buffer, err = readAll(fs.storage, bucket, path.Join(object, fsMetaJSONFile)) + if err != nil { + return fsMetaV1{}, err + } + err = json.Unmarshal(buffer, &fsMeta) + if err != nil { + return fsMetaV1{}, err + } + return fsMeta, nil +} + +// newFSMetaV1 - initializes new fsMetaV1. +func newFSMetaV1() (fsMeta fsMetaV1) { + fsMeta = fsMetaV1{} + fsMeta.Version = "1" + fsMeta.Format = "fs" + fsMeta.Minio.Release = minioReleaseTag + return fsMeta +} + +// writeFSMetadata - writes `fs.json` metadata. +func (fs fsObjects) writeFSMetadata(bucket, prefix string, fsMeta fsMetaV1) error { + metadataBytes, err := json.Marshal(fsMeta) + if err != nil { + return err + } + n, err := fs.storage.AppendFile(bucket, path.Join(prefix, fsMetaJSONFile), metadataBytes) + if err != nil { + return err + } + if n != int64(len(metadataBytes)) { + return errUnexpected + } + return nil +} diff --git a/fs-v1-multipart-common.go b/fs-v1-multipart-common.go new file mode 100644 index 000000000..96ecea0bf --- /dev/null +++ b/fs-v1-multipart-common.go @@ -0,0 +1,70 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "path" + "strings" +) + +// Returns if the prefix is a multipart upload. +func (fs fsObjects) isMultipartUpload(bucket, prefix string) bool { + _, err := fs.storage.StatFile(bucket, pathJoin(prefix, uploadsJSONFile)) + return err == nil +} + +// listUploadsInfo - list all uploads info. +func (fs fsObjects) listUploadsInfo(prefixPath string) (uploads []uploadInfo, err error) { + splitPrefixes := strings.SplitN(prefixPath, "/", 3) + uploadIDs, err := readUploadsJSON(splitPrefixes[1], splitPrefixes[2], fs.storage) + if err != nil { + if err == errFileNotFound { + return []uploadInfo{}, nil + } + return nil, err + } + uploads = uploadIDs.Uploads + return uploads, nil +} + +// Checks whether bucket exists. +func (fs fsObjects) isBucketExist(bucket string) bool { + // Check whether bucket exists. + _, err := fs.storage.StatVol(bucket) + if err != nil { + if err == errVolumeNotFound { + return false + } + errorIf(err, "Stat failed on bucket "+bucket+".") + return false + } + return true +} + +// isUploadIDExists - verify if a given uploadID exists and is valid. +func (fs fsObjects) isUploadIDExists(bucket, object, uploadID string) bool { + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + _, err := fs.storage.StatFile(minioMetaBucket, path.Join(uploadIDPath, fsMetaJSONFile)) + if err != nil { + if err == errFileNotFound { + return false + } + errorIf(err, "Unable to access upload id"+uploadIDPath) + return false + } + return true +} diff --git a/fs-v1-multipart.go b/fs-v1-multipart.go new file mode 100644 index 000000000..e5f13e8a3 --- /dev/null +++ b/fs-v1-multipart.go @@ -0,0 +1,648 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "path" + "strconv" + "strings" + "time" + + "github.com/skyrings/skyring-common/tools/uuid" +) + +// listMultipartUploads - lists all multipart uploads. +func (fs fsObjects) listMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { + result := ListMultipartsInfo{} + recursive := true + if delimiter == slashSeparator { + recursive = false + } + + result.IsTruncated = true + result.MaxUploads = maxUploads + result.KeyMarker = keyMarker + result.Prefix = prefix + result.Delimiter = delimiter + + // Not using path.Join() as it strips off the trailing '/'. + multipartPrefixPath := pathJoin(mpartMetaPrefix, bucket, prefix) + if prefix == "" { + // Should have a trailing "/" if prefix is "" + // For ex. multipartPrefixPath should be "multipart/bucket/" if prefix is "" + multipartPrefixPath += slashSeparator + } + multipartMarkerPath := "" + if keyMarker != "" { + multipartMarkerPath = pathJoin(mpartMetaPrefix, bucket, keyMarker) + } + var uploads []uploadMetadata + var err error + var eof bool + if uploadIDMarker != "" { + nsMutex.RLock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, keyMarker)) + uploads, _, err = listMultipartUploadIDs(bucket, keyMarker, uploadIDMarker, maxUploads, fs.storage) + nsMutex.RUnlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, keyMarker)) + if err != nil { + return ListMultipartsInfo{}, err + } + maxUploads = maxUploads - len(uploads) + } + if maxUploads > 0 { + walker := fs.lookupTreeWalk(listParams{minioMetaBucket, recursive, multipartMarkerPath, multipartPrefixPath}) + if walker == nil { + walker = fs.startTreeWalk(minioMetaBucket, multipartPrefixPath, multipartMarkerPath, recursive, func(bucket, object string) bool { + return fs.isMultipartUpload(bucket, object) + }) + } + for maxUploads > 0 { + walkResult, ok := <-walker.ch + if !ok { + // Closed channel. + eof = true + break + } + // For any walk error return right away. + if walkResult.err != nil { + // File not found or Disk not found is a valid case. + if walkResult.err == errFileNotFound || walkResult.err == errDiskNotFound { + eof = true + break + } + return ListMultipartsInfo{}, err + } + entry := strings.TrimPrefix(walkResult.entry, retainSlash(pathJoin(mpartMetaPrefix, bucket))) + if strings.HasSuffix(walkResult.entry, slashSeparator) { + uploads = append(uploads, uploadMetadata{ + Object: entry, + }) + maxUploads-- + if maxUploads == 0 { + if walkResult.end { + eof = true + break + } + } + continue + } + var tmpUploads []uploadMetadata + var end bool + uploadIDMarker = "" + nsMutex.RLock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, entry)) + tmpUploads, end, err = listMultipartUploadIDs(bucket, entry, uploadIDMarker, maxUploads, fs.storage) + nsMutex.RUnlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, entry)) + if err != nil { + return ListMultipartsInfo{}, err + } + uploads = append(uploads, tmpUploads...) + maxUploads -= len(tmpUploads) + if walkResult.end && end { + eof = true + break + } + } + } + // Loop through all the received uploads fill in the multiparts result. + for _, upload := range uploads { + var objectName string + var uploadID string + if strings.HasSuffix(upload.Object, slashSeparator) { + // All directory entries are common prefixes. + uploadID = "" // Upload ids are empty for CommonPrefixes. + objectName = upload.Object + result.CommonPrefixes = append(result.CommonPrefixes, objectName) + } else { + uploadID = upload.UploadID + objectName = upload.Object + result.Uploads = append(result.Uploads, upload) + } + result.NextKeyMarker = objectName + result.NextUploadIDMarker = uploadID + } + result.IsTruncated = !eof + if !result.IsTruncated { + result.NextKeyMarker = "" + result.NextUploadIDMarker = "" + } + return result, nil +} + +// ListMultipartUploads - lists all the pending multipart uploads on a +// bucket. Additionally takes 'prefix, keyMarker, uploadIDmarker and a +// delimiter' which allows us to list uploads match a particular +// prefix or lexically starting from 'keyMarker' or delimiting the +// output to get a directory like listing. +// +// Implements S3 compatible ListMultipartUploads API. The resulting +// ListMultipartsInfo structure is unmarshalled directly into XML and +// replied back to the client. +func (fs fsObjects) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { + // Validate input arguments. + if !IsValidBucketName(bucket) { + return ListMultipartsInfo{}, BucketNameInvalid{Bucket: bucket} + } + if !fs.isBucketExist(bucket) { + return ListMultipartsInfo{}, BucketNotFound{Bucket: bucket} + } + if !IsValidObjectPrefix(prefix) { + return ListMultipartsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} + } + // Verify if delimiter is anything other than '/', which we do not support. + if delimiter != "" && delimiter != slashSeparator { + return ListMultipartsInfo{}, UnsupportedDelimiter{ + Delimiter: delimiter, + } + } + // Verify if marker has prefix. + if keyMarker != "" && !strings.HasPrefix(keyMarker, prefix) { + return ListMultipartsInfo{}, InvalidMarkerPrefixCombination{ + Marker: keyMarker, + Prefix: prefix, + } + } + if uploadIDMarker != "" { + if strings.HasSuffix(keyMarker, slashSeparator) { + return ListMultipartsInfo{}, InvalidUploadIDKeyCombination{ + UploadIDMarker: uploadIDMarker, + KeyMarker: keyMarker, + } + } + id, err := uuid.Parse(uploadIDMarker) + if err != nil { + return ListMultipartsInfo{}, err + } + if id.IsZero() { + return ListMultipartsInfo{}, MalformedUploadID{ + UploadID: uploadIDMarker, + } + } + } + return fs.listMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) +} + +// newMultipartUpload - wrapper for initializing a new multipart +// request, returns back a unique upload id. +// +// Internally this function creates 'uploads.json' associated for the +// incoming object at '.minio/multipart/bucket/object/uploads.json' on +// all the disks. `uploads.json` carries metadata regarding on going +// multipart operation on the object. +func (fs fsObjects) newMultipartUpload(bucket string, object string, meta map[string]string) (uploadID string, err error) { + // No metadata is set, allocate a new one. + if meta == nil { + meta = make(map[string]string) + } + + // Initialize `fs.json` values. + fsMeta := newFSMetaV1() + + // This lock needs to be held for any changes to the directory contents of ".minio/multipart/object/" + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + + uploadID = getUUID() + initiated := time.Now().UTC() + // Create 'uploads.json' + if err = writeUploadJSON(bucket, object, uploadID, initiated, fs.storage); err != nil { + return "", err + } + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + tempUploadIDPath := path.Join(tmpMetaPrefix, uploadID) + if err = fs.writeFSMetadata(minioMetaBucket, tempUploadIDPath, fsMeta); err != nil { + return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) + } + err = fs.storage.RenameFile(minioMetaBucket, path.Join(tempUploadIDPath, fsMetaJSONFile), minioMetaBucket, path.Join(uploadIDPath, fsMetaJSONFile)) + if err != nil { + if dErr := fs.storage.DeleteFile(minioMetaBucket, path.Join(tempUploadIDPath, fsMetaJSONFile)); dErr != nil { + return "", toObjectErr(dErr, minioMetaBucket, tempUploadIDPath) + } + return "", toObjectErr(err, minioMetaBucket, uploadIDPath) + } + // Return success. + return uploadID, nil +} + +// NewMultipartUpload - initialize a new multipart upload, returns a +// unique id. The unique id returned here is of UUID form, for each +// subsequent request each UUID is unique. +// +// Implements S3 compatible initiate multipart API. +func (fs fsObjects) NewMultipartUpload(bucket, object string, meta map[string]string) (string, error) { + meta = make(map[string]string) // Reset the meta value, we are not going to save headers for fs. + // Verify if bucket name is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !fs.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + // Verify if object name is valid. + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{Bucket: bucket, Object: object} + } + return fs.newMultipartUpload(bucket, object, meta) +} + +// PutObjectPart - reads incoming data until EOF for the part file on +// an ongoing multipart transaction. Internally incoming data is +// written to '.minio/tmp' location and safely renamed to +// '.minio/multipart' for reach parts. +func (fs fsObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !fs.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{Bucket: bucket, Object: object} + } + + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + + nsMutex.RLock(minioMetaBucket, uploadIDPath) + // Just check if the uploadID exists to avoid copy if it doesn't. + uploadIDExists := fs.isUploadIDExists(bucket, object, uploadID) + nsMutex.RUnlock(minioMetaBucket, uploadIDPath) + if !uploadIDExists { + return "", InvalidUploadID{UploadID: uploadID} + } + + // Hold write lock on the part so that there is no parallel upload on the part. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID, strconv.Itoa(partID))) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID, strconv.Itoa(partID))) + + partSuffix := fmt.Sprintf("object%d", partID) + tmpPartPath := path.Join(tmpMetaPrefix, uploadID, partSuffix) + + // Initialize md5 writer. + md5Writer := md5.New() + + var buf = make([]byte, blockSizeV1) + for { + n, err := io.ReadFull(data, buf) + if err == io.EOF { + break + } + if err != nil && err != io.ErrUnexpectedEOF { + return "", toObjectErr(err, bucket, object) + } + // Update md5 writer. + md5Writer.Write(buf[:n]) + m, err := fs.storage.AppendFile(minioMetaBucket, tmpPartPath, buf[:n]) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + if m != int64(len(buf[:n])) { + return "", toObjectErr(errUnexpected, bucket, object) + } + } + + newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) + if md5Hex != "" { + if newMD5Hex != md5Hex { + return "", BadDigest{md5Hex, newMD5Hex} + } + } + + // Hold write lock as we are updating fs.json + nsMutex.Lock(minioMetaBucket, uploadIDPath) + defer nsMutex.Unlock(minioMetaBucket, uploadIDPath) + + // Just check if the uploadID exists to avoid copy if it doesn't. + if !fs.isUploadIDExists(bucket, object, uploadID) { + return "", InvalidUploadID{UploadID: uploadID} + } + + fsMeta, err := fs.readFSMetadata(minioMetaBucket, uploadIDPath) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, uploadIDPath) + } + fsMeta.AddObjectPart(partID, partSuffix, newMD5Hex, size) + + partPath := path.Join(mpartMetaPrefix, bucket, object, uploadID, partSuffix) + err = fs.storage.RenameFile(minioMetaBucket, tmpPartPath, minioMetaBucket, partPath) + if err != nil { + if dErr := fs.storage.DeleteFile(minioMetaBucket, tmpPartPath); dErr != nil { + return "", toObjectErr(dErr, minioMetaBucket, tmpPartPath) + } + return "", toObjectErr(err, minioMetaBucket, partPath) + } + uploadIDPath = path.Join(mpartMetaPrefix, bucket, object, uploadID) + tempUploadIDPath := path.Join(tmpMetaPrefix, uploadID) + if err = fs.writeFSMetadata(minioMetaBucket, tempUploadIDPath, fsMeta); err != nil { + return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) + } + err = fs.storage.RenameFile(minioMetaBucket, path.Join(tempUploadIDPath, fsMetaJSONFile), minioMetaBucket, path.Join(uploadIDPath, fsMetaJSONFile)) + if err != nil { + if dErr := fs.storage.DeleteFile(minioMetaBucket, path.Join(tempUploadIDPath, fsMetaJSONFile)); dErr != nil { + return "", toObjectErr(dErr, minioMetaBucket, tempUploadIDPath) + } + return "", toObjectErr(err, minioMetaBucket, uploadIDPath) + } + return newMD5Hex, nil +} + +// listObjectParts - wrapper scanning through +// '.minio/multipart/bucket/object/UPLOADID'. Lists all the parts +// saved inside '.minio/multipart/bucket/object/UPLOADID'. +func (fs fsObjects) listObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { + result := ListPartsInfo{} + + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + fsMeta, err := fs.readFSMetadata(minioMetaBucket, uploadIDPath) + if err != nil { + return ListPartsInfo{}, toObjectErr(err, minioMetaBucket, uploadIDPath) + } + // Only parts with higher part numbers will be listed. + partIdx := fsMeta.ObjectPartIndex(partNumberMarker) + parts := fsMeta.Parts + if partIdx != -1 { + parts = fsMeta.Parts[partIdx+1:] + } + count := maxParts + for _, part := range parts { + var fi FileInfo + partNamePath := path.Join(mpartMetaPrefix, bucket, object, uploadID, part.Name) + fi, err = fs.storage.StatFile(minioMetaBucket, partNamePath) + if err != nil { + return ListPartsInfo{}, toObjectErr(err, minioMetaBucket, partNamePath) + } + result.Parts = append(result.Parts, partInfo{ + PartNumber: part.Number, + ETag: part.ETag, + LastModified: fi.ModTime, + Size: fi.Size, + }) + count-- + if count == 0 { + break + } + } + // If listed entries are more than maxParts, we set IsTruncated as true. + if len(parts) > len(result.Parts) { + result.IsTruncated = true + // Make sure to fill next part number marker if IsTruncated is + // true for subsequent listing. + nextPartNumberMarker := result.Parts[len(result.Parts)-1].PartNumber + result.NextPartNumberMarker = nextPartNumberMarker + } + result.Bucket = bucket + result.Object = object + result.UploadID = uploadID + result.MaxParts = maxParts + return result, nil +} + +// ListObjectParts - lists all previously uploaded parts for a given +// object and uploadID. Takes additional input of part-number-marker +// to indicate where the listing should begin from. +// +// Implements S3 compatible ListObjectParts API. The resulting +// ListPartsInfo structure is unmarshalled directly into XML and +// replied back to the client. +func (fs fsObjects) ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ListPartsInfo{}, BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !fs.isBucketExist(bucket) { + return ListPartsInfo{}, BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return ListPartsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: object} + } + // Hold lock so that there is no competing abort-multipart-upload or complete-multipart-upload. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + + if !fs.isUploadIDExists(bucket, object, uploadID) { + return ListPartsInfo{}, InvalidUploadID{UploadID: uploadID} + } + return fs.listObjectParts(bucket, object, uploadID, partNumberMarker, maxParts) +} + +// CompleteMultipartUpload - completes an ongoing multipart +// transaction after receiving all the parts indicated by the client. +// Returns an md5sum calculated by concatenating all the individual +// md5sums of all the parts. +// +// Implements S3 compatible Complete multipart API. +func (fs fsObjects) CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (string, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !fs.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{ + Bucket: bucket, + Object: object, + } + } + + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + // Hold lock so that + // 1) no one aborts this multipart upload + // 2) no one does a parallel complete-multipart-upload on this + // multipart upload + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + + if !fs.isUploadIDExists(bucket, object, uploadID) { + return "", InvalidUploadID{UploadID: uploadID} + } + + // Read saved fs metadata for ongoing multipart. + fsMeta, err := fs.readFSMetadata(minioMetaBucket, uploadIDPath) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, uploadIDPath) + } + + // Calculate s3 compatible md5sum for complete multipart. + s3MD5, err := completeMultipartMD5(parts...) + if err != nil { + return "", err + } + + tempObj := path.Join(tmpMetaPrefix, uploadID, "object1") + var buffer = make([]byte, blockSizeV1) + + // Loop through all parts, validate them and then commit to disk. + for i, part := range parts { + partIdx := fsMeta.ObjectPartIndex(part.PartNumber) + if partIdx == -1 { + return "", InvalidPart{} + } + if fsMeta.Parts[partIdx].ETag != part.ETag { + return "", BadDigest{} + } + // All parts except the last part has to be atleast 5MB. + if (i < len(parts)-1) && !isMinAllowedPartSize(fsMeta.Parts[partIdx].Size) { + return "", PartTooSmall{} + } + // Construct part suffix. + partSuffix := fmt.Sprintf("object%d", part.PartNumber) + multipartPartFile := path.Join(mpartMetaPrefix, bucket, object, uploadID, partSuffix) + offset := int64(0) + totalLeft := fsMeta.Parts[partIdx].Size + for totalLeft > 0 { + var n int64 + n, err = fs.storage.ReadFile(minioMetaBucket, multipartPartFile, offset, buffer) + if err != nil { + if err == errFileNotFound { + return "", InvalidPart{} + } + return "", toObjectErr(err, minioMetaBucket, multipartPartFile) + } + n, err = fs.storage.AppendFile(minioMetaBucket, tempObj, buffer[:n]) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, tempObj) + } + offset += n + totalLeft -= n + } + } + + // Rename the file back to original location, if not delete the temporary object. + err = fs.storage.RenameFile(minioMetaBucket, tempObj, bucket, object) + if err != nil { + if dErr := fs.storage.DeleteFile(minioMetaBucket, tempObj); dErr != nil { + return "", toObjectErr(dErr, minioMetaBucket, tempObj) + } + return "", toObjectErr(err, bucket, object) + } + + // Cleanup all the parts if everything else has been safely committed. + if err = cleanupUploadedParts(bucket, object, uploadID, fs.storage); err != nil { + return "", err + } + + // Hold the lock so that two parallel complete-multipart-uploads do not + // leave a stale uploads.json behind. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + + // Validate if there are other incomplete upload-id's present for + // the object, if yes do not attempt to delete 'uploads.json'. + uploadsJSON, err := readUploadsJSON(bucket, object, fs.storage) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, object) + } + // If we have successfully read `uploads.json`, then we proceed to + // purge or update `uploads.json`. + uploadIDIdx := uploadsJSON.Index(uploadID) + if uploadIDIdx != -1 { + uploadsJSON.Uploads = append(uploadsJSON.Uploads[:uploadIDIdx], uploadsJSON.Uploads[uploadIDIdx+1:]...) + } + if len(uploadsJSON.Uploads) > 0 { + if err = updateUploadsJSON(bucket, object, uploadsJSON, fs.storage); err != nil { + return "", toObjectErr(err, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + } + // Return success. + return s3MD5, nil + } + + if err = fs.storage.DeleteFile(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile)); err != nil { + return "", toObjectErr(err, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + } + + // Return md5sum. + return s3MD5, nil +} + +// abortMultipartUpload - wrapper for purging an ongoing multipart +// transaction, deletes uploadID entry from `uploads.json` and purges +// the directory at '.minio/multipart/bucket/object/uploadID' holding +// all the upload parts. +func (fs fsObjects) abortMultipartUpload(bucket, object, uploadID string) error { + // Cleanup all uploaded parts. + if err := cleanupUploadedParts(bucket, object, uploadID, fs.storage); err != nil { + return err + } + + // Validate if there are other incomplete upload-id's present for + // the object, if yes do not attempt to delete 'uploads.json'. + uploadsJSON, err := readUploadsJSON(bucket, object, fs.storage) + if err == nil { + uploadIDIdx := uploadsJSON.Index(uploadID) + if uploadIDIdx != -1 { + uploadsJSON.Uploads = append(uploadsJSON.Uploads[:uploadIDIdx], uploadsJSON.Uploads[uploadIDIdx+1:]...) + } + // There are pending uploads for the same object, preserve + // them update 'uploads.json' in-place. + if len(uploadsJSON.Uploads) > 0 { + err = updateUploadsJSON(bucket, object, uploadsJSON, fs.storage) + if err != nil { + return toObjectErr(err, bucket, object) + } + return nil + } + } // No more pending uploads for the object, we purge the entire + // entry at '.minio/multipart/bucket/object'. + if err = fs.storage.DeleteFile(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile)); err != nil { + return toObjectErr(err, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + } + return nil +} + +// AbortMultipartUpload - aborts an ongoing multipart operation +// signified by the input uploadID. This is an atomic operation +// doesn't require clients to initiate multiple such requests. +// +// All parts are purged from all disks and reference to the uploadID +// would be removed from the system, rollback is not possible on this +// operation. +// +// Implements S3 compatible Abort multipart API, slight difference is +// that this is an atomic idempotent operation. Subsequent calls have +// no affect and further requests to the same uploadID would not be +// honored. +func (fs fsObjects) AbortMultipartUpload(bucket, object, uploadID string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + if !fs.isBucketExist(bucket) { + return BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return ObjectNameInvalid{Bucket: bucket, Object: object} + } + + // Hold lock so that there is no competing complete-multipart-upload or put-object-part. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + + if !fs.isUploadIDExists(bucket, object, uploadID) { + return InvalidUploadID{UploadID: uploadID} + } + + err := fs.abortMultipartUpload(bucket, object, uploadID) + return err +} diff --git a/fs-v1.go b/fs-v1.go new file mode 100644 index 000000000..4a9f8674d --- /dev/null +++ b/fs-v1.go @@ -0,0 +1,435 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "crypto/md5" + "encoding/hex" + "io" + "os" + "path" + "path/filepath" + "sort" + "strings" + "sync" + + "github.com/minio/minio/pkg/disk" + "github.com/minio/minio/pkg/mimedb" +) + +// fsObjects - Implements fs object layer. +type fsObjects struct { + storage StorageAPI + physicalDisk string + listObjectMap map[listParams][]*treeWalkerFS + listObjectMapMutex *sync.Mutex +} + +// newFSObjects - initialize new fs object layer. +func newFSObjects(disk string) (ObjectLayer, error) { + storage, err := newStorageAPI(disk) + if err != nil { + return nil, err + } + + // Runs house keeping code, like creating minioMetaBucket, cleaning up tmp files etc. + fsHouseKeeping(storage) + + // Return successfully initialized object layer. + return fsObjects{ + storage: storage, + physicalDisk: disk, + listObjectMap: make(map[listParams][]*treeWalkerFS), + listObjectMapMutex: &sync.Mutex{}, + }, nil +} + +// StorageInfo - returns underlying storage statistics. +func (fs fsObjects) StorageInfo() StorageInfo { + info, err := disk.GetInfo(fs.physicalDisk) + fatalIf(err, "Unable to get disk info "+fs.physicalDisk) + return StorageInfo{ + Total: info.Total, + Free: info.Free, + } +} + +/// Bucket operations + +// MakeBucket - make a bucket. +func (fs fsObjects) MakeBucket(bucket string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + if err := fs.storage.MakeVol(bucket); err != nil { + return toObjectErr(err, bucket) + } + return nil +} + +// GetBucketInfo - get bucket info. +func (fs fsObjects) GetBucketInfo(bucket string) (BucketInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketInfo{}, BucketNameInvalid{Bucket: bucket} + } + vi, err := fs.storage.StatVol(bucket) + if err != nil { + return BucketInfo{}, toObjectErr(err, bucket) + } + return BucketInfo{ + Name: bucket, + Created: vi.Created, + }, nil +} + +// ListBuckets - list buckets. +func (fs fsObjects) ListBuckets() ([]BucketInfo, error) { + var bucketInfos []BucketInfo + vols, err := fs.storage.ListVols() + if err != nil { + return nil, toObjectErr(err) + } + for _, vol := range vols { + // StorageAPI can send volume names which are incompatible + // with buckets, handle it and skip them. + if !IsValidBucketName(vol.Name) { + continue + } + bucketInfos = append(bucketInfos, BucketInfo{ + Name: vol.Name, + Created: vol.Created, + }) + } + sort.Sort(byBucketName(bucketInfos)) + return bucketInfos, nil +} + +// DeleteBucket - delete a bucket. +func (fs fsObjects) DeleteBucket(bucket string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + if err := fs.storage.DeleteVol(bucket); err != nil { + return toObjectErr(err, bucket) + } + return nil +} + +/// Object Operations + +// GetObject - get an object. +func (fs fsObjects) GetObject(bucket, object string, startOffset int64, length int64, writer io.Writer) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + // Verify if object is valid. + if !IsValidObjectName(object) { + return ObjectNameInvalid{Bucket: bucket, Object: object} + } + var totalLeft = length + for totalLeft > 0 { + // Figure out the right blockSize as it was encoded before. + var curBlockSize int64 + if blockSizeV1 < totalLeft { + curBlockSize = blockSizeV1 + } else { + curBlockSize = totalLeft + } + buf := make([]byte, curBlockSize) + n, err := fs.storage.ReadFile(bucket, object, startOffset, buf) + if err != nil { + return toObjectErr(err, bucket, object) + } + _, err = writer.Write(buf[:n]) + if err != nil { + return toObjectErr(err, bucket, object) + } + totalLeft -= n + startOffset += n + } + return nil +} + +// GetObjectInfo - get object info. +func (fs fsObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ObjectInfo{}, (BucketNameInvalid{Bucket: bucket}) + } + // Verify if object is valid. + if !IsValidObjectName(object) { + return ObjectInfo{}, (ObjectNameInvalid{Bucket: bucket, Object: object}) + } + fi, err := fs.storage.StatFile(bucket, object) + if err != nil { + return ObjectInfo{}, toObjectErr(err, bucket, object) + } + contentType := "application/octet-stream" + if objectExt := filepath.Ext(object); objectExt != "" { + content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))] + if ok { + contentType = content.ContentType + } + } + return ObjectInfo{ + Bucket: bucket, + Name: object, + ModTime: fi.ModTime, + Size: fi.Size, + IsDir: fi.Mode.IsDir(), + ContentType: contentType, + MD5Sum: "", // Read from metadata. + }, nil +} + +// PutObject - create an object. +func (fs fsObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{ + Bucket: bucket, + Object: object, + } + } + + uniqueID := getUUID() + + // Temporary object. + tempObj := path.Join(tmpMetaPrefix, uniqueID) + + // Initialize md5 writer. + md5Writer := md5.New() + + if size == 0 { + // For size 0 we write a 0byte file. + _, err := fs.storage.AppendFile(minioMetaBucket, tempObj, []byte("")) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + } else { + // Allocate buffer. + buf := make([]byte, blockSizeV1) + for { + n, rErr := data.Read(buf) + if rErr == io.EOF { + break + } + if rErr != nil { + return "", toObjectErr(rErr, bucket, object) + } + // Update md5 writer. + md5Writer.Write(buf[:n]) + m, wErr := fs.storage.AppendFile(minioMetaBucket, tempObj, buf[:n]) + if wErr != nil { + return "", toObjectErr(wErr, bucket, object) + } + if m != int64(len(buf[:n])) { + return "", toObjectErr(errUnexpected, bucket, object) + } + } + } + + newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) + // md5Hex representation. + var md5Hex string + if len(metadata) != 0 { + md5Hex = metadata["md5Sum"] + } + if md5Hex != "" { + if newMD5Hex != md5Hex { + return "", BadDigest{md5Hex, newMD5Hex} + } + } + + err := fs.storage.RenameFile(minioMetaBucket, tempObj, bucket, object) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Return md5sum, successfully wrote object. + return newMD5Hex, nil +} + +func (fs fsObjects) DeleteObject(bucket, object string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + if !IsValidObjectName(object) { + return ObjectNameInvalid{Bucket: bucket, Object: object} + } + if err := fs.storage.DeleteFile(bucket, object); err != nil { + return toObjectErr(err, bucket, object) + } + return nil +} + +// Checks whether bucket exists. +func isBucketExist(storage StorageAPI, bucketName string) bool { + // Check whether bucket exists. + _, err := storage.StatVol(bucketName) + if err != nil { + if err == errVolumeNotFound { + return false + } + errorIf(err, "Stat failed on bucket "+bucketName+".") + return false + } + return true +} + +func (fs fsObjects) listObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { + // Convert entry to FileInfo + entryToFileInfo := func(entry string) (fileInfo FileInfo, err error) { + if strings.HasSuffix(entry, slashSeparator) { + // Object name needs to be full path. + fileInfo.Name = entry + fileInfo.Mode = os.ModeDir + return + } + if fileInfo, err = fs.storage.StatFile(bucket, entry); err != nil { + return + } + // Object name needs to be full path. + fileInfo.Name = entry + return + } + + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ListObjectsInfo{}, BucketNameInvalid{Bucket: bucket} + } + // Verify if bucket exists. + if !isBucketExist(fs.storage, bucket) { + return ListObjectsInfo{}, BucketNotFound{Bucket: bucket} + } + if !IsValidObjectPrefix(prefix) { + return ListObjectsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} + } + // Verify if delimiter is anything other than '/', which we do not support. + if delimiter != "" && delimiter != slashSeparator { + return ListObjectsInfo{}, UnsupportedDelimiter{ + Delimiter: delimiter, + } + } + // Verify if marker has prefix. + if marker != "" { + if !strings.HasPrefix(marker, prefix) { + return ListObjectsInfo{}, InvalidMarkerPrefixCombination{ + Marker: marker, + Prefix: prefix, + } + } + } + + // With max keys of zero we have reached eof, return right here. + if maxKeys == 0 { + return ListObjectsInfo{}, nil + } + + // For delimiter and prefix as '/' we do not list anything at all + // since according to s3 spec we stop at the 'delimiter' + // along // with the prefix. On a flat namespace with 'prefix' + // as '/' we don't have any entries, since all the keys are + // of form 'keyName/...' + if delimiter == slashSeparator && prefix == slashSeparator { + return ListObjectsInfo{}, nil + } + + // Over flowing count - reset to maxObjectList. + if maxKeys < 0 || maxKeys > maxObjectList { + maxKeys = maxObjectList + } + + // Default is recursive, if delimiter is set then list non recursive. + recursive := true + if delimiter == slashSeparator { + recursive = false + } + + walker := fs.lookupTreeWalk(listParams{bucket, recursive, marker, prefix}) + if walker == nil { + walker = fs.startTreeWalk(bucket, prefix, marker, recursive, func(bucket, object string) bool { + return !strings.HasSuffix(object, slashSeparator) + }) + } + var fileInfos []FileInfo + var eof bool + var nextMarker string + for i := 0; i < maxKeys; { + walkResult, ok := <-walker.ch + if !ok { + // Closed channel. + eof = true + break + } + // For any walk error return right away. + if walkResult.err != nil { + // File not found is a valid case. + if walkResult.err == errFileNotFound { + return ListObjectsInfo{}, nil + } + return ListObjectsInfo{}, toObjectErr(walkResult.err, bucket, prefix) + } + fileInfo, err := entryToFileInfo(walkResult.entry) + if err != nil { + return ListObjectsInfo{}, nil + } + nextMarker = fileInfo.Name + fileInfos = append(fileInfos, fileInfo) + if walkResult.end { + eof = true + break + } + i++ + } + params := listParams{bucket, recursive, nextMarker, prefix} + if !eof { + fs.saveTreeWalk(params, walker) + } + + result := ListObjectsInfo{IsTruncated: !eof} + for _, fileInfo := range fileInfos { + // With delimiter set we fill in NextMarker and Prefixes. + if delimiter == slashSeparator { + result.NextMarker = fileInfo.Name + if fileInfo.Mode.IsDir() { + result.Prefixes = append(result.Prefixes, fileInfo.Name) + continue + } + } + result.Objects = append(result.Objects, ObjectInfo{ + Name: fileInfo.Name, + ModTime: fileInfo.ModTime, + Size: fileInfo.Size, + IsDir: false, + }) + } + return result, nil +} + +// ListObjects - list all objects. +func (fs fsObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { + return fs.listObjects(bucket, prefix, marker, delimiter, maxKeys) +} diff --git a/httprange.go b/httprange.go index b00a37771..4bb23806e 100644 --- a/httprange.go +++ b/httprange.go @@ -28,13 +28,10 @@ const ( ) // InvalidRange - invalid range -type InvalidRange struct { - Start int64 - Length int64 -} +type InvalidRange struct{} func (e InvalidRange) Error() string { - return fmt.Sprintf("Invalid range start:%d length:%d", e.Start, e.Length) + return "The requested range is not satisfiable" } // HttpRange specifies the byte range to be sent to the client. diff --git a/main.go b/main.go index 56e61245e..836895a10 100644 --- a/main.go +++ b/main.go @@ -118,8 +118,8 @@ func registerApp() *cli.App { app := cli.NewApp() app.Name = "Minio" app.Author = "Minio.io" - app.Usage = "Distributed Object Storage Server for Micro Services." - app.Description = `Micro services environment provisions one Minio server per application instance. Scalability is achieved through large number of smaller personalized instances. This version of the Minio binary is built using Filesystem storage backend for magnetic and solid state disks.` + app.Usage = "Cloud Storage Server." + app.Description = `Minio is an Amazon S3 compatible object storage server. Use it to store photos, videos, VMs, containers, log files, or any blob of data as objects.` app.Flags = append(minioFlags, globalFlags...) app.Commands = commands app.CustomAppHelpTemplate = minioHelpTemplate diff --git a/namespace-lock.go b/namespace-lock.go index c49fa1f14..07ca23691 100644 --- a/namespace-lock.go +++ b/namespace-lock.go @@ -64,7 +64,7 @@ func (n *nsLockMap) lock(volume, path string, readLock bool) { } n.lockMap[param] = nsLk } - nsLk.ref++ + nsLk.ref++ // Update ref count here to avoid multiple races. // Unlock map before Locking NS which might block. n.mutex.Unlock() diff --git a/object-api-getobjectinfo_test.go b/object-api-getobjectinfo_test.go index df1109371..963523bed 100644 --- a/object-api-getobjectinfo_test.go +++ b/object-api-getobjectinfo_test.go @@ -20,7 +20,6 @@ import ( "bytes" "crypto/md5" "encoding/hex" - "io" "io/ioutil" "os" "strconv" @@ -111,7 +110,7 @@ func testGetObjectInfo(obj ObjectLayer, instanceType string, t *testing.T) { } } -func BenchmarkGetObject(b *testing.B) { +func BenchmarkGetObjectFS(b *testing.B) { // Make a temporary directory to use as the obj. directory, err := ioutil.TempDir("", "minio-benchmark-getobject") if err != nil { @@ -146,16 +145,12 @@ func BenchmarkGetObject(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { var buffer = new(bytes.Buffer) - r, err := obj.GetObject("bucket", "object"+strconv.Itoa(i%10), 0) + err = obj.GetObject("bucket", "object"+strconv.Itoa(i%10), 0, int64(len([]byte(text))), buffer) if err != nil { b.Error(err) } - if _, err := io.Copy(buffer, r); err != nil { - b.Error(err) - } if buffer.Len() != len(text) { b.Errorf("GetObject returned incorrect length %d (should be %d)\n", buffer.Len(), len(text)) } - r.Close() } } diff --git a/object-api-listobjects_test.go b/object-api-listobjects_test.go index 3578e5510..b02d742bc 100644 --- a/object-api-listobjects_test.go +++ b/object-api-listobjects_test.go @@ -413,6 +413,12 @@ func testListObjects(obj ObjectLayer, instanceType string, t *testing.T) { {Name: "obj2"}, }, }, + // ListObjectsResult-30. + // Prefix and Delimiter is set to '/', (testCase 62). + { + IsTruncated: false, + Objects: []ObjectInfo{}, + }, } testCases := []struct { @@ -521,6 +527,8 @@ func testListObjects(obj ObjectLayer, instanceType string, t *testing.T) { // Test with marker set as hierarhical value and with delimiter. (60-61) {"test-bucket-list-object", "", "Asia/India/India-summer-photos-1", "/", 10, resultCases[28], nil, true}, {"test-bucket-list-object", "", "Asia/India/Karnataka/Bangalore/Koramangala/pics", "/", 10, resultCases[29], nil, true}, + // Test with prefix and delimiter set to '/'. (62) + {"test-bucket-list-object", "/", "", "/", 10, resultCases[30], nil, true}, } for i, testCase := range testCases { diff --git a/object-api-multipart_test.go b/object-api-multipart_test.go index 91e4f6a3b..e6abb0095 100644 --- a/object-api-multipart_test.go +++ b/object-api-multipart_test.go @@ -19,6 +19,7 @@ package main import ( "bytes" "fmt" + "strings" "testing" ) @@ -169,22 +170,23 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t *testing // Case with valid UploadID, existing bucket name. // But using the bucket name from which NewMultipartUpload is not constructed from. {"unused-bucket", object, uploadID, 1, "", "", 0, false, "", fmt.Errorf("%s", "Invalid upload id "+uploadID)}, - // Test Case - 10. + // Test Case - 11. // Case with valid UploadID, existing bucket name. // But using the object name from which NewMultipartUpload is not constructed from. {bucket, "none-object", uploadID, 1, "", "", 0, false, "", fmt.Errorf("%s", "Invalid upload id "+uploadID)}, - // Test case - 11. + // Test case - 12. // Input to replicate Md5 mismatch. {bucket, object, uploadID, 1, "", "a35", 0, false, "", fmt.Errorf("%s", "Bad digest: Expected a35 is not valid with what we calculated "+"d41d8cd98f00b204e9800998ecf8427e")}, - // Test case - 12. - // Input with size more than the size of actual data inside the reader. - {bucket, object, uploadID, 1, "abcd", "a35", int64(len("abcd") + 1), false, "", fmt.Errorf("%s", "EOF")}, // Test case - 13. + // Input with size more than the size of actual data inside the reader. + {bucket, object, uploadID, 1, "abcd", "a35", int64(len("abcd") + 1), false, "", + fmt.Errorf("%s", "Bad digest: Expected a35 is not valid with what we calculated e2fc714c4727ee9395f324cd2e7f331f")}, + // Test case - 14. // Input with size less than the size of actual data inside the reader. {bucket, object, uploadID, 1, "abcd", "a35", int64(len("abcd") - 1), false, "", - fmt.Errorf("%s", "Contains more data than specified size of 3 bytes.")}, - // Test case - 14-17. + fmt.Errorf("%s", "Bad digest: Expected a35 is not valid with what we calculated e2fc714c4727ee9395f324cd2e7f331f")}, + // Test case - 15-18. // Validating for success cases. {bucket, object, uploadID, 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), true, "", nil}, {bucket, object, uploadID, 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh")), true, "", nil}, @@ -219,3 +221,1136 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t *testing } } } + +// Wrapper for calling TestListMultipartUploads tests for both XL multiple disks and single node setup. +func TestListMultipartUploads(t *testing.T) { + ExecObjectLayerTest(t, testListMultipartUploads) +} + +// testListMultipartUploads - Tests validate listing of multipart uploads. +func testListMultipartUploads(obj ObjectLayer, instanceType string, t *testing.T) { + + bucketNames := []string{"minio-bucket", "minio-2-bucket", "minio-3-bucket"} + objectNames := []string{"minio-object-1.txt", "minio-object.txt", "neymar-1.jpeg", "neymar.jpeg", "parrot-1.png", "parrot.png"} + uploadIDs := []string{} + + // bucketnames[0]. + // objectNames[0]. + // uploadIds [0]. + // Create bucket before intiating NewMultipartUpload. + err := obj.MakeBucket(bucketNames[0]) + if err != nil { + // Failed to create newbucket, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + // Initiate Multipart Upload on the above created bucket. + uploadID, err := obj.NewMultipartUpload(bucketNames[0], objectNames[0], nil) + if err != nil { + // Failed to create NewMultipartUpload, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + + uploadIDs = append(uploadIDs, uploadID) + + // bucketnames[1]. + // objectNames[0]. + // uploadIds [1-3]. + // Bucket to test for mutiple upload Id's for a given object. + err = obj.MakeBucket(bucketNames[1]) + if err != nil { + // Failed to create newbucket, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + for i := 0; i < 3; i++ { + // Initiate Multipart Upload on bucketNames[1] for the same object 3 times. + // Used to test the listing for the case of multiple uploadID's for a given object. + uploadID, err = obj.NewMultipartUpload(bucketNames[1], objectNames[0], nil) + if err != nil { + // Failed to create NewMultipartUpload, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + + uploadIDs = append(uploadIDs, uploadID) + } + + // Bucket to test for mutiple objects, each with unique UUID. + // bucketnames[2]. + // objectNames[0-2]. + // uploadIds [4-9]. + err = obj.MakeBucket(bucketNames[2]) + if err != nil { + // Failed to create newbucket, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + // Initiate Multipart Upload on bucketNames[2]. + // Used to test the listing for the case of multiple objects for a given bucket. + for i := 0; i < 6; i++ { + uploadID, err := obj.NewMultipartUpload(bucketNames[2], objectNames[i], nil) + if err != nil { + // Failed to create NewMultipartUpload, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + // uploadIds [4-9]. + uploadIDs = append(uploadIDs, uploadID) + } + // Create multipart parts. + // Need parts to be uploaded before MultipartLists can be called and tested. + createPartCases := []struct { + bucketName string + objName string + uploadID string + PartID int + inputReaderData string + inputMd5 string + intputDataSize int64 + expectedMd5 string + }{ + // Case 1-4. + // Creating sequence of parts for same uploadID. + // Used to ensure that the ListMultipartResult produces one output for the four parts uploaded below for the given upload ID. + {bucketNames[0], objectNames[0], uploadIDs[0], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[0], objectNames[0], uploadIDs[0], 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh")), "1f7690ebdd9b4caf8fab49ca1757bf27"}, + {bucketNames[0], objectNames[0], uploadIDs[0], 3, "ijkl", "09a0877d04abf8759f99adec02baf579", int64(len("abcd")), "09a0877d04abf8759f99adec02baf579"}, + {bucketNames[0], objectNames[0], uploadIDs[0], 4, "mnop", "e132e96a5ddad6da8b07bba6f6131fef", int64(len("abcd")), "e132e96a5ddad6da8b07bba6f6131fef"}, + // Cases 5-7. + // Create parts with 3 uploadID's for the same object. + // Testing for listing of all the uploadID's for given object. + // Insertion with 3 different uploadID's are done for same bucket and object. + {bucketNames[1], objectNames[0], uploadIDs[1], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[1], objectNames[0], uploadIDs[2], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[1], objectNames[0], uploadIDs[3], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + // Case 8-13. + // Generating parts for different objects. + {bucketNames[2], objectNames[0], uploadIDs[4], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[2], objectNames[1], uploadIDs[5], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[2], objectNames[2], uploadIDs[6], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[2], objectNames[3], uploadIDs[7], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[2], objectNames[4], uploadIDs[8], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[2], objectNames[5], uploadIDs[9], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + } + // Iterating over creatPartCases to generate multipart chunks. + for _, testCase := range createPartCases { + _, err := obj.PutObjectPart(testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, testCase.intputDataSize, + bytes.NewBufferString(testCase.inputReaderData), testCase.inputMd5) + if err != nil { + t.Fatalf("%s : %s", instanceType, err.Error()) + } + + } + + // Expected Results set for asserting ListObjectMultipart test. + listMultipartResults := []ListMultipartsInfo{ + // listMultipartResults - 1. + // Used to check that the result produces only one output for the 4 parts uploaded in cases 1-4 of createPartCases above. + // ListMultipartUploads doesn't list the parts. + { + MaxUploads: 100, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 2. + // Used to check that the result produces if keyMarker is set to the only available object. + // `KeyMarker` is set. + // ListMultipartUploads doesn't list the parts. + { + MaxUploads: 100, + KeyMarker: "minio-object-1.txt", + }, + // listMultipartResults - 3. + // `KeyMarker` is set, no uploadMetadata expected. + // ListMultipartUploads doesn't list the parts. + // `Maxupload` value is asserted. + { + MaxUploads: 100, + KeyMarker: "orange", + }, + // listMultipartResults - 4. + // `KeyMarker` is set, no uploadMetadata expected. + // Maxupload value is asserted. + { + MaxUploads: 1, + KeyMarker: "orange", + }, + // listMultipartResults - 5. + // `KeyMarker` is set. It contains part of the objectname as `KeyPrefix`. + // Expecting the result to contain one uploadMetadata entry and Istruncated to be false. + { + MaxUploads: 10, + KeyMarker: "min", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 6. + // `KeyMarker` is set. It contains part of the objectname as `KeyPrefix`. + // `MaxUploads` is set equal to the number of meta data entries in the result, the result contains only one entry. + // Expecting the result to contain one uploadMetadata entry and IsTruncated to be false. + { + MaxUploads: 1, + KeyMarker: "min", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 7. + // `KeyMarker` is set. It contains part of the objectname as `KeyPrefix`. + // Testing for the case with `MaxUploads` set to 0. + // Expecting the result to contain no uploadMetadata entry since `MaxUploads` is set to 0. + // Expecting `IsTruncated` to be true. + { + MaxUploads: 0, + KeyMarker: "min", + IsTruncated: true, + }, + // listMultipartResults - 8. + // `KeyMarker` is set. It contains part of the objectname as KeyPrefix. + // Testing for the case with `MaxUploads` set to 0. + // Expecting the result to contain no uploadMetadata entry since `MaxUploads` is set to 0. + // Expecting `isTruncated` to be true. + { + MaxUploads: 0, + KeyMarker: "min", + IsTruncated: true, + }, + // listMultipartResults - 9. + // `KeyMarker` is set. It contains part of the objectname as KeyPrefix. + // `KeyMarker` is set equal to the object name in the result. + // Expecting the result to contain one uploadMetadata entry and IsTruncated to be false. + { + MaxUploads: 2, + KeyMarker: "minio-object", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 10. + // Prefix is set. It is set equal to the object name. + // MaxUploads is set more than number of meta data entries in the result. + // Expecting the result to contain one uploadMetadata entry and IsTruncated to be false. + { + MaxUploads: 2, + Prefix: "minio-object-1.txt", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 11. + // Setting `Prefix` to contain the object name as its prefix. + // MaxUploads is set more than number of meta data entries in the result. + // Expecting the result to contain one uploadMetadata entry and IsTruncated to be false. + { + MaxUploads: 2, + Prefix: "min", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 12. + // Setting `Prefix` to contain the object name as its prefix. + // MaxUploads is set equal to number of meta data entries in the result. + // Expecting the result to contain one uploadMetadata entry and IsTruncated to be false. + { + MaxUploads: 1, + Prefix: "min", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 13. + // `Prefix` is set. It doesn't contain object name as its preifx. + // MaxUploads is set more than number of meta data entries in the result. + // Expecting no `Uploads` metadata. + { + MaxUploads: 2, + Prefix: "orange", + IsTruncated: false, + }, + // listMultipartResults - 14. + // `Prefix` is set. It doesn't contain object name as its preifx. + // MaxUploads is set more than number of meta data entries in the result. + // Expecting the result to contain 0 uploads and isTruncated to false. + { + MaxUploads: 2, + Prefix: "Asia", + IsTruncated: false, + }, + // listMultipartResults - 15. + // Setting `Delimiter`. + // MaxUploads is set more than number of meta data entries in the result. + // Expecting the result to contain one uploadMetadata entry and IsTruncated to be false. + { + MaxUploads: 2, + Delimiter: "/", + Prefix: "", + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[0], + }, + }, + }, + // listMultipartResults - 16. + // Testing for listing of 3 uploadID's for a given object. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 100, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[1], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[2], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[3], + }, + }, + }, + // listMultipartResults - 17. + // Testing for listing of 3 uploadID's (uploadIDs[1-3]) for a given object with uploadID Marker set. + // uploadIDs[1] is set as UploadMarker, Expecting it to be skipped in the result. + // uploadIDs[2] and uploadIDs[3] are expected to be in the result. + // Istruncted is expected to be false. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 100, + KeyMarker: "minio-object-1.txt", + UploadIDMarker: uploadIDs[1], + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[2], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[3], + }, + }, + }, + // listMultipartResults - 18. + // Testing for listing of 3 uploadID's (uploadIDs[1-3]) for a given object with uploadID Marker set. + // uploadIDs[2] is set as UploadMarker, Expecting it to be skipped in the result. + // Only uploadIDs[3] are expected to be in the result. + // Istruncted is expected to be false. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 100, + KeyMarker: "minio-object-1.txt", + UploadIDMarker: uploadIDs[2], + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[3], + }, + }, + }, + // listMultipartResults - 19. + // Testing for listing of 3 uploadID's for a given object, setting maxKeys to be 2. + // There are 3 uploadMetadata in the result (uploadIDs[1-3]), it should be truncated to 2. + // Since there is only single object for bucketNames[1], the NextKeyMarker is set to its name. + // The last entry in the result, uploadIDs[2], that is should be set as NextUploadIDMarker. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 2, + IsTruncated: true, + NextKeyMarker: objectNames[0], + NextUploadIDMarker: uploadIDs[2], + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[1], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[2], + }, + }, + }, + // listMultipartResults - 20. + // Testing for listing of 3 uploadID's for a given object, setting maxKeys to be 1. + // There are 3 uploadMetadata in the result (uploadIDs[1-3]), it should be truncated to 1. + // The last entry in the result, uploadIDs[1], that is should be set as NextUploadIDMarker. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 1, + IsTruncated: true, + NextKeyMarker: objectNames[0], + NextUploadIDMarker: uploadIDs[1], + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[1], + }, + }, + }, + // listMultipartResults - 21. + // Testing for listing of 3 uploadID's for a given object, setting maxKeys to be 3. + // There are 3 uploadMetadata in the result (uploadIDs[1-3]), hence no truncation is expected. + // Since all the uploadMetadata is listed, expecting no values for NextUploadIDMarker and NextKeyMarker. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 3, + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[1], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[2], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[3], + }, + }, + }, + // listMultipartResults - 22. + // Testing for listing of 3 uploadID's for a given object, setting `prefix` to be "min". + // Will be used to list on bucketNames[1]. + { + MaxUploads: 10, + IsTruncated: false, + Prefix: "min", + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[1], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[2], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[3], + }, + }, + }, + // listMultipartResults - 23. + // Testing for listing of 3 uploadID's for a given object + // setting `prefix` to be "orange". + // Will be used to list on bucketNames[1]. + { + MaxUploads: 10, + IsTruncated: false, + Prefix: "orange", + }, + // listMultipartResults - 24. + // Testing for listing of 3 uploadID's for a given object. + // setting `prefix` to be "Asia". + // Will be used to list on bucketNames[1]. + { + MaxUploads: 10, + IsTruncated: false, + Prefix: "Asia", + }, + // listMultipartResults - 25. + // Testing for listing of 3 uploadID's for a given object. + // setting `prefix` and uploadIDMarker. + // Will be used to list on bucketNames[1]. + { + MaxUploads: 10, + KeyMarker: "minio-object-1.txt", + IsTruncated: false, + Prefix: "min", + UploadIDMarker: uploadIDs[1], + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[2], + }, + { + Object: objectNames[0], + UploadID: uploadIDs[3], + }, + }, + }, + + // Operations on bucket 2. + // listMultipartResults - 26. + // checking listing everything. + { + MaxUploads: 100, + IsTruncated: false, + + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[4], + }, + { + Object: objectNames[1], + UploadID: uploadIDs[5], + }, + { + Object: objectNames[2], + UploadID: uploadIDs[6], + }, + { + Object: objectNames[3], + UploadID: uploadIDs[7], + }, + { + Object: objectNames[4], + UploadID: uploadIDs[8], + }, + { + Object: objectNames[5], + UploadID: uploadIDs[9], + }, + }, + }, + // listMultipartResults - 27. + // listing with `prefix` "min". + { + MaxUploads: 100, + IsTruncated: false, + Prefix: "min", + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[4], + }, + { + Object: objectNames[1], + UploadID: uploadIDs[5], + }, + }, + }, + // listMultipartResults - 28. + // listing with `prefix` "ney". + { + MaxUploads: 100, + IsTruncated: false, + Prefix: "ney", + Uploads: []uploadMetadata{ + { + Object: objectNames[2], + UploadID: uploadIDs[6], + }, + { + Object: objectNames[3], + UploadID: uploadIDs[7], + }, + }, + }, + // listMultipartResults - 29. + // listing with `prefix` "parrot". + { + MaxUploads: 100, + IsTruncated: false, + Prefix: "parrot", + Uploads: []uploadMetadata{ + { + Object: objectNames[4], + UploadID: uploadIDs[8], + }, + { + Object: objectNames[5], + UploadID: uploadIDs[9], + }, + }, + }, + // listMultipartResults - 30. + // listing with `prefix` "neymar.jpeg". + // prefix set to object name. + { + MaxUploads: 100, + IsTruncated: false, + Prefix: "neymar.jpeg", + Uploads: []uploadMetadata{ + { + Object: objectNames[3], + UploadID: uploadIDs[7], + }, + }, + }, + + // listMultipartResults - 31. + // checking listing with marker set to 3. + // `NextUploadIDMarker` is expected to be set on last uploadID in the result. + // `NextKeyMarker` is expected to be set on the last object key in the list. + { + MaxUploads: 3, + IsTruncated: true, + NextUploadIDMarker: uploadIDs[6], + NextKeyMarker: objectNames[2], + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[4], + }, + { + Object: objectNames[1], + UploadID: uploadIDs[5], + }, + { + Object: objectNames[2], + UploadID: uploadIDs[6], + }, + }, + }, + // listMultipartResults - 32. + // checking listing with marker set to no of objects in the list. + // `NextUploadIDMarker` is expected to be empty since all results are listed. + // `NextKeyMarker` is expected to be empty since all results are listed. + { + MaxUploads: 6, + IsTruncated: false, + Uploads: []uploadMetadata{ + { + Object: objectNames[0], + UploadID: uploadIDs[4], + }, + { + Object: objectNames[1], + UploadID: uploadIDs[5], + }, + { + Object: objectNames[2], + UploadID: uploadIDs[6], + }, + { + Object: objectNames[3], + UploadID: uploadIDs[7], + }, + { + Object: objectNames[4], + UploadID: uploadIDs[8], + }, + { + Object: objectNames[5], + UploadID: uploadIDs[9], + }, + }, + }, + // listMultipartResults - 33. + // checking listing with `UploadIDMarker` set. + { + MaxUploads: 10, + IsTruncated: false, + UploadIDMarker: uploadIDs[6], + Uploads: []uploadMetadata{ + { + Object: objectNames[3], + UploadID: uploadIDs[7], + }, + { + Object: objectNames[4], + UploadID: uploadIDs[8], + }, + { + Object: objectNames[5], + UploadID: uploadIDs[9], + }, + }, + }, + // listMultipartResults - 34. + // checking listing with `KeyMarker` set. + { + MaxUploads: 10, + IsTruncated: false, + KeyMarker: objectNames[3], + Uploads: []uploadMetadata{ + { + Object: objectNames[4], + UploadID: uploadIDs[8], + }, + { + Object: objectNames[5], + UploadID: uploadIDs[9], + }, + }, + }, + // listMultipartResults - 35. + // Checking listing with `Prefix` and `KeyMarker`. + // No upload uploadMetadata in the result expected since KeyMarker is set to last Key in the result. + { + MaxUploads: 10, + IsTruncated: false, + Prefix: "minio-object", + KeyMarker: objectNames[1], + }, + // listMultipartResults - 36. + // checking listing with `Prefix` and `UploadIDMarker` set. + { + MaxUploads: 10, + IsTruncated: false, + Prefix: "minio", + UploadIDMarker: uploadIDs[4], + Uploads: []uploadMetadata{ + { + Object: objectNames[1], + UploadID: uploadIDs[5], + }, + }, + }, + // listMultipartResults - 37. + // Checking listing with `KeyMarker` and `UploadIDMarker` set. + { + MaxUploads: 10, + IsTruncated: false, + KeyMarker: "minio-object.txt", + UploadIDMarker: uploadIDs[5], + }, + } + + testCases := []struct { + // Inputs to ListObjects. + bucket string + prefix string + keyMarker string + uploadIDMarker string + delimiter string + maxUploads int + // Expected output of ListObjects. + expectedResult ListMultipartsInfo + expectedErr error + // Flag indicating whether the test is expected to pass or not. + shouldPass bool + }{ + // Test cases with invalid bucket names ( Test number 1-4 ). + {".test", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: ".test"}, false}, + {"Test", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: "Test"}, false}, + {"---", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: "---"}, false}, + {"ad", "", "", "", "", 0, ListMultipartsInfo{}, BucketNameInvalid{Bucket: "ad"}, false}, + // Valid bucket names, but they donot exist (Test number 5-7). + {"volatile-bucket-1", "", "", "", "", 0, ListMultipartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-1"}, false}, + {"volatile-bucket-2", "", "", "", "", 0, ListMultipartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-2"}, false}, + {"volatile-bucket-3", "", "", "", "", 0, ListMultipartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-3"}, false}, + // Valid, existing bucket, but sending invalid delimeter values (Test number 8-9). + // Empty string < "" > and forward slash < / > are the ony two valid arguments for delimeter. + {bucketNames[0], "", "", "", "*", 0, ListMultipartsInfo{}, fmt.Errorf("delimiter '%s' is not supported", "*"), false}, + {bucketNames[0], "", "", "", "-", 0, ListMultipartsInfo{}, fmt.Errorf("delimiter '%s' is not supported", "-"), false}, + // Testing for failure cases with both perfix and marker (Test number 10). + // The prefix and marker combination to be valid it should satisy strings.HasPrefix(marker, prefix). + {bucketNames[0], "asia", "europe-object", "", "", 0, ListMultipartsInfo{}, + fmt.Errorf("Invalid combination of marker '%s' and prefix '%s'", "europe-object", "asia"), false}, + // Setting an invalid combination of uploadIDMarker and Marker (Test number 11-12). + {bucketNames[0], "asia", "asia/europe/", "abc", "", 0, ListMultipartsInfo{}, + fmt.Errorf("Invalid combination of uploadID marker '%s' and marker '%s'", "abc", "asia/europe/"), false}, + {bucketNames[0], "asia", "asia/europe", "abc", "", 0, ListMultipartsInfo{}, + fmt.Errorf("unknown UUID string %s", "abc"), false}, + + // Setting up valid case of ListMultiPartUploads. + // Test case with multiple parts for a single uploadID (Test number 13). + {bucketNames[0], "", "", "", "", 100, listMultipartResults[0], nil, true}, + // Test with a KeyMarker (Test number 14-17). + {bucketNames[0], "", "minio-object-1.txt", "", "", 100, listMultipartResults[1], nil, true}, + {bucketNames[0], "", "orange", "", "", 100, listMultipartResults[2], nil, true}, + {bucketNames[0], "", "orange", "", "", 1, listMultipartResults[3], nil, true}, + {bucketNames[0], "", "min", "", "", 10, listMultipartResults[4], nil, true}, + // Test case with keyMarker set equal to number of parts in the result. (Test number 18). + {bucketNames[0], "", "min", "", "", 1, listMultipartResults[5], nil, true}, + // Test case with keyMarker set to 0. (Test number 19). + {bucketNames[0], "", "min", "", "", 0, listMultipartResults[6], nil, true}, + // Test case with keyMarker less than 0. (Test number 20). + // {bucketNames[0], "", "min", "", "", -1, listMultipartResults[7], nil, true}, + // The result contains only one entry. The KeyPrefix is set to the object name in the result. + // Expecting the result to skip the KeyPrefix entry in the result (Test number 21). + {bucketNames[0], "", "minio-object", "", "", 2, listMultipartResults[8], nil, true}, + // Test case containing prefix values. + // Setting prefix to be equal to object name.(Test number 22). + {bucketNames[0], "minio-object-1.txt", "", "", "", 2, listMultipartResults[9], nil, true}, + // Setting `prefix` to contain the object name as its prefix (Test number 23). + {bucketNames[0], "min", "", "", "", 2, listMultipartResults[10], nil, true}, + // Setting `prefix` to contain the object name as its prefix (Test number 24). + {bucketNames[0], "min", "", "", "", 1, listMultipartResults[11], nil, true}, + // Setting `prefix` to not to contain the object name as its prefix (Test number 25-26). + {bucketNames[0], "orange", "", "", "", 2, listMultipartResults[12], nil, true}, + {bucketNames[0], "Asia", "", "", "", 2, listMultipartResults[13], nil, true}, + // setting delimiter (Test number 27). + {bucketNames[0], "", "", "", "/", 2, listMultipartResults[14], nil, true}, + //Test case with multiple uploadID listing for given object (Test number 28). + {bucketNames[1], "", "", "", "", 100, listMultipartResults[15], nil, true}, + // Test case with multiple uploadID listing for given object, but uploadID marker set. + // Testing whether the marker entry is skipped (Test number 29-30). + {bucketNames[1], "", "minio-object-1.txt", uploadIDs[1], "", 100, listMultipartResults[16], nil, true}, + {bucketNames[1], "", "minio-object-1.txt", uploadIDs[2], "", 100, listMultipartResults[17], nil, true}, + // Test cases with multiple uploadID listing for a given object (Test number 31-32). + // MaxKeys set to values lesser than the number of entries in the uploadMetadata. + // IsTruncated is expected to be true. + {bucketNames[1], "", "", "", "", 2, listMultipartResults[18], nil, true}, + {bucketNames[1], "", "", "", "", 1, listMultipartResults[19], nil, true}, + // MaxKeys set to the value which is equal to no of entries in the uploadMetadata (Test number 33). + // In case of bucketNames[1], there are 3 entries. + // Since all available entries are listed, IsTruncated is expected to be false + // and NextMarkers are expected to empty. + {bucketNames[1], "", "", "", "", 3, listMultipartResults[20], nil, true}, + // Adding prefix (Test number 34-36). + {bucketNames[1], "min", "", "", "", 10, listMultipartResults[21], nil, true}, + {bucketNames[1], "orange", "", "", "", 10, listMultipartResults[22], nil, true}, + {bucketNames[1], "Asia", "", "", "", 10, listMultipartResults[23], nil, true}, + // Test case with `Prefix` and `UploadIDMarker` (Test number 37). + {bucketNames[1], "min", "minio-object-1.txt", uploadIDs[1], "", 10, listMultipartResults[24], nil, true}, + // Test case with `KeyMarker` and `UploadIDMarker` (Test number 38). + // {bucketNames[1], "", "minio-object-1.txt", uploadIDs[1], "", 10, listMultipartResults[24], nil, true}, + + // Test case for bucket with multiple objects in it. + // Bucket used : `bucketNames[2]`. + // Objects used: `objectNames[1-5]`. + // UploadId's used: uploadIds[4-8]. + // (Test number 39). + {bucketNames[2], "", "", "", "", 100, listMultipartResults[25], nil, true}, + //Test cases with prefixes. + //Testing listing with prefix set to "min" (Test number 40) . + {bucketNames[2], "min", "", "", "", 100, listMultipartResults[26], nil, true}, + //Testing listing with prefix set to "ney" (Test number 41). + {bucketNames[2], "ney", "", "", "", 100, listMultipartResults[27], nil, true}, + //Testing listing with prefix set to "par" (Test number 42). + {bucketNames[2], "parrot", "", "", "", 100, listMultipartResults[28], nil, true}, + //Testing listing with prefix set to object name "neymar.jpeg" (Test number 43). + {bucketNames[2], "neymar.jpeg", "", "", "", 100, listMultipartResults[29], nil, true}, + // Testing listing with `MaxUploads` set to 3 (Test number 44). + {bucketNames[2], "", "", "", "", 3, listMultipartResults[30], nil, true}, + // In case of bucketNames[2], there are 6 entries (Test number 45). + // Since all available entries are listed, IsTruncated is expected to be false + // and NextMarkers are expected to empty. + {bucketNames[2], "", "", "", "", 6, listMultipartResults[31], nil, true}, + // Test case with `uploadIDMarker` (Test number 46). + // {bucketNames[2], "", "", uploadIDs[6], "", 10, listMultipartResults[32], nil, true}, + // Test case with `KeyMarker` (Test number 47). + {bucketNames[2], "", objectNames[3], "", "", 10, listMultipartResults[33], nil, true}, + // Test case with `prefix` and `KeyMarker` (Test number 48). + {bucketNames[2], "minio-object", objectNames[1], "", "", 10, listMultipartResults[34], nil, true}, + // Test case with `prefix` and `uploadIDMarker` (Test number 49). + // {bucketNames[2], "minio", "", uploadIDs[4], "", 10, listMultipartResults[35], nil, true}, + // Test case with `KeyMarker` and `uploadIDMarker` (Test number 50). + // {bucketNames[2], "minio-object.txt", "", uploadIDs[5], "", 10, listMultipartResults[36], nil, true}, + } + + for i, testCase := range testCases { + actualResult, actualErr := obj.ListMultipartUploads(testCase.bucket, testCase.prefix, testCase.keyMarker, testCase.uploadIDMarker, testCase.delimiter, testCase.maxUploads) + if actualErr != nil && testCase.shouldPass { + t.Errorf("Test %d: %s: Expected to pass, but failed with: %s", i+1, instanceType, actualErr.Error()) + } + if actualErr == nil && !testCase.shouldPass { + t.Errorf("Test %d: %s: Expected to fail with \"%s\", but passed instead", i+1, instanceType, testCase.expectedErr.Error()) + } + // Failed as expected, but does it fail for the expected reason. + if actualErr != nil && !testCase.shouldPass { + if !strings.Contains(actualErr.Error(), testCase.expectedErr.Error()) { + t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, instanceType, testCase.expectedErr.Error(), actualErr.Error()) + } + } + // Passes as expected, but asserting the results. + if actualErr == nil && testCase.shouldPass { + expectedResult := testCase.expectedResult + // Asserting the MaxUploads. + if actualResult.MaxUploads != expectedResult.MaxUploads { + t.Errorf("Test %d: %s: Expected the MaxUploads to be %d, but instead found it to be %d", i+1, instanceType, expectedResult.MaxUploads, actualResult.MaxUploads) + } + // Asserting Prefix. + if actualResult.Prefix != expectedResult.Prefix { + t.Errorf("Test %d: %s: Expected Prefix to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Prefix, actualResult.Prefix) + } + // Asserting Delimiter. + if actualResult.Delimiter != expectedResult.Delimiter { + t.Errorf("Test %d: %s: Expected Delimiter to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Delimiter, actualResult.Delimiter) + } + // Asserting NextUploadIDMarker. + if actualResult.NextUploadIDMarker != expectedResult.NextUploadIDMarker { + t.Errorf("Test %d: %s: Expected NextUploadIDMarker to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.NextUploadIDMarker, actualResult.NextUploadIDMarker) + } + // Asserting NextKeyMarker. + if actualResult.NextKeyMarker != expectedResult.NextKeyMarker { + t.Errorf("Test %d: %s: Expected NextKeyMarker to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.NextKeyMarker, actualResult.NextKeyMarker) + } + // Asserting the keyMarker. + if actualResult.KeyMarker != expectedResult.KeyMarker { + t.Errorf("Test %d: %s: Expected keyMarker to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.KeyMarker, actualResult.KeyMarker) + } + // Asserting IsTruncated. + if actualResult.IsTruncated != testCase.expectedResult.IsTruncated { + t.Errorf("Test %d: %s: Expected Istruncated to be \"%v\", but found it to \"%v\"", i+1, instanceType, expectedResult.IsTruncated, actualResult.IsTruncated) + } + // Asserting the number of upload Metadata info. + if len(expectedResult.Uploads) != len(actualResult.Uploads) { + t.Errorf("Test %d: %s: Expected the result to contain info of %d Multipart Uploads, but found %d instead", i+1, instanceType, len(expectedResult.Uploads), len(actualResult.Uploads)) + } else { + // Iterating over the uploads Metadata and asserting the fields. + for j, actualMetaData := range actualResult.Uploads { + // Asserting the object name in the upload Metadata. + if actualMetaData.Object != expectedResult.Uploads[j].Object { + t.Errorf("Test %d: %s: Metadata %d: Expected Metadata Object to be \"%s\", but instead found \"%s\"", i+1, instanceType, j+1, expectedResult.Uploads[j].Object, actualMetaData.Object) + } + // Asserting the uploadID in the upload Metadata. + if actualMetaData.UploadID != expectedResult.Uploads[j].UploadID { + t.Errorf("Test %d: %s: Metadata %d: Expected Metadata UploadID to be \"%s\", but instead found \"%s\"", i+1, instanceType, j+1, expectedResult.Uploads[j].UploadID, actualMetaData.UploadID) + } + } + } + } + } +} + +// Wrapper for calling TestListObjectParts tests for both XL multiple disks and single node setup. +func TestListObjectParts(t *testing.T) { + ExecObjectLayerTest(t, testListObjectParts) +} + +// testListMultipartUploads - Tests validate listing of multipart uploads. +func testListObjectParts(obj ObjectLayer, instanceType string, t *testing.T) { + + bucketNames := []string{"minio-bucket", "minio-2-bucket"} + objectNames := []string{"minio-object-1.txt"} + uploadIDs := []string{} + + // bucketnames[0]. + // objectNames[0]. + // uploadIds [0]. + // Create bucket before intiating NewMultipartUpload. + err := obj.MakeBucket(bucketNames[0]) + if err != nil { + // Failed to create newbucket, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + // Initiate Multipart Upload on the above created bucket. + uploadID, err := obj.NewMultipartUpload(bucketNames[0], objectNames[0], nil) + if err != nil { + // Failed to create NewMultipartUpload, abort. + t.Fatalf("%s : %s", instanceType, err.Error()) + } + + uploadIDs = append(uploadIDs, uploadID) + + // Create multipart parts. + // Need parts to be uploaded before MultipartLists can be called and tested. + createPartCases := []struct { + bucketName string + objName string + uploadID string + PartID int + inputReaderData string + inputMd5 string + intputDataSize int64 + expectedMd5 string + }{ + // Case 1-4. + // Creating sequence of parts for same uploadID. + // Used to ensure that the ListMultipartResult produces one output for the four parts uploaded below for the given upload ID. + {bucketNames[0], objectNames[0], uploadIDs[0], 1, "abcd", "e2fc714c4727ee9395f324cd2e7f331f", int64(len("abcd")), "e2fc714c4727ee9395f324cd2e7f331f"}, + {bucketNames[0], objectNames[0], uploadIDs[0], 2, "efgh", "1f7690ebdd9b4caf8fab49ca1757bf27", int64(len("efgh")), "1f7690ebdd9b4caf8fab49ca1757bf27"}, + {bucketNames[0], objectNames[0], uploadIDs[0], 3, "ijkl", "09a0877d04abf8759f99adec02baf579", int64(len("abcd")), "09a0877d04abf8759f99adec02baf579"}, + {bucketNames[0], objectNames[0], uploadIDs[0], 4, "mnop", "e132e96a5ddad6da8b07bba6f6131fef", int64(len("abcd")), "e132e96a5ddad6da8b07bba6f6131fef"}, + } + // Iterating over creatPartCases to generate multipart chunks. + for _, testCase := range createPartCases { + _, err := obj.PutObjectPart(testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, testCase.intputDataSize, + bytes.NewBufferString(testCase.inputReaderData), testCase.inputMd5) + if err != nil { + t.Fatalf("%s : %s", instanceType, err.Error()) + } + } + + partInfos := []ListPartsInfo{ + // partinfos - 0. + { + Bucket: bucketNames[0], + Object: objectNames[0], + MaxParts: 10, + UploadID: uploadIDs[0], + Parts: []partInfo{ + { + PartNumber: 1, + Size: 4, + ETag: "e2fc714c4727ee9395f324cd2e7f331f", + }, + { + PartNumber: 2, + Size: 4, + ETag: "1f7690ebdd9b4caf8fab49ca1757bf27", + }, + { + PartNumber: 3, + Size: 4, + ETag: "09a0877d04abf8759f99adec02baf579", + }, + { + PartNumber: 4, + Size: 4, + ETag: "e132e96a5ddad6da8b07bba6f6131fef", + }, + }, + }, + // partinfos - 1. + { + Bucket: bucketNames[0], + Object: objectNames[0], + MaxParts: 3, + NextPartNumberMarker: 3, + IsTruncated: true, + UploadID: uploadIDs[0], + Parts: []partInfo{ + { + PartNumber: 1, + Size: 4, + ETag: "e2fc714c4727ee9395f324cd2e7f331f", + }, + { + PartNumber: 2, + Size: 4, + ETag: "1f7690ebdd9b4caf8fab49ca1757bf27", + }, + { + PartNumber: 3, + Size: 4, + ETag: "09a0877d04abf8759f99adec02baf579", + }, + }, + }, + // partinfos - 2. + { + Bucket: bucketNames[0], + Object: objectNames[0], + MaxParts: 2, + IsTruncated: false, + UploadID: uploadIDs[0], + Parts: []partInfo{ + { + PartNumber: 4, + Size: 4, + ETag: "e132e96a5ddad6da8b07bba6f6131fef", + }, + }, + }, + } + + testCases := []struct { + bucket string + object string + uploadID string + partNumberMarker int + maxParts int + // Expected output of ListPartsInfo. + expectedResult ListPartsInfo + expectedErr error + // Flag indicating whether the test is expected to pass or not. + shouldPass bool + }{ + // Test cases with invalid bucket names (Test number 1-4). + {".test", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: ".test"}, false}, + {"Test", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "Test"}, false}, + {"---", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "---"}, false}, + {"ad", "", "", 0, 0, ListPartsInfo{}, BucketNameInvalid{Bucket: "ad"}, false}, + // Test cases for listing uploadID with single part. + // Valid bucket names, but they donot exist (Test number 5-7). + {"volatile-bucket-1", "", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-1"}, false}, + {"volatile-bucket-2", "", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-2"}, false}, + {"volatile-bucket-3", "", "", 0, 0, ListPartsInfo{}, BucketNotFound{Bucket: "volatile-bucket-3"}, false}, + // Test case for Asserting for invalid objectName (Test number 8). + {bucketNames[0], "", "", 0, 0, ListPartsInfo{}, ObjectNameInvalid{Bucket: bucketNames[0]}, false}, + // Asserting for Invalid UploadID (Test number 9). + {bucketNames[0], objectNames[0], "abc", 0, 0, ListPartsInfo{}, InvalidUploadID{UploadID: "abc"}, false}, + // Test case for uploadID with multiple parts (Test number 12). + {bucketNames[0], objectNames[0], uploadIDs[0], 0, 10, partInfos[0], nil, true}, + // Test case with maxParts set to less than number of parts (Test number 13). + {bucketNames[0], objectNames[0], uploadIDs[0], 0, 3, partInfos[1], nil, true}, + // Test case with partNumberMarker set (Test number 14)-. + {bucketNames[0], objectNames[0], uploadIDs[0], 3, 2, partInfos[2], nil, true}, + } + + for i, testCase := range testCases { + actualResult, actualErr := obj.ListObjectParts(testCase.bucket, testCase.object, testCase.uploadID, testCase.partNumberMarker, testCase.maxParts) + if actualErr != nil && testCase.shouldPass { + t.Errorf("Test %d: %s: Expected to pass, but failed with: %s", i+1, instanceType, actualErr.Error()) + } + if actualErr == nil && !testCase.shouldPass { + t.Errorf("Test %d: %s: Expected to fail with \"%s\", but passed instead", i+1, instanceType, testCase.expectedErr.Error()) + } + // Failed as expected, but does it fail for the expected reason. + if actualErr != nil && !testCase.shouldPass { + if !strings.Contains(actualErr.Error(), testCase.expectedErr.Error()) { + t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, instanceType, testCase.expectedErr.Error(), actualErr.Error()) + } + } + // Passes as expected, but asserting the results. + if actualErr == nil && testCase.shouldPass { + expectedResult := testCase.expectedResult + // Asserting the MaxParts. + if actualResult.MaxParts != expectedResult.MaxParts { + t.Errorf("Test %d: %s: Expected the MaxParts to be %d, but instead found it to be %d", i+1, instanceType, expectedResult.MaxParts, actualResult.MaxParts) + } + // Asserting Object Name. + if actualResult.Object != expectedResult.Object { + t.Errorf("Test %d: %s: Expected Object name to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Object, actualResult.Object) + } + // Asserting UploadID. + if actualResult.UploadID != expectedResult.UploadID { + t.Errorf("Test %d: %s: Expected UploadID to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.UploadID, actualResult.UploadID) + } + // Asserting NextPartNumberMarker. + if actualResult.NextPartNumberMarker != expectedResult.NextPartNumberMarker { + t.Errorf("Test %d: %s: Expected NextPartNumberMarker to be \"%d\", but instead found it to be \"%d\"", i+1, instanceType, expectedResult.NextPartNumberMarker, actualResult.NextPartNumberMarker) + } + // Asserting PartNumberMarker. + if actualResult.PartNumberMarker != expectedResult.PartNumberMarker { + t.Errorf("Test %d: %s: Expected PartNumberMarker to be \"%d\", but instead found it to be \"%d\"", i+1, instanceType, expectedResult.PartNumberMarker, actualResult.PartNumberMarker) + } + // Asserting the BucketName. + if actualResult.Bucket != expectedResult.Bucket { + t.Errorf("Test %d: %s: Expected Bucket to be \"%s\", but instead found it to be \"%s\"", i+1, instanceType, expectedResult.Bucket, actualResult.Bucket) + } + // Asserting IsTruncated. + if actualResult.IsTruncated != testCase.expectedResult.IsTruncated { + t.Errorf("Test %d: %s: Expected IsTruncated to be \"%v\", but found it to \"%v\"", i+1, instanceType, expectedResult.IsTruncated, actualResult.IsTruncated) + } + // Asserting the number of Parts. + if len(expectedResult.Parts) != len(actualResult.Parts) { + t.Errorf("Test %d: %s: Expected the result to contain info of %d Parts, but found %d instead", i+1, instanceType, len(expectedResult.Parts), len(actualResult.Parts)) + } else { + // Iterating over the partInfos and asserting the fields. + for j, actualMetaData := range actualResult.Parts { + // Asserting the PartNumber in the PartInfo. + if actualMetaData.PartNumber != expectedResult.Parts[j].PartNumber { + t.Errorf("Test %d: %s: Part %d: Expected PartNumber to be \"%d\", but instead found \"%d\"", i+1, instanceType, j+1, expectedResult.Parts[j].PartNumber, actualMetaData.PartNumber) + } + // Asserting the Size in the PartInfo. + if actualMetaData.Size != expectedResult.Parts[j].Size { + t.Errorf("Test %d: %s: Part %d: Expected Part Size to be \"%d\", but instead found \"%d\"", i+1, instanceType, j+1, expectedResult.Parts[j].Size, actualMetaData.Size) + } + // Asserting the ETag in the PartInfo. + if actualMetaData.ETag != expectedResult.Parts[j].ETag { + t.Errorf("Test %d: %s: Part %d: Expected Etag to be \"%s\", but instead found \"%s\"", i+1, instanceType, j+1, expectedResult.Parts[j].ETag, actualMetaData.ETag) + } + } + } + } + } +} diff --git a/object-common-multipart.go b/object-common-multipart.go deleted file mode 100644 index 583cd2f25..000000000 --- a/object-common-multipart.go +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "crypto/md5" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "path" - "sort" - "strconv" - "strings" - - "github.com/skyrings/skyring-common/tools/uuid" -) - -const ( - incompleteFile = "00000.incomplete" - uploadsJSONFile = "uploads.json" -) - -// createUploadsJSON - create uploads.json placeholder file. -func createUploadsJSON(storage StorageAPI, bucket, object, uploadID string) error { - // Place holder uploads.json - uploadsPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) - uploadsJSONSuffix := fmt.Sprintf("%s.%s", uploadID, uploadsJSONFile) - tmpUploadsPath := path.Join(tmpMetaPrefix, bucket, object, uploadsJSONSuffix) - w, err := storage.CreateFile(minioMetaBucket, uploadsPath) - if err != nil { - return err - } - if err = w.Close(); err != nil { - if clErr := safeCloseAndRemove(w); clErr != nil { - return clErr - } - return err - } - _, err = storage.StatFile(minioMetaBucket, uploadsPath) - if err != nil { - if err == errFileNotFound { - err = storage.RenameFile(minioMetaBucket, tmpUploadsPath, minioMetaBucket, uploadsPath) - if err == nil { - return nil - } - } - if derr := storage.DeleteFile(minioMetaBucket, tmpUploadsPath); derr != nil { - return derr - } - return err - } - return nil -} - -/// Common multipart object layer functions. - -// newMultipartUploadCommon - initialize a new multipart, is a common -// function for both object layers. -func newMultipartUploadCommon(storage StorageAPI, bucket string, object string, meta map[string]string) (uploadID string, err error) { - // Verify if bucket name is valid. - if !IsValidBucketName(bucket) { - return "", BucketNameInvalid{Bucket: bucket} - } - // Verify whether the bucket exists. - if !isBucketExist(storage, bucket) { - return "", BucketNotFound{Bucket: bucket} - } - // Verify if object name is valid. - if !IsValidObjectName(object) { - return "", ObjectNameInvalid{Bucket: bucket, Object: object} - } - // No metadata is set, allocate a new one. - if meta == nil { - meta = make(map[string]string) - } - // This lock needs to be held for any changes to the directory contents of ".minio/multipart/object/" - nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) - defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) - // Loops through until successfully generates a new unique upload id. - for { - uuid, err := uuid.New() - if err != nil { - return "", err - } - uploadID := uuid.String() - // Create placeholder file 'uploads.json' - err = createUploadsJSON(storage, bucket, object, uploadID) - if err != nil { - return "", err - } - uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID, incompleteFile) - incompleteSuffix := fmt.Sprintf("%s.%s", uploadID, incompleteFile) - tempUploadIDPath := path.Join(tmpMetaPrefix, bucket, object, incompleteSuffix) - if _, err = storage.StatFile(minioMetaBucket, uploadIDPath); err != nil { - if err != errFileNotFound { - return "", toObjectErr(err, minioMetaBucket, uploadIDPath) - } - // uploadIDPath doesn't exist, so create empty file to reserve the name - var w io.WriteCloser - if w, err = storage.CreateFile(minioMetaBucket, tempUploadIDPath); err != nil { - return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) - } - - // Encode the uploaded metadata into incomplete file. - encoder := json.NewEncoder(w) - err = encoder.Encode(&meta) - if err != nil { - if clErr := safeCloseAndRemove(w); clErr != nil { - return "", toObjectErr(clErr, minioMetaBucket, tempUploadIDPath) - } - return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) - } - - // Close the writer. - if err = w.Close(); err != nil { - if clErr := safeCloseAndRemove(w); clErr != nil { - return "", toObjectErr(clErr, minioMetaBucket, tempUploadIDPath) - } - return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) - } - - // Rename the file to the actual location from temporary path. - err = storage.RenameFile(minioMetaBucket, tempUploadIDPath, minioMetaBucket, uploadIDPath) - if err != nil { - if derr := storage.DeleteFile(minioMetaBucket, tempUploadIDPath); derr != nil { - return "", toObjectErr(derr, minioMetaBucket, tempUploadIDPath) - } - return "", toObjectErr(err, minioMetaBucket, uploadIDPath) - } - return uploadID, nil - } - // uploadIDPath already exists. - // loop again to try with different uuid generated. - } -} - -// putObjectPartCommon - put object part. -func putObjectPartCommon(storage StorageAPI, bucket string, object string, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return "", BucketNameInvalid{Bucket: bucket} - } - // Verify whether the bucket exists. - if !isBucketExist(storage, bucket) { - return "", BucketNotFound{Bucket: bucket} - } - if !IsValidObjectName(object) { - return "", ObjectNameInvalid{Bucket: bucket, Object: object} - } - if !isUploadIDExists(storage, bucket, object, uploadID) { - return "", InvalidUploadID{UploadID: uploadID} - } - // Hold read lock on the uploadID so that no one aborts it. - nsMutex.RLock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - defer nsMutex.RUnlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - - // Hold write lock on the part so that there is no parallel upload on the part. - nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID, strconv.Itoa(partID))) - defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID, strconv.Itoa(partID))) - - partSuffix := fmt.Sprintf("%s.%.5d", uploadID, partID) - partSuffixPath := path.Join(tmpMetaPrefix, bucket, object, partSuffix) - fileWriter, err := storage.CreateFile(minioMetaBucket, partSuffixPath) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Initialize md5 writer. - md5Writer := md5.New() - - // Instantiate a new multi writer. - multiWriter := io.MultiWriter(md5Writer, fileWriter) - - // Instantiate checksum hashers and create a multiwriter. - if size > 0 { - if _, err = io.CopyN(multiWriter, data, size); err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - // Reader shouldn't have more data what mentioned in size argument. - // reading one more byte from the reader to validate it. - // expected to fail, success validates existence of more data in the reader. - if _, err = io.CopyN(ioutil.Discard, data, 1); err == nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", UnExpectedDataSize{Size: int(size)} - } - } else { - if _, err = io.Copy(multiWriter, data); err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - } - - newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) - if md5Hex != "" { - if newMD5Hex != md5Hex { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", BadDigest{md5Hex, newMD5Hex} - } - } - err = fileWriter.Close() - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", err - } - - partSuffixMD5 := fmt.Sprintf("%.5d.%s", partID, newMD5Hex) - partSuffixMD5Path := path.Join(mpartMetaPrefix, bucket, object, uploadID, partSuffixMD5) - if _, err = storage.StatFile(minioMetaBucket, partSuffixMD5Path); err == nil { - // Part already uploaded as md5sum matches with the previous part. - // Just delete the temporary file. - if err = storage.DeleteFile(minioMetaBucket, partSuffixPath); err != nil { - return "", toObjectErr(err, minioMetaBucket, partSuffixPath) - } - return newMD5Hex, nil - } - err = storage.RenameFile(minioMetaBucket, partSuffixPath, minioMetaBucket, partSuffixMD5Path) - if err != nil { - if derr := storage.DeleteFile(minioMetaBucket, partSuffixPath); derr != nil { - return "", toObjectErr(derr, minioMetaBucket, partSuffixPath) - } - return "", toObjectErr(err, minioMetaBucket, partSuffixMD5Path) - } - return newMD5Hex, nil -} - -// Wrapper to which removes all the uploaded parts after a successful -// complete multipart upload. -func cleanupUploadedParts(storage StorageAPI, bucket, object, uploadID string) error { - return cleanupDir(storage, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object, uploadID)) -} - -// abortMultipartUploadCommon - aborts a multipart upload, common -// function used by both object layers. -func abortMultipartUploadCommon(storage StorageAPI, bucket, object, uploadID string) error { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return BucketNameInvalid{Bucket: bucket} - } - if !isBucketExist(storage, bucket) { - return BucketNotFound{Bucket: bucket} - } - if !IsValidObjectName(object) { - return ObjectNameInvalid{Bucket: bucket, Object: object} - } - if !isUploadIDExists(storage, bucket, object, uploadID) { - return InvalidUploadID{UploadID: uploadID} - } - - // Hold lock so that there is no competing complete-multipart-upload or put-object-part. - nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - - if err := cleanupUploadedParts(storage, bucket, object, uploadID); err != nil { - return err - } - - // Validate if there are other incomplete upload-id's present for - // the object, if yes do not attempt to delete 'uploads.json'. - if entries, err := storage.ListDir(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)); err == nil { - if len(entries) > 1 { - return nil - } - } - - uploadsJSONPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) - if err := storage.DeleteFile(minioMetaBucket, uploadsJSONPath); err != nil { - return err - } - - return nil -} - -// isIncompleteMultipart - is object incomplete multipart. -func isIncompleteMultipart(storage StorageAPI, objectPath string) (bool, error) { - _, err := storage.StatFile(minioMetaBucket, path.Join(objectPath, uploadsJSONFile)) - if err != nil { - if err == errFileNotFound { - return false, nil - } - return false, err - } - return true, nil -} - -// listLeafEntries - lists all entries if a given prefixPath is a leaf -// directory, returns error if any - returns empty list if prefixPath -// is not a leaf directory. -func listLeafEntries(storage StorageAPI, prefixPath string) (entries []string, err error) { - var ok bool - if ok, err = isIncompleteMultipart(storage, prefixPath); err != nil { - return nil, err - } else if !ok { - return nil, nil - } - entries, err = storage.ListDir(minioMetaBucket, prefixPath) - if err != nil { - return nil, err - } - var newEntries []string - for _, entry := range entries { - if strings.HasSuffix(entry, slashSeparator) { - newEntries = append(newEntries, entry) - } - } - return newEntries, nil -} - -// listMetaBucketMultipartFiles - list all files at a given prefix inside minioMetaBucket. -func listMetaBucketMultipartFiles(layer ObjectLayer, prefixPath string, markerPath string, recursive bool, maxKeys int) (fileInfos []FileInfo, eof bool, err error) { - var storage StorageAPI - switch l := layer.(type) { - case fsObjects: - storage = l.storage - case xlObjects: - storage = l.storage - } - - if recursive && markerPath != "" { - markerPath = pathJoin(markerPath, incompleteFile) - } - - walker := lookupTreeWalk(layer, listParams{minioMetaBucket, recursive, markerPath, prefixPath}) - if walker == nil { - walker = startTreeWalk(layer, minioMetaBucket, prefixPath, markerPath, recursive) - } - - // newMaxKeys tracks the size of entries which are going to be - // returned back. - var newMaxKeys int - - // Following loop gathers and filters out special files inside - // minio meta volume. -outerLoop: - for { - walkResult, ok := <-walker.ch - if !ok { - // Closed channel. - eof = true - break - } - // For any walk error return right away. - if walkResult.err != nil { - // File not found or Disk not found is a valid case. - if walkResult.err == errFileNotFound || walkResult.err == errDiskNotFound { - return nil, true, nil - } - return nil, false, toObjectErr(walkResult.err, minioMetaBucket, prefixPath) - } - fi := walkResult.fileInfo - var entries []string - if fi.Mode.IsDir() { - // List all the entries if fi.Name is a leaf directory, if - // fi.Name is not a leaf directory then the resulting - // entries are empty. - entries, err = listLeafEntries(storage, fi.Name) - if err != nil { - return nil, false, err - } - } - if len(entries) > 0 { - // We reach here for non-recursive case and a leaf entry. - sort.Strings(entries) - for _, entry := range entries { - var fileInfo FileInfo - incompleteUploadFile := path.Join(fi.Name, entry, incompleteFile) - fileInfo, err = storage.StatFile(minioMetaBucket, incompleteUploadFile) - if err != nil { - return nil, false, err - } - fileInfo.Name = path.Join(fi.Name, entry) - fileInfos = append(fileInfos, fileInfo) - newMaxKeys++ - // If we have reached the maxKeys, it means we have listed - // everything that was requested. - if newMaxKeys == maxKeys { - break outerLoop - } - } - } else { - // We reach here for a non-recursive case non-leaf entry - // OR recursive case with fi.Name. - if !fi.Mode.IsDir() { // Do not skip non-recursive case directory entries. - // Validate if 'fi.Name' is incomplete multipart. - if !strings.HasSuffix(fi.Name, incompleteFile) { - continue - } - fi.Name = path.Dir(fi.Name) - } - fileInfos = append(fileInfos, fi) - newMaxKeys++ - // If we have reached the maxKeys, it means we have listed - // everything that was requested. - if newMaxKeys == maxKeys { - break - } - } - } - - if !eof && len(fileInfos) != 0 { - // EOF has not reached, hence save the walker channel to the map so that the walker go routine - // can continue from where it left off for the next list request. - lastFileInfo := fileInfos[len(fileInfos)-1] - markerPath = lastFileInfo.Name - saveTreeWalk(layer, listParams{minioMetaBucket, recursive, markerPath, prefixPath}, walker) - } - // Return entries here. - return fileInfos, eof, nil -} - -// FIXME: Currently the code sorts based on keyName/upload-id which is -// in correct based on the S3 specs. According to s3 specs we are -// supposed to only lexically sort keyNames and then for keyNames with -// multiple upload ids should be sorted based on the initiated time. -// Currently this case is not handled. - -// listMultipartUploadsCommon - lists all multipart uploads, common -// function for both object layers. -func listMultipartUploadsCommon(layer ObjectLayer, bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { - var storage StorageAPI - switch l := layer.(type) { - case xlObjects: - storage = l.storage - case fsObjects: - storage = l.storage - } - result := ListMultipartsInfo{} - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return ListMultipartsInfo{}, BucketNameInvalid{Bucket: bucket} - } - if !isBucketExist(storage, bucket) { - return ListMultipartsInfo{}, BucketNotFound{Bucket: bucket} - } - if !IsValidObjectPrefix(prefix) { - return ListMultipartsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} - } - // Verify if delimiter is anything other than '/', which we do not support. - if delimiter != "" && delimiter != slashSeparator { - return ListMultipartsInfo{}, UnsupportedDelimiter{ - Delimiter: delimiter, - } - } - // Verify if marker has prefix. - if keyMarker != "" && !strings.HasPrefix(keyMarker, prefix) { - return ListMultipartsInfo{}, InvalidMarkerPrefixCombination{ - Marker: keyMarker, - Prefix: prefix, - } - } - if uploadIDMarker != "" { - if strings.HasSuffix(keyMarker, slashSeparator) { - return result, InvalidUploadIDKeyCombination{ - UploadIDMarker: uploadIDMarker, - KeyMarker: keyMarker, - } - } - id, err := uuid.Parse(uploadIDMarker) - if err != nil { - return result, err - } - if id.IsZero() { - return result, MalformedUploadID{ - UploadID: uploadIDMarker, - } - } - } - - recursive := true - if delimiter == slashSeparator { - recursive = false - } - - result.IsTruncated = true - result.MaxUploads = maxUploads - - // Not using path.Join() as it strips off the trailing '/'. - multipartPrefixPath := pathJoin(mpartMetaPrefix, pathJoin(bucket, prefix)) - if prefix == "" { - // Should have a trailing "/" if prefix is "" - // For ex. multipartPrefixPath should be "multipart/bucket/" if prefix is "" - multipartPrefixPath += slashSeparator - } - multipartMarkerPath := "" - if keyMarker != "" { - keyMarkerPath := pathJoin(pathJoin(bucket, keyMarker), uploadIDMarker) - multipartMarkerPath = pathJoin(mpartMetaPrefix, keyMarkerPath) - } - - // List all the multipart files at prefixPath, starting with marker keyMarkerPath. - fileInfos, eof, err := listMetaBucketMultipartFiles(layer, multipartPrefixPath, multipartMarkerPath, recursive, maxUploads) - if err != nil { - return ListMultipartsInfo{}, err - } - - // Loop through all the received files fill in the multiparts result. - for _, fi := range fileInfos { - var objectName string - var uploadID string - if fi.Mode.IsDir() { - // All directory entries are common prefixes. - uploadID = "" // Upload ids are empty for CommonPrefixes. - objectName = strings.TrimPrefix(fi.Name, retainSlash(pathJoin(mpartMetaPrefix, bucket))) - result.CommonPrefixes = append(result.CommonPrefixes, objectName) - } else { - uploadID = path.Base(fi.Name) - objectName = strings.TrimPrefix(path.Dir(fi.Name), retainSlash(pathJoin(mpartMetaPrefix, bucket))) - result.Uploads = append(result.Uploads, uploadMetadata{ - Object: objectName, - UploadID: uploadID, - Initiated: fi.ModTime, - }) - } - result.NextKeyMarker = objectName - result.NextUploadIDMarker = uploadID - } - result.IsTruncated = !eof - if !result.IsTruncated { - result.NextKeyMarker = "" - result.NextUploadIDMarker = "" - } - return result, nil -} - -// ListObjectParts - list object parts, common function across both object layers. -func listObjectPartsCommon(storage StorageAPI, bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return ListPartsInfo{}, BucketNameInvalid{Bucket: bucket} - } - // Verify whether the bucket exists. - if !isBucketExist(storage, bucket) { - return ListPartsInfo{}, BucketNotFound{Bucket: bucket} - } - if !IsValidObjectName(object) { - return ListPartsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: object} - } - if !isUploadIDExists(storage, bucket, object, uploadID) { - return ListPartsInfo{}, InvalidUploadID{UploadID: uploadID} - } - // Hold lock so that there is no competing abort-multipart-upload or complete-multipart-upload. - nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - result := ListPartsInfo{} - entries, err := storage.ListDir(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object, uploadID)) - if err != nil { - return result, err - } - sort.Strings(entries) - var newEntries []string - for _, entry := range entries { - newEntries = append(newEntries, path.Base(entry)) - } - idx := sort.SearchStrings(newEntries, fmt.Sprintf("%.5d.", partNumberMarker+1)) - newEntries = newEntries[idx:] - count := maxParts - for _, entry := range newEntries { - fi, err := storage.StatFile(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object, uploadID, entry)) - splitEntry := strings.SplitN(entry, ".", 2) - partStr := splitEntry[0] - etagStr := splitEntry[1] - partNum, err := strconv.Atoi(partStr) - if err != nil { - return ListPartsInfo{}, err - } - result.Parts = append(result.Parts, partInfo{ - PartNumber: partNum, - LastModified: fi.ModTime, - ETag: etagStr, - Size: fi.Size, - }) - count-- - if count == 0 { - break - } - } - // If listed entries are more than maxParts, we set IsTruncated as true. - if len(newEntries) > len(result.Parts) { - result.IsTruncated = true - // Make sure to fill next part number marker if IsTruncated is - // true for subsequent listing. - nextPartNumberMarker := result.Parts[len(result.Parts)-1].PartNumber - result.NextPartNumberMarker = nextPartNumberMarker - } - result.Bucket = bucket - result.Object = object - result.UploadID = uploadID - result.MaxParts = maxParts - return result, nil -} - -// isUploadIDExists - verify if a given uploadID exists and is valid. -func isUploadIDExists(storage StorageAPI, bucket, object, uploadID string) bool { - uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID, incompleteFile) - st, err := storage.StatFile(minioMetaBucket, uploadIDPath) - if err != nil { - if err == errFileNotFound { - return false - } - errorIf(err, "Stat failed on "+minioMetaBucket+"/"+uploadIDPath+".") - return false - } - return st.Mode.IsRegular() -} diff --git a/object-common.go b/object-common.go index a95615b1a..330bee093 100644 --- a/object-common.go +++ b/object-common.go @@ -17,27 +17,92 @@ package main import ( - "sort" + "path/filepath" "strings" + "sync" ) -// Common initialization needed for both object layers. -func initObjectLayer(storageDisks ...StorageAPI) error { +const ( + // Block size used for all internal operations version 1. + blockSizeV1 = 10 * 1024 * 1024 // 10MiB. +) + +// House keeping code needed for FS. +func fsHouseKeeping(storageDisk StorageAPI) error { + // Attempt to create `.minio`. + err := storageDisk.MakeVol(minioMetaBucket) + if err != nil { + if err != errVolumeExists && err != errDiskNotFound { + return err + } + } + // Cleanup all temp entries upon start. + err = cleanupDir(storageDisk, minioMetaBucket, tmpMetaPrefix) + if err != nil { + return err + } + return nil +} + +// Depending on the disk type network or local, initialize storage API. +func newStorageAPI(disk string) (storage StorageAPI, err error) { + if !strings.ContainsRune(disk, ':') || filepath.VolumeName(disk) != "" { + // Initialize filesystem storage API. + return newPosix(disk) + } + // Initialize rpc client storage API. + return newRPCClient(disk) +} + +// House keeping code needed for XL. +func xlHouseKeeping(storageDisks []StorageAPI) error { // This happens for the first time, but keep this here since this // is the only place where it can be made expensive optimizing all // other calls. Create minio meta volume, if it doesn't exist yet. - for _, storage := range storageDisks { - if err := storage.MakeVol(minioMetaBucket); err != nil { - if err != errVolumeExists && err != errDiskNotFound { - return toObjectErr(err, minioMetaBucket) + var wg = &sync.WaitGroup{} + + // Initialize errs to collect errors inside go-routine. + var errs = make([]error, len(storageDisks)) + + // Initialize all disks in parallel. + for index, disk := range storageDisks { + if disk == nil { + errs[index] = errDiskNotFound + continue + } + wg.Add(1) + go func(index int, disk StorageAPI) { + // Indicate this wait group is done. + defer wg.Done() + + // Attempt to create `.minio`. + err := disk.MakeVol(minioMetaBucket) + if err != nil && err != errVolumeExists && err != errDiskNotFound { + errs[index] = err + return } - } - // Cleanup all temp entries upon start. - err := cleanupDir(storage, minioMetaBucket, tmpMetaPrefix) - if err != nil { - return toObjectErr(err, minioMetaBucket, tmpMetaPrefix) - } + // Cleanup all temp entries upon start. + err = cleanupDir(disk, minioMetaBucket, tmpMetaPrefix) + if err != nil { + errs[index] = err + return + } + errs[index] = nil + }(index, disk) } + + // Wait for all cleanup to finish. + wg.Wait() + + // Return upon first error. + for _, err := range errs { + if err == nil { + continue + } + return toObjectErr(err, minioMetaBucket, tmpMetaPrefix) + } + + // Return success here. return nil } @@ -67,194 +132,6 @@ func cleanupDir(storage StorageAPI, volume, dirPath string) error { } return nil } - return delFunc(retainSlash(pathJoin(dirPath))) -} - -/// Common object layer functions. - -// makeBucket - create a bucket, is a common function for both object layers. -func makeBucket(storage StorageAPI, bucket string) error { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return BucketNameInvalid{Bucket: bucket} - } - if err := storage.MakeVol(bucket); err != nil { - return toObjectErr(err, bucket) - } - return nil -} - -// getBucketInfo - fetch bucket info, is a common function for both object layers. -func getBucketInfo(storage StorageAPI, bucket string) (BucketInfo, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return BucketInfo{}, BucketNameInvalid{Bucket: bucket} - } - vi, err := storage.StatVol(bucket) - if err != nil { - return BucketInfo{}, toObjectErr(err, bucket) - } - return BucketInfo{ - Name: bucket, - Created: vi.Created, - Total: vi.Total, - Free: vi.Free, - }, nil -} - -// listBuckets - list all buckets, is a common function for both object layers. -func listBuckets(storage StorageAPI) ([]BucketInfo, error) { - var bucketInfos []BucketInfo - vols, err := storage.ListVols() - if err != nil { - return nil, toObjectErr(err) - } - for _, vol := range vols { - // StorageAPI can send volume names which are incompatible - // with buckets, handle it and skip them. - if !IsValidBucketName(vol.Name) { - continue - } - bucketInfos = append(bucketInfos, BucketInfo{ - Name: vol.Name, - Created: vol.Created, - Total: vol.Total, - Free: vol.Free, - }) - } - sort.Sort(byBucketName(bucketInfos)) - return bucketInfos, nil -} - -// deleteBucket - deletes a bucket, is a common function for both the layers. -func deleteBucket(storage StorageAPI, bucket string) error { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return BucketNameInvalid{Bucket: bucket} - } - if err := storage.DeleteVol(bucket); err != nil { - return toObjectErr(err, bucket) - } - return nil -} - -func listObjectsCommon(layer ObjectLayer, bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { - var storage StorageAPI - switch l := layer.(type) { - case xlObjects: - storage = l.storage - case fsObjects: - storage = l.storage - } - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return ListObjectsInfo{}, BucketNameInvalid{Bucket: bucket} - } - // Verify if bucket exists. - if !isBucketExist(storage, bucket) { - return ListObjectsInfo{}, BucketNotFound{Bucket: bucket} - } - if !IsValidObjectPrefix(prefix) { - return ListObjectsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} - } - // Verify if delimiter is anything other than '/', which we do not support. - if delimiter != "" && delimiter != slashSeparator { - return ListObjectsInfo{}, UnsupportedDelimiter{ - Delimiter: delimiter, - } - } - // Verify if marker has prefix. - if marker != "" { - if !strings.HasPrefix(marker, prefix) { - return ListObjectsInfo{}, InvalidMarkerPrefixCombination{ - Marker: marker, - Prefix: prefix, - } - } - } - - // With max keys of zero we have reached eof, return right here. - if maxKeys == 0 { - return ListObjectsInfo{}, nil - } - - // Over flowing count - reset to maxObjectList. - if maxKeys < 0 || maxKeys > maxObjectList { - maxKeys = maxObjectList - } - - // Default is recursive, if delimiter is set then list non recursive. - recursive := true - if delimiter == slashSeparator { - recursive = false - } - - walker := lookupTreeWalk(layer, listParams{bucket, recursive, marker, prefix}) - if walker == nil { - walker = startTreeWalk(layer, bucket, prefix, marker, recursive) - } - var fileInfos []FileInfo - var eof bool - var nextMarker string - for i := 0; i < maxKeys; { - walkResult, ok := <-walker.ch - if !ok { - // Closed channel. - eof = true - break - } - // For any walk error return right away. - if walkResult.err != nil { - // File not found is a valid case. - if walkResult.err == errFileNotFound { - return ListObjectsInfo{}, nil - } - return ListObjectsInfo{}, toObjectErr(walkResult.err, bucket, prefix) - } - fileInfo := walkResult.fileInfo - nextMarker = fileInfo.Name - fileInfos = append(fileInfos, fileInfo) - if walkResult.end { - eof = true - break - } - i++ - } - params := listParams{bucket, recursive, nextMarker, prefix} - if !eof { - saveTreeWalk(layer, params, walker) - } - - result := ListObjectsInfo{IsTruncated: !eof} - for _, fileInfo := range fileInfos { - // With delimiter set we fill in NextMarker and Prefixes. - if delimiter == slashSeparator { - result.NextMarker = fileInfo.Name - if fileInfo.Mode.IsDir() { - result.Prefixes = append(result.Prefixes, fileInfo.Name) - continue - } - } - result.Objects = append(result.Objects, ObjectInfo{ - Name: fileInfo.Name, - ModTime: fileInfo.ModTime, - Size: fileInfo.Size, - IsDir: false, - }) - } - return result, nil -} - -// checks whether bucket exists. -func isBucketExist(storage StorageAPI, bucketName string) bool { - // Check whether bucket exists. - _, err := storage.StatVol(bucketName) - if err != nil { - if err == errVolumeNotFound { - return false - } - errorIf(err, "Stat failed on bucket "+bucketName+".") - return false - } - return true + err := delFunc(retainSlash(pathJoin(dirPath))) + return err } diff --git a/object-datatypes.go b/object-datatypes.go index a893dfd8d..f25e8c978 100644 --- a/object-datatypes.go +++ b/object-datatypes.go @@ -18,12 +18,16 @@ package main import "time" +// StorageInfo - represents total capacity of underlying storage. +type StorageInfo struct { + Total int64 // Total disk space. + Free int64 // Free total available disk space. +} + // BucketInfo - bucket name and create date type BucketInfo struct { Name string Created time.Time - Total int64 - Free int64 } // ObjectInfo - object info. diff --git a/object-errors.go b/object-errors.go index f22272738..78431ffb5 100644 --- a/object-errors.go +++ b/object-errors.go @@ -40,10 +40,6 @@ func toObjectErr(err error, params ...string) error { } case errDiskFull: return StorageFull{} - case errReadQuorum: - return InsufficientReadQuorum{} - case errWriteQuorum: - return InsufficientWriteQuorum{} case errIsNotRegular, errFileAccessDenied: if len(params) >= 2 { return ObjectExistsAsDirectory{ @@ -65,6 +61,10 @@ func toObjectErr(err error, params ...string) error { Object: params[1], } } + case errXLReadQuorum: + return InsufficientReadQuorum{} + case errXLWriteQuorum: + return InsufficientWriteQuorum{} case io.ErrUnexpectedEOF, io.ErrShortWrite: return IncompleteBody{} } diff --git a/object-handlers.go b/object-handlers.go index 1b8bcc33b..fc2757845 100644 --- a/object-handlers.go +++ b/object-handlers.go @@ -124,38 +124,22 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req return } - // Get the object. - startOffset := hrange.start - readCloser, err := api.ObjectAPI.GetObject(bucket, object, startOffset) - if err != nil { - errorIf(err, "Unable to read object.") - apiErr := toAPIErrorCode(err) - if apiErr == ErrNoSuchKey { - apiErr = errAllowableObjectNotFound(bucket, r) - } - writeErrorResponse(w, r, apiErr, r.URL.Path) - return - } - defer readCloser.Close() // Close after this handler returns. - // Set standard object headers. setObjectHeaders(w, objInfo, hrange) // Set any additional requested response headers. setGetRespHeaders(w, r.URL.Query()) - if hrange.length > 0 { - if _, err := io.CopyN(w, readCloser, hrange.length); err != nil { - errorIf(err, "Writing to client failed.") - // Do not send error response here, since client could have died. - return - } - } else { - if _, err := io.Copy(w, readCloser); err != nil { - errorIf(err, "Writing to client failed.") - // Do not send error response here, since client could have died. - return - } + // Get the object. + startOffset := hrange.start + length := hrange.length + if length == 0 { + length = objInfo.Size - startOffset + } + if err := api.ObjectAPI.GetObject(bucket, object, startOffset, length, w); err != nil { + errorIf(err, "Writing to client failed.") + // Do not send error response here, client would have already died. + return } } @@ -393,14 +377,19 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re return } - startOffset := int64(0) // Read the whole file. - // Get the object. - readCloser, err := api.ObjectAPI.GetObject(sourceBucket, sourceObject, startOffset) - if err != nil { - errorIf(err, "Unable to read an object.") - writeErrorResponse(w, r, toAPIErrorCode(err), objectSource) - return - } + pipeReader, pipeWriter := io.Pipe() + go func() { + startOffset := int64(0) // Read the whole file. + // Get the object. + gErr := api.ObjectAPI.GetObject(sourceBucket, sourceObject, startOffset, objInfo.Size, pipeWriter) + if gErr != nil { + errorIf(gErr, "Unable to read an object.") + pipeWriter.CloseWithError(gErr) + return + } + pipeWriter.Close() // Close. + }() + // Size of object. size := objInfo.Size @@ -413,7 +402,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re // same md5sum as the source. // Create the object. - md5Sum, err := api.ObjectAPI.PutObject(bucket, object, size, readCloser, metadata) + md5Sum, err := api.ObjectAPI.PutObject(bucket, object, size, pipeReader, metadata) if err != nil { errorIf(err, "Unable to create an object.") writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) @@ -434,7 +423,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re // write success response. writeSuccessResponse(w, encodedSuccessResponse) // Explicitly close the reader, to avoid fd leaks. - readCloser.Close() + pipeReader.Close() } // checkCopySource implements x-amz-copy-source-if-modified-since and @@ -887,10 +876,6 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht writeErrorResponse(w, r, ErrInvalidMaxParts, r.URL.Path) return } - if maxParts == 0 { - maxParts = maxPartsList - } - listPartsInfo, err := api.ObjectAPI.ListObjectParts(bucket, object, uploadID, partNumberMarker, maxParts) if err != nil { errorIf(err, "Unable to list uploaded parts.") @@ -945,6 +930,10 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) return } + if len(complMultipartUpload.Parts) == 0 { + writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) + return + } if !sort.IsSorted(completedParts(complMultipartUpload.Parts)) { writeErrorResponse(w, r, ErrInvalidPartOrder, r.URL.Path) return diff --git a/object-interface.go b/object-interface.go index 6c6731892..43e4b6bd5 100644 --- a/object-interface.go +++ b/object-interface.go @@ -20,6 +20,9 @@ import "io" // ObjectLayer implements primitives for object API layer. type ObjectLayer interface { + // Storage operations. + StorageInfo() StorageInfo + // Bucket operations. MakeBucket(bucket string) error GetBucketInfo(bucket string) (bucketInfo BucketInfo, err error) @@ -28,7 +31,7 @@ type ObjectLayer interface { ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (result ListObjectsInfo, err error) // Object operations. - GetObject(bucket, object string, startOffset int64) (reader io.ReadCloser, err error) + GetObject(bucket, object string, startOffset int64, length int64, writer io.Writer) (err error) GetObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) PutObject(bucket, object string, size int64, data io.Reader, metadata map[string]string) (md5 string, err error) DeleteObject(bucket, object string) error diff --git a/object-utils.go b/object-utils.go index c0b0a59ff..ec7abc2cd 100644 --- a/object-utils.go +++ b/object-utils.go @@ -19,15 +19,13 @@ package main import ( "crypto/md5" "encoding/hex" - "errors" "fmt" - "io" "path" "regexp" "strings" "unicode/utf8" - "github.com/minio/minio/pkg/safe" + "github.com/skyrings/skyring-common/tools/uuid" ) const ( @@ -123,6 +121,20 @@ func pathJoin(elem ...string) string { return path.Join(elem...) + trailingSlash } +// getUUID() - get a unique uuid. +func getUUID() (uuidStr string) { + for { + uuid, err := uuid.New() + if err != nil { + errorIf(err, "Unable to initialize uuid") + continue + } + uuidStr = uuid.String() + break + } + return uuidStr +} + // Create an s3 compatible MD5sum for complete multipart transaction. func completeMultipartMD5(parts ...completePart) (string, error) { var finalMD5Bytes []byte @@ -145,18 +157,3 @@ type byBucketName []BucketInfo func (d byBucketName) Len() int { return len(d) } func (d byBucketName) Swap(i, j int) { d[i], d[j] = d[j], d[i] } func (d byBucketName) Less(i, j int) bool { return d[i].Name < d[j].Name } - -// safeCloseAndRemove - safely closes and removes underlying temporary -// file writer if possible. -func safeCloseAndRemove(writer io.WriteCloser) error { - // If writer is a safe file, Attempt to close and remove. - safeWriter, ok := writer.(*safe.File) - if ok { - return safeWriter.CloseAndRemove() - } - wCloser, ok := writer.(*waitCloser) - if ok { - return wCloser.CloseWithError(errors.New("Close and error out.")) - } - return nil -} diff --git a/object_api_suite_test.go b/object_api_suite_test.go index 4363ac21e..d817b6a8a 100644 --- a/object_api_suite_test.go +++ b/object_api_suite_test.go @@ -20,15 +20,12 @@ import ( "bytes" "crypto/md5" "encoding/hex" - "io" "math/rand" "strconv" "gopkg.in/check.v1" ) -// TODO - enable all the commented tests. - // APITestSuite - collection of API tests. func APITestSuite(c *check.C, create func() ObjectLayer) { testMakeBucket(c, create) @@ -135,24 +132,21 @@ func testMultipleObjectCreation(c *check.C, create func() ObjectLayer) { objects[key] = []byte(randomString) metadata := make(map[string]string) metadata["md5Sum"] = expectedMD5Sumhex - md5Sum, err := obj.PutObject("bucket", key, int64(len(randomString)), bytes.NewBufferString(randomString), metadata) + var md5Sum string + md5Sum, err = obj.PutObject("bucket", key, int64(len(randomString)), bytes.NewBufferString(randomString), metadata) c.Assert(err, check.IsNil) c.Assert(md5Sum, check.Equals, expectedMD5Sumhex) } for key, value := range objects { var byteBuffer bytes.Buffer - r, err := obj.GetObject("bucket", key, 0) + err = obj.GetObject("bucket", key, 0, int64(len(value)), &byteBuffer) c.Assert(err, check.IsNil) - _, e := io.Copy(&byteBuffer, r) - c.Assert(e, check.IsNil) c.Assert(byteBuffer.Bytes(), check.DeepEquals, value) - c.Assert(r.Close(), check.IsNil) objInfo, err := obj.GetObjectInfo("bucket", key) c.Assert(err, check.IsNil) c.Assert(objInfo.Size, check.Equals, int64(len(value))) - r.Close() } } @@ -269,16 +263,14 @@ func testObjectOverwriteWorks(c *check.C, create func() ObjectLayer) { _, err = obj.PutObject("bucket", "object", int64(len("The list of parts was not in ascending order. The parts list must be specified in order by part number.")), bytes.NewBufferString("The list of parts was not in ascending order. The parts list must be specified in order by part number."), nil) c.Assert(err, check.IsNil) - _, err = obj.PutObject("bucket", "object", int64(len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")), bytes.NewBufferString("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."), nil) + length := int64(len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")) + _, err = obj.PutObject("bucket", "object", length, bytes.NewBufferString("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."), nil) c.Assert(err, check.IsNil) var bytesBuffer bytes.Buffer - r, err := obj.GetObject("bucket", "object", 0) + err = obj.GetObject("bucket", "object", 0, length, &bytesBuffer) c.Assert(err, check.IsNil) - _, e := io.Copy(&bytesBuffer, r) - c.Assert(e, check.IsNil) c.Assert(string(bytesBuffer.Bytes()), check.Equals, "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.") - c.Assert(r.Close(), check.IsNil) } // Tests validate that bucket operation on non-existent bucket fails. @@ -305,17 +297,14 @@ func testPutObjectInSubdir(c *check.C, create func() ObjectLayer) { err := obj.MakeBucket("bucket") c.Assert(err, check.IsNil) - _, err = obj.PutObject("bucket", "dir1/dir2/object", int64(len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")), bytes.NewBufferString("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."), nil) + length := int64(len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")) + _, err = obj.PutObject("bucket", "dir1/dir2/object", length, bytes.NewBufferString("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."), nil) c.Assert(err, check.IsNil) var bytesBuffer bytes.Buffer - r, err := obj.GetObject("bucket", "dir1/dir2/object", 0) + err = obj.GetObject("bucket", "dir1/dir2/object", 0, length, &bytesBuffer) c.Assert(err, check.IsNil) - n, e := io.Copy(&bytesBuffer, r) - c.Assert(e, check.IsNil) c.Assert(len(bytesBuffer.Bytes()), check.Equals, len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")) - c.Assert(int64(len(bytesBuffer.Bytes())), check.Equals, int64(n)) - c.Assert(r.Close(), check.IsNil) } // Tests validate ListBuckets. @@ -386,7 +375,8 @@ func testNonExistantObjectInBucket(c *check.C, create func() ObjectLayer) { err := obj.MakeBucket("bucket") c.Assert(err, check.IsNil) - _, err = obj.GetObject("bucket", "dir1", 0) + var bytesBuffer bytes.Buffer + err = obj.GetObject("bucket", "dir1", 0, 10, &bytesBuffer) c.Assert(err, check.Not(check.IsNil)) switch err := err.(type) { case ObjectNotFound: @@ -405,7 +395,8 @@ func testGetDirectoryReturnsObjectNotFound(c *check.C, create func() ObjectLayer _, err = obj.PutObject("bucket", "dir1/dir3/object", int64(len("The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.")), bytes.NewBufferString("One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag."), nil) c.Assert(err, check.IsNil) - _, err = obj.GetObject("bucket", "dir1", 0) + var bytesBuffer bytes.Buffer + err = obj.GetObject("bucket", "dir1", 0, 10, &bytesBuffer) switch err := err.(type) { case ObjectNotFound: c.Assert(err.Bucket, check.Equals, "bucket") @@ -415,7 +406,7 @@ func testGetDirectoryReturnsObjectNotFound(c *check.C, create func() ObjectLayer c.Assert(err, check.Equals, "ObjectNotFound") } - _, err = obj.GetObject("bucket", "dir1/", 0) + err = obj.GetObject("bucket", "dir1/", 0, 10, &bytesBuffer) switch err := err.(type) { case ObjectNameInvalid: c.Assert(err.Bucket, check.Equals, "bucket") diff --git a/pkg/safe/safe.go b/pkg/safe/safe.go index fca649aec..8d5413c7e 100644 --- a/pkg/safe/safe.go +++ b/pkg/safe/safe.go @@ -16,115 +16,109 @@ // NOTE - Rename() not guaranteed to be safe on all filesystems which are not fully POSIX compatible -// Package safe provides safe file write semantics by leveraging Rename's() safeity. package safe import ( - "io" + "errors" "io/ioutil" "os" "path/filepath" ) -// Vault - vault is an interface for different implementations of safe -// i/o semantics. -type Vault interface { - io.ReadWriteCloser - SyncClose() error - CloseAndRemove() error -} - -// File provides for safe file writes. +// File represents safe file descriptor. type File struct { - *os.File - file string + name string + tmpfile *os.File + closed bool + aborted bool } -// SyncClose sync file to disk and close, returns an error if any -func (f *File) SyncClose() error { - // sync to the disk - if err := f.File.Sync(); err != nil { - return err +// Write writes len(b) bytes to the temporary File. In case of error, the temporary file is removed. +func (file *File) Write(b []byte) (n int, err error) { + if file.aborted { + err = errors.New("write on aborted file") + return } - // Close the fd. - if err := f.Close(); err != nil { - return err + if file.closed { + err = errors.New("write on closed file") + return } - return nil + + defer func() { + if err != nil { + os.Remove(file.tmpfile.Name()) + file.aborted = true + } + }() + + n, err = file.tmpfile.Write(b) + return } -// Close the file, returns an error if any -func (f *File) Close() error { - // Close the embedded fd. - if err := f.File.Close(); err != nil { - return err +// Close closes the temporary File and renames to the named file. In case of error, the temporary file is removed. +func (file *File) Close() (err error) { + defer func() { + if err != nil { + os.Remove(file.tmpfile.Name()) + file.aborted = true + } + }() + + if file.aborted || file.closed { + return } - // Safe rename to final destination - if err := os.Rename(f.Name(), f.file); err != nil { - return err + + if err = file.tmpfile.Close(); err != nil { + return } - return nil + + err = os.Rename(file.tmpfile.Name(), file.name) + + file.closed = true + return } -// CloseAndRemove closes the temp file, and safely removes it. Returns -// error if any. -func (f *File) CloseAndRemove() error { - // close the embedded fd - f.File.Close() - - // Remove the temp file. - if err := os.Remove(f.Name()); err != nil { - return err +// Abort aborts the temporary File by closing and removing the temporary file. +func (file *File) Abort() (err error) { + if file.aborted || file.closed { + return } - return nil + + file.tmpfile.Close() + err = os.Remove(file.tmpfile.Name()) + file.aborted = true + return } -// CreateFile creates a new file at filePath for safe writes, it also -// creates parent directories if they don't exist. -func CreateFile(filePath string) (*File, error) { - return CreateFileWithPrefix(filePath, "$deleteme.") -} - -// CreateFileWithSuffix is similar to CreateFileWithPrefix, but the -// second argument is treated as suffix for the temporary files. -func CreateFileWithSuffix(filePath string, suffix string) (*File, error) { - // If parent directories do not exist, ioutil.TempFile doesn't create them - // handle such a case with os.MkdirAll() - if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil { +// CreateFile creates the named file safely from unique temporary file. +// The temporary file is renamed to the named file upon successful close +// to safeguard intermediate state in the named file. The temporary file +// is created in the name of the named file with suffixed unique number +// and prefixed "$tmpfile" string. While creating the temporary file, +// missing parent directories are also created. The temporary file is +// removed if case of any intermediate failure. Not removed temporary +// files can be cleaned up by identifying them using "$tmpfile" prefix +// string. +func CreateFile(name string) (*File, error) { + // ioutil.TempFile() fails if parent directory is missing. + // Create parent directory to avoid such error. + dname := filepath.Dir(name) + if err := os.MkdirAll(dname, 0700); err != nil { return nil, err } - f, err := ioutil.TempFile(filepath.Dir(filePath), filepath.Base(filePath)+suffix) + + fname := filepath.Base(name) + tmpfile, err := ioutil.TempFile(dname, "$tmpfile."+fname+".") if err != nil { return nil, err } - if err = os.Chmod(f.Name(), 0600); err != nil { - if err = os.Remove(f.Name()); err != nil { - return nil, err - } - return nil, err - } - return &File{File: f, file: filePath}, nil -} -// CreateFileWithPrefix creates a new file at filePath for safe -// writes, it also creates parent directories if they don't exist. -// prefix specifies the prefix of the temporary files so that cleaning -// stale temp files is easy. -func CreateFileWithPrefix(filePath string, prefix string) (*File, error) { - // If parent directories do not exist, ioutil.TempFile doesn't create them - // handle such a case with os.MkdirAll() - if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil { - return nil, err - } - f, err := ioutil.TempFile(filepath.Dir(filePath), prefix+filepath.Base(filePath)) - if err != nil { - return nil, err - } - if err = os.Chmod(f.Name(), 0600); err != nil { - if err = os.Remove(f.Name()); err != nil { - return nil, err + if err = os.Chmod(tmpfile.Name(), 0600); err != nil { + if rerr := os.Remove(tmpfile.Name()); rerr != nil { + err = rerr } return nil, err } - return &File{File: f, file: filePath}, nil + + return &File{name: name, tmpfile: tmpfile}, nil } diff --git a/pkg/safe/safe_test.go b/pkg/safe/safe_test.go index 70af3b146..795f0c32e 100644 --- a/pkg/safe/safe_test.go +++ b/pkg/safe/safe_test.go @@ -34,13 +34,14 @@ type MySuite struct { var _ = Suite(&MySuite{}) func (s *MySuite) SetUpSuite(c *C) { - root, err := ioutil.TempDir(os.TempDir(), "safe-") + root, err := ioutil.TempDir(os.TempDir(), "safe_test.go.") c.Assert(err, IsNil) s.root = root } func (s *MySuite) TearDownSuite(c *C) { - os.RemoveAll(s.root) + err := os.Remove(s.root) + c.Assert(err, IsNil) } func (s *MySuite) TestSafe(c *C) { @@ -52,15 +53,17 @@ func (s *MySuite) TestSafe(c *C) { c.Assert(err, IsNil) _, err = os.Stat(filepath.Join(s.root, "testfile")) c.Assert(err, IsNil) + err = os.Remove(filepath.Join(s.root, "testfile")) + c.Assert(err, IsNil) } -func (s *MySuite) TestSafeRemove(c *C) { +func (s *MySuite) TestSafeAbort(c *C) { f, err := CreateFile(filepath.Join(s.root, "purgefile")) c.Assert(err, IsNil) _, err = os.Stat(filepath.Join(s.root, "purgefile")) c.Assert(err, Not(IsNil)) - err = f.CloseAndRemove() + err = f.Abort() c.Assert(err, IsNil) - err = f.Close() + _, err = os.Stat(filepath.Join(s.root, "purgefile")) c.Assert(err, Not(IsNil)) } diff --git a/posix.go b/posix.go index bc0de22e9..c543be094 100644 --- a/posix.go +++ b/posix.go @@ -17,23 +17,24 @@ package main import ( + "bytes" "io" "os" slashpath "path" + "path/filepath" "runtime" "strings" "syscall" "github.com/minio/minio/pkg/disk" - "github.com/minio/minio/pkg/safe" ) const ( fsMinSpacePercent = 5 ) -// fsStorage - implements StorageAPI interface. -type fsStorage struct { +// posix - implements StorageAPI interface. +type posix struct { diskPath string minFreeDisk int64 } @@ -90,7 +91,7 @@ func newPosix(diskPath string) (StorageAPI, error) { if diskPath == "" { return nil, errInvalidArgument } - fs := fsStorage{ + fs := posix{ diskPath: diskPath, minFreeDisk: fsMinSpacePercent, // Minimum 5% disk should be free. } @@ -169,7 +170,7 @@ func listVols(dirPath string) ([]VolInfo, error) { // corresponding valid volume names on the backend in a platform // compatible way for all operating systems. If volume is not found // an error is generated. -func (s fsStorage) getVolDir(volume string) (string, error) { +func (s posix) getVolDir(volume string) (string, error) { if !isValidVolname(volume) { return "", errInvalidArgument } @@ -181,7 +182,7 @@ func (s fsStorage) getVolDir(volume string) (string, error) { } // Make a volume entry. -func (s fsStorage) MakeVol(volume string) (err error) { +func (s posix) MakeVol(volume string) (err error) { // Validate if disk is free. if err = checkDiskFree(s.diskPath, s.minFreeDisk); err != nil { return err @@ -201,16 +202,7 @@ func (s fsStorage) MakeVol(volume string) (err error) { } // ListVols - list volumes. -func (s fsStorage) ListVols() (volsInfo []VolInfo, err error) { - // Get disk info to be populated for VolInfo. - var diskInfo disk.Info - diskInfo, err = disk.GetInfo(s.diskPath) - if err != nil { - if os.IsNotExist(err) { - return nil, errDiskNotFound - } - return nil, err - } +func (s posix) ListVols() (volsInfo []VolInfo, err error) { volsInfo, err = listVols(s.diskPath) if err != nil { return nil, err @@ -219,9 +211,6 @@ func (s fsStorage) ListVols() (volsInfo []VolInfo, err error) { volInfo := VolInfo{ Name: vol.Name, Created: vol.Created, - Total: diskInfo.Total, - Free: diskInfo.Free, - FSType: diskInfo.FSType, } volsInfo[i] = volInfo } @@ -229,7 +218,12 @@ func (s fsStorage) ListVols() (volsInfo []VolInfo, err error) { } // StatVol - get volume info. -func (s fsStorage) StatVol(volume string) (volInfo VolInfo, err error) { +func (s posix) StatVol(volume string) (volInfo VolInfo, err error) { + // Validate if disk is free. + if err = checkDiskFree(s.diskPath, s.minFreeDisk); err != nil { + return VolInfo{}, err + } + // Verify if volume is valid and it exists. volumeDir, err := s.getVolDir(volume) if err != nil { @@ -244,29 +238,17 @@ func (s fsStorage) StatVol(volume string) (volInfo VolInfo, err error) { } return VolInfo{}, err } - // Get disk info, to be returned back along with volume info. - var diskInfo disk.Info - diskInfo, err = disk.GetInfo(s.diskPath) - if err != nil { - if os.IsNotExist(err) { - return VolInfo{}, errDiskNotFound - } - return VolInfo{}, err - } // As os.Stat() doesn't carry other than ModTime(), use ModTime() // as CreatedTime. createdTime := st.ModTime() return VolInfo{ Name: volume, Created: createdTime, - Free: diskInfo.Free, - Total: diskInfo.Total, - FSType: diskInfo.FSType, }, nil } // DeleteVol - delete a volume. -func (s fsStorage) DeleteVol(volume string) error { +func (s posix) DeleteVol(volume string) error { // Verify if volume is valid and it exists. volumeDir, err := s.getVolDir(volume) if err != nil { @@ -291,7 +273,7 @@ func (s fsStorage) DeleteVol(volume string) error { // ListDir - return all the entries at the given directory path. // If an entry is a directory it will be returned with a trailing "/". -func (s fsStorage) ListDir(volume, dirPath string) ([]string, error) { +func (s posix) ListDir(volume, dirPath string) ([]string, error) { // Verify if volume is valid and it exists. volumeDir, err := s.getVolDir(volume) if err != nil { @@ -308,91 +290,119 @@ func (s fsStorage) ListDir(volume, dirPath string) ([]string, error) { return readDir(pathJoin(volumeDir, dirPath)) } -// ReadFile - read a file at a given offset. -func (s fsStorage) ReadFile(volume string, path string, offset int64) (readCloser io.ReadCloser, err error) { +// ReadFile reads exactly len(buf) bytes into buf. It returns the +// number of bytes copied. The error is EOF only if no bytes were +// read. On return, n == len(buf) if and only if err == nil. n == 0 +// for io.EOF. Additionally ReadFile also starts reading from an +// offset. +func (s posix) ReadFile(volume string, path string, offset int64, buf []byte) (n int64, err error) { volumeDir, err := s.getVolDir(volume) if err != nil { - return nil, err + return 0, err } // Stat a volume entry. _, err = os.Stat(volumeDir) if err != nil { if os.IsNotExist(err) { - return nil, errVolumeNotFound + return 0, errVolumeNotFound } - return nil, err + return 0, err } filePath := pathJoin(volumeDir, path) if err = checkPathLength(filePath); err != nil { - return nil, err + return 0, err } file, err := os.Open(filePath) if err != nil { if os.IsNotExist(err) { - return nil, errFileNotFound + return 0, errFileNotFound } else if os.IsPermission(err) { - return nil, errFileAccessDenied + return 0, errFileAccessDenied + } else if strings.Contains(err.Error(), "not a directory") { + return 0, errFileNotFound } - return nil, err + return 0, err } st, err := file.Stat() if err != nil { - return nil, err + return 0, err } // Verify if its not a regular file, since subsequent Seek is undefined. if !st.Mode().IsRegular() { - return nil, errFileNotFound + return 0, errFileNotFound } // Seek to requested offset. _, err = file.Seek(offset, os.SEEK_SET) if err != nil { - return nil, err + return 0, err } - return file, nil + + // Close the reader. + defer file.Close() + + // Read file. + m, err := io.ReadFull(file, buf) + + // Error unexpected is valid, set this back to nil. + if err == io.ErrUnexpectedEOF { + err = nil + } + + // Success. + return int64(m), err } -// CreateFile - create a file at path. -func (s fsStorage) CreateFile(volume, path string) (writeCloser io.WriteCloser, err error) { +// AppendFile - append a byte array at path, if file doesn't exist at +// path this call explicitly creates it. +func (s posix) AppendFile(volume, path string, buf []byte) (n int64, err error) { volumeDir, err := s.getVolDir(volume) if err != nil { - return nil, err + return 0, err } // Stat a volume entry. _, err = os.Stat(volumeDir) if err != nil { if os.IsNotExist(err) { - return nil, errVolumeNotFound + return 0, errVolumeNotFound } - return nil, err + return 0, err } if err = checkDiskFree(s.diskPath, s.minFreeDisk); err != nil { - return nil, err + return 0, err } filePath := pathJoin(volumeDir, path) if err = checkPathLength(filePath); err != nil { - return nil, err + return 0, err } // Verify if the file already exists and is not of regular type. var st os.FileInfo if st, err = os.Stat(filePath); err == nil { if st.IsDir() { - return nil, errIsNotRegular + return 0, errIsNotRegular } } - w, err := safe.CreateFileWithPrefix(filePath, "$tmpfile") + // Create top level directories if they don't exist. + if err = os.MkdirAll(filepath.Dir(filePath), 0700); err != nil { + return 0, err + } + w, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) if err != nil { // File path cannot be verified since one of the parents is a file. if strings.Contains(err.Error(), "not a directory") { - return nil, errFileAccessDenied + return 0, errFileAccessDenied } - return nil, err + return 0, err } - return w, nil + // Close upon return. + defer w.Close() + + // Return io.Copy + return io.Copy(w, bytes.NewReader(buf)) } // StatFile - get file info. -func (s fsStorage) StatFile(volume, path string) (file FileInfo, err error) { +func (s posix) StatFile(volume, path string) (file FileInfo, err error) { volumeDir, err := s.getVolDir(volume) if err != nil { return FileInfo{}, err @@ -425,7 +435,6 @@ func (s fsStorage) StatFile(volume, path string) (file FileInfo, err error) { // Return all errors here. return FileInfo{}, err } - // If its a directory its not a regular file. if st.Mode().IsDir() { return FileInfo{}, errFileNotFound @@ -470,7 +479,7 @@ func deleteFile(basePath, deletePath string) error { } // DeleteFile - delete a file at path. -func (s fsStorage) DeleteFile(volume, path string) error { +func (s posix) DeleteFile(volume, path string) error { volumeDir, err := s.getVolDir(volume) if err != nil { return err @@ -495,8 +504,8 @@ func (s fsStorage) DeleteFile(volume, path string) error { return deleteFile(volumeDir, filePath) } -// RenameFile - rename file. -func (s fsStorage) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) error { +// RenameFile - rename source path to destination path atomically. +func (s posix) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) error { srcVolumeDir, err := s.getVolDir(srcVolume) if err != nil { return err diff --git a/routers.go b/routers.go index c8a40de84..78c742a1f 100644 --- a/routers.go +++ b/routers.go @@ -33,7 +33,7 @@ func newObjectLayer(exportPaths []string) (ObjectLayer, error) { } // Initialize XL object layer. objAPI, err := newXLObjects(exportPaths) - if err == errWriteQuorum { + if err == errXLWriteQuorum { return objAPI, errors.New("Disks are different with last minio server run.") } return objAPI, err diff --git a/rpc-client.go b/rpc-client.go index 7374ccf04..a2055b4c6 100644 --- a/rpc-client.go +++ b/rpc-client.go @@ -17,14 +17,8 @@ package main import ( - "errors" - "fmt" - "io" "net/http" "net/rpc" - "net/url" - urlpath "path" - "strconv" "strings" "time" ) @@ -151,34 +145,15 @@ func (n networkStorage) DeleteVol(volume string) error { // File operations. // CreateFile - create file. -func (n networkStorage) CreateFile(volume, path string) (writeCloser io.WriteCloser, err error) { - writeURL := new(url.URL) - writeURL.Scheme = n.netScheme - writeURL.Host = n.netAddr - writeURL.Path = fmt.Sprintf("%s/upload/%s", storageRPCPath, urlpath.Join(volume, path)) - - contentType := "application/octet-stream" - readCloser, writeCloser := io.Pipe() - go func() { - resp, err := n.httpClient.Post(writeURL.String(), contentType, readCloser) - if err != nil { - readCloser.CloseWithError(err) - return - } - if resp != nil { - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNotFound { - readCloser.CloseWithError(errFileNotFound) - return - } - readCloser.CloseWithError(errors.New("Invalid response.")) - return - } - // Close the reader. - readCloser.Close() - } - }() - return writeCloser, nil +func (n networkStorage) AppendFile(volume, path string, buffer []byte) (m int64, err error) { + if err = n.rpcClient.Call("Storage.AppendFileHandler", AppendFileArgs{ + Vol: volume, + Path: path, + Buffer: buffer, + }, &m); err != nil { + return 0, toStorageErr(err) + } + return m, nil } // StatFile - get latest Stat information for a file at path. @@ -193,27 +168,16 @@ func (n networkStorage) StatFile(volume, path string) (fileInfo FileInfo, err er } // ReadFile - reads a file. -func (n networkStorage) ReadFile(volume string, path string, offset int64) (reader io.ReadCloser, err error) { - readURL := new(url.URL) - readURL.Scheme = n.netScheme - readURL.Host = n.netAddr - readURL.Path = fmt.Sprintf("%s/download/%s", storageRPCPath, urlpath.Join(volume, path)) - readQuery := make(url.Values) - readQuery.Set("offset", strconv.FormatInt(offset, 10)) - readURL.RawQuery = readQuery.Encode() - resp, err := n.httpClient.Get(readURL.String()) - if err != nil { - return nil, err +func (n networkStorage) ReadFile(volume string, path string, offset int64, buffer []byte) (m int64, err error) { + if err = n.rpcClient.Call("Storage.ReadFileHandler", ReadFileArgs{ + Vol: volume, + Path: path, + Offset: offset, + Buffer: buffer, + }, &m); err != nil { + return 0, toStorageErr(err) } - if resp != nil { - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNotFound { - return nil, errFileNotFound - } - return nil, errors.New("Invalid response") - } - } - return resp.Body, nil + return m, nil } // ListDir - list all entries at prefix. diff --git a/rpc-server-datatypes.go b/rpc-server-datatypes.go index 7202e9668..478e0a8f4 100644 --- a/rpc-server-datatypes.go +++ b/rpc-server-datatypes.go @@ -27,25 +27,40 @@ type ListVolsReply struct { Vols []VolInfo } -// StatFileArgs stat file args. +// ReadFileArgs contains read file arguments. +type ReadFileArgs struct { + Vol string + Path string + Offset int64 + Buffer []byte +} + +// AppendFileArgs contains append file arguments. +type AppendFileArgs struct { + Vol string + Path string + Buffer []byte +} + +// StatFileArgs contains stat file arguments. type StatFileArgs struct { Vol string Path string } -// DeleteFileArgs delete file args. +// DeleteFileArgs contains delete file arguments. type DeleteFileArgs struct { Vol string Path string } -// ListDirArgs list dir args. +// ListDirArgs contains list dir arguments. type ListDirArgs struct { Vol string Path string } -// RenameFileArgs rename file args. +// RenameFileArgs contains rename file arguments. type RenameFileArgs struct { SrcVol string SrcPath string diff --git a/rpc-server.go b/rpc-server.go index 1ebe2bfe9..a3ba64e1f 100644 --- a/rpc-server.go +++ b/rpc-server.go @@ -1,10 +1,7 @@ package main import ( - "io" - "net/http" "net/rpc" - "strconv" router "github.com/gorilla/mux" ) @@ -78,6 +75,26 @@ func (s *storageServer) ListDirHandler(arg *ListDirArgs, reply *[]string) error return nil } +// ReadFileHandler - read file handler is rpc wrapper to read file. +func (s *storageServer) ReadFileHandler(arg *ReadFileArgs, reply *int64) error { + n, err := s.storage.ReadFile(arg.Vol, arg.Path, arg.Offset, arg.Buffer) + if err != nil { + return err + } + reply = &n + return nil +} + +// AppendFileHandler - append file handler is rpc wrapper to append file. +func (s *storageServer) AppendFileHandler(arg *AppendFileArgs, reply *int64) error { + n, err := s.storage.AppendFile(arg.Vol, arg.Path, arg.Buffer) + if err != nil { + return err + } + reply = &n + return nil +} + // DeleteFileHandler - delete file handler is rpc wrapper to delete file. func (s *storageServer) DeleteFileHandler(arg *DeleteFileArgs, reply *GenericReply) error { err := s.storage.DeleteFile(arg.Vol, arg.Path) @@ -115,60 +132,4 @@ func registerStorageRPCRouter(mux *router.Router, stServer *storageServer) { storageRouter := mux.NewRoute().PathPrefix(reservedBucket).Subrouter() // Add minio storage routes. storageRouter.Path("/storage").Handler(storageRPCServer) - // StreamUpload - stream upload handler. - storageRouter.Methods("POST").Path("/storage/upload/{volume}/{path:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - vars := router.Vars(r) - volume := vars["volume"] - path := vars["path"] - writeCloser, err := stServer.storage.CreateFile(volume, path) - if err != nil { - httpErr := http.StatusInternalServerError - if err == errVolumeNotFound { - httpErr = http.StatusNotFound - } else if err == errIsNotRegular { - httpErr = http.StatusConflict - } - http.Error(w, err.Error(), httpErr) - return - } - reader := r.Body - if _, err = io.Copy(writeCloser, reader); err != nil { - safeCloseAndRemove(writeCloser) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - writeCloser.Close() - reader.Close() - }) - // StreamDownloadHandler - stream download handler. - storageRouter.Methods("GET").Path("/storage/download/{volume}/{path:.+}").Queries("offset", "{offset:.*}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - vars := router.Vars(r) - volume := vars["volume"] - path := vars["path"] - offset, err := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - readCloser, err := stServer.storage.ReadFile(volume, path, offset) - if err != nil { - httpErr := http.StatusBadRequest - if err == errVolumeNotFound { - httpErr = http.StatusNotFound - } else if err == errFileNotFound { - httpErr = http.StatusNotFound - } - http.Error(w, err.Error(), httpErr) - return - } - - // Copy reader to writer. - io.Copy(w, readCloser) - - // Flush out any remaining buffers to client. - w.(http.Flusher).Flush() - - // Close the reader. - readCloser.Close() - }) } diff --git a/server-main.go b/server-main.go index 371d7a7a7..fd72e2119 100644 --- a/server-main.go +++ b/server-main.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" "syscall" + "time" "github.com/minio/cli" "github.com/minio/mc/pkg/console" @@ -33,7 +34,7 @@ import ( var serverCmd = cli.Command{ Name: "server", - Usage: "Start Minio cloud storage server.", + Usage: "Start object storage server.", Flags: []cli.Flag{ cli.StringFlag{ Name: "address", @@ -64,9 +65,10 @@ EXAMPLES: 3. Start minio server on Windows. $ minio {{.Name}} C:\MyShare - 4. Start minio server 8 disks to enable erasure coded layer with 4 data and 4 parity. + 4. Start minio server 12 disks to enable erasure coded layer with 6 data and 6 parity. $ minio {{.Name}} /mnt/export1/backend /mnt/export2/backend /mnt/export3/backend /mnt/export4/backend \ - /mnt/export5/backend /mnt/export6/backend /mnt/export7/backend /mnt/export8/backend + /mnt/export5/backend /mnt/export6/backend /mnt/export7/backend /mnt/export8/backend /mnt/export9/backend \ + /mnt/export10/backend /mnt/export11/backend /mnt/export12/backend `, } @@ -79,7 +81,10 @@ type serverCmdConfig struct { func configureServer(srvCmdConfig serverCmdConfig) *http.Server { // Minio server config apiServer := &http.Server{ - Addr: srvCmdConfig.serverAddr, + Addr: srvCmdConfig.serverAddr, + // Adding timeout of 10 minutes for unresponsive client connections. + ReadTimeout: 10 * time.Minute, + WriteTimeout: 10 * time.Minute, Handler: configureServerHandler(srvCmdConfig), MaxHeaderBytes: 1 << 20, } diff --git a/server_test.go b/server_test.go index e7c6fba3b..dbfc8ad46 100644 --- a/server_test.go +++ b/server_test.go @@ -444,7 +444,7 @@ func (s *MyAPISuite) TestBucket(c *C) { c.Assert(response.StatusCode, Equals, http.StatusOK) } -func (s *MyAPISuite) TestObject(c *C) { +func (s *MyAPISuite) TestObjectGet(c *C) { buffer := bytes.NewReader([]byte("hello world")) request, err := s.newRequest("PUT", testAPIFSCacheServer.URL+"/testobject", 0, nil) c.Assert(err, IsNil) diff --git a/server_xl_test.go b/server_xl_test.go index c6eff8e3d..69cf96d75 100644 --- a/server_xl_test.go +++ b/server_xl_test.go @@ -920,18 +920,27 @@ func (s *MyAPIXLSuite) TestPartialContent(c *C) { c.Assert(response.StatusCode, Equals, http.StatusOK) // Prepare request - request, err = s.newRequest("GET", testAPIXLServer.URL+"/partial-content/bar", 0, nil) - c.Assert(err, IsNil) - request.Header.Add("Range", "bytes=6-7") + var table = []struct { + byteRange string + expectedString string + }{ + {"6-7", "Wo"}, + {"6-", "World"}, + {"-7", "o World"}, + } + for _, t := range table { + request, err = s.newRequest("GET", testAPIXLServer.URL+"/partial-content/bar", 0, nil) + c.Assert(err, IsNil) + request.Header.Add("Range", "bytes="+t.byteRange) - client = http.Client{} - response, err = client.Do(request) - c.Assert(err, IsNil) - c.Assert(response.StatusCode, Equals, http.StatusPartialContent) - partialObject, err := ioutil.ReadAll(response.Body) - c.Assert(err, IsNil) - - c.Assert(string(partialObject), Equals, "Wo") + client = http.Client{} + response, err = client.Do(request) + c.Assert(err, IsNil) + c.Assert(response.StatusCode, Equals, http.StatusPartialContent) + partialObject, err := ioutil.ReadAll(response.Body) + c.Assert(err, IsNil) + c.Assert(string(partialObject), Equals, t.expectedString) + } } func (s *MyAPIXLSuite) TestListObjectsHandlerErrors(c *C) { diff --git a/storage-datatypes.go b/storage-datatypes.go index c963f3e59..353a7a8e3 100644 --- a/storage-datatypes.go +++ b/storage-datatypes.go @@ -25,9 +25,6 @@ import ( type VolInfo struct { Name string Created time.Time - Total int64 - Free int64 - FSType string } // FileInfo - file stat information. diff --git a/storage-errors.go b/storage-errors.go index 95e1fba44..c92a82828 100644 --- a/storage-errors.go +++ b/storage-errors.go @@ -18,6 +18,9 @@ package main import "errors" +// errUnexpected - unexpected error, requires manual intervention. +var errUnexpected = errors.New("Unexpected error, please report this issue at https://github.com/minio/minio/issues") + // errCorruptedFormat - corrupted backend format. var errCorruptedFormat = errors.New("corrupted backend format") @@ -48,18 +51,8 @@ var errVolumeNotFound = errors.New("volume not found") // errVolumeNotEmpty - volume not empty. var errVolumeNotEmpty = errors.New("volume is not empty") -// errVolumeAccessDenied - cannot access volume, insufficient -// permissions. +// errVolumeAccessDenied - cannot access volume, insufficient permissions. var errVolumeAccessDenied = errors.New("volume access denied") // errVolumeAccessDenied - cannot access file, insufficient permissions. var errFileAccessDenied = errors.New("file access denied") - -// errReadQuorum - did not meet read quorum. -var errReadQuorum = errors.New("I/O error. did not meet read quorum.") - -// errWriteQuorum - did not meet write quorum. -var errWriteQuorum = errors.New("I/O error. did not meet write quorum.") - -// errDataCorrupt - err data corrupt. -var errDataCorrupt = errors.New("data likely corrupted, all blocks are zero in length") diff --git a/storage-api-interface.go b/storage-interface.go similarity index 85% rename from storage-api-interface.go rename to storage-interface.go index c27c798c4..9eefc9a42 100644 --- a/storage-api-interface.go +++ b/storage-interface.go @@ -16,8 +16,6 @@ package main -import "io" - // StorageAPI interface. type StorageAPI interface { // Volume operations. @@ -28,9 +26,9 @@ type StorageAPI interface { // File operations. ListDir(volume, dirPath string) ([]string, error) - ReadFile(volume string, path string, offset int64) (readCloser io.ReadCloser, err error) - CreateFile(volume string, path string) (writeCloser io.WriteCloser, err error) + ReadFile(volume string, path string, offset int64, buf []byte) (n int64, err error) + AppendFile(volume string, path string, buf []byte) (n int64, err error) + RenameFile(srcVolume, srcPath, dstVolume, dstPath string) error StatFile(volume string, path string) (file FileInfo, err error) DeleteFile(volume string, path string) (err error) - RenameFile(srcVolume, srcPath, dstVolume, dstPath string) error } diff --git a/test-utils_test.go b/test-utils_test.go index 9c9747e5e..7f45bf127 100644 --- a/test-utils_test.go +++ b/test-utils_test.go @@ -44,6 +44,10 @@ func ExecObjectLayerTest(t *testing.T, objTest func(obj ObjectLayer, instanceTyp } erasureDisks = append(erasureDisks, path) } + + // Initialize name space lock. + initNSLock() + objLayer, err := newXLObjects(erasureDisks) if err != nil { return nil, nil, err @@ -59,6 +63,9 @@ func ExecObjectLayerTest(t *testing.T, objTest func(obj ObjectLayer, instanceTyp return nil, "", err } + // Initialize name space lock. + initNSLock() + // Create the obj. objLayer, err := newFSObjects(fsDir) if err != nil { @@ -80,7 +87,7 @@ func ExecObjectLayerTest(t *testing.T, objTest func(obj ObjectLayer, instanceTyp } // Executing the object layer tests for single node setup. objTest(objLayer, singleNodeTestStr, t) - initNSLock() + objLayer, fsDirs, err := getXLObjectLayer() if err != nil { t.Fatalf("Initialization of object layer failed for XL setup: %s", err.Error()) diff --git a/tree-walk-fs.go b/tree-walk-fs.go new file mode 100644 index 000000000..25db24ee3 --- /dev/null +++ b/tree-walk-fs.go @@ -0,0 +1,206 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "path" + "sort" + "strings" + "time" +) + +// Tree walk notify carries a channel which notifies tree walk +// results, additionally it also carries information if treeWalk +// should be timedOut. +type treeWalkerFS struct { + ch <-chan treeWalkResultFS + timedOut bool +} + +// Tree walk result carries results of tree walking. +type treeWalkResultFS struct { + entry string + err error + end bool +} + +// treeWalk walks FS directory tree recursively pushing fileInfo into the channel as and when it encounters files. +func (fs fsObjects) treeWalk(bucket, prefixDir, entryPrefixMatch, marker string, recursive bool, send func(treeWalkResultFS) bool, count *int, isLeaf func(string, string) bool) bool { + // Example: + // if prefixDir="one/two/three/" and marker="four/five.txt" treeWalk is recursively + // called with prefixDir="one/two/three/four/" and marker="five.txt" + + var markerBase, markerDir string + if marker != "" { + // Ex: if marker="four/five.txt", markerDir="four/" markerBase="five.txt" + markerSplit := strings.SplitN(marker, slashSeparator, 2) + markerDir = markerSplit[0] + if len(markerSplit) == 2 { + markerDir += slashSeparator + markerBase = markerSplit[1] + } + } + entries, err := fs.storage.ListDir(bucket, prefixDir) + if err != nil { + send(treeWalkResultFS{err: err}) + return false + } + + for i, entry := range entries { + if entryPrefixMatch != "" { + if !strings.HasPrefix(entry, entryPrefixMatch) { + entries[i] = "" + continue + } + } + if isLeaf(bucket, pathJoin(prefixDir, entry)) { + entries[i] = strings.TrimSuffix(entry, slashSeparator) + } + } + sort.Strings(entries) + // Skip the empty strings + for len(entries) > 0 && entries[0] == "" { + entries = entries[1:] + } + if len(entries) == 0 { + return true + } + // example: + // If markerDir="four/" Search() returns the index of "four/" in the sorted + // entries list so we skip all the entries till "four/" + idx := sort.Search(len(entries), func(i int) bool { + return entries[i] >= markerDir + }) + entries = entries[idx:] + *count += len(entries) + for i, entry := range entries { + if i == 0 && markerDir == entry { + if !recursive { + // Skip as the marker would already be listed in the previous listing. + *count-- + continue + } + if recursive && !strings.HasSuffix(entry, slashSeparator) { + // We should not skip for recursive listing and if markerDir is a directory + // for ex. if marker is "four/five.txt" markerDir will be "four/" which + // should not be skipped, instead it will need to be treeWalk()'ed into. + + // Skip if it is a file though as it would be listed in previous listing. + *count-- + continue + } + } + + if recursive && strings.HasSuffix(entry, slashSeparator) { + // If the entry is a directory, we will need recurse into it. + markerArg := "" + if entry == markerDir { + // We need to pass "five.txt" as marker only if we are + // recursing into "four/" + markerArg = markerBase + } + *count-- + prefixMatch := "" // Valid only for first level treeWalk and empty for subdirectories. + if !fs.treeWalk(bucket, path.Join(prefixDir, entry), prefixMatch, markerArg, recursive, send, count, isLeaf) { + return false + } + continue + } + *count-- + if !send(treeWalkResultFS{entry: pathJoin(prefixDir, entry)}) { + return false + } + } + return true +} + +// Initiate a new treeWalk in a goroutine. +func (fs fsObjects) startTreeWalk(bucket, prefix, marker string, recursive bool, isLeaf func(string, string) bool) *treeWalkerFS { + // Example 1 + // If prefix is "one/two/three/" and marker is "one/two/three/four/five.txt" + // treeWalk is called with prefixDir="one/two/three/" and marker="four/five.txt" + // and entryPrefixMatch="" + + // Example 2 + // if prefix is "one/two/th" and marker is "one/two/three/four/five.txt" + // treeWalk is called with prefixDir="one/two/" and marker="three/four/five.txt" + // and entryPrefixMatch="th" + + ch := make(chan treeWalkResultFS, maxObjectList) + walkNotify := treeWalkerFS{ch: ch} + entryPrefixMatch := prefix + prefixDir := "" + lastIndex := strings.LastIndex(prefix, slashSeparator) + if lastIndex != -1 { + entryPrefixMatch = prefix[lastIndex+1:] + prefixDir = prefix[:lastIndex+1] + } + count := 0 + marker = strings.TrimPrefix(marker, prefixDir) + go func() { + defer close(ch) + send := func(walkResult treeWalkResultFS) bool { + if count == 0 { + walkResult.end = true + } + timer := time.After(time.Second * 60) + select { + case ch <- walkResult: + return true + case <-timer: + walkNotify.timedOut = true + return false + } + } + fs.treeWalk(bucket, prefixDir, entryPrefixMatch, marker, recursive, send, &count, isLeaf) + }() + return &walkNotify +} + +// Save the goroutine reference in the map +func (fs fsObjects) saveTreeWalk(params listParams, walker *treeWalkerFS) { + fs.listObjectMapMutex.Lock() + defer fs.listObjectMapMutex.Unlock() + + walkers, _ := fs.listObjectMap[params] + walkers = append(walkers, walker) + + fs.listObjectMap[params] = walkers +} + +// Lookup the goroutine reference from map +func (fs fsObjects) lookupTreeWalk(params listParams) *treeWalkerFS { + fs.listObjectMapMutex.Lock() + defer fs.listObjectMapMutex.Unlock() + + if walkChs, ok := fs.listObjectMap[params]; ok { + for i, walkCh := range walkChs { + if !walkCh.timedOut { + newWalkChs := walkChs[i+1:] + if len(newWalkChs) > 0 { + fs.listObjectMap[params] = newWalkChs + } else { + delete(fs.listObjectMap, params) + } + return walkCh + } + } + // As all channels are timed out, delete the map entry + delete(fs.listObjectMap, params) + } + return nil +} diff --git a/tree-walk.go b/tree-walk-xl.go similarity index 53% rename from tree-walk.go rename to tree-walk-xl.go index 3f61a6af7..f49a6c931 100644 --- a/tree-walk.go +++ b/tree-walk-xl.go @@ -17,11 +17,8 @@ package main import ( - "os" - "path" "sort" "strings" - "sync" "time" ) @@ -35,9 +32,9 @@ type listParams struct { // Tree walk result carries results of tree walking. type treeWalkResult struct { - fileInfo FileInfo - err error - end bool + entry string + err error + end bool } // Tree walk notify carries a channel which notifies tree walk @@ -48,58 +45,44 @@ type treeWalker struct { timedOut bool } -// treeWalk walks FS directory tree recursively pushing fileInfo into the channel as and when it encounters files. -func treeWalk(layer ObjectLayer, bucket, prefixDir, entryPrefixMatch, marker string, recursive bool, send func(treeWalkResult) bool, count *int) bool { +// listDir - listDir. +func (xl xlObjects) listDir(bucket, prefixDir string, filter func(entry string) bool, isLeaf func(string, string) bool) (entries []string, err error) { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + entries, err = disk.ListDir(bucket, prefixDir) + if err != nil { + break + } + // Skip the entries which do not match the filter. + for i, entry := range entries { + if filter(entry) { + entries[i] = "" + continue + } + if strings.HasSuffix(entry, slashSeparator) && isLeaf(bucket, pathJoin(prefixDir, entry)) { + entries[i] = strings.TrimSuffix(entry, slashSeparator) + } + } + sort.Strings(entries) + // Skip the empty strings + for len(entries) > 0 && entries[0] == "" { + entries = entries[1:] + } + return entries, nil + } + + // Return error at the end. + return nil, err +} + +// treeWalk walks directory tree recursively pushing fileInfo into the channel as and when it encounters files. +func (xl xlObjects) treeWalk(bucket, prefixDir, entryPrefixMatch, marker string, recursive bool, send func(treeWalkResult) bool, count *int, isLeaf func(string, string) bool) bool { // Example: // if prefixDir="one/two/three/" and marker="four/five.txt" treeWalk is recursively // called with prefixDir="one/two/three/four/" and marker="five.txt" - var isXL bool - var disk StorageAPI - switch l := layer.(type) { - case xlObjects: - isXL = true - disk = l.storage - case fsObjects: - disk = l.storage - } - - // Convert entry to FileInfo - entryToFileInfo := func(entry string) (fileInfo FileInfo, err error) { - if strings.HasSuffix(entry, slashSeparator) { - // Object name needs to be full path. - fileInfo.Name = path.Join(prefixDir, entry) - fileInfo.Name += slashSeparator - fileInfo.Mode = os.ModeDir - return - } - if isXL && strings.HasSuffix(entry, multipartSuffix) { - // If the entry was detected as a multipart file we use - // getMultipartObjectInfo() to fill the FileInfo structure. - entry = strings.TrimSuffix(entry, multipartSuffix) - var info MultipartObjectInfo - info, err = getMultipartObjectInfo(disk, bucket, path.Join(prefixDir, entry)) - if err != nil { - return - } - // Set the Mode to a "regular" file. - fileInfo.Mode = 0 - // Trim the suffix that was temporarily added to indicate that this - // is a multipart file. - fileInfo.Name = path.Join(prefixDir, entry) - fileInfo.Size = info.Size - fileInfo.MD5Sum = info.MD5Sum - fileInfo.ModTime = info.ModTime - return - } - if fileInfo, err = disk.StatFile(bucket, path.Join(prefixDir, entry)); err != nil { - return - } - // Object name needs to be full path. - fileInfo.Name = path.Join(prefixDir, entry) - return - } - var markerBase, markerDir string if marker != "" { // Ex: if marker="four/five.txt", markerDir="four/" markerBase="five.txt" @@ -110,41 +93,22 @@ func treeWalk(layer ObjectLayer, bucket, prefixDir, entryPrefixMatch, marker str markerBase = markerSplit[1] } } - entries, err := disk.ListDir(bucket, prefixDir) + entries, err := xl.listDir(bucket, prefixDir, func(entry string) bool { + return !strings.HasPrefix(entry, entryPrefixMatch) + }, isLeaf) if err != nil { send(treeWalkResult{err: err}) return false } - - if entryPrefixMatch != "" { - for i, entry := range entries { - if !strings.HasPrefix(entry, entryPrefixMatch) { - entries[i] = "" - } - } - } - // For XL multipart files strip the trailing "/" and append ".minio.multipart" to the entry so that - // entryToFileInfo() can call StatFile for regular files or getMultipartObjectInfo() for multipart files. - for i, entry := range entries { - if isXL && strings.HasSuffix(entry, slashSeparator) { - if isMultipartObject(disk, bucket, path.Join(prefixDir, entry)) { - entries[i] = strings.TrimSuffix(entry, slashSeparator) + multipartSuffix - } - } - } - sort.Sort(byMultipartFiles(entries)) - // Skip the empty strings - for len(entries) > 0 && entries[0] == "" { - entries = entries[1:] - } if len(entries) == 0 { return true } + // example: // If markerDir="four/" Search() returns the index of "four/" in the sorted // entries list so we skip all the entries till "four/" idx := sort.Search(len(entries), func(i int) bool { - return strings.TrimSuffix(entries[i], multipartSuffix) >= markerDir + return entries[i] >= markerDir }) entries = entries[idx:] *count += len(entries) @@ -176,19 +140,13 @@ func treeWalk(layer ObjectLayer, bucket, prefixDir, entryPrefixMatch, marker str } *count-- prefixMatch := "" // Valid only for first level treeWalk and empty for subdirectories. - if !treeWalk(layer, bucket, path.Join(prefixDir, entry), prefixMatch, markerArg, recursive, send, count) { + if !xl.treeWalk(bucket, pathJoin(prefixDir, entry), prefixMatch, markerArg, recursive, send, count, isLeaf) { return false } continue } *count-- - fileInfo, err := entryToFileInfo(entry) - if err != nil { - // The file got deleted in the interim between ListDir() and StatFile() - // Ignore error and continue. - continue - } - if !send(treeWalkResult{fileInfo: fileInfo}) { + if !send(treeWalkResult{entry: pathJoin(prefixDir, entry)}) { return false } } @@ -196,7 +154,7 @@ func treeWalk(layer ObjectLayer, bucket, prefixDir, entryPrefixMatch, marker str } // Initiate a new treeWalk in a goroutine. -func startTreeWalk(layer ObjectLayer, bucket, prefix, marker string, recursive bool) *treeWalker { +func (xl xlObjects) startTreeWalk(bucket, prefix, marker string, recursive bool, isLeaf func(string, string) bool) *treeWalker { // Example 1 // If prefix is "one/two/three/" and marker is "one/two/three/four/five.txt" // treeWalk is called with prefixDir="one/two/three/" and marker="four/five.txt" @@ -233,61 +191,41 @@ func startTreeWalk(layer ObjectLayer, bucket, prefix, marker string, recursive b return false } } - treeWalk(layer, bucket, prefixDir, entryPrefixMatch, marker, recursive, send, &count) + xl.treeWalk(bucket, prefixDir, entryPrefixMatch, marker, recursive, send, &count, isLeaf) }() return &walkNotify } // Save the goroutine reference in the map -func saveTreeWalk(layer ObjectLayer, params listParams, walker *treeWalker) { - var listObjectMap map[listParams][]*treeWalker - var listObjectMapMutex *sync.Mutex - switch l := layer.(type) { - case xlObjects: - listObjectMap = l.listObjectMap - listObjectMapMutex = l.listObjectMapMutex - case fsObjects: - listObjectMap = l.listObjectMap - listObjectMapMutex = l.listObjectMapMutex - } - listObjectMapMutex.Lock() - defer listObjectMapMutex.Unlock() +func (xl xlObjects) saveTreeWalk(params listParams, walker *treeWalker) { + xl.listObjectMapMutex.Lock() + defer xl.listObjectMapMutex.Unlock() - walkers, _ := listObjectMap[params] + walkers, _ := xl.listObjectMap[params] walkers = append(walkers, walker) - listObjectMap[params] = walkers + xl.listObjectMap[params] = walkers } // Lookup the goroutine reference from map -func lookupTreeWalk(layer ObjectLayer, params listParams) *treeWalker { - var listObjectMap map[listParams][]*treeWalker - var listObjectMapMutex *sync.Mutex - switch l := layer.(type) { - case xlObjects: - listObjectMap = l.listObjectMap - listObjectMapMutex = l.listObjectMapMutex - case fsObjects: - listObjectMap = l.listObjectMap - listObjectMapMutex = l.listObjectMapMutex - } - listObjectMapMutex.Lock() - defer listObjectMapMutex.Unlock() +func (xl xlObjects) lookupTreeWalk(params listParams) *treeWalker { + xl.listObjectMapMutex.Lock() + defer xl.listObjectMapMutex.Unlock() - if walkChs, ok := listObjectMap[params]; ok { + if walkChs, ok := xl.listObjectMap[params]; ok { for i, walkCh := range walkChs { if !walkCh.timedOut { newWalkChs := walkChs[i+1:] if len(newWalkChs) > 0 { - listObjectMap[params] = newWalkChs + xl.listObjectMap[params] = newWalkChs } else { - delete(listObjectMap, params) + delete(xl.listObjectMap, params) } return walkCh } } // As all channels are timed out, delete the map entry - delete(listObjectMap, params) + delete(xl.listObjectMap, params) } return nil } diff --git a/vendor/github.com/minio/miniobrowser/README.md b/vendor/github.com/minio/miniobrowser/README.md index 37abc8a9b..b8ce6ff56 100644 --- a/vendor/github.com/minio/miniobrowser/README.md +++ b/vendor/github.com/minio/miniobrowser/README.md @@ -6,6 +6,7 @@ ```sh $ git clone https://github.com/minio/MinioBrowser +$ cd MinioBrowser $ npm install ``` diff --git a/vendor/github.com/minio/miniobrowser/ui-assets.go b/vendor/github.com/minio/miniobrowser/ui-assets.go index b8969f8ed..45479d568 100644 --- a/vendor/github.com/minio/miniobrowser/ui-assets.go +++ b/vendor/github.com/minio/miniobrowser/ui-assets.go @@ -4,7 +4,7 @@ // production/favicon.ico // production/firefox.png // production/index.html -// production/index_bundle-2016-04-22T02-56-05Z.js +// production/index_bundle-2016-05-31T00-28-05Z.js // production/loader.css // production/logo.svg // production/safari.png @@ -65,7 +65,7 @@ func productionChromePng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/chrome.png", size: 3726, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/chrome.png", size: 3726, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -82,7 +82,7 @@ func productionFaviconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/favicon.ico", size: 1340, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/favicon.ico", size: 1340, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -99,7 +99,7 @@ func productionFirefoxPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/firefox.png", size: 4795, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/firefox.png", size: 4795, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -157,8 +157,8 @@ var _productionIndexHTML = []byte(` - - + + `) @@ -173,19 +173,19 @@ func productionIndexHTML() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/index.html", size: 2042, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/index.html", size: 2042, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _productionIndex_bundle20160422t025605zJs = []byte(`!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(224)},function(e,t,n){"use strict";e.exports=n(389)},function(e,t,n){"use strict";function r(e,t,n,r,o,i,a,s){if(!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,o,i,a,s],c=0;u=new Error(t.replace(/%s/g,function(){return l[c++]})),u.name="Invariant Violation"}throw u.framesToPop=1,u}}e.exports=r},function(e,t){"use strict";function n(e,t){if(null==e)throw new TypeError("Object.assign target cannot be null or undefined");for(var n=Object(e),r=Object.prototype.hasOwnProperty,o=1;o2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o]}t.__esModule=!0,t["default"]=o;var i=n(20);r(i);e.exports=t["default"]},function(e,t){"use strict";var n=!("undefined"==typeof window||!window.document||!window.document.createElement),r={canUseDOM:n,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:n&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:n&&!!window.screen,isInWorker:!n};e.exports=r},function(e,t,n){"use strict";function r(e){return function(){for(var t=arguments.length,n=Array(t),r=0;t>r;r++)n[r]=arguments[r];var o=n[n.length-1];return"function"==typeof o?e.apply(void 0,n):function(t){return e.apply(void 0,n.concat([t]))}}}function o(e,t){return void 0===e&&(e={}),(e.bsClass||"").trim()?void 0:d["default"](!1),e.bsClass+(t?"-"+t:"")}var i=n(6)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(1),u=n(35),l=a(u),c=n(137),d=a(c),p=n(60),f=(a(p),r(function(e,t){var n=t.propTypes||(t.propTypes={}),r=t.defaultProps||(t.defaultProps={});return n.bsClass=s.PropTypes.string,r.bsClass=e,t}));t.bsClass=f;var h=r(function(e,t,n){"string"!=typeof t&&(n=t,t=void 0);var r=n.STYLES||[],o=n.propTypes||{};e.forEach(function(e){-1===r.indexOf(e)&&r.push(e)});var a=s.PropTypes.oneOf(r);if(n.STYLES=a._values=r,n.propTypes=i({},o,{bsStyle:a}),void 0!==t){var u=n.defaultProps||(n.defaultProps={});u.bsStyle=t}return n});t.bsStyles=h;var m=r(function(e,t,n){"string"!=typeof t&&(n=t,t=void 0);var r=n.SIZES||[],o=n.propTypes||{};e.forEach(function(e){-1===r.indexOf(e)&&r.push(e)});var a=r.reduce(function(e,t){return l["default"].SIZES[t]&&l["default"].SIZES[t]!==t&&e.push(l["default"].SIZES[t]),e.concat(t)},[]),u=s.PropTypes.oneOf(a);if(u._values=a,n.SIZES=r,n.propTypes=i({},o,{bsSize:u}),void 0!==t){var c=n.defaultProps||(n.defaultProps={});c.bsSize=t}return n});t.bsSizes=m,t["default"]={prefix:o,getClassSet:function(e){var t={},n=o(e);if(n){var r=void 0;t[n]=!0,e.bsSize&&(r=l["default"].SIZES[e.bsSize]||r),r&&(t[o(e,r)]=!0),e.bsStyle&&(0===e.bsStyle.indexOf(o(e))?t[e.bsStyle]=!0:t[o(e,e.bsStyle)]=!0)}return t},addStyle:function(e,t){h(t,e)}};var g=r;t._curry=g},function(e,t,n){"use strict";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;n>r;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){return e?e.nodeType===F?e.documentElement:e.firstChild:null}function i(e){var t=o(e);return t&&q.getID(t)}function a(e){var t=s(e);if(t)if(B.hasOwnProperty(t)){var n=B[t];n!==e&&(d(n,t)?z(!1):void 0,B[t]=e)}else B[t]=e;return t}function s(e){return e&&e.getAttribute&&e.getAttribute(Y)||""}function u(e,t){var n=s(e);n!==t&&delete B[n],e.setAttribute(Y,t),B[t]=e}function l(e){return B.hasOwnProperty(e)&&d(B[e],e)||(B[e]=q.findReactNodeByID(e)),B[e]}function c(e){var t=w.get(e)._rootNodeID;return A.isNullComponentID(t)?null:(B.hasOwnProperty(t)&&d(B[t],t)||(B[t]=q.findReactNodeByID(t)),B[t])}function d(e,t){if(e){s(e)!==t?z(!1):void 0;var n=q.findReactContainerForID(t);if(n&&P(n,e))return!0}return!1}function p(e){delete B[e]}function f(e){var t=B[e];return t&&d(t,e)?void(G=t):!1}function h(e){G=null,N.traverseAncestors(e,f);var t=G;return G=null,t}function m(e,t,n,r,o,i){x.useCreateElement&&(i=L({},i),n.nodeType===F?i[H]=n:i[H]=n.ownerDocument);var a=D.mountComponent(e,t,r,i);e._renderedComponent._topLevelWrapper=e,q._mountImageIntoNode(a,n,o,r)}function g(e,t,n,r,o){var i=k.ReactReconcileTransaction.getPooled(r);i.perform(m,null,e,t,n,i,r,o),k.ReactReconcileTransaction.release(i)}function y(e,t){for(D.unmountComponent(e),t.nodeType===F&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)}function v(e){var t=i(e);return t?t!==N.getReactRootIDFromNodeID(t):!1}function M(e){for(;e&&e.parentNode!==e;e=e.parentNode)if(1===e.nodeType){var t=s(e);if(t){var n,r=N.getReactRootIDFromNodeID(t),o=e;do if(n=s(o),o=o.parentNode,null==o)return null;while(n!==r);if(o===Q[r])return e}}return null}var T=n(42),b=n(63),x=(n(23),n(189)),E=n(13),A=n(196),N=n(43),w=n(53),I=n(199),C=n(17),D=n(33),S=n(100),k=n(18),L=n(3),O=n(55),P=n(211),j=n(107),z=n(2),R=n(70),U=n(110),Y=(n(112),n(4),T.ID_ATTRIBUTE_NAME),B={},W=1,F=9,V=11,H="__ReactMount_ownerDocument$"+Math.random().toString(36).slice(2),_={},Q={},Z=[],G=null,X=function(){};X.prototype.isReactComponent={},X.prototype.render=function(){return this.props};var q={TopLevelWrapper:X,_instancesByReactRootID:_,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r){return q.scrollMonitor(n,function(){S.enqueueElementInternal(e,t),r&&S.enqueueCallbackInternal(e,r)}),e},_registerComponent:function(e,t){!t||t.nodeType!==W&&t.nodeType!==F&&t.nodeType!==V?z(!1):void 0,b.ensureScrollValueMonitoring();var n=q.registerContainer(t);return _[n]=e,n},_renderNewRootComponent:function(e,t,n,r){var o=j(e,null),i=q._registerComponent(o,t);return k.batchedUpdates(g,o,i,t,n,r),o},renderSubtreeIntoContainer:function(e,t,n,r){return null==e||null==e._reactInternalInstance?z(!1):void 0,q._renderSubtreeIntoContainer(e,t,n,r)},_renderSubtreeIntoContainer:function(e,t,n,r){E.isValidElement(t)?void 0:z(!1);var a=new E(X,null,null,null,null,null,t),u=_[i(n)];if(u){var l=u._currentElement,c=l.props;if(U(c,t)){var d=u._renderedComponent.getPublicInstance(),p=r&&function(){r.call(d)};return q._updateRootComponent(u,a,n,p),d}q.unmountComponentAtNode(n)}var f=o(n),h=f&&!!s(f),m=v(n),g=h&&!u&&!m,y=q._renderNewRootComponent(a,n,g,null!=e?e._reactInternalInstance._processChildContext(e._reactInternalInstance._context):O)._renderedComponent.getPublicInstance();return r&&r.call(y),y},render:function(e,t,n){return q._renderSubtreeIntoContainer(null,e,t,n)},registerContainer:function(e){var t=i(e);return t&&(t=N.getReactRootIDFromNodeID(t)),t||(t=N.createReactRootID()),Q[t]=e,t},unmountComponentAtNode:function(e){!e||e.nodeType!==W&&e.nodeType!==F&&e.nodeType!==V?z(!1):void 0;var t=i(e),n=_[t];if(!n){var r=(v(e),s(e));r&&r===N.getReactRootIDFromNodeID(r);return!1}return k.batchedUpdates(y,n,e),delete _[t],delete Q[t],!0},findReactContainerForID:function(e){var t=N.getReactRootIDFromNodeID(e),n=Q[t];return n},findReactNodeByID:function(e){var t=q.findReactContainerForID(e);return q.findComponentRoot(t,e)},getFirstReactDOM:function(e){return M(e)},findComponentRoot:function(e,t){var n=Z,r=0,o=h(t)||e;for(n[0]=o.firstChild,n.length=1;r1){for(var f=Array(p),h=0;p>h;h++)f[h]=arguments[h+2];i.children=f}if(e&&e.defaultProps){var m=e.defaultProps;for(o in m)"undefined"==typeof i[o]&&(i[o]=m[o])}return s(e,u,l,c,d,r.current,i)},s.createFactory=function(e){var t=s.createElement.bind(null,e);return t.type=e,t},s.cloneAndReplaceKey=function(e,t){var n=s(e.type,t,e.ref,e._self,e._source,e._owner,e.props);return n},s.cloneAndReplaceProps=function(e,t){var n=s(e.type,e.key,e.ref,e._self,e._source,e._owner,t);return n},s.cloneElement=function(e,t,n){var i,u=o({},e.props),l=e.key,c=e.ref,d=e._self,p=e._source,f=e._owner;if(null!=t){void 0!==t.ref&&(c=t.ref,f=r.current),void 0!==t.key&&(l=""+t.key);for(i in t)t.hasOwnProperty(i)&&!a.hasOwnProperty(i)&&(u[i]=t[i])}var h=arguments.length-2;if(1===h)u.children=n;else if(h>1){for(var m=Array(h),g=0;h>g;g++)m[g]=arguments[g+2];u.children=m}return s(e.type,l,c,d,p,f,u)},s.isValidElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===i},e.exports=s},function(e,t){"use strict";t["default"]=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},t.__esModule=!0},function(e,t,n){"use strict";var r=n(125)["default"],o=n(250)["default"];t["default"]=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=r(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(o?o(e,t):e.__proto__=t)},t.__esModule=!0},function(e,t,n){"use strict";var r=function(e,t,n,r,o,i,a,s){if(!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,o,i,a,s],c=0;u=new Error(t.replace(/%s/g,function(){return l[c++]})),u.name="Invariant Violation"}throw u.framesToPop=1,u}};e.exports=r},function(e,t,n){"use strict";function r(e,t,n){return n}var o={enableMeasure:!1,storedMeasure:r,measureMethods:function(e,t,n){},measure:function(e,t,n){return n},injection:{injectMeasure:function(e){o.storedMeasure=e}}};e.exports=o},function(e,t,n){"use strict";function r(){w.ReactReconcileTransaction&&T?void 0:g(!1)}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=c.getPooled(),this.reconcileTransaction=w.ReactReconcileTransaction.getPooled(!1)}function i(e,t,n,o,i,a){r(),T.batchedUpdates(e,t,n,o,i,a)}function a(e,t){return e._mountOrder-t._mountOrder}function s(e){var t=e.dirtyComponentsLength;t!==y.length?g(!1):void 0,y.sort(a);for(var n=0;t>n;n++){var r=y[n],o=r._pendingCallbacks;if(r._pendingCallbacks=null,f.performUpdateIfNecessary(r,e.reconcileTransaction),o)for(var i=0;i should not have a "'+t+'" prop'):void 0}t.__esModule=!0,t.falsy=r;var o=n(1),i=o.PropTypes.func,a=o.PropTypes.object,s=o.PropTypes.arrayOf,u=o.PropTypes.oneOfType,l=o.PropTypes.element,c=o.PropTypes.shape,d=o.PropTypes.string,p=c({listen:i.isRequired,pushState:i.isRequired,replaceState:i.isRequired,go:i.isRequired});t.history=p;var f=c({pathname:d.isRequired,search:d.isRequired,state:a,action:d.isRequired,key:d});t.location=f;var h=u([i,d]);t.component=h;var m=u([h,a]);t.components=m;var g=u([a,l]);t.route=g;var y=u([g,s(g)]);t.routes=y,t["default"]={falsy:r,history:p,location:f,component:h,components:m,route:g}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){var t=e.match(/^https?:\/\/[^\/]*/);return null==t?e:e.substring(t[0].length)}function i(e){var t=o(e),n="",r="",i=t.indexOf("#");-1!==i&&(r=t.substring(i),t=t.substring(0,i));var a=t.indexOf("?");return-1!==a&&(n=t.substring(a),t=t.substring(0,a)),""===t&&(t="/"),{pathname:t,search:n,hash:r}}t.__esModule=!0,t.extractPath=o,t.parsePath=i;var a=n(20);r(a)},function(e,t,n){"use strict";function r(){o.attachRefs(this,this._currentElement)}var o=n(408),i={mountComponent:function(e,t,n,o){var i=e.mountComponent(t,n,o);return e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e),i},unmountComponent:function(e){o.detachRefs(e,e._currentElement),e.unmountComponent()},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||i!==e._context){var s=o.shouldUpdateRefs(a,t);s&&o.detachRefs(e,a),e.receiveComponent(t,n,i),s&&e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t){e.performUpdateIfNecessary(t)}};e.exports=i},function(e,t,n){"use strict";function r(e,t,n,r){this.dispatchConfig=e,this.dispatchMarker=t,this.nativeEvent=n;var o=this.constructor.Interface;for(var i in o)if(o.hasOwnProperty(i)){var s=o[i];s?this[i]=s(n):"target"===i?this.target=r:this[i]=n[i]}var u=null!=n.defaultPrevented?n.defaultPrevented:n.returnValue===!1;u?this.isDefaultPrevented=a.thatReturnsTrue:this.isDefaultPrevented=a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse}var o=n(27),i=n(3),a=n(21),s=(n(4),{type:null,target:null,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null});i(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():e.returnValue=!1,this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0,this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;this.dispatchConfig=null,this.dispatchMarker=null,this.nativeEvent=null}}),r.Interface=s,r.augmentClass=function(e,t){var n=this,r=Object.create(n.prototype);i(r,e.prototype),e.prototype=r,e.prototype.constructor=e,e.Interface=i({},n.Interface,t),e.augmentClass=n.augmentClass,o.addPoolingTo(e,o.fourArgumentPooler)},o.addPoolingTo(r,o.fourArgumentPooler),e.exports=r},function(e,t,n){"use strict";var r=n(124)["default"],o=n(125)["default"],i=n(72)["default"];t.__esModule=!0;var a=function(e){return r(o({values:function(){var e=this;return i(this).map(function(t){return e[t]})}}),e)},s={SIZES:{large:"lg",medium:"md",small:"sm",xsmall:"xs",lg:"lg",md:"md",sm:"sm",xs:"xs"},GRID_COLUMNS:12},u=a({LARGE:"large",MEDIUM:"medium",SMALL:"small",XSMALL:"xsmall"});t.Sizes=u;var l=a({SUCCESS:"success",WARNING:"warning",DANGER:"danger",INFO:"info"});t.State=l;var c="default";t.DEFAULT=c;var d="primary";t.PRIMARY=d;var p="link";t.LINK=p;var f="inverse";t.INVERSE=f,t["default"]=s},function(e,t){"use strict";function n(){for(var e=arguments.length,t=Array(e),n=0;e>n;n++)t[n]=arguments[n];return t.filter(function(e){return null!=e}).reduce(function(e,t){if("function"!=typeof t)throw new Error("Invalid Argument Type, must only provide functions, undefined, or null.");return null===e?t:function(){for(var n=arguments.length,r=Array(n),o=0;n>o;o++)r[o]=arguments[o];e.apply(this,r),t.apply(this,r)}},null)}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){"use strict";t["default"]=function(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n},t.__esModule=!0},function(e,t){"use strict";function n(e){return e&&e.ownerDocument||document}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&r>=e}var r=9007199254740991;e.exports=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function i(e){return o(e).replace(/\/+/g,"/+")}function a(e){for(var t="",n=[],r=[],o=void 0,a=0,s=/:([a-zA-Z_$][a-zA-Z0-9_$]*)|\*\*|\*|\(|\)/g;o=s.exec(e);)o.index!==a&&(r.push(e.slice(a,o.index)),t+=i(e.slice(a,o.index))),o[1]?(t+="([^/?#]+)",n.push(o[1])):"**"===o[0]?(t+="([\\s\\S]*)",n.push("splat")):"*"===o[0]?(t+="([\\s\\S]*?)",n.push("splat")):"("===o[0]?t+="(?:":")"===o[0]&&(t+=")?"),r.push(o[0]),a=s.lastIndex;return a!==e.length&&(r.push(e.slice(a,e.length)),t+=i(e.slice(a,e.length))),{pattern:e,regexpSource:t,paramNames:n,tokens:r}}function s(e){return e in h||(h[e]=a(e)),h[e]}function u(e,t){"/"!==e.charAt(0)&&(e="/"+e),"/"!==t.charAt(0)&&(t="/"+t);var n=s(e),r=n.regexpSource,o=n.paramNames,i=n.tokens;r+="/*";var a="*"!==i[i.length-1];a&&(r+="([\\s\\S]*?)");var u=t.match(new RegExp("^"+r+"$","i")),l=void 0,c=void 0;if(null!=u){if(a){l=u.pop();var d=u[0].substr(0,u[0].length-l.length);if(l&&"/"!==d.charAt(d.length-1))return{remainingPathname:null,paramNames:o,paramValues:null}}else l="";c=u.slice(1).map(function(e){return null!=e?decodeURIComponent(e):e})}else l=c=null;return{remainingPathname:l,paramNames:o,paramValues:c}}function l(e){return s(e).paramNames}function c(e,t){var n=u(e,t),r=n.paramNames,o=n.paramValues;return null!=o?r.reduce(function(e,t,n){return e[t]=o[n],e},{}):null}function d(e,t){t=t||{};for(var n=s(e),r=n.tokens,o=0,i="",a=0,u=void 0,l=void 0,c=void 0,d=0,p=r.length;p>d;++d)u=r[d],"*"===u||"**"===u?(c=Array.isArray(t.splat)?t.splat[a++]:t.splat,null!=c||o>0?void 0:f["default"](!1),null!=c&&(i+=encodeURI(c))):"("===u?o+=1:")"===u?o-=1:":"===u.charAt(0)?(l=u.substring(1),c=t[l],null!=c||o>0?void 0:f["default"](!1),null!=c&&(i+=encodeURIComponent(c))):i+=u;return i.replace(/\/+/g,"/")}t.__esModule=!0,t.compilePattern=s,t.matchPattern=u,t.getParamNames=l,t.getParams=c,t.formatPattern=d;var p=n(16),f=r(p),h={}},function(e,t){"use strict";t.__esModule=!0;var n="PUSH";t.PUSH=n;var r="REPLACE";t.REPLACE=r;var o="POP";t.POP=o,t["default"]={PUSH:n,REPLACE:r,POP:o}},function(e,t,n){"use strict";function r(e,t){return(e&t)===t}var o=n(2),i={MUST_USE_ATTRIBUTE:1,MUST_USE_PROPERTY:2,HAS_SIDE_EFFECTS:4,HAS_BOOLEAN_VALUE:8,HAS_NUMERIC_VALUE:16,HAS_POSITIVE_NUMERIC_VALUE:48,HAS_OVERLOADED_BOOLEAN_VALUE:64,injectDOMPropertyConfig:function(e){var t=i,n=e.Properties||{},a=e.DOMAttributeNamespaces||{},u=e.DOMAttributeNames||{},l=e.DOMPropertyNames||{},c=e.DOMMutationMethods||{};e.isCustomAttribute&&s._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var d in n){s.properties.hasOwnProperty(d)?o(!1):void 0;var p=d.toLowerCase(),f=n[d],h={attributeName:p,attributeNamespace:null,propertyName:d,mutationMethod:null,mustUseAttribute:r(f,t.MUST_USE_ATTRIBUTE),mustUseProperty:r(f,t.MUST_USE_PROPERTY),hasSideEffects:r(f,t.HAS_SIDE_EFFECTS),hasBooleanValue:r(f,t.HAS_BOOLEAN_VALUE),hasNumericValue:r(f,t.HAS_NUMERIC_VALUE),hasPositiveNumericValue:r(f,t.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:r(f,t.HAS_OVERLOADED_BOOLEAN_VALUE)};if(h.mustUseAttribute&&h.mustUseProperty?o(!1):void 0,!h.mustUseProperty&&h.hasSideEffects?o(!1):void 0,h.hasBooleanValue+h.hasNumericValue+h.hasOverloadedBooleanValue<=1?void 0:o(!1),u.hasOwnProperty(d)){var m=u[d];h.attributeName=m}a.hasOwnProperty(d)&&(h.attributeNamespace=a[d]),l.hasOwnProperty(d)&&(h.propertyName=l[d]),c.hasOwnProperty(d)&&(h.mutationMethod=c[d]),s.properties[d]=h}}},a={},s={ID_ATTRIBUTE_NAME:"data-reactid",properties:{},getPossibleStandardName:null,_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t=a;a++)if(o(e,a)&&o(t,a))r=a;else if(e.charAt(a)!==t.charAt(a))break;var s=e.substr(0,r);return i(s)?void 0:p(!1),s}function c(e,t,n,r,o,i){e=e||"",t=t||"",e===t?p(!1):void 0;var l=a(t,e);l||a(e,t)?void 0:p(!1);for(var c=0,d=l?s:u,f=e;;f=d(f,t)){var h;if(o&&f===e||i&&f===t||(h=n(f,l,r)),h===!1||f===t)break;c++1){var t=e.indexOf(f,1);return t>-1?e.substr(0,t):e}return null},traverseEnterLeave:function(e,t,n,r,o){var i=l(e,t);i!==e&&c(e,i,n,r,!1,!0),i!==t&&c(i,t,n,o,!0,!1)},traverseTwoPhase:function(e,t,n){e&&(c("",e,t,n,!0,!1),c(e,"",t,n,!1,!0))},traverseTwoPhaseSkipTarget:function(e,t,n){e&&(c("",e,t,n,!0,!0),c(e,"",t,n,!0,!0))},traverseAncestors:function(e,t,n){c("",e,t,n,!0,!1)},getFirstCommonAncestorID:l,_getNextDescendantID:u,isAncestorIDOf:a,SEPARATOR:f};e.exports=g},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0}),t.setSettings=t.hideSettings=t.showSettings=t.setLatestUIVersion=t.setSortDateOrder=t.setSortSizeOrder=t.setSortNameOrder=t.hideAbout=t.showAbout=t.uploadFile=t.setLoginError=t.setShowAbortModal=t.setUpload=t.selectPrefix=t.selectBucket=t.setServerInfo=t.setDiskInfo=t.setCurrentPath=t.setCurrentBucket=t.setObjects=t.setVisibleBuckets=t.hideMakeBucketModal=t.setSidebarStatus=t.showAlert=t.hideAlert=t.showMakeBucketModal=t.addObject=t.addBucket=t.setBuckets=t.setWeb=t.setLoadBucket=t.setLoadPath=t.setLoginRedirectPath=t.SET_SETTINGS=t.SHOW_SETTINGS=t.SET_LOAD_PATH=t.SET_LOAD_BUCKET=t.SET_LOGIN_REDIRECT_PATH=t.SET_SIDEBAR_STATUS=t.SET_LATEST_UI_VERSION=t.SET_SORT_DATE_ORDER=t.SET_SORT_SIZE_ORDER=t.SET_SORT_NAME_ORDER=t.SHOW_ABOUT=t.SET_SHOW_ABORT_MODAL=t.SET_LOGIN_ERROR=t.SET_ALERT=t.SET_UPLOAD=t.SHOW_MAKEBUCKET_MODAL=t.SET_SERVER_INFO=t.SET_DISK_INFO=t.SET_OBJECTS=t.SET_VISIBLE_BUCKETS=t.ADD_OBJECT=t.ADD_BUCKET=t.SET_BUCKETS=t.SET_CURRENT_PATH=t.SET_CURRENT_BUCKET=t.SET_WEB=void 0;var i=n(223),a=(o(i),n(46)),s=o(a),u=n(116),l=(o(u),n(115)),c=r(l),d=t.SET_WEB="SET_WEB",p=t.SET_CURRENT_BUCKET="SET_CURRENT_BUCKET",f=t.SET_CURRENT_PATH="SET_CURRENT_PATH",h=t.SET_BUCKETS="SET_BUCKETS",m=t.ADD_BUCKET="ADD_BUCKET",g=t.ADD_OBJECT="ADD_OBJECT",y=t.SET_VISIBLE_BUCKETS="SET_VISIBLE_BUCKETS",v=t.SET_OBJECTS="SET_OBJECTS",M=t.SET_DISK_INFO="SET_DISK_INFO",T=t.SET_SERVER_INFO="SET_SERVER_INFO",b=t.SHOW_MAKEBUCKET_MODAL="SHOW_MAKEBUCKET_MODAL",x=t.SET_UPLOAD="SET_UPLOAD",E=t.SET_ALERT="SET_ALERT",A=t.SET_LOGIN_ERROR="SET_LOGIN_ERROR",N=t.SET_SHOW_ABORT_MODAL="SET_SHOW_ABORT_MODAL",w=t.SHOW_ABOUT="SHOW_ABOUT",I=t.SET_SORT_NAME_ORDER="SET_SORT_NAME_ORDER",C=t.SET_SORT_SIZE_ORDER="SET_SORT_SIZE_ORDER",D=t.SET_SORT_DATE_ORDER="SET_SORT_DATE_ORDER",S=t.SET_LATEST_UI_VERSION="SET_LATEST_UI_VERSION",k=t.SET_SIDEBAR_STATUS="SET_SIDEBAR_STATUS",L=t.SET_LOGIN_REDIRECT_PATH="SET_LOGIN_REDIRECT_PATH",O=t.SET_LOAD_BUCKET="SET_LOAD_BUCKET",P=t.SET_LOAD_PATH="SET_LOAD_PATH",j=t.SHOW_SETTINGS="SHOW_SETTINGS",z=t.SET_SETTINGS="SET_SETTINGS",R=(t.setLoginRedirectPath=function(e){return{type:L,path:e}},t.setLoadPath=function(e){return{type:P,loadPath:e}}),U=t.setLoadBucket=function(e){return{type:O,loadBucket:e}},Y=(t.setWeb=function(e){return{type:d,web:e}},t.setBuckets=function(e){return{type:h,buckets:e}},t.addBucket=function(e){return{type:m,bucket:e}},t.addObject=function(e){return{type:g,object:e}},t.showMakeBucketModal=function(){return{type:b,showMakeBucketModal:!0}},t.hideAlert=function(){return{type:E,alert:{show:!1,message:"",type:""}}},t.showAlert=function(e){return function(t,n){var r=null;"danger"!==e.type&&(r=setTimeout(function(){t({type:E,alert:{show:!1}})},5e3)),t({type:E,alert:Object.assign({},e,{show:!0,alertTimeout:r})})}}),B=(t.setSidebarStatus=function(e){return{type:k,sidebarStatus:e}},t.hideMakeBucketModal=function(){return{type:b,showMakeBucketModal:!1}},t.setVisibleBuckets=function(e){return{type:y,visibleBuckets:e}},t.setObjects=function(e){return{type:v,objects:e}}),W=t.setCurrentBucket=function(e){return{type:p,currentBucket:e}},F=t.setCurrentPath=function(e){return{type:f,currentPath:e}},V=(t.setDiskInfo=function(e){return{type:M,diskInfo:e}},t.setServerInfo=function(e){return{type:T,serverInfo:e}},t.selectBucket=function(e,t){return t||(t=""),function(n,r){var o=(r().web,r().currentBucket);o!==e&&n(U(e)),n(W(e)),n(V(t))}},t.selectPrefix=function(e){return function(t,n){var r=n(),o=r.currentBucket,i=r.web;t(R(e)),i.ListObjects({bucketName:o,prefix:e}).then(function(n){var r=n.objects;r||(r=[]),t(B(c.sortObjectsByName(r.map(function(t){return t.name=t.name.replace(""+e,""),t})))),t(Q(!1)),t(F(e)),t(U("")),t(R(""))})["catch"](function(e){t(Y({type:"danger",message:e.message})),t(U("")),t(R(""))})}}),H=t.setUpload=function(){var e=arguments.length<=0||void 0===arguments[0]?{inProgress:!1, -percent:0}:arguments[0];return{type:x,upload:e}},_=t.setShowAbortModal=function(e){return{type:N,showAbortModal:e}},Q=(t.setLoginError=function(){return{type:A,loginError:!0}},t.uploadFile=function(e,t){return function(n,r){var o=r(),i=o.currentBucket,a=o.currentPath,u=(o.web,""+a+e.name),l=window.location.origin+"/minio/upload/"+i+"/"+u;t.open("PUT",l,!0),t.withCredentials=!1,t.setRequestHeader("Authorization","Bearer "+localStorage.token),t.setRequestHeader("x-minio-date",(0,s["default"])().utc().format("YYYYMMDDTHHmmss")+"Z"),n(H({inProgress:!0,loaded:0,total:e.size,filename:e.name})),t.upload.addEventListener("error",function(t){n(Y({type:"danger",message:"Error occurred uploading '"+e.name+"'."})),n(H({inProgress:!1}))}),t.upload.addEventListener("progress",function(t){if(t.lengthComputable){var r=t.loaded,o=t.total;n(H({inProgress:!0,loaded:r,total:o,filename:e.name})),r===o&&(_(!1),n(H({inProgress:!1})),n(Y({type:"success",message:"File '"+e.name+"' uploaded successfully."})),n(V(a)))}}),t.send(e)}},t.showAbout=function(){return{type:w,showAbout:!0}},t.hideAbout=function(){return{type:w,showAbout:!1}},t.setSortNameOrder=function(e){return{type:I,sortNameOrder:e}});t.setSortSizeOrder=function(e){return{type:C,sortSizeOrder:e}},t.setSortDateOrder=function(e){return{type:D,sortDateOrder:e}},t.setLatestUIVersion=function(e){return{type:S,latestUiVersion:e}},t.showSettings=function(){return{type:j,showSettings:!0}},t.hideSettings=function(){return{type:j,showSettings:!1}},t.setSettings=function(e){return{type:z,settings:e}}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.minioBrowserPrefix="/minio"},function(e,t,n){(function(e){!function(t,n){e.exports=n()}(this,function(){"use strict";function t(){return Gn.apply(null,arguments)}function n(e){Gn=e}function r(e){return"[object Array]"===Object.prototype.toString.call(e)}function o(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function i(e,t){var n,r=[];for(n=0;n0)for(n in qn)r=qn[n],o=t[r],f(o)||(e[r]=o);return e}function m(e){h(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),Kn===!1&&(Kn=!0,t.updateOffset(this),Kn=!1)}function g(e){return e instanceof m||null!=e&&null!=e._isAMomentObject}function y(e){return 0>e?Math.ceil(e):Math.floor(e)}function v(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=y(t)),n}function M(e,t,n){var r,o=Math.min(e.length,t.length),i=Math.abs(e.length-t.length),a=0;for(r=0;o>r;r++)(n&&e[r]!==t[r]||!n&&v(e[r])!==v(t[r]))&&a++;return a+i}function T(){}function b(e){return e?e.toLowerCase().replace("_","-"):e}function x(e){for(var t,n,r,o,i=0;i0;){if(r=E(o.slice(0,t).join("-")))return r;if(n&&n.length>=t&&M(o,n,!0)>=t-1)break;t--}i++}return null}function E(t){var n=null;if(!Jn[t]&&"undefined"!=typeof e&&e&&e.exports)try{n=Xn._abbr,!function(){var e=new Error('Cannot find module "./locale"');throw e.code="MODULE_NOT_FOUND",e}(),A(n)}catch(r){}return Jn[t]}function A(e,t){var n;return e&&(n=f(t)?w(e):N(e,t),n&&(Xn=n)),Xn._abbr}function N(e,t){return null!==t?(t.abbr=e,Jn[e]=Jn[e]||new T,Jn[e].set(t),A(e),Jn[e]):(delete Jn[e],null)}function w(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Xn;if(!r(e)){if(t=E(e))return t;e=[e]}return x(e)}function I(e,t){var n=e.toLowerCase();$n[n]=$n[n+"s"]=$n[t]=e}function C(e){return"string"==typeof e?$n[e]||$n[e.toLowerCase()]:void 0}function D(e){var t,n,r={};for(n in e)a(e,n)&&(t=C(n),t&&(r[t]=e[n]));return r}function S(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function k(e,n){return function(r){return null!=r?(O(this,e,r),t.updateOffset(this,n),this):L(this,e)}}function L(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function O(e,t,n){e.isValid()&&e._d["set"+(e._isUTC?"UTC":"")+t](n)}function P(e,t){var n;if("object"==typeof e)for(n in e)this.set(n,e[n]);else if(e=C(e),S(this[e]))return this[e](t);return this}function j(e,t,n){var r=""+Math.abs(e),o=t-r.length,i=e>=0;return(i?n?"+":"":"-")+Math.pow(10,Math.max(0,o)).toString().substr(1)+r}function z(e,t,n,r){var o=r;"string"==typeof r&&(o=function(){return this[r]()}),e&&(rr[e]=o),t&&(rr[t[0]]=function(){return j(o.apply(this,arguments),t[1],t[2])}),n&&(rr[n]=function(){return this.localeData().ordinal(o.apply(this,arguments),e)})}function R(e){return e.match(/\[[\s\S]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"")}function U(e){var t,n,r=e.match(er);for(t=0,n=r.length;n>t;t++)rr[r[t]]?r[t]=rr[r[t]]:r[t]=R(r[t]);return function(o){var i="";for(t=0;n>t;t++)i+=r[t]instanceof Function?r[t].call(o,e):r[t];return i}}function Y(e,t){return e.isValid()?(t=B(t,e.localeData()),nr[t]=nr[t]||U(t),nr[t](e)):e.localeData().invalidDate()}function B(e,t){function n(e){return t.longDateFormat(e)||e}var r=5;for(tr.lastIndex=0;r>=0&&tr.test(e);)e=e.replace(tr,n),tr.lastIndex=0,r-=1;return e}function W(e,t,n){br[e]=S(t)?t:function(e,r){return e&&n?n:t}}function F(e,t){return a(br,e)?br[e](t._strict,t._locale):new RegExp(V(e))}function V(e){return H(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,r,o){return t||n||r||o}))}function H(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function _(e,t){var n,r=t;for("string"==typeof e&&(e=[e]),"number"==typeof t&&(r=function(e,n){n[t]=v(e)}),n=0;nr;r++){if(o=u([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(o,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(o,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(i="^"+this.months(o,"")+"|^"+this.monthsShort(o,""),this._monthsParse[r]=new RegExp(i.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[r].test(e))return r;if(n&&"MMM"===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}}function J(e,t){var n;return e.isValid()?"string"==typeof t&&(t=e.localeData().monthsParse(t),"number"!=typeof t)?e:(n=Math.min(e.date(),G(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n),e):e}function $(e){return null!=e?(J(this,e),t.updateOffset(this,!0),this):L(this,"Month")}function ee(){return G(this.year(),this.month())}function te(e){return this._monthsParseExact?(a(this,"_monthsRegex")||re.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex}function ne(e){return this._monthsParseExact?(a(this,"_monthsRegex")||re.call(this),e?this._monthsStrictRegex:this._monthsRegex):this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex}function re(){function e(e,t){return t.length-e.length}var t,n,r=[],o=[],i=[];for(t=0;12>t;t++)n=u([2e3,t]),r.push(this.monthsShort(n,"")),o.push(this.months(n,"")),i.push(this.months(n,"")),i.push(this.monthsShort(n,""));for(r.sort(e),o.sort(e),i.sort(e),t=0;12>t;t++)r[t]=H(r[t]),o[t]=H(o[t]),i[t]=H(i[t]);this._monthsRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+o.join("|")+")$","i"),this._monthsShortStrictRegex=new RegExp("^("+r.join("|")+")$","i")}function oe(e){var t,n=e._a;return n&&-2===c(e).overflow&&(t=n[Ar]<0||n[Ar]>11?Ar:n[Nr]<1||n[Nr]>G(n[Er],n[Ar])?Nr:n[wr]<0||n[wr]>24||24===n[wr]&&(0!==n[Ir]||0!==n[Cr]||0!==n[Dr])?wr:n[Ir]<0||n[Ir]>59?Ir:n[Cr]<0||n[Cr]>59?Cr:n[Dr]<0||n[Dr]>999?Dr:-1,c(e)._overflowDayOfYear&&(Er>t||t>Nr)&&(t=Nr),c(e)._overflowWeeks&&-1===t&&(t=Sr),c(e)._overflowWeekday&&-1===t&&(t=kr),c(e).overflow=t),e}function ie(e){t.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function ae(e,t){var n=!0;return s(function(){return n&&(ie(e+"\nArguments: "+Array.prototype.slice.call(arguments).join(", ")+"\n"+(new Error).stack),n=!1),t.apply(this,arguments)},t)}function se(e,t){Rr[e]||(ie(t),Rr[e]=!0)}function ue(e){var t,n,r,o,i,a,s=e._i,u=Ur.exec(s)||Yr.exec(s);if(u){for(c(e).iso=!0,t=0,n=Wr.length;n>t;t++)if(Wr[t][1].exec(u[1])){o=Wr[t][0],r=Wr[t][2]!==!1;break}if(null==o)return void(e._isValid=!1);if(u[3]){for(t=0,n=Fr.length;n>t;t++)if(Fr[t][1].exec(u[3])){i=(u[2]||" ")+Fr[t][0];break}if(null==i)return void(e._isValid=!1)}if(!r&&null!=i)return void(e._isValid=!1);if(u[4]){if(!Br.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=o+(i||"")+(a||""),Ee(e)}else e._isValid=!1}function le(e){var n=Vr.exec(e._i);return null!==n?void(e._d=new Date(+n[1])):(ue(e),void(e._isValid===!1&&(delete e._isValid,t.createFromInputFallback(e))))}function ce(e,t,n,r,o,i,a){var s=new Date(e,t,n,r,o,i,a);return 100>e&&e>=0&&isFinite(s.getFullYear())&&s.setFullYear(e),s}function de(e){var t=new Date(Date.UTC.apply(null,arguments));return 100>e&&e>=0&&isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e),t}function pe(e){return fe(e)?366:365}function fe(e){return e%4===0&&e%100!==0||e%400===0}function he(){return fe(this.year())}function me(e,t,n){var r=7+t-n,o=(7+de(e,0,r).getUTCDay()-t)%7;return-o+r-1}function ge(e,t,n,r,o){var i,a,s=(7+n-r)%7,u=me(e,r,o),l=1+7*(t-1)+s+u;return 0>=l?(i=e-1,a=pe(i)+l):l>pe(e)?(i=e+1,a=l-pe(e)):(i=e,a=l),{year:i,dayOfYear:a}}function ye(e,t,n){var r,o,i=me(e.year(),t,n),a=Math.floor((e.dayOfYear()-i-1)/7)+1;return 1>a?(o=e.year()-1,r=a+ve(o,t,n)):a>ve(e.year(),t,n)?(r=a-ve(e.year(),t,n),o=e.year()+1):(o=e.year(),r=a),{week:r,year:o}}function ve(e,t,n){var r=me(e,t,n),o=me(e+1,t,n);return(pe(e)-r+o)/7}function Me(e,t,n){return null!=e?e:null!=t?t:n}function Te(e){var n=new Date(t.now());return e._useUTC?[n.getUTCFullYear(),n.getUTCMonth(),n.getUTCDate()]:[n.getFullYear(),n.getMonth(),n.getDate()]}function be(e){var t,n,r,o,i=[];if(!e._d){for(r=Te(e),e._w&&null==e._a[Nr]&&null==e._a[Ar]&&xe(e),e._dayOfYear&&(o=Me(e._a[Er],r[Er]),e._dayOfYear>pe(o)&&(c(e)._overflowDayOfYear=!0),n=de(o,0,e._dayOfYear),e._a[Ar]=n.getUTCMonth(),e._a[Nr]=n.getUTCDate()),t=0;3>t&&null==e._a[t];++t)e._a[t]=i[t]=r[t];for(;7>t;t++)e._a[t]=i[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[wr]&&0===e._a[Ir]&&0===e._a[Cr]&&0===e._a[Dr]&&(e._nextDay=!0,e._a[wr]=0),e._d=(e._useUTC?de:ce).apply(null,i),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[wr]=24)}}function xe(e){var t,n,r,o,i,a,s,u;t=e._w,null!=t.GG||null!=t.W||null!=t.E?(i=1,a=4,n=Me(t.GG,e._a[Er],ye(ke(),1,4).year),r=Me(t.W,1),o=Me(t.E,1),(1>o||o>7)&&(u=!0)):(i=e._locale._week.dow,a=e._locale._week.doy,n=Me(t.gg,e._a[Er],ye(ke(),i,a).year),r=Me(t.w,1),null!=t.d?(o=t.d,(0>o||o>6)&&(u=!0)):null!=t.e?(o=t.e+i,(t.e<0||t.e>6)&&(u=!0)):o=i),1>r||r>ve(n,i,a)?c(e)._overflowWeeks=!0:null!=u?c(e)._overflowWeekday=!0:(s=ge(n,r,o,i,a),e._a[Er]=s.year,e._dayOfYear=s.dayOfYear)}function Ee(e){if(e._f===t.ISO_8601)return void ue(e);e._a=[],c(e).empty=!0;var n,r,o,i,a,s=""+e._i,u=s.length,l=0;for(o=B(e._f,e._locale).match(er)||[],n=0;n0&&c(e).unusedInput.push(a),s=s.slice(s.indexOf(r)+r.length),l+=r.length),rr[i]?(r?c(e).empty=!1:c(e).unusedTokens.push(i),Z(i,r,e)):e._strict&&!r&&c(e).unusedTokens.push(i);c(e).charsLeftOver=u-l,s.length>0&&c(e).unusedInput.push(s),c(e).bigHour===!0&&e._a[wr]<=12&&e._a[wr]>0&&(c(e).bigHour=void 0),e._a[wr]=Ae(e._locale,e._a[wr],e._meridiem),be(e),oe(e)}function Ae(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?(r=e.isPM(n),r&&12>t&&(t+=12),r||12!==t||(t=0),t):t}function Ne(e){var t,n,r,o,i;if(0===e._f.length)return c(e).invalidFormat=!0,void(e._d=new Date(NaN));for(o=0;oi)&&(r=i,n=t));s(e,n||t)}function we(e){if(!e._d){var t=D(e._i);e._a=i([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),be(e)}}function Ie(e){var t=new m(oe(Ce(e)));return t._nextDay&&(t.add(1,"d"),t._nextDay=void 0),t}function Ce(e){var t=e._i,n=e._f;return e._locale=e._locale||w(e._l),null===t||void 0===n&&""===t?p({nullInput:!0}):("string"==typeof t&&(e._i=t=e._locale.preparse(t)),g(t)?new m(oe(t)):(r(n)?Ne(e):n?Ee(e):o(t)?e._d=t:De(e),d(e)||(e._d=null),e))}function De(e){var n=e._i;void 0===n?e._d=new Date(t.now()):o(n)?e._d=new Date(+n):"string"==typeof n?le(e):r(n)?(e._a=i(n.slice(0),function(e){return parseInt(e,10)}),be(e)):"object"==typeof n?we(e):"number"==typeof n?e._d=new Date(n):t.createFromInputFallback(e)}function Se(e,t,n,r,o){var i={};return"boolean"==typeof n&&(r=n,n=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=o,i._l=n,i._i=e,i._f=t,i._strict=r,Ie(i)}function ke(e,t,n,r){return Se(e,t,n,r,!1)}function Le(e,t){var n,o;if(1===t.length&&r(t[0])&&(t=t[0]),!t.length)return ke();for(n=t[0],o=1;oe&&(e=-e,n="-"),n+j(~~(e/60),2)+t+j(~~e%60,2)})}function Ue(e,t){var n=(t||"").match(e)||[],r=n[n.length-1]||[],o=(r+"").match(Gr)||["-",0,0],i=+(60*o[1])+v(o[2]);return"+"===o[0]?i:-i}function Ye(e,n){var r,i;return n._isUTC?(r=n.clone(),i=(g(e)||o(e)?+e:+ke(e))-+r,r._d.setTime(+r._d+i),t.updateOffset(r,!1),r):ke(e).local()}function Be(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function We(e,n){var r,o=this._offset||0;return this.isValid()?null!=e?("string"==typeof e?e=Ue(vr,e):Math.abs(e)<16&&(e=60*e),!this._isUTC&&n&&(r=Be(this)),this._offset=e,this._isUTC=!0,null!=r&&this.add(r,"m"),o!==e&&(!n||this._changeInProgress?rt(this,Je(e-o,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,t.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?o:Be(this):null!=e?this:NaN}function Fe(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}function Ve(e){return this.utcOffset(0,e)}function He(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Be(this),"m")),this}function _e(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ue(yr,this._i)),this}function Qe(e){return this.isValid()?(e=e?ke(e).utcOffset():0,(this.utcOffset()-e)%60===0):!1}function Ze(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ge(){if(!f(this._isDSTShifted))return this._isDSTShifted;var e={};if(h(e,this),e=Ce(e),e._a){var t=e._isUTC?u(e._a):ke(e._a);this._isDSTShifted=this.isValid()&&M(e._a,t.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Xe(){return this.isValid()?!this._isUTC:!1}function qe(){return this.isValid()?this._isUTC:!1}function Ke(){return this.isValid()?this._isUTC&&0===this._offset:!1}function Je(e,t){var n,r,o,i=e,s=null;return ze(e)?i={ms:e._milliseconds,d:e._days,M:e._months}:"number"==typeof e?(i={},t?i[t]=e:i.milliseconds=e):(s=Xr.exec(e))?(n="-"===s[1]?-1:1,i={y:0,d:v(s[Nr])*n,h:v(s[wr])*n,m:v(s[Ir])*n,s:v(s[Cr])*n,ms:v(s[Dr])*n}):(s=qr.exec(e))?(n="-"===s[1]?-1:1,i={y:$e(s[2],n),M:$e(s[3],n),d:$e(s[4],n),h:$e(s[5],n),m:$e(s[6],n),s:$e(s[7],n),w:$e(s[8],n)}):null==i?i={}:"object"==typeof i&&("from"in i||"to"in i)&&(o=tt(ke(i.from),ke(i.to)),i={},i.ms=o.milliseconds,i.M=o.months),r=new je(i),ze(e)&&a(e,"_locale")&&(r._locale=e._locale),r}function $e(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function et(e,t){var n={milliseconds:0,months:0};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function tt(e,t){var n;return e.isValid()&&t.isValid()?(t=Ye(t,e),e.isBefore(t)?n=et(e,t):(n=et(t,e),n.milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}function nt(e,t){return function(n,r){var o,i;return null===r||isNaN(+r)||(se(t,"moment()."+t+"(period, number) is deprecated. Please use moment()."+t+"(number, period)."),i=n,n=r,r=i),n="string"==typeof n?+n:n,o=Je(n,r),rt(this,o,e),this}}function rt(e,n,r,o){var i=n._milliseconds,a=n._days,s=n._months;e.isValid()&&(o=null==o?!0:o,i&&e._d.setTime(+e._d+i*r),a&&O(e,"Date",L(e,"Date")+a*r),s&&J(e,L(e,"Month")+s*r),o&&t.updateOffset(e,a||s))}function ot(e,t){var n=e||ke(),r=Ye(n,this).startOf("day"),o=this.diff(r,"days",!0),i=-6>o?"sameElse":-1>o?"lastWeek":0>o?"lastDay":1>o?"sameDay":2>o?"nextDay":7>o?"nextWeek":"sameElse",a=t&&(S(t[i])?t[i]():t[i]);return this.format(a||this.localeData().calendar(i,this,ke(n)))}function it(){return new m(this)}function at(e,t){var n=g(e)?e:ke(e);return this.isValid()&&n.isValid()?(t=C(f(t)?"millisecond":t),"millisecond"===t?+this>+n:+n<+this.clone().startOf(t)):!1}function st(e,t){var n=g(e)?e:ke(e);return this.isValid()&&n.isValid()?(t=C(f(t)?"millisecond":t),"millisecond"===t?+n>+this:+this.clone().endOf(t)<+n):!1}function ut(e,t,n){return this.isAfter(e,n)&&this.isBefore(t,n)}function lt(e,t){var n,r=g(e)?e:ke(e);return this.isValid()&&r.isValid()?(t=C(t||"millisecond"),"millisecond"===t?+this===+r:(n=+r,+this.clone().startOf(t)<=n&&n<=+this.clone().endOf(t))):!1}function ct(e,t){return this.isSame(e,t)||this.isAfter(e,t)}function dt(e,t){return this.isSame(e,t)||this.isBefore(e,t)}function pt(e,t,n){var r,o,i,a;return this.isValid()?(r=Ye(e,this),r.isValid()?(o=6e4*(r.utcOffset()-this.utcOffset()),t=C(t),"year"===t||"month"===t||"quarter"===t?(a=ft(this,r),"quarter"===t?a/=3:"year"===t&&(a/=12)):(i=this-r,a="second"===t?i/1e3:"minute"===t?i/6e4:"hour"===t?i/36e5:"day"===t?(i-o)/864e5:"week"===t?(i-o)/6048e5:i),n?a:y(a)):NaN):NaN}function ft(e,t){var n,r,o=12*(t.year()-e.year())+(t.month()-e.month()),i=e.clone().add(o,"months");return 0>t-i?(n=e.clone().add(o-1,"months"),r=(t-i)/(i-n)):(n=e.clone().add(o+1,"months"),r=(t-i)/(n-i)),-(o+r)}function ht(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function mt(){var e=this.clone().utc();return 0i&&(t=i),Wt.call(this,e,t,n,r,o))}function Wt(e,t,n,r,o){var i=ge(e,t,n,r,o),a=de(i.year,0,i.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}function Ft(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)}function Vt(e){return ye(e,this._week.dow,this._week.doy).week}function Ht(){return this._week.dow}function _t(){return this._week.doy}function Qt(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")}function Zt(e){var t=ye(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")}function Gt(e,t){return"string"!=typeof e?e:isNaN(e)?(e=t.weekdaysParse(e),"number"==typeof e?e:null):parseInt(e,10)}function Xt(e,t){return r(this._weekdays)?this._weekdays[e.day()]:this._weekdays[this._weekdays.isFormat.test(t)?"format":"standalone"][e.day()]}function qt(e){return this._weekdaysShort[e.day()]}function Kt(e){return this._weekdaysMin[e.day()]}function Jt(e,t,n){var r,o,i;for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;7>r;r++){if(o=ke([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(o,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(o,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(o,"").replace(".",".?")+"$","i")),this._weekdaysParse[r]||(i="^"+this.weekdays(o,"")+"|^"+this.weekdaysShort(o,"")+"|^"+this.weekdaysMin(o,""),this._weekdaysParse[r]=new RegExp(i.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&"ddd"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&"dd"===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}}function $t(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=Gt(e,this.localeData()),this.add(e-t,"d")):t}function en(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")}function tn(e){return this.isValid()?null==e?this.day()||7:this.day(this.day()%7?e:e-7):null!=e?this:NaN}function nn(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")}function rn(){return this.hours()%12||12}function on(e,t){z(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function an(e,t){return t._meridiemParse}function sn(e){return"p"===(e+"").toLowerCase().charAt(0)}function un(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"}function ln(e,t){t[Dr]=v(1e3*("0."+e))}function cn(){return this._isUTC?"UTC":""}function dn(){return this._isUTC?"Coordinated Universal Time":""}function pn(e){return ke(1e3*e)}function fn(){return ke.apply(null,arguments).parseZone()}function hn(e,t,n){var r=this._calendar[e];return S(r)?r.call(t,n):r}function mn(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])}function gn(){return this._invalidDate}function yn(e){return this._ordinal.replace("%d",e)}function vn(e){return e}function Mn(e,t,n,r){var o=this._relativeTime[n];return S(o)?o(e,t,n,r):o.replace(/%d/i,e)}function Tn(e,t){var n=this._relativeTime[e>0?"future":"past"];return S(n)?n(t):n.replace(/%s/i,t)}function bn(e){var t,n;for(n in e)t=e[n],S(t)?this[n]=t:this["_"+n]=t;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function xn(e,t,n,r){var o=w(),i=u().set(r,t);return o[n](i,e)}function En(e,t,n,r,o){if("number"==typeof e&&(t=e,e=void 0),e=e||"",null!=t)return xn(e,t,n,o);var i,a=[];for(i=0;r>i;i++)a[i]=xn(e,i,n,o);return a}function An(e,t){return En(e,t,"months",12,"month")}function Nn(e,t){return En(e,t,"monthsShort",12,"month")}function wn(e,t){return En(e,t,"weekdays",7,"day")}function In(e,t){return En(e,t,"weekdaysShort",7,"day")}function Cn(e,t){return En(e,t,"weekdaysMin",7,"day")}function Dn(){var e=this._data;return this._milliseconds=bo(this._milliseconds),this._days=bo(this._days),this._months=bo(this._months),e.milliseconds=bo(e.milliseconds),e.seconds=bo(e.seconds),e.minutes=bo(e.minutes),e.hours=bo(e.hours),e.months=bo(e.months),e.years=bo(e.years),this}function Sn(e,t,n,r){var o=Je(t,n);return e._milliseconds+=r*o._milliseconds,e._days+=r*o._days,e._months+=r*o._months,e._bubble()}function kn(e,t){return Sn(this,e,t,1)}function Ln(e,t){return Sn(this,e,t,-1)}function On(e){return 0>e?Math.floor(e):Math.ceil(e)}function Pn(){var e,t,n,r,o,i=this._milliseconds,a=this._days,s=this._months,u=this._data;return i>=0&&a>=0&&s>=0||0>=i&&0>=a&&0>=s||(i+=864e5*On(zn(s)+a),a=0,s=0),u.milliseconds=i%1e3,e=y(i/1e3),u.seconds=e%60,t=y(e/60),u.minutes=t%60,n=y(t/60),u.hours=n%24,a+=y(n/24),o=y(jn(a)),s+=o,a-=On(zn(o)),r=y(s/12),s%=12,u.days=a,u.months=s,u.years=r,this}function jn(e){return 4800*e/146097}function zn(e){return 146097*e/4800}function Rn(e){var t,n,r=this._milliseconds;if(e=C(e),"month"===e||"year"===e)return t=this._days+r/864e5,n=this._months+jn(t),"month"===e?n:n/12;switch(t=this._days+Math.round(zn(this._months)),e){case"week":return t/7+r/6048e5;case"day":return t+r/864e5;case"hour":return 24*t+r/36e5;case"minute":return 1440*t+r/6e4;case"second":return 86400*t+r/1e3;case"millisecond":return Math.floor(864e5*t)+r;default:throw new Error("Unknown unit "+e)}}function Un(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*v(this._months/12)}function Yn(e){return function(){return this.as(e)}}function Bn(e){return e=C(e),this[e+"s"]()}function Wn(e){return function(){return this._data[e]}}function Fn(){return y(this.days()/7)}function Vn(e,t,n,r,o){return o.relativeTime(t||1,!!n,e,r)}function Hn(e,t,n){var r=Je(e).abs(),o=Ro(r.as("s")),i=Ro(r.as("m")),a=Ro(r.as("h")),s=Ro(r.as("d")),u=Ro(r.as("M")),l=Ro(r.as("y")),c=o=i&&["m"]||i=a&&["h"]||a=s&&["d"]||s=u&&["M"]||u=l&&["y"]||["yy",l];return c[2]=t,c[3]=+e>0,c[4]=n,Vn.apply(null,c)}function _n(e,t){return void 0===Uo[e]?!1:void 0===t?Uo[e]:(Uo[e]=t,!0)}function Qn(e){var t=this.localeData(),n=Hn(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)}function Zn(){var e,t,n,r=Yo(this._milliseconds)/1e3,o=Yo(this._days),i=Yo(this._months);e=y(r/60),t=y(e/60),r%=60,e%=60,n=y(i/12),i%=12;var a=n,s=i,u=o,l=t,c=e,d=r,p=this.asSeconds();return p?(0>p?"-":"")+"P"+(a?a+"Y":"")+(s?s+"M":"")+(u?u+"D":"")+(l||c||d?"T":"")+(l?l+"H":"")+(c?c+"M":"")+(d?d+"S":""):"P0D"}var Gn,Xn,qn=t.momentProperties=[],Kn=!1,Jn={},$n={},er=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,tr=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,nr={},rr={},or=/\d/,ir=/\d\d/,ar=/\d{3}/,sr=/\d{4}/,ur=/[+-]?\d{6}/,lr=/\d\d?/,cr=/\d\d\d\d?/,dr=/\d\d\d\d\d\d?/,pr=/\d{1,3}/,fr=/\d{1,4}/,hr=/[+-]?\d{1,6}/,mr=/\d+/,gr=/[+-]?\d+/,yr=/Z|[+-]\d\d:?\d\d/gi,vr=/Z|[+-]\d\d(?::?\d\d)?/gi,Mr=/[+-]?\d+(\.\d{1,3})?/,Tr=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,br={},xr={},Er=0,Ar=1,Nr=2,wr=3,Ir=4,Cr=5,Dr=6,Sr=7,kr=8;z("M",["MM",2],"Mo",function(){return this.month()+1}),z("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),z("MMMM",0,0,function(e){return this.localeData().months(this,e)}),I("month","M"),W("M",lr),W("MM",lr,ir),W("MMM",function(e,t){return t.monthsShortRegex(e)}),W("MMMM",function(e,t){return t.monthsRegex(e)}),_(["M","MM"],function(e,t){t[Ar]=v(e)-1}),_(["MMM","MMMM"],function(e,t,n,r){var o=n._locale.monthsParse(e,r,n._strict);null!=o?t[Ar]=o:c(n).invalidMonth=e});var Lr=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/,Or="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Pr="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),jr=Tr,zr=Tr,Rr={};t.suppressDeprecationWarnings=!1;var Ur=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,Yr=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,Br=/Z|[+-]\d\d(?::?\d\d)?/,Wr=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Fr=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Vr=/^\/?Date\((\-?\d+)/i;t.createFromInputFallback=ae("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(e){ -e._d=new Date(e._i+(e._useUTC?" UTC":""))}),z("Y",0,0,function(){var e=this.year();return 9999>=e?""+e:"+"+e}),z(0,["YY",2],0,function(){return this.year()%100}),z(0,["YYYY",4],0,"year"),z(0,["YYYYY",5],0,"year"),z(0,["YYYYYY",6,!0],0,"year"),I("year","y"),W("Y",gr),W("YY",lr,ir),W("YYYY",fr,sr),W("YYYYY",hr,ur),W("YYYYYY",hr,ur),_(["YYYYY","YYYYYY"],Er),_("YYYY",function(e,n){n[Er]=2===e.length?t.parseTwoDigitYear(e):v(e)}),_("YY",function(e,n){n[Er]=t.parseTwoDigitYear(e)}),_("Y",function(e,t){t[Er]=parseInt(e,10)}),t.parseTwoDigitYear=function(e){return v(e)+(v(e)>68?1900:2e3)};var Hr=k("FullYear",!1);t.ISO_8601=function(){};var _r=ae("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var e=ke.apply(null,arguments);return this.isValid()&&e.isValid()?this>e?this:e:p()}),Qr=ae("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var e=ke.apply(null,arguments);return this.isValid()&&e.isValid()?e>this?this:e:p()}),Zr=function(){return Date.now?Date.now():+new Date};Re("Z",":"),Re("ZZ",""),W("Z",vr),W("ZZ",vr),_(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Ue(vr,e)});var Gr=/([\+\-]|\d\d)/gi;t.updateOffset=function(){};var Xr=/(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,qr=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Je.fn=je.prototype;var Kr=nt(1,"add"),Jr=nt(-1,"subtract");t.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var $r=ae("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});z(0,["gg",2],0,function(){return this.weekYear()%100}),z(0,["GG",2],0,function(){return this.isoWeekYear()%100}),jt("gggg","weekYear"),jt("ggggg","weekYear"),jt("GGGG","isoWeekYear"),jt("GGGGG","isoWeekYear"),I("weekYear","gg"),I("isoWeekYear","GG"),W("G",gr),W("g",gr),W("GG",lr,ir),W("gg",lr,ir),W("GGGG",fr,sr),W("gggg",fr,sr),W("GGGGG",hr,ur),W("ggggg",hr,ur),Q(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,r){t[r.substr(0,2)]=v(e)}),Q(["gg","GG"],function(e,n,r,o){n[o]=t.parseTwoDigitYear(e)}),z("Q",0,"Qo","quarter"),I("quarter","Q"),W("Q",or),_("Q",function(e,t){t[Ar]=3*(v(e)-1)}),z("w",["ww",2],"wo","week"),z("W",["WW",2],"Wo","isoWeek"),I("week","w"),I("isoWeek","W"),W("w",lr),W("ww",lr,ir),W("W",lr),W("WW",lr,ir),Q(["w","ww","W","WW"],function(e,t,n,r){t[r.substr(0,1)]=v(e)});var eo={dow:0,doy:6};z("D",["DD",2],"Do","date"),I("date","D"),W("D",lr),W("DD",lr,ir),W("Do",function(e,t){return e?t._ordinalParse:t._ordinalParseLenient}),_(["D","DD"],Nr),_("Do",function(e,t){t[Nr]=v(e.match(lr)[0],10)});var to=k("Date",!0);z("d",0,"do","day"),z("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),z("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),z("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),z("e",0,0,"weekday"),z("E",0,0,"isoWeekday"),I("day","d"),I("weekday","e"),I("isoWeekday","E"),W("d",lr),W("e",lr),W("E",lr),W("dd",Tr),W("ddd",Tr),W("dddd",Tr),Q(["dd","ddd","dddd"],function(e,t,n,r){var o=n._locale.weekdaysParse(e,r,n._strict);null!=o?t.d=o:c(n).invalidWeekday=e}),Q(["d","e","E"],function(e,t,n,r){t[r]=v(e)});var no="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),ro="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),oo="Su_Mo_Tu_We_Th_Fr_Sa".split("_");z("DDD",["DDDD",3],"DDDo","dayOfYear"),I("dayOfYear","DDD"),W("DDD",pr),W("DDDD",ar),_(["DDD","DDDD"],function(e,t,n){n._dayOfYear=v(e)}),z("H",["HH",2],0,"hour"),z("h",["hh",2],0,rn),z("hmm",0,0,function(){return""+rn.apply(this)+j(this.minutes(),2)}),z("hmmss",0,0,function(){return""+rn.apply(this)+j(this.minutes(),2)+j(this.seconds(),2)}),z("Hmm",0,0,function(){return""+this.hours()+j(this.minutes(),2)}),z("Hmmss",0,0,function(){return""+this.hours()+j(this.minutes(),2)+j(this.seconds(),2)}),on("a",!0),on("A",!1),I("hour","h"),W("a",an),W("A",an),W("H",lr),W("h",lr),W("HH",lr,ir),W("hh",lr,ir),W("hmm",cr),W("hmmss",dr),W("Hmm",cr),W("Hmmss",dr),_(["H","HH"],wr),_(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),_(["h","hh"],function(e,t,n){t[wr]=v(e),c(n).bigHour=!0}),_("hmm",function(e,t,n){var r=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r)),c(n).bigHour=!0}),_("hmmss",function(e,t,n){var r=e.length-4,o=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r,2)),t[Cr]=v(e.substr(o)),c(n).bigHour=!0}),_("Hmm",function(e,t,n){var r=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r))}),_("Hmmss",function(e,t,n){var r=e.length-4,o=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r,2)),t[Cr]=v(e.substr(o))});var io=/[ap]\.?m?\.?/i,ao=k("Hours",!0);z("m",["mm",2],0,"minute"),I("minute","m"),W("m",lr),W("mm",lr,ir),_(["m","mm"],Ir);var so=k("Minutes",!1);z("s",["ss",2],0,"second"),I("second","s"),W("s",lr),W("ss",lr,ir),_(["s","ss"],Cr);var uo=k("Seconds",!1);z("S",0,0,function(){return~~(this.millisecond()/100)}),z(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),z(0,["SSS",3],0,"millisecond"),z(0,["SSSS",4],0,function(){return 10*this.millisecond()}),z(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),z(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),z(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),z(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),z(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),I("millisecond","ms"),W("S",pr,or),W("SS",pr,ir),W("SSS",pr,ar);var lo;for(lo="SSSS";lo.length<=9;lo+="S")W(lo,mr);for(lo="S";lo.length<=9;lo+="S")_(lo,ln);var co=k("Milliseconds",!1);z("z",0,0,"zoneAbbr"),z("zz",0,0,"zoneName");var po=m.prototype;po.add=Kr,po.calendar=ot,po.clone=it,po.diff=pt,po.endOf=At,po.format=gt,po.from=yt,po.fromNow=vt,po.to=Mt,po.toNow=Tt,po.get=P,po.invalidAt=Ot,po.isAfter=at,po.isBefore=st,po.isBetween=ut,po.isSame=lt,po.isSameOrAfter=ct,po.isSameOrBefore=dt,po.isValid=kt,po.lang=$r,po.locale=bt,po.localeData=xt,po.max=Qr,po.min=_r,po.parsingFlags=Lt,po.set=P,po.startOf=Et,po.subtract=Jr,po.toArray=Ct,po.toObject=Dt,po.toDate=It,po.toISOString=mt,po.toJSON=St,po.toString=ht,po.unix=wt,po.valueOf=Nt,po.creationData=Pt,po.year=Hr,po.isLeapYear=he,po.weekYear=zt,po.isoWeekYear=Rt,po.quarter=po.quarters=Ft,po.month=$,po.daysInMonth=ee,po.week=po.weeks=Qt,po.isoWeek=po.isoWeeks=Zt,po.weeksInYear=Yt,po.isoWeeksInYear=Ut,po.date=to,po.day=po.days=$t,po.weekday=en,po.isoWeekday=tn,po.dayOfYear=nn,po.hour=po.hours=ao,po.minute=po.minutes=so,po.second=po.seconds=uo,po.millisecond=po.milliseconds=co,po.utcOffset=We,po.utc=Ve,po.local=He,po.parseZone=_e,po.hasAlignedHourOffset=Qe,po.isDST=Ze,po.isDSTShifted=Ge,po.isLocal=Xe,po.isUtcOffset=qe,po.isUtc=Ke,po.isUTC=Ke,po.zoneAbbr=cn,po.zoneName=dn,po.dates=ae("dates accessor is deprecated. Use date instead.",to),po.months=ae("months accessor is deprecated. Use month instead",$),po.years=ae("years accessor is deprecated. Use year instead",Hr),po.zone=ae("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Fe);var fo=po,ho={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},mo={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},go="Invalid date",yo="%d",vo=/\d{1,2}/,Mo={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},To=T.prototype;To._calendar=ho,To.calendar=hn,To._longDateFormat=mo,To.longDateFormat=mn,To._invalidDate=go,To.invalidDate=gn,To._ordinal=yo,To.ordinal=yn,To._ordinalParse=vo,To.preparse=vn,To.postformat=vn,To._relativeTime=Mo,To.relativeTime=Mn,To.pastFuture=Tn,To.set=bn,To.months=X,To._months=Or,To.monthsShort=q,To._monthsShort=Pr,To.monthsParse=K,To._monthsRegex=zr,To.monthsRegex=ne,To._monthsShortRegex=jr,To.monthsShortRegex=te,To.week=Vt,To._week=eo,To.firstDayOfYear=_t,To.firstDayOfWeek=Ht,To.weekdays=Xt,To._weekdays=no,To.weekdaysMin=Kt,To._weekdaysMin=oo,To.weekdaysShort=qt,To._weekdaysShort=ro,To.weekdaysParse=Jt,To.isPM=sn,To._meridiemParse=io,To.meridiem=un,A("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=1===v(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n}}),t.lang=ae("moment.lang is deprecated. Use moment.locale instead.",A),t.langData=ae("moment.langData is deprecated. Use moment.localeData instead.",w);var bo=Math.abs,xo=Yn("ms"),Eo=Yn("s"),Ao=Yn("m"),No=Yn("h"),wo=Yn("d"),Io=Yn("w"),Co=Yn("M"),Do=Yn("y"),So=Wn("milliseconds"),ko=Wn("seconds"),Lo=Wn("minutes"),Oo=Wn("hours"),Po=Wn("days"),jo=Wn("months"),zo=Wn("years"),Ro=Math.round,Uo={s:45,m:45,h:22,d:26,M:11},Yo=Math.abs,Bo=je.prototype;Bo.abs=Dn,Bo.add=kn,Bo.subtract=Ln,Bo.as=Rn,Bo.asMilliseconds=xo,Bo.asSeconds=Eo,Bo.asMinutes=Ao,Bo.asHours=No,Bo.asDays=wo,Bo.asWeeks=Io,Bo.asMonths=Co,Bo.asYears=Do,Bo.valueOf=Un,Bo._bubble=Pn,Bo.get=Bn,Bo.milliseconds=So,Bo.seconds=ko,Bo.minutes=Lo,Bo.hours=Oo,Bo.days=Po,Bo.weeks=Fn,Bo.months=jo,Bo.years=zo,Bo.humanize=Qn,Bo.toISOString=Zn,Bo.toString=Zn,Bo.toJSON=Zn,Bo.locale=bt,Bo.localeData=xt,Bo.toIsoString=ae("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Zn),Bo.lang=$r,z("X",0,0,"unix"),z("x",0,0,"valueOf"),W("x",gr),W("X",Mr),_("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),_("x",function(e,t,n){n._d=new Date(v(e))}),t.version="2.11.1",n(ke),t.fn=fo,t.min=Oe,t.max=Pe,t.now=Zr,t.utc=u,t.unix=pn,t.months=An,t.isDate=o,t.locale=A,t.invalid=p,t.duration=Je,t.isMoment=g,t.weekdays=wn,t.parseZone=fn,t.localeData=w,t.isDuration=ze,t.monthsShort=Nn,t.weekdaysMin=Cn,t.defineLocale=N,t.weekdaysShort=In,t.normalizeUnits=C,t.relativeTimeThreshold=_n,t.prototype=fo;var Wo=t;return Wo})}).call(t,n(222)(e))},function(e,t,n){"use strict";function r(e,t,n){var r=0;return d["default"].Children.map(e,function(e){if(d["default"].isValidElement(e)){var o=r;return r++,t.call(n,e,o)}return e})}function o(e,t,n){var r=0;return d["default"].Children.forEach(e,function(e){d["default"].isValidElement(e)&&(t.call(n,e,r),r++)})}function i(e){var t=0;return d["default"].Children.forEach(e,function(e){d["default"].isValidElement(e)&&t++}),t}function a(e){var t=!1;return d["default"].Children.forEach(e,function(e){!t&&d["default"].isValidElement(e)&&(t=!0)}),t}function s(e,t){var n=void 0;return o(e,function(r,o){!n&&t(r,o,e)&&(n=r)}),n}function u(e,t,n){var r=0,o=[];return d["default"].Children.forEach(e,function(e){d["default"].isValidElement(e)&&(t.call(n,e,r)&&o.push(e),r++)}),o}var l=n(5)["default"];t.__esModule=!0;var c=n(1),d=l(c);t["default"]={map:r,forEach:o,numberOf:i,find:s,findValidComponents:u,hasValidComponent:a},e.exports=t["default"]},function(e,t){var n=e.exports={version:"1.2.6"};"number"==typeof __e&&(__e=n)},function(e,t,n){"use strict";var r=n(29),o=function(){var e=r&&document.documentElement;return e&&e.contains?function(e,t){return e.contains(t)}:e&&e.compareDocumentPosition?function(e,t){return e===t||!!(16&e.compareDocumentPosition(t))}:function(e,t){if(t)do if(t===e)return!0;while(t=t.parentNode);return!1}}();e.exports=o},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(12),i=r(o),a=n(38),s=r(a);t["default"]=function(e){return s["default"](i["default"].findDOMNode(e))},e.exports=t["default"]},function(e,t,n){"use strict";var r=n(184),o=n(386),i=n(197),a=n(206),s=n(207),u=n(2),l=(n(4),{}),c=null,d=function(e,t){e&&(o.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e))},p=function(e){return d(e,!0)},f=function(e){return d(e,!1)},h=null,m={injection:{injectMount:o.injection.injectMount,injectInstanceHandle:function(e){h=e},getInstanceHandle:function(){return h},injectEventPluginOrder:r.injectEventPluginOrder,injectEventPluginsByName:r.injectEventPluginsByName},eventNameDispatchConfigs:r.eventNameDispatchConfigs,registrationNameModules:r.registrationNameModules,putListener:function(e,t,n){"function"!=typeof n?u(!1):void 0;var o=l[t]||(l[t]={});o[e]=n;var i=r.registrationNameModules[t];i&&i.didPutListener&&i.didPutListener(e,t,n)},getListener:function(e,t){var n=l[t];return n&&n[e]},deleteListener:function(e,t){var n=r.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t);var o=l[t];o&&delete o[e]},deleteAllListeners:function(e){for(var t in l)if(l[t][e]){var n=r.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t),delete l[t][e]}},extractEvents:function(e,t,n,o,i){for(var s,u=r.plugins,l=0;l=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){return l.stringify(e).replace(/%20/g,"+")}function a(e){return function(){function t(e){if(null==e.query){var t=e.search;e.query=x(t.substring(1)),e[m]={search:t,searchBase:""}}return e}function n(e,t){var n,r=e[m],o=t?b(t):"";if(!r&&!o)return e;"string"==typeof e&&(e=p.parsePath(e));var i=void 0;i=r&&e.search===r.search?r.searchBase:e.search||"";var a=i;return o&&(a+=(a?"&":"?")+o),s({},e,(n={search:a},n[m]={search:a,searchBase:i},n))}function r(e){return A.listenBefore(function(n,r){d["default"](e,t(n),r)})}function a(e){return A.listen(function(n){e(t(n))})}function u(e){A.push(n(e,e.query))}function l(e){A.replace(n(e,e.query))}function c(e,t){return A.createPath(n(e,t||e.query))}function f(e,t){return A.createHref(n(e,t||e.query))}function y(e){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];var a=A.createLocation.apply(A,[n(e,e.query)].concat(o));return e.query&&(a.query=e.query),t(a)}function v(e,t,n){"string"==typeof t&&(t=p.parsePath(t)),u(s({state:e},t,{query:n}))}function M(e,t,n){"string"==typeof t&&(t=p.parsePath(t)),l(s({state:e},t,{query:n}))}var T=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],b=T.stringifyQuery,x=T.parseQueryString,E=o(T,["stringifyQuery","parseQueryString"]),A=e(E);return"function"!=typeof b&&(b=i),"function"!=typeof x&&(x=g),s({},A,{listenBefore:r,listen:a,push:u,replace:l,createPath:c,createHref:f,createLocation:y,pushState:h["default"](v,"pushState is deprecated; use push instead"),replaceState:h["default"](M,"replaceState is deprecated; use replace instead")})}}t.__esModule=!0;var s=Object.assign||function(e){for(var t=1;t":">","<":"<",'"':""","'":"'"},i=/[&><"']/g;e.exports=r},function(e,t,n){"use strict";var r=n(9),o=/^[ \r\n\t\f]/,i=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,a=function(e,t){e.innerHTML=t};if("undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction&&(a=function(e,t){MSApp.execUnsafeLocalFunction(function(){e.innerHTML=t})}),r.canUseDOM){var s=document.createElement("div");s.innerHTML=" ",""===s.innerHTML&&(a=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),o.test(t)||"<"===t[0]&&i.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t})}e.exports=a},function(e,t,n){"use strict";var r=n(2),o=function(e){var t,n={};e instanceof Object&&!Array.isArray(e)?void 0:r(!1);for(t in e)e.hasOwnProperty(t)&&(n[t]=t);return n};e.exports=o},function(e,t,n){e.exports={"default":n(253),__esModule:!0}},function(e,t,n){var r=n(259),o=n(48),i=n(126),a="prototype",s=function(e,t,n){var u,l,c,d=e&s.F,p=e&s.G,f=e&s.S,h=e&s.P,m=e&s.B,g=e&s.W,y=p?o:o[t]||(o[t]={}),v=p?r:f?r[t]:(r[t]||{})[a];p&&(n=t);for(u in n)l=!d&&v&&u in v,l&&u in y||(c=l?v[u]:n[u],y[u]=p&&"function"!=typeof v[u]?n[u]:m&&l?i(c,r):g&&v[u]==c?function(e){var t=function(t){return this instanceof e?new e(t):e(t)};return t[a]=e[a],t}(c):h&&"function"==typeof c?i(Function.call,c):c,h&&((y[a]||(y[a]={}))[u]=c))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,e.exports=s},function(e,t){var n=Object;e.exports={create:n.create,getProto:n.getPrototypeOf,isEnum:{}.propertyIsEnumerable,getDesc:n.getOwnPropertyDescriptor,setDesc:n.defineProperty,setDescs:n.defineProperties,getKeys:n.keys,getNames:n.getOwnPropertyNames,getSymbols:n.getOwnPropertySymbols,each:[].forEach}},function(e,t,n){"use strict";var r=n(29),o=function(){};r&&(o=function(){return document.addEventListener?function(e,t,n,r){return e.addEventListener(t,n,r||!1)}:document.attachEvent?function(e,t,n){return e.attachEvent("on"+t,n)}:void 0}()),e.exports=o},function(e,t,n){"use strict";var r=n(135),o=n(281),i=n(276),a=n(277),s=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var u="",l=t;if("string"==typeof t){if(void 0===n)return e.style[r(t)]||i(e).getPropertyValue(o(t));(l={})[t]=n}for(var c in l)s.call(l,c)&&(l[c]||0===l[c]?u+=o(c)+":"+l[c]+";":a(e,o(c)));e.style.cssText+=";"+u}},function(e,t,n){function r(e,t,n){if("function"!=typeof e)return o;if(void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 3:return function(n,r,o){return e.call(t,n,r,o)};case 4:return function(n,r,o,i){return e.call(t,n,r,o,i)};case 5:return function(n,r,o,i,a){return e.call(t,n,r,o,i,a)}}return function(){return e.apply(t,arguments)}}var o=n(155);e.exports=r},function(e,t,n){function r(e){return null!=e&&i(o(e))}var o=n(145),i=n(39);e.exports=r},function(e,t,n){function r(e){return i(e)&&o(e)&&s.call(e,"callee")&&!u.call(e,"callee")}var o=n(78),i=n(30),a=Object.prototype,s=a.hasOwnProperty,u=a.propertyIsEnumerable;e.exports=r},function(e,t,n){function r(e){return"string"==typeof e||o(e)&&s.call(e)==i}var o=n(30),i="[object String]",a=Object.prototype,s=a.toString;e.exports=r},function(e,t,n){var r=n(58),o=n(78),i=n(25),a=n(315),s=n(83),u=r(Object,"keys"),l=u?function(e){var t=null==e?void 0:e.constructor;return"function"==typeof t&&t.prototype===e||("function"==typeof e?s.enumPrototypes:o(e))?a(e):i(e)?u(e):[]}:a;e.exports=l},function(e,t,n){function r(e){if(null==e)return[];c(e)||(e=Object(e));var t=e.length;t=t&&l(t)&&(a(e)||i(e)||d(e))&&t||0;for(var n=e.constructor,r=-1,o=s(n)&&n.prototype||A,f=o===e,h=Array(t),m=t>0,y=p.enumErrorProps&&(e===E||e instanceof Error),v=p.enumPrototypes&&s(e);++rn;n++)t[n]=arguments[n];if(void 0===t)throw new Error("No validations provided");if(t.some(function(e){return"function"!=typeof e}))throw new Error("Invalid arguments, must be functions");if(0===t.length)throw new Error("No validations provided");return function(e,n,r){for(var o=0;oa&&l;)l=!1,t.call(this,a++,i,r);return u=!1,s?void n.apply(this,c):void(a>=e&&l&&(s=!0,n()))}}var a=0,s=!1,u=!1,l=!1,c=void 0;i()}function r(e,t,n){function r(e,t,r){a||(t?(a=!0,n(t)):(i[e]=r,a=++s===o,a&&n(null,i)))}var o=e.length,i=[];if(0===o)return n(null,i);var a=!1,s=0;e.forEach(function(e,n){t(e,n,function(e,t){r(n,e,t)})})}t.__esModule=!0;var o=Array.prototype.slice;t.loopAsync=n,t.mapAsync=r},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=Object.assign||function(e){for(var t=1;t2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o]}t.__esModule=!0,t["default"]=o;var i=n(20);r(i);e.exports=t["default"]},function(e,t){"use strict";var n=!("undefined"==typeof window||!window.document||!window.document.createElement),r={canUseDOM:n,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:n&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:n&&!!window.screen,isInWorker:!n};e.exports=r},function(e,t,n){"use strict";function r(e){return function(){for(var t=arguments.length,n=Array(t),r=0;t>r;r++)n[r]=arguments[r];var o=n[n.length-1];return"function"==typeof o?e.apply(void 0,n):function(t){return e.apply(void 0,n.concat([t]))}}}function o(e,t){return void 0===e&&(e={}),(e.bsClass||"").trim()?void 0:d["default"](!1),e.bsClass+(t?"-"+t:"")}var i=n(6)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(1),u=n(35),l=a(u),c=n(137),d=a(c),p=n(60),f=(a(p),r(function(e,t){var n=t.propTypes||(t.propTypes={}),r=t.defaultProps||(t.defaultProps={});return n.bsClass=s.PropTypes.string,r.bsClass=e,t}));t.bsClass=f;var h=r(function(e,t,n){"string"!=typeof t&&(n=t,t=void 0);var r=n.STYLES||[],o=n.propTypes||{};e.forEach(function(e){-1===r.indexOf(e)&&r.push(e)});var a=s.PropTypes.oneOf(r);if(n.STYLES=a._values=r,n.propTypes=i({},o,{bsStyle:a}),void 0!==t){var u=n.defaultProps||(n.defaultProps={});u.bsStyle=t}return n});t.bsStyles=h;var m=r(function(e,t,n){"string"!=typeof t&&(n=t,t=void 0);var r=n.SIZES||[],o=n.propTypes||{};e.forEach(function(e){-1===r.indexOf(e)&&r.push(e)});var a=r.reduce(function(e,t){return l["default"].SIZES[t]&&l["default"].SIZES[t]!==t&&e.push(l["default"].SIZES[t]),e.concat(t)},[]),u=s.PropTypes.oneOf(a);if(u._values=a,n.SIZES=r,n.propTypes=i({},o,{bsSize:u}),void 0!==t){var c=n.defaultProps||(n.defaultProps={});c.bsSize=t}return n});t.bsSizes=m,t["default"]={prefix:o,getClassSet:function(e){var t={},n=o(e);if(n){var r=void 0;t[n]=!0,e.bsSize&&(r=l["default"].SIZES[e.bsSize]||r),r&&(t[o(e,r)]=!0),e.bsStyle&&(0===e.bsStyle.indexOf(o(e))?t[e.bsStyle]=!0:t[o(e,e.bsStyle)]=!0)}return t},addStyle:function(e,t){h(t,e)}};var g=r;t._curry=g},function(e,t,n){"use strict";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;n>r;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){return e?e.nodeType===F?e.documentElement:e.firstChild:null}function i(e){var t=o(e);return t&&q.getID(t)}function a(e){var t=s(e);if(t)if(B.hasOwnProperty(t)){var n=B[t];n!==e&&(d(n,t)?z(!1):void 0,B[t]=e)}else B[t]=e;return t}function s(e){return e&&e.getAttribute&&e.getAttribute(Y)||""}function u(e,t){var n=s(e);n!==t&&delete B[n],e.setAttribute(Y,t),B[t]=e}function l(e){return B.hasOwnProperty(e)&&d(B[e],e)||(B[e]=q.findReactNodeByID(e)),B[e]}function c(e){var t=w.get(e)._rootNodeID;return A.isNullComponentID(t)?null:(B.hasOwnProperty(t)&&d(B[t],t)||(B[t]=q.findReactNodeByID(t)),B[t])}function d(e,t){if(e){s(e)!==t?z(!1):void 0;var n=q.findReactContainerForID(t);if(n&&P(n,e))return!0}return!1}function p(e){delete B[e]}function f(e){var t=B[e];return t&&d(t,e)?void(Z=t):!1}function h(e){Z=null,N.traverseAncestors(e,f);var t=Z;return Z=null,t}function m(e,t,n,r,o,i){x.useCreateElement&&(i=L({},i),n.nodeType===F?i[H]=n:i[H]=n.ownerDocument);var a=D.mountComponent(e,t,r,i);e._renderedComponent._topLevelWrapper=e,q._mountImageIntoNode(a,n,o,r)}function g(e,t,n,r,o){var i=k.ReactReconcileTransaction.getPooled(r);i.perform(m,null,e,t,n,i,r,o),k.ReactReconcileTransaction.release(i)}function y(e,t){for(D.unmountComponent(e),t.nodeType===F&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)}function v(e){var t=i(e);return t?t!==N.getReactRootIDFromNodeID(t):!1}function M(e){for(;e&&e.parentNode!==e;e=e.parentNode)if(1===e.nodeType){var t=s(e);if(t){var n,r=N.getReactRootIDFromNodeID(t),o=e;do if(n=s(o),o=o.parentNode,null==o)return null;while(n!==r);if(o===Q[r])return e}}return null}var T=n(42),b=n(63),x=(n(23),n(189)),E=n(13),A=n(196),N=n(43),w=n(53),I=n(199),C=n(17),D=n(33),S=n(100),k=n(18),L=n(3),O=n(55),P=n(211),j=n(107),z=n(2),R=n(70),U=n(110),Y=(n(112),n(4),T.ID_ATTRIBUTE_NAME),B={},W=1,F=9,V=11,H="__ReactMount_ownerDocument$"+Math.random().toString(36).slice(2),_={},Q={},G=[],Z=null,X=function(){};X.prototype.isReactComponent={},X.prototype.render=function(){return this.props};var q={TopLevelWrapper:X,_instancesByReactRootID:_,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r){return q.scrollMonitor(n,function(){S.enqueueElementInternal(e,t),r&&S.enqueueCallbackInternal(e,r)}),e},_registerComponent:function(e,t){!t||t.nodeType!==W&&t.nodeType!==F&&t.nodeType!==V?z(!1):void 0,b.ensureScrollValueMonitoring();var n=q.registerContainer(t);return _[n]=e,n},_renderNewRootComponent:function(e,t,n,r){var o=j(e,null),i=q._registerComponent(o,t);return k.batchedUpdates(g,o,i,t,n,r),o},renderSubtreeIntoContainer:function(e,t,n,r){return null==e||null==e._reactInternalInstance?z(!1):void 0,q._renderSubtreeIntoContainer(e,t,n,r)},_renderSubtreeIntoContainer:function(e,t,n,r){E.isValidElement(t)?void 0:z(!1);var a=new E(X,null,null,null,null,null,t),u=_[i(n)];if(u){var l=u._currentElement,c=l.props;if(U(c,t)){var d=u._renderedComponent.getPublicInstance(),p=r&&function(){r.call(d)};return q._updateRootComponent(u,a,n,p),d}q.unmountComponentAtNode(n)}var f=o(n),h=f&&!!s(f),m=v(n),g=h&&!u&&!m,y=q._renderNewRootComponent(a,n,g,null!=e?e._reactInternalInstance._processChildContext(e._reactInternalInstance._context):O)._renderedComponent.getPublicInstance();return r&&r.call(y),y},render:function(e,t,n){return q._renderSubtreeIntoContainer(null,e,t,n)},registerContainer:function(e){var t=i(e);return t&&(t=N.getReactRootIDFromNodeID(t)),t||(t=N.createReactRootID()),Q[t]=e,t},unmountComponentAtNode:function(e){!e||e.nodeType!==W&&e.nodeType!==F&&e.nodeType!==V?z(!1):void 0;var t=i(e),n=_[t];if(!n){var r=(v(e),s(e));r&&r===N.getReactRootIDFromNodeID(r);return!1}return k.batchedUpdates(y,n,e),delete _[t],delete Q[t],!0},findReactContainerForID:function(e){var t=N.getReactRootIDFromNodeID(e),n=Q[t];return n},findReactNodeByID:function(e){var t=q.findReactContainerForID(e);return q.findComponentRoot(t,e)},getFirstReactDOM:function(e){return M(e)},findComponentRoot:function(e,t){var n=G,r=0,o=h(t)||e;for(n[0]=o.firstChild,n.length=1;r1){for(var f=Array(p),h=0;p>h;h++)f[h]=arguments[h+2];i.children=f}if(e&&e.defaultProps){var m=e.defaultProps;for(o in m)"undefined"==typeof i[o]&&(i[o]=m[o])}return s(e,u,l,c,d,r.current,i)},s.createFactory=function(e){var t=s.createElement.bind(null,e);return t.type=e,t},s.cloneAndReplaceKey=function(e,t){var n=s(e.type,t,e.ref,e._self,e._source,e._owner,e.props);return n},s.cloneAndReplaceProps=function(e,t){var n=s(e.type,e.key,e.ref,e._self,e._source,e._owner,t);return n},s.cloneElement=function(e,t,n){var i,u=o({},e.props),l=e.key,c=e.ref,d=e._self,p=e._source,f=e._owner;if(null!=t){void 0!==t.ref&&(c=t.ref,f=r.current),void 0!==t.key&&(l=""+t.key);for(i in t)t.hasOwnProperty(i)&&!a.hasOwnProperty(i)&&(u[i]=t[i])}var h=arguments.length-2;if(1===h)u.children=n;else if(h>1){for(var m=Array(h),g=0;h>g;g++)m[g]=arguments[g+2];u.children=m}return s(e.type,l,c,d,p,f,u)},s.isValidElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===i},e.exports=s},function(e,t){"use strict";t["default"]=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},t.__esModule=!0},function(e,t,n){"use strict";var r=n(125)["default"],o=n(250)["default"];t["default"]=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=r(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(o?o(e,t):e.__proto__=t)},t.__esModule=!0},function(e,t,n){"use strict";var r=function(e,t,n,r,o,i,a,s){if(!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,o,i,a,s],c=0;u=new Error(t.replace(/%s/g,function(){return l[c++]})),u.name="Invariant Violation"}throw u.framesToPop=1,u}};e.exports=r},function(e,t,n){"use strict";function r(e,t,n){return n}var o={enableMeasure:!1,storedMeasure:r,measureMethods:function(e,t,n){},measure:function(e,t,n){return n},injection:{injectMeasure:function(e){o.storedMeasure=e}}};e.exports=o},function(e,t,n){"use strict";function r(){w.ReactReconcileTransaction&&T?void 0:g(!1)}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=c.getPooled(),this.reconcileTransaction=w.ReactReconcileTransaction.getPooled(!1)}function i(e,t,n,o,i,a){r(),T.batchedUpdates(e,t,n,o,i,a)}function a(e,t){return e._mountOrder-t._mountOrder}function s(e){var t=e.dirtyComponentsLength;t!==y.length?g(!1):void 0,y.sort(a);for(var n=0;t>n;n++){var r=y[n],o=r._pendingCallbacks;if(r._pendingCallbacks=null,f.performUpdateIfNecessary(r,e.reconcileTransaction),o)for(var i=0;i should not have a "'+t+'" prop'):void 0}t.__esModule=!0,t.falsy=r;var o=n(1),i=o.PropTypes.func,a=o.PropTypes.object,s=o.PropTypes.arrayOf,u=o.PropTypes.oneOfType,l=o.PropTypes.element,c=o.PropTypes.shape,d=o.PropTypes.string,p=c({listen:i.isRequired,pushState:i.isRequired,replaceState:i.isRequired,go:i.isRequired});t.history=p;var f=c({pathname:d.isRequired,search:d.isRequired,state:a,action:d.isRequired,key:d});t.location=f;var h=u([i,d]);t.component=h;var m=u([h,a]);t.components=m;var g=u([a,l]);t.route=g;var y=u([g,s(g)]);t.routes=y,t["default"]={falsy:r,history:p,location:f,component:h,components:m,route:g}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){var t=e.match(/^https?:\/\/[^\/]*/);return null==t?e:e.substring(t[0].length)}function i(e){var t=o(e),n="",r="",i=t.indexOf("#");-1!==i&&(r=t.substring(i),t=t.substring(0,i));var a=t.indexOf("?");return-1!==a&&(n=t.substring(a),t=t.substring(0,a)),""===t&&(t="/"),{pathname:t,search:n,hash:r}}t.__esModule=!0,t.extractPath=o,t.parsePath=i;var a=n(20);r(a)},function(e,t,n){"use strict";function r(){o.attachRefs(this,this._currentElement)}var o=n(408),i={mountComponent:function(e,t,n,o){var i=e.mountComponent(t,n,o);return e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e),i},unmountComponent:function(e){o.detachRefs(e,e._currentElement),e.unmountComponent()},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||i!==e._context){var s=o.shouldUpdateRefs(a,t);s&&o.detachRefs(e,a),e.receiveComponent(t,n,i),s&&e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t){e.performUpdateIfNecessary(t)}};e.exports=i},function(e,t,n){"use strict";function r(e,t,n,r){this.dispatchConfig=e,this.dispatchMarker=t,this.nativeEvent=n;var o=this.constructor.Interface;for(var i in o)if(o.hasOwnProperty(i)){var s=o[i];s?this[i]=s(n):"target"===i?this.target=r:this[i]=n[i]}var u=null!=n.defaultPrevented?n.defaultPrevented:n.returnValue===!1;u?this.isDefaultPrevented=a.thatReturnsTrue:this.isDefaultPrevented=a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse}var o=n(27),i=n(3),a=n(21),s=(n(4),{type:null,target:null,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null});i(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():e.returnValue=!1,this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0,this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;this.dispatchConfig=null,this.dispatchMarker=null,this.nativeEvent=null}}),r.Interface=s,r.augmentClass=function(e,t){var n=this,r=Object.create(n.prototype);i(r,e.prototype),e.prototype=r,e.prototype.constructor=e,e.Interface=i({},n.Interface,t),e.augmentClass=n.augmentClass,o.addPoolingTo(e,o.fourArgumentPooler)},o.addPoolingTo(r,o.fourArgumentPooler),e.exports=r},function(e,t,n){"use strict";var r=n(124)["default"],o=n(125)["default"],i=n(72)["default"];t.__esModule=!0;var a=function(e){return r(o({values:function(){var e=this;return i(this).map(function(t){return e[t]})}}),e)},s={SIZES:{large:"lg",medium:"md",small:"sm",xsmall:"xs",lg:"lg",md:"md",sm:"sm",xs:"xs"},GRID_COLUMNS:12},u=a({LARGE:"large",MEDIUM:"medium",SMALL:"small",XSMALL:"xsmall"});t.Sizes=u;var l=a({SUCCESS:"success",WARNING:"warning",DANGER:"danger",INFO:"info"});t.State=l;var c="default";t.DEFAULT=c;var d="primary";t.PRIMARY=d;var p="link";t.LINK=p;var f="inverse";t.INVERSE=f,t["default"]=s},function(e,t){"use strict";function n(){for(var e=arguments.length,t=Array(e),n=0;e>n;n++)t[n]=arguments[n];return t.filter(function(e){return null!=e}).reduce(function(e,t){if("function"!=typeof t)throw new Error("Invalid Argument Type, must only provide functions, undefined, or null.");return null===e?t:function(){for(var n=arguments.length,r=Array(n),o=0;n>o;o++)r[o]=arguments[o];e.apply(this,r),t.apply(this,r)}},null)}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){"use strict";t["default"]=function(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n},t.__esModule=!0},function(e,t){"use strict";function n(e){return e&&e.ownerDocument||document}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&r>=e}var r=9007199254740991;e.exports=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function i(e){return o(e).replace(/\/+/g,"/+")}function a(e){for(var t="",n=[],r=[],o=void 0,a=0,s=/:([a-zA-Z_$][a-zA-Z0-9_$]*)|\*\*|\*|\(|\)/g;o=s.exec(e);)o.index!==a&&(r.push(e.slice(a,o.index)),t+=i(e.slice(a,o.index))),o[1]?(t+="([^/?#]+)",n.push(o[1])):"**"===o[0]?(t+="([\\s\\S]*)",n.push("splat")):"*"===o[0]?(t+="([\\s\\S]*?)",n.push("splat")):"("===o[0]?t+="(?:":")"===o[0]&&(t+=")?"),r.push(o[0]),a=s.lastIndex;return a!==e.length&&(r.push(e.slice(a,e.length)),t+=i(e.slice(a,e.length))),{pattern:e,regexpSource:t,paramNames:n,tokens:r}}function s(e){return e in h||(h[e]=a(e)),h[e]}function u(e,t){"/"!==e.charAt(0)&&(e="/"+e),"/"!==t.charAt(0)&&(t="/"+t);var n=s(e),r=n.regexpSource,o=n.paramNames,i=n.tokens;r+="/*";var a="*"!==i[i.length-1];a&&(r+="([\\s\\S]*?)");var u=t.match(new RegExp("^"+r+"$","i")),l=void 0,c=void 0;if(null!=u){if(a){l=u.pop();var d=u[0].substr(0,u[0].length-l.length);if(l&&"/"!==d.charAt(d.length-1))return{remainingPathname:null,paramNames:o,paramValues:null}}else l="";c=u.slice(1).map(function(e){return null!=e?decodeURIComponent(e):e})}else l=c=null;return{remainingPathname:l,paramNames:o,paramValues:c}}function l(e){return s(e).paramNames}function c(e,t){var n=u(e,t),r=n.paramNames,o=n.paramValues;return null!=o?r.reduce(function(e,t,n){return e[t]=o[n],e},{}):null}function d(e,t){t=t||{};for(var n=s(e),r=n.tokens,o=0,i="",a=0,u=void 0,l=void 0,c=void 0,d=0,p=r.length;p>d;++d)u=r[d],"*"===u||"**"===u?(c=Array.isArray(t.splat)?t.splat[a++]:t.splat,null!=c||o>0?void 0:f["default"](!1),null!=c&&(i+=encodeURI(c))):"("===u?o+=1:")"===u?o-=1:":"===u.charAt(0)?(l=u.substring(1),c=t[l],null!=c||o>0?void 0:f["default"](!1),null!=c&&(i+=encodeURIComponent(c))):i+=u;return i.replace(/\/+/g,"/")}t.__esModule=!0,t.compilePattern=s,t.matchPattern=u,t.getParamNames=l,t.getParams=c,t.formatPattern=d;var p=n(16),f=r(p),h={}},function(e,t){"use strict";t.__esModule=!0;var n="PUSH";t.PUSH=n;var r="REPLACE";t.REPLACE=r;var o="POP";t.POP=o,t["default"]={PUSH:n,REPLACE:r,POP:o}},function(e,t,n){"use strict";function r(e,t){return(e&t)===t}var o=n(2),i={MUST_USE_ATTRIBUTE:1,MUST_USE_PROPERTY:2,HAS_SIDE_EFFECTS:4,HAS_BOOLEAN_VALUE:8,HAS_NUMERIC_VALUE:16,HAS_POSITIVE_NUMERIC_VALUE:48,HAS_OVERLOADED_BOOLEAN_VALUE:64,injectDOMPropertyConfig:function(e){var t=i,n=e.Properties||{},a=e.DOMAttributeNamespaces||{},u=e.DOMAttributeNames||{},l=e.DOMPropertyNames||{},c=e.DOMMutationMethods||{};e.isCustomAttribute&&s._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var d in n){s.properties.hasOwnProperty(d)?o(!1):void 0;var p=d.toLowerCase(),f=n[d],h={attributeName:p,attributeNamespace:null,propertyName:d,mutationMethod:null,mustUseAttribute:r(f,t.MUST_USE_ATTRIBUTE),mustUseProperty:r(f,t.MUST_USE_PROPERTY),hasSideEffects:r(f,t.HAS_SIDE_EFFECTS),hasBooleanValue:r(f,t.HAS_BOOLEAN_VALUE),hasNumericValue:r(f,t.HAS_NUMERIC_VALUE),hasPositiveNumericValue:r(f,t.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:r(f,t.HAS_OVERLOADED_BOOLEAN_VALUE)};if(h.mustUseAttribute&&h.mustUseProperty?o(!1):void 0,!h.mustUseProperty&&h.hasSideEffects?o(!1):void 0,h.hasBooleanValue+h.hasNumericValue+h.hasOverloadedBooleanValue<=1?void 0:o(!1),u.hasOwnProperty(d)){var m=u[d];h.attributeName=m}a.hasOwnProperty(d)&&(h.attributeNamespace=a[d]),l.hasOwnProperty(d)&&(h.propertyName=l[d]),c.hasOwnProperty(d)&&(h.mutationMethod=c[d]),s.properties[d]=h}}},a={},s={ID_ATTRIBUTE_NAME:"data-reactid",properties:{},getPossibleStandardName:null,_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t=a;a++)if(o(e,a)&&o(t,a))r=a;else if(e.charAt(a)!==t.charAt(a))break;var s=e.substr(0,r);return i(s)?void 0:p(!1),s}function c(e,t,n,r,o,i){e=e||"",t=t||"",e===t?p(!1):void 0;var l=a(t,e);l||a(e,t)?void 0:p(!1);for(var c=0,d=l?s:u,f=e;;f=d(f,t)){var h;if(o&&f===e||i&&f===t||(h=n(f,l,r)),h===!1||f===t)break;c++1){var t=e.indexOf(f,1);return t>-1?e.substr(0,t):e}return null},traverseEnterLeave:function(e,t,n,r,o){var i=l(e,t);i!==e&&c(e,i,n,r,!1,!0),i!==t&&c(i,t,n,o,!0,!1)},traverseTwoPhase:function(e,t,n){e&&(c("",e,t,n,!0,!1),c(e,"",t,n,!1,!0))},traverseTwoPhaseSkipTarget:function(e,t,n){e&&(c("",e,t,n,!0,!0),c(e,"",t,n,!0,!0))},traverseAncestors:function(e,t,n){c("",e,t,n,!0,!1)},getFirstCommonAncestorID:l,_getNextDescendantID:u,isAncestorIDOf:a,SEPARATOR:f};e.exports=g},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0}),t.setSettings=t.hideSettings=t.showSettings=t.setLatestUIVersion=t.setSortDateOrder=t.setSortSizeOrder=t.setSortNameOrder=t.hideAbout=t.showAbout=t.uploadFile=t.setLoginError=t.setShowAbortModal=t.setUpload=t.selectPrefix=t.selectBucket=t.setServerInfo=t.setStorageInfo=t.setCurrentPath=t.setCurrentBucket=t.setObjects=t.setVisibleBuckets=t.hideMakeBucketModal=t.setSidebarStatus=t.showAlert=t.hideAlert=t.showMakeBucketModal=t.addObject=t.addBucket=t.setBuckets=t.setWeb=t.setLoadBucket=t.setLoadPath=t.setLoginRedirectPath=t.SET_SETTINGS=t.SHOW_SETTINGS=t.SET_LOAD_PATH=t.SET_LOAD_BUCKET=t.SET_LOGIN_REDIRECT_PATH=t.SET_SIDEBAR_STATUS=t.SET_LATEST_UI_VERSION=t.SET_SORT_DATE_ORDER=t.SET_SORT_SIZE_ORDER=t.SET_SORT_NAME_ORDER=t.SHOW_ABOUT=t.SET_SHOW_ABORT_MODAL=t.SET_LOGIN_ERROR=t.SET_ALERT=t.SET_UPLOAD=t.SHOW_MAKEBUCKET_MODAL=t.SET_SERVER_INFO=t.SET_STORAGE_INFO=t.SET_OBJECTS=t.SET_VISIBLE_BUCKETS=t.ADD_OBJECT=t.ADD_BUCKET=t.SET_BUCKETS=t.SET_CURRENT_PATH=t.SET_CURRENT_BUCKET=t.SET_WEB=void 0;var i=n(223),a=(o(i),n(46)),s=o(a),u=n(116),l=(o(u),n(115)),c=r(l),d=t.SET_WEB="SET_WEB",p=t.SET_CURRENT_BUCKET="SET_CURRENT_BUCKET",f=t.SET_CURRENT_PATH="SET_CURRENT_PATH",h=t.SET_BUCKETS="SET_BUCKETS",m=t.ADD_BUCKET="ADD_BUCKET",g=t.ADD_OBJECT="ADD_OBJECT",y=t.SET_VISIBLE_BUCKETS="SET_VISIBLE_BUCKETS",v=t.SET_OBJECTS="SET_OBJECTS",M=t.SET_STORAGE_INFO="SET_STORAGE_INFO",T=t.SET_SERVER_INFO="SET_SERVER_INFO",b=t.SHOW_MAKEBUCKET_MODAL="SHOW_MAKEBUCKET_MODAL",x=t.SET_UPLOAD="SET_UPLOAD",E=t.SET_ALERT="SET_ALERT",A=t.SET_LOGIN_ERROR="SET_LOGIN_ERROR",N=t.SET_SHOW_ABORT_MODAL="SET_SHOW_ABORT_MODAL",w=t.SHOW_ABOUT="SHOW_ABOUT",I=t.SET_SORT_NAME_ORDER="SET_SORT_NAME_ORDER",C=t.SET_SORT_SIZE_ORDER="SET_SORT_SIZE_ORDER",D=t.SET_SORT_DATE_ORDER="SET_SORT_DATE_ORDER",S=t.SET_LATEST_UI_VERSION="SET_LATEST_UI_VERSION",k=t.SET_SIDEBAR_STATUS="SET_SIDEBAR_STATUS",L=t.SET_LOGIN_REDIRECT_PATH="SET_LOGIN_REDIRECT_PATH",O=t.SET_LOAD_BUCKET="SET_LOAD_BUCKET",P=t.SET_LOAD_PATH="SET_LOAD_PATH",j=t.SHOW_SETTINGS="SHOW_SETTINGS",z=t.SET_SETTINGS="SET_SETTINGS",R=(t.setLoginRedirectPath=function(e){return{type:L,path:e}},t.setLoadPath=function(e){return{type:P,loadPath:e}}),U=t.setLoadBucket=function(e){return{type:O,loadBucket:e}},Y=(t.setWeb=function(e){return{type:d,web:e}},t.setBuckets=function(e){return{type:h,buckets:e}},t.addBucket=function(e){return{type:m,bucket:e}},t.addObject=function(e){return{type:g,object:e}},t.showMakeBucketModal=function(){return{type:b,showMakeBucketModal:!0}},t.hideAlert=function(){return{type:E,alert:{show:!1,message:"",type:""}}},t.showAlert=function(e){return function(t,n){var r=null;"danger"!==e.type&&(r=setTimeout(function(){t({type:E,alert:{show:!1}})},5e3)),t({type:E,alert:Object.assign({},e,{show:!0,alertTimeout:r})})}}),B=(t.setSidebarStatus=function(e){return{type:k,sidebarStatus:e}},t.hideMakeBucketModal=function(){return{type:b,showMakeBucketModal:!1}},t.setVisibleBuckets=function(e){return{type:y,visibleBuckets:e}},t.setObjects=function(e){return{type:v,objects:e}}),W=t.setCurrentBucket=function(e){return{type:p,currentBucket:e}},F=t.setCurrentPath=function(e){return{type:f,currentPath:e}},V=(t.setStorageInfo=function(e){return{type:M,storageInfo:e}},t.setServerInfo=function(e){return{type:T,serverInfo:e}},t.selectBucket=function(e,t){return t||(t=""),function(n,r){var o=(r().web,r().currentBucket);o!==e&&n(U(e)),n(W(e)),n(V(t))}},t.selectPrefix=function(e){return function(t,n){var r=n(),o=r.currentBucket,i=r.web;t(R(e)),i.ListObjects({bucketName:o,prefix:e}).then(function(n){var r=n.objects;r||(r=[]),t(B(c.sortObjectsByName(r.map(function(t){return t.name=t.name.replace(""+e,""),t})))),t(Q(!1)),t(F(e)),t(U("")),t(R(""))})["catch"](function(e){t(Y({type:"danger",message:e.message})),t(U("")),t(R(""))})}}),H=t.setUpload=function(){var e=arguments.length<=0||void 0===arguments[0]?{ +inProgress:!1,percent:0}:arguments[0];return{type:x,upload:e}},_=t.setShowAbortModal=function(e){return{type:N,showAbortModal:e}},Q=(t.setLoginError=function(){return{type:A,loginError:!0}},t.uploadFile=function(e,t){return function(n,r){var o=r(),i=o.currentBucket,a=o.currentPath,u=(o.web,""+a+e.name),l=window.location.origin+"/minio/upload/"+i+"/"+u;t.open("PUT",l,!0),t.withCredentials=!1,t.setRequestHeader("Authorization","Bearer "+localStorage.token),t.setRequestHeader("x-minio-date",(0,s["default"])().utc().format("YYYYMMDDTHHmmss")+"Z"),n(H({inProgress:!0,loaded:0,total:e.size,filename:e.name})),t.upload.addEventListener("error",function(t){n(Y({type:"danger",message:"Error occurred uploading '"+e.name+"'."})),n(H({inProgress:!1}))}),t.upload.addEventListener("progress",function(t){if(t.lengthComputable){var r=t.loaded,o=t.total;n(H({inProgress:!0,loaded:r,total:o,filename:e.name})),r===o&&(_(!1),n(H({inProgress:!1})),n(Y({type:"success",message:"File '"+e.name+"' uploaded successfully."})),n(V(a)))}}),t.send(e)}},t.showAbout=function(){return{type:w,showAbout:!0}},t.hideAbout=function(){return{type:w,showAbout:!1}},t.setSortNameOrder=function(e){return{type:I,sortNameOrder:e}});t.setSortSizeOrder=function(e){return{type:C,sortSizeOrder:e}},t.setSortDateOrder=function(e){return{type:D,sortDateOrder:e}},t.setLatestUIVersion=function(e){return{type:S,latestUiVersion:e}},t.showSettings=function(){return{type:j,showSettings:!0}},t.hideSettings=function(){return{type:j,showSettings:!1}},t.setSettings=function(e){return{type:z,settings:e}}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.minioBrowserPrefix="/minio"},function(e,t,n){(function(e){!function(t,n){e.exports=n()}(this,function(){"use strict";function t(){return Zn.apply(null,arguments)}function n(e){Zn=e}function r(e){return"[object Array]"===Object.prototype.toString.call(e)}function o(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function i(e,t){var n,r=[];for(n=0;n0)for(n in qn)r=qn[n],o=t[r],f(o)||(e[r]=o);return e}function m(e){h(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),Kn===!1&&(Kn=!0,t.updateOffset(this),Kn=!1)}function g(e){return e instanceof m||null!=e&&null!=e._isAMomentObject}function y(e){return 0>e?Math.ceil(e):Math.floor(e)}function v(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=y(t)),n}function M(e,t,n){var r,o=Math.min(e.length,t.length),i=Math.abs(e.length-t.length),a=0;for(r=0;o>r;r++)(n&&e[r]!==t[r]||!n&&v(e[r])!==v(t[r]))&&a++;return a+i}function T(){}function b(e){return e?e.toLowerCase().replace("_","-"):e}function x(e){for(var t,n,r,o,i=0;i0;){if(r=E(o.slice(0,t).join("-")))return r;if(n&&n.length>=t&&M(o,n,!0)>=t-1)break;t--}i++}return null}function E(t){var n=null;if(!Jn[t]&&"undefined"!=typeof e&&e&&e.exports)try{n=Xn._abbr,!function(){var e=new Error('Cannot find module "./locale"');throw e.code="MODULE_NOT_FOUND",e}(),A(n)}catch(r){}return Jn[t]}function A(e,t){var n;return e&&(n=f(t)?w(e):N(e,t),n&&(Xn=n)),Xn._abbr}function N(e,t){return null!==t?(t.abbr=e,Jn[e]=Jn[e]||new T,Jn[e].set(t),A(e),Jn[e]):(delete Jn[e],null)}function w(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Xn;if(!r(e)){if(t=E(e))return t;e=[e]}return x(e)}function I(e,t){var n=e.toLowerCase();$n[n]=$n[n+"s"]=$n[t]=e}function C(e){return"string"==typeof e?$n[e]||$n[e.toLowerCase()]:void 0}function D(e){var t,n,r={};for(n in e)a(e,n)&&(t=C(n),t&&(r[t]=e[n]));return r}function S(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function k(e,n){return function(r){return null!=r?(O(this,e,r),t.updateOffset(this,n),this):L(this,e)}}function L(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function O(e,t,n){e.isValid()&&e._d["set"+(e._isUTC?"UTC":"")+t](n)}function P(e,t){var n;if("object"==typeof e)for(n in e)this.set(n,e[n]);else if(e=C(e),S(this[e]))return this[e](t);return this}function j(e,t,n){var r=""+Math.abs(e),o=t-r.length,i=e>=0;return(i?n?"+":"":"-")+Math.pow(10,Math.max(0,o)).toString().substr(1)+r}function z(e,t,n,r){var o=r;"string"==typeof r&&(o=function(){return this[r]()}),e&&(rr[e]=o),t&&(rr[t[0]]=function(){return j(o.apply(this,arguments),t[1],t[2])}),n&&(rr[n]=function(){return this.localeData().ordinal(o.apply(this,arguments),e)})}function R(e){return e.match(/\[[\s\S]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"")}function U(e){var t,n,r=e.match(er);for(t=0,n=r.length;n>t;t++)rr[r[t]]?r[t]=rr[r[t]]:r[t]=R(r[t]);return function(o){var i="";for(t=0;n>t;t++)i+=r[t]instanceof Function?r[t].call(o,e):r[t];return i}}function Y(e,t){return e.isValid()?(t=B(t,e.localeData()),nr[t]=nr[t]||U(t),nr[t](e)):e.localeData().invalidDate()}function B(e,t){function n(e){return t.longDateFormat(e)||e}var r=5;for(tr.lastIndex=0;r>=0&&tr.test(e);)e=e.replace(tr,n),tr.lastIndex=0,r-=1;return e}function W(e,t,n){br[e]=S(t)?t:function(e,r){return e&&n?n:t}}function F(e,t){return a(br,e)?br[e](t._strict,t._locale):new RegExp(V(e))}function V(e){return H(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,r,o){return t||n||r||o}))}function H(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function _(e,t){var n,r=t;for("string"==typeof e&&(e=[e]),"number"==typeof t&&(r=function(e,n){n[t]=v(e)}),n=0;nr;r++){if(o=u([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(o,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(o,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(i="^"+this.months(o,"")+"|^"+this.monthsShort(o,""),this._monthsParse[r]=new RegExp(i.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[r].test(e))return r;if(n&&"MMM"===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}}function J(e,t){var n;return e.isValid()?"string"==typeof t&&(t=e.localeData().monthsParse(t),"number"!=typeof t)?e:(n=Math.min(e.date(),Z(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n),e):e}function $(e){return null!=e?(J(this,e),t.updateOffset(this,!0),this):L(this,"Month")}function ee(){return Z(this.year(),this.month())}function te(e){return this._monthsParseExact?(a(this,"_monthsRegex")||re.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex}function ne(e){return this._monthsParseExact?(a(this,"_monthsRegex")||re.call(this),e?this._monthsStrictRegex:this._monthsRegex):this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex}function re(){function e(e,t){return t.length-e.length}var t,n,r=[],o=[],i=[];for(t=0;12>t;t++)n=u([2e3,t]),r.push(this.monthsShort(n,"")),o.push(this.months(n,"")),i.push(this.months(n,"")),i.push(this.monthsShort(n,""));for(r.sort(e),o.sort(e),i.sort(e),t=0;12>t;t++)r[t]=H(r[t]),o[t]=H(o[t]),i[t]=H(i[t]);this._monthsRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+o.join("|")+")$","i"),this._monthsShortStrictRegex=new RegExp("^("+r.join("|")+")$","i")}function oe(e){var t,n=e._a;return n&&-2===c(e).overflow&&(t=n[Ar]<0||n[Ar]>11?Ar:n[Nr]<1||n[Nr]>Z(n[Er],n[Ar])?Nr:n[wr]<0||n[wr]>24||24===n[wr]&&(0!==n[Ir]||0!==n[Cr]||0!==n[Dr])?wr:n[Ir]<0||n[Ir]>59?Ir:n[Cr]<0||n[Cr]>59?Cr:n[Dr]<0||n[Dr]>999?Dr:-1,c(e)._overflowDayOfYear&&(Er>t||t>Nr)&&(t=Nr),c(e)._overflowWeeks&&-1===t&&(t=Sr),c(e)._overflowWeekday&&-1===t&&(t=kr),c(e).overflow=t),e}function ie(e){t.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function ae(e,t){var n=!0;return s(function(){return n&&(ie(e+"\nArguments: "+Array.prototype.slice.call(arguments).join(", ")+"\n"+(new Error).stack),n=!1),t.apply(this,arguments)},t)}function se(e,t){Rr[e]||(ie(t),Rr[e]=!0)}function ue(e){var t,n,r,o,i,a,s=e._i,u=Ur.exec(s)||Yr.exec(s);if(u){for(c(e).iso=!0,t=0,n=Wr.length;n>t;t++)if(Wr[t][1].exec(u[1])){o=Wr[t][0],r=Wr[t][2]!==!1;break}if(null==o)return void(e._isValid=!1);if(u[3]){for(t=0,n=Fr.length;n>t;t++)if(Fr[t][1].exec(u[3])){i=(u[2]||" ")+Fr[t][0];break}if(null==i)return void(e._isValid=!1)}if(!r&&null!=i)return void(e._isValid=!1);if(u[4]){if(!Br.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=o+(i||"")+(a||""),Ee(e)}else e._isValid=!1}function le(e){var n=Vr.exec(e._i);return null!==n?void(e._d=new Date(+n[1])):(ue(e),void(e._isValid===!1&&(delete e._isValid,t.createFromInputFallback(e))))}function ce(e,t,n,r,o,i,a){var s=new Date(e,t,n,r,o,i,a);return 100>e&&e>=0&&isFinite(s.getFullYear())&&s.setFullYear(e),s}function de(e){var t=new Date(Date.UTC.apply(null,arguments));return 100>e&&e>=0&&isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e),t}function pe(e){return fe(e)?366:365}function fe(e){return e%4===0&&e%100!==0||e%400===0}function he(){return fe(this.year())}function me(e,t,n){var r=7+t-n,o=(7+de(e,0,r).getUTCDay()-t)%7;return-o+r-1}function ge(e,t,n,r,o){var i,a,s=(7+n-r)%7,u=me(e,r,o),l=1+7*(t-1)+s+u;return 0>=l?(i=e-1,a=pe(i)+l):l>pe(e)?(i=e+1,a=l-pe(e)):(i=e,a=l),{year:i,dayOfYear:a}}function ye(e,t,n){var r,o,i=me(e.year(),t,n),a=Math.floor((e.dayOfYear()-i-1)/7)+1;return 1>a?(o=e.year()-1,r=a+ve(o,t,n)):a>ve(e.year(),t,n)?(r=a-ve(e.year(),t,n),o=e.year()+1):(o=e.year(),r=a),{week:r,year:o}}function ve(e,t,n){var r=me(e,t,n),o=me(e+1,t,n);return(pe(e)-r+o)/7}function Me(e,t,n){return null!=e?e:null!=t?t:n}function Te(e){var n=new Date(t.now());return e._useUTC?[n.getUTCFullYear(),n.getUTCMonth(),n.getUTCDate()]:[n.getFullYear(),n.getMonth(),n.getDate()]}function be(e){var t,n,r,o,i=[];if(!e._d){for(r=Te(e),e._w&&null==e._a[Nr]&&null==e._a[Ar]&&xe(e),e._dayOfYear&&(o=Me(e._a[Er],r[Er]),e._dayOfYear>pe(o)&&(c(e)._overflowDayOfYear=!0),n=de(o,0,e._dayOfYear),e._a[Ar]=n.getUTCMonth(),e._a[Nr]=n.getUTCDate()),t=0;3>t&&null==e._a[t];++t)e._a[t]=i[t]=r[t];for(;7>t;t++)e._a[t]=i[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[wr]&&0===e._a[Ir]&&0===e._a[Cr]&&0===e._a[Dr]&&(e._nextDay=!0,e._a[wr]=0),e._d=(e._useUTC?de:ce).apply(null,i),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[wr]=24)}}function xe(e){var t,n,r,o,i,a,s,u;t=e._w,null!=t.GG||null!=t.W||null!=t.E?(i=1,a=4,n=Me(t.GG,e._a[Er],ye(ke(),1,4).year),r=Me(t.W,1),o=Me(t.E,1),(1>o||o>7)&&(u=!0)):(i=e._locale._week.dow,a=e._locale._week.doy,n=Me(t.gg,e._a[Er],ye(ke(),i,a).year),r=Me(t.w,1),null!=t.d?(o=t.d,(0>o||o>6)&&(u=!0)):null!=t.e?(o=t.e+i,(t.e<0||t.e>6)&&(u=!0)):o=i),1>r||r>ve(n,i,a)?c(e)._overflowWeeks=!0:null!=u?c(e)._overflowWeekday=!0:(s=ge(n,r,o,i,a),e._a[Er]=s.year,e._dayOfYear=s.dayOfYear)}function Ee(e){if(e._f===t.ISO_8601)return void ue(e);e._a=[],c(e).empty=!0;var n,r,o,i,a,s=""+e._i,u=s.length,l=0;for(o=B(e._f,e._locale).match(er)||[],n=0;n0&&c(e).unusedInput.push(a),s=s.slice(s.indexOf(r)+r.length),l+=r.length),rr[i]?(r?c(e).empty=!1:c(e).unusedTokens.push(i),G(i,r,e)):e._strict&&!r&&c(e).unusedTokens.push(i);c(e).charsLeftOver=u-l,s.length>0&&c(e).unusedInput.push(s),c(e).bigHour===!0&&e._a[wr]<=12&&e._a[wr]>0&&(c(e).bigHour=void 0),e._a[wr]=Ae(e._locale,e._a[wr],e._meridiem),be(e),oe(e)}function Ae(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?(r=e.isPM(n),r&&12>t&&(t+=12),r||12!==t||(t=0),t):t}function Ne(e){var t,n,r,o,i;if(0===e._f.length)return c(e).invalidFormat=!0,void(e._d=new Date(NaN));for(o=0;oi)&&(r=i,n=t));s(e,n||t)}function we(e){if(!e._d){var t=D(e._i);e._a=i([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),be(e)}}function Ie(e){var t=new m(oe(Ce(e)));return t._nextDay&&(t.add(1,"d"),t._nextDay=void 0),t}function Ce(e){var t=e._i,n=e._f;return e._locale=e._locale||w(e._l),null===t||void 0===n&&""===t?p({nullInput:!0}):("string"==typeof t&&(e._i=t=e._locale.preparse(t)),g(t)?new m(oe(t)):(r(n)?Ne(e):n?Ee(e):o(t)?e._d=t:De(e),d(e)||(e._d=null),e))}function De(e){var n=e._i;void 0===n?e._d=new Date(t.now()):o(n)?e._d=new Date(+n):"string"==typeof n?le(e):r(n)?(e._a=i(n.slice(0),function(e){return parseInt(e,10)}),be(e)):"object"==typeof n?we(e):"number"==typeof n?e._d=new Date(n):t.createFromInputFallback(e)}function Se(e,t,n,r,o){var i={};return"boolean"==typeof n&&(r=n,n=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=o,i._l=n,i._i=e,i._f=t,i._strict=r,Ie(i)}function ke(e,t,n,r){return Se(e,t,n,r,!1)}function Le(e,t){var n,o;if(1===t.length&&r(t[0])&&(t=t[0]),!t.length)return ke();for(n=t[0],o=1;oe&&(e=-e,n="-"),n+j(~~(e/60),2)+t+j(~~e%60,2)})}function Ue(e,t){var n=(t||"").match(e)||[],r=n[n.length-1]||[],o=(r+"").match(Zr)||["-",0,0],i=+(60*o[1])+v(o[2]);return"+"===o[0]?i:-i}function Ye(e,n){var r,i;return n._isUTC?(r=n.clone(),i=(g(e)||o(e)?+e:+ke(e))-+r,r._d.setTime(+r._d+i),t.updateOffset(r,!1),r):ke(e).local()}function Be(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function We(e,n){var r,o=this._offset||0;return this.isValid()?null!=e?("string"==typeof e?e=Ue(vr,e):Math.abs(e)<16&&(e=60*e),!this._isUTC&&n&&(r=Be(this)),this._offset=e,this._isUTC=!0,null!=r&&this.add(r,"m"),o!==e&&(!n||this._changeInProgress?rt(this,Je(e-o,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,t.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?o:Be(this):null!=e?this:NaN}function Fe(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}function Ve(e){return this.utcOffset(0,e)}function He(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Be(this),"m")),this}function _e(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ue(yr,this._i)),this}function Qe(e){return this.isValid()?(e=e?ke(e).utcOffset():0,(this.utcOffset()-e)%60===0):!1}function Ge(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ze(){if(!f(this._isDSTShifted))return this._isDSTShifted;var e={};if(h(e,this),e=Ce(e),e._a){var t=e._isUTC?u(e._a):ke(e._a);this._isDSTShifted=this.isValid()&&M(e._a,t.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Xe(){return this.isValid()?!this._isUTC:!1}function qe(){return this.isValid()?this._isUTC:!1}function Ke(){return this.isValid()?this._isUTC&&0===this._offset:!1}function Je(e,t){var n,r,o,i=e,s=null;return ze(e)?i={ms:e._milliseconds,d:e._days,M:e._months}:"number"==typeof e?(i={},t?i[t]=e:i.milliseconds=e):(s=Xr.exec(e))?(n="-"===s[1]?-1:1,i={y:0,d:v(s[Nr])*n,h:v(s[wr])*n,m:v(s[Ir])*n,s:v(s[Cr])*n,ms:v(s[Dr])*n}):(s=qr.exec(e))?(n="-"===s[1]?-1:1,i={y:$e(s[2],n),M:$e(s[3],n),d:$e(s[4],n),h:$e(s[5],n),m:$e(s[6],n),s:$e(s[7],n),w:$e(s[8],n)}):null==i?i={}:"object"==typeof i&&("from"in i||"to"in i)&&(o=tt(ke(i.from),ke(i.to)),i={},i.ms=o.milliseconds,i.M=o.months),r=new je(i),ze(e)&&a(e,"_locale")&&(r._locale=e._locale),r}function $e(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function et(e,t){var n={milliseconds:0,months:0};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function tt(e,t){var n;return e.isValid()&&t.isValid()?(t=Ye(t,e),e.isBefore(t)?n=et(e,t):(n=et(t,e),n.milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}function nt(e,t){return function(n,r){var o,i;return null===r||isNaN(+r)||(se(t,"moment()."+t+"(period, number) is deprecated. Please use moment()."+t+"(number, period)."),i=n,n=r,r=i),n="string"==typeof n?+n:n,o=Je(n,r),rt(this,o,e),this}}function rt(e,n,r,o){var i=n._milliseconds,a=n._days,s=n._months;e.isValid()&&(o=null==o?!0:o,i&&e._d.setTime(+e._d+i*r),a&&O(e,"Date",L(e,"Date")+a*r),s&&J(e,L(e,"Month")+s*r),o&&t.updateOffset(e,a||s))}function ot(e,t){var n=e||ke(),r=Ye(n,this).startOf("day"),o=this.diff(r,"days",!0),i=-6>o?"sameElse":-1>o?"lastWeek":0>o?"lastDay":1>o?"sameDay":2>o?"nextDay":7>o?"nextWeek":"sameElse",a=t&&(S(t[i])?t[i]():t[i]);return this.format(a||this.localeData().calendar(i,this,ke(n)))}function it(){return new m(this)}function at(e,t){var n=g(e)?e:ke(e);return this.isValid()&&n.isValid()?(t=C(f(t)?"millisecond":t),"millisecond"===t?+this>+n:+n<+this.clone().startOf(t)):!1}function st(e,t){var n=g(e)?e:ke(e);return this.isValid()&&n.isValid()?(t=C(f(t)?"millisecond":t),"millisecond"===t?+n>+this:+this.clone().endOf(t)<+n):!1}function ut(e,t,n){return this.isAfter(e,n)&&this.isBefore(t,n)}function lt(e,t){var n,r=g(e)?e:ke(e);return this.isValid()&&r.isValid()?(t=C(t||"millisecond"),"millisecond"===t?+this===+r:(n=+r,+this.clone().startOf(t)<=n&&n<=+this.clone().endOf(t))):!1}function ct(e,t){return this.isSame(e,t)||this.isAfter(e,t)}function dt(e,t){return this.isSame(e,t)||this.isBefore(e,t)}function pt(e,t,n){var r,o,i,a;return this.isValid()?(r=Ye(e,this),r.isValid()?(o=6e4*(r.utcOffset()-this.utcOffset()),t=C(t),"year"===t||"month"===t||"quarter"===t?(a=ft(this,r),"quarter"===t?a/=3:"year"===t&&(a/=12)):(i=this-r,a="second"===t?i/1e3:"minute"===t?i/6e4:"hour"===t?i/36e5:"day"===t?(i-o)/864e5:"week"===t?(i-o)/6048e5:i),n?a:y(a)):NaN):NaN}function ft(e,t){var n,r,o=12*(t.year()-e.year())+(t.month()-e.month()),i=e.clone().add(o,"months");return 0>t-i?(n=e.clone().add(o-1,"months"),r=(t-i)/(i-n)):(n=e.clone().add(o+1,"months"),r=(t-i)/(n-i)),-(o+r)}function ht(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function mt(){var e=this.clone().utc();return 0i&&(t=i),Wt.call(this,e,t,n,r,o))}function Wt(e,t,n,r,o){var i=ge(e,t,n,r,o),a=de(i.year,0,i.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}function Ft(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)}function Vt(e){return ye(e,this._week.dow,this._week.doy).week}function Ht(){return this._week.dow}function _t(){return this._week.doy}function Qt(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")}function Gt(e){var t=ye(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")}function Zt(e,t){return"string"!=typeof e?e:isNaN(e)?(e=t.weekdaysParse(e),"number"==typeof e?e:null):parseInt(e,10)}function Xt(e,t){return r(this._weekdays)?this._weekdays[e.day()]:this._weekdays[this._weekdays.isFormat.test(t)?"format":"standalone"][e.day()]}function qt(e){return this._weekdaysShort[e.day()]}function Kt(e){return this._weekdaysMin[e.day()]}function Jt(e,t,n){var r,o,i;for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;7>r;r++){if(o=ke([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(o,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(o,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(o,"").replace(".",".?")+"$","i")),this._weekdaysParse[r]||(i="^"+this.weekdays(o,"")+"|^"+this.weekdaysShort(o,"")+"|^"+this.weekdaysMin(o,""),this._weekdaysParse[r]=new RegExp(i.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&"ddd"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&"dd"===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}}function $t(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=Zt(e,this.localeData()),this.add(e-t,"d")):t}function en(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")}function tn(e){return this.isValid()?null==e?this.day()||7:this.day(this.day()%7?e:e-7):null!=e?this:NaN}function nn(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")}function rn(){return this.hours()%12||12}function on(e,t){z(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function an(e,t){return t._meridiemParse}function sn(e){return"p"===(e+"").toLowerCase().charAt(0)}function un(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"}function ln(e,t){t[Dr]=v(1e3*("0."+e))}function cn(){return this._isUTC?"UTC":""}function dn(){return this._isUTC?"Coordinated Universal Time":""}function pn(e){return ke(1e3*e)}function fn(){return ke.apply(null,arguments).parseZone()}function hn(e,t,n){var r=this._calendar[e];return S(r)?r.call(t,n):r}function mn(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])}function gn(){return this._invalidDate}function yn(e){return this._ordinal.replace("%d",e)}function vn(e){return e}function Mn(e,t,n,r){var o=this._relativeTime[n];return S(o)?o(e,t,n,r):o.replace(/%d/i,e)}function Tn(e,t){var n=this._relativeTime[e>0?"future":"past"];return S(n)?n(t):n.replace(/%s/i,t)}function bn(e){var t,n;for(n in e)t=e[n],S(t)?this[n]=t:this["_"+n]=t;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function xn(e,t,n,r){var o=w(),i=u().set(r,t);return o[n](i,e)}function En(e,t,n,r,o){if("number"==typeof e&&(t=e,e=void 0),e=e||"",null!=t)return xn(e,t,n,o);var i,a=[];for(i=0;r>i;i++)a[i]=xn(e,i,n,o);return a}function An(e,t){return En(e,t,"months",12,"month")}function Nn(e,t){return En(e,t,"monthsShort",12,"month")}function wn(e,t){return En(e,t,"weekdays",7,"day")}function In(e,t){return En(e,t,"weekdaysShort",7,"day")}function Cn(e,t){return En(e,t,"weekdaysMin",7,"day")}function Dn(){var e=this._data;return this._milliseconds=bo(this._milliseconds),this._days=bo(this._days),this._months=bo(this._months),e.milliseconds=bo(e.milliseconds),e.seconds=bo(e.seconds),e.minutes=bo(e.minutes),e.hours=bo(e.hours),e.months=bo(e.months),e.years=bo(e.years),this}function Sn(e,t,n,r){var o=Je(t,n);return e._milliseconds+=r*o._milliseconds,e._days+=r*o._days,e._months+=r*o._months,e._bubble()}function kn(e,t){return Sn(this,e,t,1)}function Ln(e,t){return Sn(this,e,t,-1)}function On(e){return 0>e?Math.floor(e):Math.ceil(e)}function Pn(){var e,t,n,r,o,i=this._milliseconds,a=this._days,s=this._months,u=this._data;return i>=0&&a>=0&&s>=0||0>=i&&0>=a&&0>=s||(i+=864e5*On(zn(s)+a),a=0,s=0),u.milliseconds=i%1e3,e=y(i/1e3),u.seconds=e%60,t=y(e/60),u.minutes=t%60,n=y(t/60),u.hours=n%24,a+=y(n/24),o=y(jn(a)),s+=o,a-=On(zn(o)),r=y(s/12),s%=12,u.days=a,u.months=s,u.years=r,this}function jn(e){return 4800*e/146097}function zn(e){return 146097*e/4800}function Rn(e){var t,n,r=this._milliseconds;if(e=C(e),"month"===e||"year"===e)return t=this._days+r/864e5,n=this._months+jn(t),"month"===e?n:n/12;switch(t=this._days+Math.round(zn(this._months)),e){case"week":return t/7+r/6048e5;case"day":return t+r/864e5;case"hour":return 24*t+r/36e5;case"minute":return 1440*t+r/6e4;case"second":return 86400*t+r/1e3;case"millisecond":return Math.floor(864e5*t)+r;default:throw new Error("Unknown unit "+e)}}function Un(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*v(this._months/12)}function Yn(e){return function(){return this.as(e)}}function Bn(e){return e=C(e),this[e+"s"]()}function Wn(e){return function(){return this._data[e]}}function Fn(){return y(this.days()/7)}function Vn(e,t,n,r,o){return o.relativeTime(t||1,!!n,e,r)}function Hn(e,t,n){var r=Je(e).abs(),o=Ro(r.as("s")),i=Ro(r.as("m")),a=Ro(r.as("h")),s=Ro(r.as("d")),u=Ro(r.as("M")),l=Ro(r.as("y")),c=o=i&&["m"]||i=a&&["h"]||a=s&&["d"]||s=u&&["M"]||u=l&&["y"]||["yy",l];return c[2]=t,c[3]=+e>0,c[4]=n,Vn.apply(null,c)}function _n(e,t){return void 0===Uo[e]?!1:void 0===t?Uo[e]:(Uo[e]=t,!0)}function Qn(e){var t=this.localeData(),n=Hn(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)}function Gn(){var e,t,n,r=Yo(this._milliseconds)/1e3,o=Yo(this._days),i=Yo(this._months);e=y(r/60),t=y(e/60),r%=60,e%=60,n=y(i/12),i%=12;var a=n,s=i,u=o,l=t,c=e,d=r,p=this.asSeconds();return p?(0>p?"-":"")+"P"+(a?a+"Y":"")+(s?s+"M":"")+(u?u+"D":"")+(l||c||d?"T":"")+(l?l+"H":"")+(c?c+"M":"")+(d?d+"S":""):"P0D"}var Zn,Xn,qn=t.momentProperties=[],Kn=!1,Jn={},$n={},er=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,tr=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,nr={},rr={},or=/\d/,ir=/\d\d/,ar=/\d{3}/,sr=/\d{4}/,ur=/[+-]?\d{6}/,lr=/\d\d?/,cr=/\d\d\d\d?/,dr=/\d\d\d\d\d\d?/,pr=/\d{1,3}/,fr=/\d{1,4}/,hr=/[+-]?\d{1,6}/,mr=/\d+/,gr=/[+-]?\d+/,yr=/Z|[+-]\d\d:?\d\d/gi,vr=/Z|[+-]\d\d(?::?\d\d)?/gi,Mr=/[+-]?\d+(\.\d{1,3})?/,Tr=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,br={},xr={},Er=0,Ar=1,Nr=2,wr=3,Ir=4,Cr=5,Dr=6,Sr=7,kr=8;z("M",["MM",2],"Mo",function(){return this.month()+1}),z("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),z("MMMM",0,0,function(e){return this.localeData().months(this,e)}),I("month","M"),W("M",lr),W("MM",lr,ir),W("MMM",function(e,t){return t.monthsShortRegex(e)}),W("MMMM",function(e,t){return t.monthsRegex(e)}),_(["M","MM"],function(e,t){t[Ar]=v(e)-1}),_(["MMM","MMMM"],function(e,t,n,r){var o=n._locale.monthsParse(e,r,n._strict);null!=o?t[Ar]=o:c(n).invalidMonth=e});var Lr=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/,Or="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Pr="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),jr=Tr,zr=Tr,Rr={};t.suppressDeprecationWarnings=!1;var Ur=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,Yr=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,Br=/Z|[+-]\d\d(?::?\d\d)?/,Wr=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Fr=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Vr=/^\/?Date\((\-?\d+)/i;t.createFromInputFallback=ae("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(e){ +e._d=new Date(e._i+(e._useUTC?" UTC":""))}),z("Y",0,0,function(){var e=this.year();return 9999>=e?""+e:"+"+e}),z(0,["YY",2],0,function(){return this.year()%100}),z(0,["YYYY",4],0,"year"),z(0,["YYYYY",5],0,"year"),z(0,["YYYYYY",6,!0],0,"year"),I("year","y"),W("Y",gr),W("YY",lr,ir),W("YYYY",fr,sr),W("YYYYY",hr,ur),W("YYYYYY",hr,ur),_(["YYYYY","YYYYYY"],Er),_("YYYY",function(e,n){n[Er]=2===e.length?t.parseTwoDigitYear(e):v(e)}),_("YY",function(e,n){n[Er]=t.parseTwoDigitYear(e)}),_("Y",function(e,t){t[Er]=parseInt(e,10)}),t.parseTwoDigitYear=function(e){return v(e)+(v(e)>68?1900:2e3)};var Hr=k("FullYear",!1);t.ISO_8601=function(){};var _r=ae("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var e=ke.apply(null,arguments);return this.isValid()&&e.isValid()?this>e?this:e:p()}),Qr=ae("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var e=ke.apply(null,arguments);return this.isValid()&&e.isValid()?e>this?this:e:p()}),Gr=function(){return Date.now?Date.now():+new Date};Re("Z",":"),Re("ZZ",""),W("Z",vr),W("ZZ",vr),_(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Ue(vr,e)});var Zr=/([\+\-]|\d\d)/gi;t.updateOffset=function(){};var Xr=/(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,qr=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Je.fn=je.prototype;var Kr=nt(1,"add"),Jr=nt(-1,"subtract");t.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var $r=ae("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});z(0,["gg",2],0,function(){return this.weekYear()%100}),z(0,["GG",2],0,function(){return this.isoWeekYear()%100}),jt("gggg","weekYear"),jt("ggggg","weekYear"),jt("GGGG","isoWeekYear"),jt("GGGGG","isoWeekYear"),I("weekYear","gg"),I("isoWeekYear","GG"),W("G",gr),W("g",gr),W("GG",lr,ir),W("gg",lr,ir),W("GGGG",fr,sr),W("gggg",fr,sr),W("GGGGG",hr,ur),W("ggggg",hr,ur),Q(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,r){t[r.substr(0,2)]=v(e)}),Q(["gg","GG"],function(e,n,r,o){n[o]=t.parseTwoDigitYear(e)}),z("Q",0,"Qo","quarter"),I("quarter","Q"),W("Q",or),_("Q",function(e,t){t[Ar]=3*(v(e)-1)}),z("w",["ww",2],"wo","week"),z("W",["WW",2],"Wo","isoWeek"),I("week","w"),I("isoWeek","W"),W("w",lr),W("ww",lr,ir),W("W",lr),W("WW",lr,ir),Q(["w","ww","W","WW"],function(e,t,n,r){t[r.substr(0,1)]=v(e)});var eo={dow:0,doy:6};z("D",["DD",2],"Do","date"),I("date","D"),W("D",lr),W("DD",lr,ir),W("Do",function(e,t){return e?t._ordinalParse:t._ordinalParseLenient}),_(["D","DD"],Nr),_("Do",function(e,t){t[Nr]=v(e.match(lr)[0],10)});var to=k("Date",!0);z("d",0,"do","day"),z("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),z("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),z("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),z("e",0,0,"weekday"),z("E",0,0,"isoWeekday"),I("day","d"),I("weekday","e"),I("isoWeekday","E"),W("d",lr),W("e",lr),W("E",lr),W("dd",Tr),W("ddd",Tr),W("dddd",Tr),Q(["dd","ddd","dddd"],function(e,t,n,r){var o=n._locale.weekdaysParse(e,r,n._strict);null!=o?t.d=o:c(n).invalidWeekday=e}),Q(["d","e","E"],function(e,t,n,r){t[r]=v(e)});var no="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),ro="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),oo="Su_Mo_Tu_We_Th_Fr_Sa".split("_");z("DDD",["DDDD",3],"DDDo","dayOfYear"),I("dayOfYear","DDD"),W("DDD",pr),W("DDDD",ar),_(["DDD","DDDD"],function(e,t,n){n._dayOfYear=v(e)}),z("H",["HH",2],0,"hour"),z("h",["hh",2],0,rn),z("hmm",0,0,function(){return""+rn.apply(this)+j(this.minutes(),2)}),z("hmmss",0,0,function(){return""+rn.apply(this)+j(this.minutes(),2)+j(this.seconds(),2)}),z("Hmm",0,0,function(){return""+this.hours()+j(this.minutes(),2)}),z("Hmmss",0,0,function(){return""+this.hours()+j(this.minutes(),2)+j(this.seconds(),2)}),on("a",!0),on("A",!1),I("hour","h"),W("a",an),W("A",an),W("H",lr),W("h",lr),W("HH",lr,ir),W("hh",lr,ir),W("hmm",cr),W("hmmss",dr),W("Hmm",cr),W("Hmmss",dr),_(["H","HH"],wr),_(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),_(["h","hh"],function(e,t,n){t[wr]=v(e),c(n).bigHour=!0}),_("hmm",function(e,t,n){var r=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r)),c(n).bigHour=!0}),_("hmmss",function(e,t,n){var r=e.length-4,o=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r,2)),t[Cr]=v(e.substr(o)),c(n).bigHour=!0}),_("Hmm",function(e,t,n){var r=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r))}),_("Hmmss",function(e,t,n){var r=e.length-4,o=e.length-2;t[wr]=v(e.substr(0,r)),t[Ir]=v(e.substr(r,2)),t[Cr]=v(e.substr(o))});var io=/[ap]\.?m?\.?/i,ao=k("Hours",!0);z("m",["mm",2],0,"minute"),I("minute","m"),W("m",lr),W("mm",lr,ir),_(["m","mm"],Ir);var so=k("Minutes",!1);z("s",["ss",2],0,"second"),I("second","s"),W("s",lr),W("ss",lr,ir),_(["s","ss"],Cr);var uo=k("Seconds",!1);z("S",0,0,function(){return~~(this.millisecond()/100)}),z(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),z(0,["SSS",3],0,"millisecond"),z(0,["SSSS",4],0,function(){return 10*this.millisecond()}),z(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),z(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),z(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),z(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),z(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),I("millisecond","ms"),W("S",pr,or),W("SS",pr,ir),W("SSS",pr,ar);var lo;for(lo="SSSS";lo.length<=9;lo+="S")W(lo,mr);for(lo="S";lo.length<=9;lo+="S")_(lo,ln);var co=k("Milliseconds",!1);z("z",0,0,"zoneAbbr"),z("zz",0,0,"zoneName");var po=m.prototype;po.add=Kr,po.calendar=ot,po.clone=it,po.diff=pt,po.endOf=At,po.format=gt,po.from=yt,po.fromNow=vt,po.to=Mt,po.toNow=Tt,po.get=P,po.invalidAt=Ot,po.isAfter=at,po.isBefore=st,po.isBetween=ut,po.isSame=lt,po.isSameOrAfter=ct,po.isSameOrBefore=dt,po.isValid=kt,po.lang=$r,po.locale=bt,po.localeData=xt,po.max=Qr,po.min=_r,po.parsingFlags=Lt,po.set=P,po.startOf=Et,po.subtract=Jr,po.toArray=Ct,po.toObject=Dt,po.toDate=It,po.toISOString=mt,po.toJSON=St,po.toString=ht,po.unix=wt,po.valueOf=Nt,po.creationData=Pt,po.year=Hr,po.isLeapYear=he,po.weekYear=zt,po.isoWeekYear=Rt,po.quarter=po.quarters=Ft,po.month=$,po.daysInMonth=ee,po.week=po.weeks=Qt,po.isoWeek=po.isoWeeks=Gt,po.weeksInYear=Yt,po.isoWeeksInYear=Ut,po.date=to,po.day=po.days=$t,po.weekday=en,po.isoWeekday=tn,po.dayOfYear=nn,po.hour=po.hours=ao,po.minute=po.minutes=so,po.second=po.seconds=uo,po.millisecond=po.milliseconds=co,po.utcOffset=We,po.utc=Ve,po.local=He,po.parseZone=_e,po.hasAlignedHourOffset=Qe,po.isDST=Ge,po.isDSTShifted=Ze,po.isLocal=Xe,po.isUtcOffset=qe,po.isUtc=Ke,po.isUTC=Ke,po.zoneAbbr=cn,po.zoneName=dn,po.dates=ae("dates accessor is deprecated. Use date instead.",to),po.months=ae("months accessor is deprecated. Use month instead",$),po.years=ae("years accessor is deprecated. Use year instead",Hr),po.zone=ae("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Fe);var fo=po,ho={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},mo={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},go="Invalid date",yo="%d",vo=/\d{1,2}/,Mo={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},To=T.prototype;To._calendar=ho,To.calendar=hn,To._longDateFormat=mo,To.longDateFormat=mn,To._invalidDate=go,To.invalidDate=gn,To._ordinal=yo,To.ordinal=yn,To._ordinalParse=vo,To.preparse=vn,To.postformat=vn,To._relativeTime=Mo,To.relativeTime=Mn,To.pastFuture=Tn,To.set=bn,To.months=X,To._months=Or,To.monthsShort=q,To._monthsShort=Pr,To.monthsParse=K,To._monthsRegex=zr,To.monthsRegex=ne,To._monthsShortRegex=jr,To.monthsShortRegex=te,To.week=Vt,To._week=eo,To.firstDayOfYear=_t,To.firstDayOfWeek=Ht,To.weekdays=Xt,To._weekdays=no,To.weekdaysMin=Kt,To._weekdaysMin=oo,To.weekdaysShort=qt,To._weekdaysShort=ro,To.weekdaysParse=Jt,To.isPM=sn,To._meridiemParse=io,To.meridiem=un,A("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=1===v(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n}}),t.lang=ae("moment.lang is deprecated. Use moment.locale instead.",A),t.langData=ae("moment.langData is deprecated. Use moment.localeData instead.",w);var bo=Math.abs,xo=Yn("ms"),Eo=Yn("s"),Ao=Yn("m"),No=Yn("h"),wo=Yn("d"),Io=Yn("w"),Co=Yn("M"),Do=Yn("y"),So=Wn("milliseconds"),ko=Wn("seconds"),Lo=Wn("minutes"),Oo=Wn("hours"),Po=Wn("days"),jo=Wn("months"),zo=Wn("years"),Ro=Math.round,Uo={s:45,m:45,h:22,d:26,M:11},Yo=Math.abs,Bo=je.prototype;Bo.abs=Dn,Bo.add=kn,Bo.subtract=Ln,Bo.as=Rn,Bo.asMilliseconds=xo,Bo.asSeconds=Eo,Bo.asMinutes=Ao,Bo.asHours=No,Bo.asDays=wo,Bo.asWeeks=Io,Bo.asMonths=Co,Bo.asYears=Do,Bo.valueOf=Un,Bo._bubble=Pn,Bo.get=Bn,Bo.milliseconds=So,Bo.seconds=ko,Bo.minutes=Lo,Bo.hours=Oo,Bo.days=Po,Bo.weeks=Fn,Bo.months=jo,Bo.years=zo,Bo.humanize=Qn,Bo.toISOString=Gn,Bo.toString=Gn,Bo.toJSON=Gn,Bo.locale=bt,Bo.localeData=xt,Bo.toIsoString=ae("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Gn),Bo.lang=$r,z("X",0,0,"unix"),z("x",0,0,"valueOf"),W("x",gr),W("X",Mr),_("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),_("x",function(e,t,n){n._d=new Date(v(e))}),t.version="2.11.1",n(ke),t.fn=fo,t.min=Oe,t.max=Pe,t.now=Gr,t.utc=u,t.unix=pn,t.months=An,t.isDate=o,t.locale=A,t.invalid=p,t.duration=Je,t.isMoment=g,t.weekdays=wn,t.parseZone=fn,t.localeData=w,t.isDuration=ze,t.monthsShort=Nn,t.weekdaysMin=Cn,t.defineLocale=N,t.weekdaysShort=In,t.normalizeUnits=C,t.relativeTimeThreshold=_n,t.prototype=fo;var Wo=t;return Wo})}).call(t,n(222)(e))},function(e,t,n){"use strict";function r(e,t,n){var r=0;return d["default"].Children.map(e,function(e){if(d["default"].isValidElement(e)){var o=r;return r++,t.call(n,e,o)}return e})}function o(e,t,n){var r=0;return d["default"].Children.forEach(e,function(e){d["default"].isValidElement(e)&&(t.call(n,e,r),r++)})}function i(e){var t=0;return d["default"].Children.forEach(e,function(e){d["default"].isValidElement(e)&&t++}),t}function a(e){var t=!1;return d["default"].Children.forEach(e,function(e){!t&&d["default"].isValidElement(e)&&(t=!0)}),t}function s(e,t){var n=void 0;return o(e,function(r,o){!n&&t(r,o,e)&&(n=r)}),n}function u(e,t,n){var r=0,o=[];return d["default"].Children.forEach(e,function(e){d["default"].isValidElement(e)&&(t.call(n,e,r)&&o.push(e),r++)}),o}var l=n(5)["default"];t.__esModule=!0;var c=n(1),d=l(c);t["default"]={map:r,forEach:o,numberOf:i,find:s,findValidComponents:u,hasValidComponent:a},e.exports=t["default"]},function(e,t){var n=e.exports={version:"1.2.6"};"number"==typeof __e&&(__e=n)},function(e,t,n){"use strict";var r=n(29),o=function(){var e=r&&document.documentElement;return e&&e.contains?function(e,t){return e.contains(t)}:e&&e.compareDocumentPosition?function(e,t){return e===t||!!(16&e.compareDocumentPosition(t))}:function(e,t){if(t)do if(t===e)return!0;while(t=t.parentNode);return!1}}();e.exports=o},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(12),i=r(o),a=n(38),s=r(a);t["default"]=function(e){return s["default"](i["default"].findDOMNode(e))},e.exports=t["default"]},function(e,t,n){"use strict";var r=n(184),o=n(386),i=n(197),a=n(206),s=n(207),u=n(2),l=(n(4),{}),c=null,d=function(e,t){e&&(o.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e))},p=function(e){return d(e,!0)},f=function(e){return d(e,!1)},h=null,m={injection:{injectMount:o.injection.injectMount,injectInstanceHandle:function(e){h=e},getInstanceHandle:function(){return h},injectEventPluginOrder:r.injectEventPluginOrder,injectEventPluginsByName:r.injectEventPluginsByName},eventNameDispatchConfigs:r.eventNameDispatchConfigs,registrationNameModules:r.registrationNameModules,putListener:function(e,t,n){"function"!=typeof n?u(!1):void 0;var o=l[t]||(l[t]={});o[e]=n;var i=r.registrationNameModules[t];i&&i.didPutListener&&i.didPutListener(e,t,n)},getListener:function(e,t){var n=l[t];return n&&n[e]},deleteListener:function(e,t){var n=r.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t);var o=l[t];o&&delete o[e]},deleteAllListeners:function(e){for(var t in l)if(l[t][e]){var n=r.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t),delete l[t][e]}},extractEvents:function(e,t,n,o,i){for(var s,u=r.plugins,l=0;l=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){return l.stringify(e).replace(/%20/g,"+")}function a(e){return function(){function t(e){if(null==e.query){var t=e.search;e.query=x(t.substring(1)),e[m]={search:t,searchBase:""}}return e}function n(e,t){var n,r=e[m],o=t?b(t):"";if(!r&&!o)return e;"string"==typeof e&&(e=p.parsePath(e));var i=void 0;i=r&&e.search===r.search?r.searchBase:e.search||"";var a=i;return o&&(a+=(a?"&":"?")+o),s({},e,(n={search:a},n[m]={search:a,searchBase:i},n))}function r(e){return A.listenBefore(function(n,r){d["default"](e,t(n),r)})}function a(e){return A.listen(function(n){e(t(n))})}function u(e){A.push(n(e,e.query))}function l(e){A.replace(n(e,e.query))}function c(e,t){return A.createPath(n(e,t||e.query))}function f(e,t){return A.createHref(n(e,t||e.query))}function y(e){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];var a=A.createLocation.apply(A,[n(e,e.query)].concat(o));return e.query&&(a.query=e.query),t(a)}function v(e,t,n){"string"==typeof t&&(t=p.parsePath(t)),u(s({state:e},t,{query:n}))}function M(e,t,n){"string"==typeof t&&(t=p.parsePath(t)),l(s({state:e},t,{query:n}))}var T=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],b=T.stringifyQuery,x=T.parseQueryString,E=o(T,["stringifyQuery","parseQueryString"]),A=e(E);return"function"!=typeof b&&(b=i),"function"!=typeof x&&(x=g),s({},A,{listenBefore:r,listen:a,push:u,replace:l,createPath:c,createHref:f,createLocation:y,pushState:h["default"](v,"pushState is deprecated; use push instead"),replaceState:h["default"](M,"replaceState is deprecated; use replace instead")})}}t.__esModule=!0;var s=Object.assign||function(e){for(var t=1;t":">","<":"<",'"':""","'":"'"},i=/[&><"']/g;e.exports=r},function(e,t,n){"use strict";var r=n(9),o=/^[ \r\n\t\f]/,i=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,a=function(e,t){e.innerHTML=t};if("undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction&&(a=function(e,t){MSApp.execUnsafeLocalFunction(function(){e.innerHTML=t})}),r.canUseDOM){var s=document.createElement("div");s.innerHTML=" ",""===s.innerHTML&&(a=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),o.test(t)||"<"===t[0]&&i.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t})}e.exports=a},function(e,t,n){"use strict";var r=n(2),o=function(e){var t,n={};e instanceof Object&&!Array.isArray(e)?void 0:r(!1);for(t in e)e.hasOwnProperty(t)&&(n[t]=t);return n};e.exports=o},function(e,t,n){e.exports={"default":n(253),__esModule:!0}},function(e,t,n){var r=n(259),o=n(48),i=n(126),a="prototype",s=function(e,t,n){var u,l,c,d=e&s.F,p=e&s.G,f=e&s.S,h=e&s.P,m=e&s.B,g=e&s.W,y=p?o:o[t]||(o[t]={}),v=p?r:f?r[t]:(r[t]||{})[a];p&&(n=t);for(u in n)l=!d&&v&&u in v,l&&u in y||(c=l?v[u]:n[u],y[u]=p&&"function"!=typeof v[u]?n[u]:m&&l?i(c,r):g&&v[u]==c?function(e){var t=function(t){return this instanceof e?new e(t):e(t)};return t[a]=e[a],t}(c):h&&"function"==typeof c?i(Function.call,c):c,h&&((y[a]||(y[a]={}))[u]=c))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,e.exports=s},function(e,t){var n=Object;e.exports={create:n.create,getProto:n.getPrototypeOf,isEnum:{}.propertyIsEnumerable,getDesc:n.getOwnPropertyDescriptor,setDesc:n.defineProperty,setDescs:n.defineProperties,getKeys:n.keys,getNames:n.getOwnPropertyNames,getSymbols:n.getOwnPropertySymbols,each:[].forEach}},function(e,t,n){"use strict";var r=n(29),o=function(){};r&&(o=function(){return document.addEventListener?function(e,t,n,r){return e.addEventListener(t,n,r||!1)}:document.attachEvent?function(e,t,n){return e.attachEvent("on"+t,n)}:void 0}()),e.exports=o},function(e,t,n){"use strict";var r=n(135),o=n(281),i=n(276),a=n(277),s=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var u="",l=t;if("string"==typeof t){if(void 0===n)return e.style[r(t)]||i(e).getPropertyValue(o(t));(l={})[t]=n}for(var c in l)s.call(l,c)&&(l[c]||0===l[c]?u+=o(c)+":"+l[c]+";":a(e,o(c)));e.style.cssText+=";"+u}},function(e,t,n){function r(e,t,n){if("function"!=typeof e)return o;if(void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 3:return function(n,r,o){return e.call(t,n,r,o)};case 4:return function(n,r,o,i){return e.call(t,n,r,o,i)};case 5:return function(n,r,o,i,a){return e.call(t,n,r,o,i,a)}}return function(){return e.apply(t,arguments)}}var o=n(155);e.exports=r},function(e,t,n){function r(e){return null!=e&&i(o(e))}var o=n(145),i=n(39);e.exports=r},function(e,t,n){function r(e){return i(e)&&o(e)&&s.call(e,"callee")&&!u.call(e,"callee")}var o=n(78),i=n(30),a=Object.prototype,s=a.hasOwnProperty,u=a.propertyIsEnumerable;e.exports=r},function(e,t,n){function r(e){return"string"==typeof e||o(e)&&s.call(e)==i}var o=n(30),i="[object String]",a=Object.prototype,s=a.toString;e.exports=r},function(e,t,n){var r=n(58),o=n(78),i=n(25),a=n(315),s=n(83),u=r(Object,"keys"),l=u?function(e){var t=null==e?void 0:e.constructor;return"function"==typeof t&&t.prototype===e||("function"==typeof e?s.enumPrototypes:o(e))?a(e):i(e)?u(e):[]}:a;e.exports=l},function(e,t,n){function r(e){if(null==e)return[];c(e)||(e=Object(e));var t=e.length;t=t&&l(t)&&(a(e)||i(e)||d(e))&&t||0;for(var n=e.constructor,r=-1,o=s(n)&&n.prototype||A,f=o===e,h=Array(t),m=t>0,y=p.enumErrorProps&&(e===E||e instanceof Error),v=p.enumPrototypes&&s(e);++rn;n++)t[n]=arguments[n];if(void 0===t)throw new Error("No validations provided");if(t.some(function(e){return"function"!=typeof e}))throw new Error("Invalid arguments, must be functions");if(0===t.length)throw new Error("No validations provided");return function(e,n,r){for(var o=0;oa&&l;)l=!1,t.call(this,a++,i,r);return u=!1,s?void n.apply(this,c):void(a>=e&&l&&(s=!0,n()))}}var a=0,s=!1,u=!1,l=!1,c=void 0;i()}function r(e,t,n){function r(e,t,r){a||(t?(a=!0,n(t)):(i[e]=r,a=++s===o,a&&n(null,i)))}var o=e.length,i=[];if(0===o)return n(null,i);var a=!1,s=0;e.forEach(function(e,n){t(e,n,function(e,t){r(n,e,t)})})}t.__esModule=!0;var o=Array.prototype.slice;t.loopAsync=n,t.mapAsync=r},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=Object.assign||function(e){for(var t=1;ts;++s)i=o[s](e);n(i)})}function g(){if(b.routes){for(var e=p(b.routes),t=void 0,n=0,r=e.length;"string"!=typeof t&&r>n;++n)t=e[n]();return t}}function v(e){var t=l(e,!1);t&&(delete A[t],o(A)||(N&&(N(),N=null),w&&(w(),w=null)))}function M(t,n){var r=l(t),i=A[r];if(i)-1===i.indexOf(n)&&i.push(n);else{var a=!o(A);A[r]=[n],a&&(N=e.listenBefore(h),e.listenBeforeUnload&&(w=e.listenBeforeUnload(g)))}return function(){var e=A[r];if(e){var o=e.filter(function(e){return e!==n});0===o.length?v(t):A[r]=o}}}function T(t){return e.listen(function(n){b.location===n?t(null,b):i(n,function(n,r,o){n?t(n):r?e.transitionTo(r):o&&t(null,o)})})}var b={},x=void 0,E=1,A={},N=void 0,w=void 0;return{isActive:n,match:i,listenBeforeLeavingRoute:M,listen:T}}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;tt||e.hasOverloadedBooleanValue&&t===!1}var i=n(42),a=n(17),s=n(431),u=(n(4),/^[a-zA-Z_][\w\.\-]*$/),l={},c={},d={createMarkupForID:function(e){return i.ID_ATTRIBUTE_NAME+"="+s(e)},setAttributeForID:function(e,t){e.setAttribute(i.ID_ATTRIBUTE_NAME,t)},createMarkupForProperty:function(e,t){var n=i.properties.hasOwnProperty(e)?i.properties[e]:null;if(n){if(o(n,t))return"";var r=n.attributeName;return n.hasBooleanValue||n.hasOverloadedBooleanValue&&t===!0?r+'=""':r+"="+s(t)}return i.isCustomAttribute(e)?null==t?"":e+"="+s(t):null},createMarkupForCustomAttribute:function(e,t){return r(e)&&null!=t?e+"="+s(t):""},setValueForProperty:function(e,t,n){var r=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(r){var a=r.mutationMethod;if(a)a(e,n);else if(o(r,n))this.deleteValueForProperty(e,t);else if(r.mustUseAttribute){var s=r.attributeName,u=r.attributeNamespace;u?e.setAttributeNS(u,s,""+n):r.hasBooleanValue||r.hasOverloadedBooleanValue&&n===!0?e.setAttribute(s,""):e.setAttribute(s,""+n)}else{var l=r.propertyName;r.hasSideEffects&&""+e[l]==""+n||(e[l]=n)}}else i.isCustomAttribute(t)&&d.setValueForAttribute(e,t,n)},setValueForAttribute:function(e,t,n){r(t)&&(null==n?e.removeAttribute(t):e.setAttribute(t,""+n))},deleteValueForProperty:function(e,t){var n=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(n){var r=n.mutationMethod;if(r)r(e,void 0);else if(n.mustUseAttribute)e.removeAttribute(n.attributeName);else{var o=n.propertyName,a=i.getDefaultValueForProperty(e.nodeName,o);n.hasSideEffects&&""+e[o]===a||(e[o]=a)}}else i.isCustomAttribute(t)&&e.removeAttribute(t)}};a.measureMethods(d,"DOMPropertyOperations",{setValueForProperty:"setValueForProperty",setValueForAttribute:"setValueForAttribute",deleteValueForProperty:"deleteValueForProperty"}),e.exports=d},function(e,t,n){"use strict";function r(e){null!=e.checkedLink&&null!=e.valueLink?l(!1):void 0}function o(e){r(e),null!=e.value||null!=e.onChange?l(!1):void 0}function i(e){r(e),null!=e.checked||null!=e.onChange?l(!1):void 0}function a(e){if(e){var t=e.getName();if(t)return" Check the render method of ` + "`" + `"+t+"` + "`" + `."}return""}var s=n(203),u=n(65),l=n(2),c=(n(4),{button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0}),d={value:function(e,t,n){return!e[t]||c[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a ` + "`" + `value` + "`" + ` prop to a form field without an ` + "`" + `onChange` + "`" + ` handler. This will render a read-only field. If the field should be mutable use ` + "`" + `defaultValue` + "`" + `. Otherwise, set either ` + "`" + `onChange` + "`" + ` or ` + "`" + `readOnly` + "`" + `.")},checked:function(e,t,n){return!e[t]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a ` + "`" + `checked` + "`" + ` prop to a form field without an ` + "`" + `onChange` + "`" + ` handler. This will render a read-only field. If the field should be mutable use ` + "`" + `defaultChecked` + "`" + `. Otherwise, set either ` + "`" + `onChange` + "`" + ` or ` + "`" + `readOnly` + "`" + `.")},onChange:s.func},p={},f={checkPropTypes:function(e,t,n){for(var r in d){if(d.hasOwnProperty(r))var o=d[r](t,r,e,u.prop);if(o instanceof Error&&!(o.message in p)){p[o.message]=!0;a(n)}}},getValue:function(e){return e.valueLink?(o(e),e.valueLink.value):e.value},getChecked:function(e){return e.checkedLink?(i(e),e.checkedLink.value):e.checked},executeOnChange:function(e,t){return e.valueLink?(o(e),e.valueLink.requestChange(t.target.value)):e.checkedLink?(i(e),e.checkedLink.requestChange(t.target.checked)):e.onChange?e.onChange.call(void 0,t):void 0}};e.exports=f},function(e,t,n){"use strict";var r=n(99),o=n(11),i={processChildrenUpdates:r.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkupByID:r.dangerouslyReplaceNodeWithMarkupByID,unmountIDFromEnvironment:function(e){o.purgeID(e)}};e.exports=i},function(e,t,n){"use strict";var r=n(2),o=!1,i={unmountIDFromEnvironment:null,replaceNodeWithMarkupByID:null,processChildrenUpdates:null,injection:{injectEnvironment:function(e){o?r(!1):void 0,i.unmountIDFromEnvironment=e.unmountIDFromEnvironment,i.replaceNodeWithMarkupByID=e.replaceNodeWithMarkupByID,i.processChildrenUpdates=e.processChildrenUpdates,o=!0}}};e.exports=i},function(e,t,n){"use strict";var r=n(183),o=n(95),i=n(11),a=n(17),s=n(2),u={dangerouslySetInnerHTML:"` + "`" + `dangerouslySetInnerHTML` + "`" + ` must be set using ` + "`" + `updateInnerHTMLByID()` + "`" + `.",style:"` + "`" + `style` + "`" + ` must be set using ` + "`" + `updateStylesByID()` + "`" + `."},l={updatePropertyByID:function(e,t,n){var r=i.getNode(e);u.hasOwnProperty(t)?s(!1):void 0,null!=n?o.setValueForProperty(r,t,n):o.deleteValueForProperty(r,t)},dangerouslyReplaceNodeWithMarkupByID:function(e,t){var n=i.getNode(e);r.dangerouslyReplaceNodeWithMarkup(n,t)},dangerouslyProcessChildrenUpdates:function(e,t){for(var n=0;n=32||13===t?t:0}e.exports=n},function(e,t){"use strict";function n(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=o[e];return r?!!n[r]:!1}function r(e){return n}var o={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};e.exports=r},function(e,t){"use strict";function n(e){var t=e.target||e.srcElement||window;return 3===t.nodeType?t.parentNode:t}e.exports=n},function(e,t){"use strict";function n(e){var t=e&&(r&&e[r]||e[o]);return"function"==typeof t?t:void 0}var r="function"==typeof Symbol&&Symbol.iterator,o="@@iterator";e.exports=n},function(e,t,n){"use strict";function r(e){return"function"==typeof e&&"undefined"!=typeof e.prototype&&"function"==typeof e.prototype.mountComponent&&"function"==typeof e.prototype.receiveComponent}function o(e){var t;if(null===e||e===!1)t=new a(o);else if("object"==typeof e){var n=e;!n||"function"!=typeof n.type&&"string"!=typeof n.type?l(!1):void 0,t="string"==typeof n.type?s.createInternalComponent(n):r(n.type)?new n.type(n):new c}else"string"==typeof e||"number"==typeof e?t=s.createInstanceForText(e):l(!1);return t.construct(e),t._mountIndex=0,t._mountImage=null,t}var i=n(392),a=n(195),s=n(201),u=n(3),l=n(2),c=(n(4),function(){});u(c.prototype,i.Mixin,{_instantiateReactComponent:o}),e.exports=o},function(e,t,n){"use strict";/** * Checks if an event is supported in the current execution environment. * @@ -200,7 +200,7 @@ e._d=new Date(e._i+(e._useUTC?" UTC":""))}),z("Y",0,0,function(){var e=this.year * @internal * @license Modernizr 3.0.0pre (Custom Build) | MIT */ -function r(e,t){if(!i.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,r=n in document;if(!r){var a=document.createElement("div");a.setAttribute(n,"return;"),r="function"==typeof a[n]}return!r&&o&&"wheel"===e&&(r=document.implementation.hasFeature("Events.wheel","3.0")),r}var o,i=n(9);i.canUseDOM&&(o=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("","")!==!0),e.exports=r},function(e,t,n){"use strict";var r=n(9),o=n(69),i=n(70),a=function(e,t){e.textContent=t};r.canUseDOM&&("textContent"in document.documentElement||(a=function(e,t){i(e,o(t))})),e.exports=a},function(e,t){"use strict";function n(e,t){var n=null===e||e===!1,r=null===t||t===!1;if(n||r)return n===r;var o=typeof e,i=typeof t;return"string"===o||"number"===o?"string"===i||"number"===i:"object"===i&&e.type===t.type&&e.key===t.key}e.exports=n},function(e,t,n){"use strict";function r(e){return m[e]}function o(e,t){return e&&null!=e.key?a(e.key):t.toString(36)}function i(e){return(""+e).replace(g,r)}function a(e){return"$"+i(e)}function s(e,t,n,r){var i=typeof e;if(("undefined"===i||"boolean"===i)&&(e=null),null===e||"string"===i||"number"===i||l.isValidElement(e))return n(r,e,""===t?f+o(e,0):t),1;var u,c,m=0,g=""===t?f:t+h;if(Array.isArray(e))for(var y=0;yt.name.toLowerCase()?1:0}),o=o.sort(function(e,t){return e.name.toLowerCase()t.name.toLowerCase()?1:0}),t&&(n=n.reverse(),o=o.reverse()),[].concat(r(n),r(o))},t.sortObjectsBySize=function(e,t){var n=e.filter(function(e){return e.name.endsWith("/")}),o=e.filter(function(e){return!e.name.endsWith("/")});return o=o.sort(function(e,t){return e.size-t.size}),t&&(o=o.reverse()),[].concat(r(n),r(o))},t.sortObjectsByDate=function(e,t){var n=e.filter(function(e){return e.name.endsWith("/")}),o=e.filter(function(e){return!e.name.endsWith("/")});return o=o.sort(function(e,t){return new Date(e.lastModified).getTime()-new Date(t.lastModified).getTime()}),t&&(o=o.reverse()),[].concat(r(n),r(o))},t.pathSlice=function(e){e=e.replace(o.minioBrowserPrefix,"");var t="",n="";if(!e)return{bucket:n,prefix:t};var r=e.indexOf("/",1);return-1==r?(n=e.slice(1),{bucket:n,prefix:t}):(n=e.slice(1,r),t=e.slice(r+1),{bucket:n,prefix:t})},t.pathJoin=function(e,t){return t||(t=""),o.minioBrowserPrefix+"/"+e+"/"+t}},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var a=function(){function e(e,t){for(var n=0;nt.name.toLowerCase()?1:0}),o=o.sort(function(e,t){return e.name.toLowerCase()t.name.toLowerCase()?1:0}),t&&(n=n.reverse(),o=o.reverse()),[].concat(r(n),r(o))},t.sortObjectsBySize=function(e,t){var n=e.filter(function(e){return e.name.endsWith("/")}),o=e.filter(function(e){return!e.name.endsWith("/")});return o=o.sort(function(e,t){return e.size-t.size}),t&&(o=o.reverse()),[].concat(r(n),r(o))},t.sortObjectsByDate=function(e,t){var n=e.filter(function(e){return e.name.endsWith("/")}),o=e.filter(function(e){return!e.name.endsWith("/")});return o=o.sort(function(e,t){return new Date(e.lastModified).getTime()-new Date(t.lastModified).getTime()}),t&&(o=o.reverse()),[].concat(r(n),r(o))},t.pathSlice=function(e){e=e.replace(o.minioBrowserPrefix,"");var t="",n="";if(!e)return{bucket:n,prefix:t};var r=e.indexOf("/",1);return-1==r?(n=e.slice(1),{bucket:n,prefix:t}):(n=e.slice(1,r),t=e.slice(r+1),{bucket:n,prefix:t})},t.pathJoin=function(e,t){return t||(t=""),o.minioBrowserPrefix+"/"+e+"/"+t}},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var a=function(){function e(e,t){for(var n=0;no;o++)n[String.fromCharCode(o)]=o-32;for(var o=48;58>o;o++)n[o-48]=o;for(o=1;13>o;o++)n["f"+o]=o+111;for(o=0;10>o;o++)n["numpad "+o]=o+96;var i=t.names=t.title={};for(o in n)i[n[o]]=o;for(var a in r)n[a]=r[a]},function(e,t){function n(e,t){if("function"!=typeof e)throw new TypeError(r);return t=o(void 0===t?e.length-1:+t||0,0),function(){for(var n=arguments,r=-1,i=o(n.length-t,0),a=Array(i);++rr;)e=o(e)[t[r++]];return r&&r==i?e:void 0}}var o=n(19);e.exports=r},function(e,t,n){function r(e,t,n,s,u,l){return e===t?!0:null==e||null==t||!i(e)&&!a(t)?e!==e&&t!==t:o(e,t,r,n,s,u,l)}var o=n(297),i=n(25),a=n(30);e.exports=r},function(e,t,n){function r(e){return function(t){return null==t?void 0:o(t)[e]}}var o=n(19);e.exports=r},function(e,t,n){var r=n(144),o=r("length");e.exports=o},function(e,t){var n=function(){try{Object({toString:0}+"")}catch(e){return function(){return!1}}return function(e){return"function"!=typeof e.toString&&"string"==typeof(e+"")}}();e.exports=n},function(e,t){function n(e,t){return e="number"==typeof e||r.test(e)?+e:-1,t=null==t?o:t,e>-1&&e%1==0&&t>e}var r=/^\d+$/,o=9007199254740991;e.exports=n},function(e,t,n){function r(e,t){var n=typeof e;if("string"==n&&s.test(e)||"number"==n)return!0;if(o(e))return!1;var r=!a.test(e);return r||null!=t&&e in i(t)}var o=n(24),i=n(19),a=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,s=/^\w*$/;e.exports=r},function(e,t,n){function r(e){return e===e&&!o(e)}var o=n(25);e.exports=r},function(e,t,n){function r(e,t){e=o(e);for(var n=-1,r=t.length,i={};++ne.clientHeight}t.__esModule=!0,t["default"]=a;var s=n(56),u=r(s),l=n(38),c=r(l);e.exports=t["default"]},function(e,t){"use strict";function n(e,t,n,r){return"Invalid prop '"+t+"' of value '"+e[t]+"'"+(" supplied to '"+n+"'"+r)}function r(e){function t(t,n,r,o){return o=o||"<>",null!=n[r]?e(n,r,o):t?new Error("Required prop '"+r+"' was not specified in '"+o+"'."):void 0}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}t.__esModule=!0,t.errMsg=n,t.createChainableTypeChecker=r},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t,n){var r=s.errMsg(e,t,n,". Expected an Element ` + "`" + `type` + "`" + `");if("function"!=typeof e[t]){if(a["default"].isValidElement(e[t]))return new Error(r+", not an actual Element");if("string"!=typeof e[t])return new Error(r+" such as a tag name or return value of React.createClass(...)")}}t.__esModule=!0;var i=n(1),a=r(i),s=n(160);t["default"]=s.createChainableTypeChecker(o),e.exports=t["default"]},function(e,t){"use strict";function n(e,t,n,r){return"Invalid prop '"+t+"' of value '"+e[t]+"'"+(" supplied to '"+n+"'"+r)}function r(e){function t(t,n,r,o){return o=o||"<>",null!=n[r]?e(n,r,o):t?new Error("Required prop '"+r+"' was not specified in '"+o+"'."):void 0}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}t.__esModule=!0,t.errMsg=n,t.createChainableTypeChecker=r},function(e,t){"use strict";function n(e){return function(t,n,r){return null==t[n]?new Error("The prop '"+n+"' is required to make '"+r+"' accessible for users using assistive technologies such as screen readers"):e(t,n,r)}}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t,n){function r(e,t,n){var r=l[t];if("undefined"==typeof r&&(r=i(t)),r){if(void 0===n)return e.style[r];e.style[r]=c(r,n)}}function o(e,t){for(var n in t)t.hasOwnProperty(n)&&r(e,n,t[n])}function i(e){var t=u(e),n=s(t);return l[t]=l[e]=l[n]=n,n}function a(){2===arguments.length?o(arguments[0],arguments[1]):r(arguments[0],arguments[1],arguments[2])}var s=n(340),u=n(341),l={"float":"cssFloat"},c=n(339);e.exports=a,e.exports.set=a,e.exports.get=function(e,t){return Array.isArray(t)?t.reduce(function(t,n){return t[n]=r(e,n||""),t},{}):r(e,t||"")}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e){return e.displayName||e.name||"Component"}function s(e,t,n){function s(e,t){var n=e.getState(),r=C?N(n,t):N(n);return y(h(r),"` + "`" + `mapStateToProps` + "`" + ` must return an object. Instead received %s.",r),r}function l(e,t){var n=e.dispatch,r=D?w(n,t):w(n);return y(h(r),"` + "`" + `mapDispatchToProps` + "`" + ` must return an object. Instead received %s.",r),r}function x(e,t,n){var r=I(e,t,n);return y(h(r),"` + "`" + `mergeProps` + "`" + ` must return an object. Instead received %s.",r),r}var E=arguments.length<=3||void 0===arguments[3]?{}:arguments[3],A=Boolean(e),N=e||v,w=h(t)?m(t):t||M,I=n||T,C=1!==N.length,D=1!==w.length,S=E.pure,k=void 0===S?!0:S,L=E.withRef,O=void 0===L?!1:L,P=b++;return function(e){var t=function(t){function n(e,i){r(this,n);var a=o(this,t.call(this,e,i));a.version=P,a.store=e.store||i.store,y(a.store,'Could not find "store" in either the context or '+('props of "'+a.constructor.displayName+'". ')+"Either wrap the root component in a , "+('or explicitly pass "store" as a prop to "'+a.constructor.displayName+'".'));var s=a.store.getState();return a.state={storeState:s},a.clearCache(),a}return i(n,t),n.prototype.shouldComponentUpdate=function(){return!k||this.haveOwnPropsChanged||this.hasStoreStateChanged},n.prototype.updateStatePropsIfNeeded=function(){var e=s(this.store,this.props);return this.stateProps&&f(e,this.stateProps)?!1:(this.stateProps=e,!0)},n.prototype.updateDispatchPropsIfNeeded=function(){var e=l(this.store,this.props);return this.dispatchProps&&f(e,this.dispatchProps)?!1:(this.dispatchProps=e,!0)},n.prototype.updateMergedProps=function(){this.mergedProps=x(this.stateProps,this.dispatchProps,this.props)},n.prototype.isSubscribed=function(){return"function"==typeof this.unsubscribe},n.prototype.trySubscribe=function(){A&&!this.unsubscribe&&(this.unsubscribe=this.store.subscribe(this.handleChange.bind(this)),this.handleChange())},n.prototype.tryUnsubscribe=function(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null)},n.prototype.componentDidMount=function(){this.trySubscribe()},n.prototype.componentWillReceiveProps=function(e){k&&f(e,this.props)||(this.haveOwnPropsChanged=!0)},n.prototype.componentWillUnmount=function(){this.tryUnsubscribe(),this.clearCache()},n.prototype.clearCache=function(){this.dispatchProps=null,this.stateProps=null,this.mergedProps=null,this.haveOwnPropsChanged=!0,this.hasStoreStateChanged=!0,this.renderedElement=null},n.prototype.handleChange=function(){if(this.unsubscribe){var e=this.state.storeState,t=this.store.getState();k&&e===t||(this.hasStoreStateChanged=!0,this.setState({storeState:t}))}},n.prototype.getWrappedInstance=function(){return y(O,"To access the wrapped instance, you need to specify { withRef: true } as the fourth argument of the connect() call."),this.refs.wrappedInstance},n.prototype.render=function(){var t=this.haveOwnPropsChanged,n=this.hasStoreStateChanged,r=this.renderedElement;this.haveOwnPropsChanged=!1,this.hasStoreStateChanged=!1;var o=!0,i=!0;k&&r&&(o=n||t&&C,i=t&&D);var a=!1,s=!1;o&&(a=this.updateStatePropsIfNeeded()),i&&(s=this.updateDispatchPropsIfNeeded());var l=!0;return a||s||t?this.updateMergedProps():l=!1,!l&&r?r:(O?this.renderedElement=d(e,u({},this.mergedProps,{ref:"wrappedInstance"})):this.renderedElement=d(e,this.mergedProps),this.renderedElement)},n}(c);return t.displayName="Connect("+a(e)+")",t.WrappedComponent=e,t.contextTypes={store:p},t.propTypes={store:p},g(t,e)}}var u=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){return 0===e.button}function a(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function s(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0}function u(e,t){var n=t.query,r=t.hash,o=t.state;return n||r||o?{pathname:e,query:n,hash:r,state:o}:e}t.__esModule=!0;var l=Object.assign||function(e){for(var t=1;t=0;r--){var o=e[r],i=o.path||"";if(n=i.replace(/\/*$/,"/")+n,0===i.indexOf("/"))break}return"/"+n}},propTypes:{path:p,from:p,to:p.isRequired,query:f,state:f,onEnter:c.falsy,children:c.falsy},render:function(){s["default"](!1)}});t["default"]=h,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(1),i=r(o),a=n(16),s=r(a),u=n(26),l=n(31),c=i["default"].PropTypes,d=c.string,p=c.func,f=i["default"].createClass({displayName:"Route",statics:{createRouteFromReactElement:u.createRouteFromReactElement},propTypes:{path:d,component:l.component,components:l.components,getComponent:p,getComponents:p},render:function(){s["default"](!1)}});t["default"]=f,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){return!e||!e.__v2_compatible__}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;t=0&&0===window.sessionStorage.length)return;throw n}}function a(e){var t=void 0;try{t=window.sessionStorage.getItem(o(e))}catch(n){if(n.name===c)return null}if(t)try{return JSON.parse(t)}catch(n){}return null}t.__esModule=!0,t.saveState=i,t.readState=a;var s=n(20),u=(r(s),"@@History/"),l=["QuotaExceededError","QUOTA_EXCEEDED_ERR"],c="SecurityError"},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){function t(e){return u.canUseDOM?void 0:s["default"](!1),n.listen(e)}var n=d["default"](i({getUserConfirmation:l.getUserConfirmation},e,{go:l.go}));return i({},n,{listen:t})}t.__esModule=!0;var i=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){return function(){function t(e){return M&&null==e.basename&&(0===e.pathname.indexOf(M)?(e.pathname=e.pathname.substring(M.length),e.basename=M,""===e.pathname&&(e.pathname="/")):e.basename=""),e}function n(e){if(!M)return e;"string"==typeof e&&(e=u.parsePath(e));var t=e.pathname,n="/"===M.slice(-1)?M:M+"/",r="/"===t.charAt(0)?t.slice(1):t,o=n+r;return a({},e,{pathname:o})}function r(e){return b.listenBefore(function(n,r){c["default"](e,t(n),r)})}function i(e){return b.listen(function(n){e(t(n))})}function l(e){b.push(n(e))}function d(e){b.replace(n(e))}function f(e){return b.createPath(n(e))}function h(e){return b.createHref(n(e))}function m(e){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];return t(b.createLocation.apply(b,[n(e)].concat(o)))}function g(e,t){"string"==typeof t&&(t=u.parsePath(t)),l(a({state:e},t))}function y(e,t){"string"==typeof t&&(t=u.parsePath(t)),d(a({state:e},t))}var v=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],M=v.basename,T=o(v,["basename"]),b=e(T);if(null==M&&s.canUseDOM){var x=document.getElementsByTagName("base")[0];x&&(M=u.extractPath(x.href))}return a({},b,{listenBefore:r,listen:i,push:l,replace:d,createPath:f,createHref:h,createLocation:m,pushState:p["default"](g,"pushState is deprecated; use push instead"),replaceState:p["default"](y,"replaceState is deprecated; use replace instead")})}}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;t=e.childNodes.length?null:e.childNodes.item(n);e.insertBefore(t,r)}var o=n(383),i=n(200),a=n(17),s=n(70),u=n(109),l=n(2),c={dangerouslyReplaceNodeWithMarkup:o.dangerouslyReplaceNodeWithMarkup,updateTextContent:u,processUpdates:function(e,t){for(var n,a=null,c=null,d=0;d-1?void 0:a(!1),!l.plugins[n]){t.extractEvents?void 0:a(!1),l.plugins[n]=t;var r=t.eventTypes;for(var i in r)o(r[i],t,i)?void 0:a(!1)}}}function o(e,t,n){l.eventNameDispatchConfigs.hasOwnProperty(n)?a(!1):void 0,l.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var o in r)if(r.hasOwnProperty(o)){var s=r[o];i(s,t,n)}return!0}return e.registrationName?(i(e.registrationName,t,n),!0):!1}function i(e,t,n){l.registrationNameModules[e]?a(!1):void 0,l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=n(2),s=null,u={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},injectEventPluginOrder:function(e){s?a(!1):void 0,s=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];u.hasOwnProperty(n)&&u[n]===o||(u[n]?a(!1):void 0,u[n]=o,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;for(var n in t.phasedRegistrationNames)if(t.phasedRegistrationNames.hasOwnProperty(n)){var r=l.registrationNameModules[t.phasedRegistrationNames[n]];if(r)return r}return null},_resetEventPlugins:function(){s=null;for(var e in u)u.hasOwnProperty(e)&&delete u[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};e.exports=l},function(e,t,n){"use strict";function r(e){return(""+e).replace(T,"//")}function o(e,t){this.func=e,this.context=t,this.count=0}function i(e,t,n){var r=e.func,o=e.context;r.call(o,t,e.count++)}function a(e,t,n){if(null==e)return e;var r=o.getPooled(t,n);y(e,i,r),o.release(r)}function s(e,t,n,r){this.result=e,this.keyPrefix=t,this.func=n,this.context=r,this.count=0}function u(e,t,n){var o=e.result,i=e.keyPrefix,a=e.func,s=e.context,u=a.call(s,t,e.count++);Array.isArray(u)?l(u,o,n,g.thatReturnsArgument):null!=u&&(m.isValidElement(u)&&(u=m.cloneAndReplaceKey(u,i+(u!==t?r(u.key||"")+"/":"")+n)),o.push(u))}function l(e,t,n,o,i){var a="";null!=n&&(a=r(n)+"/");var l=s.getPooled(t,a,o,i);y(e,u,l),s.release(l)}function c(e,t,n){if(null==e)return e;var r=[];return l(e,r,null,t,n),r}function d(e,t,n){return null}function p(e,t){return y(e,d,null)}function f(e){var t=[];return l(e,t,null,g.thatReturnsArgument),t}var h=n(27),m=n(13),g=n(21),y=n(111),v=h.twoArgumentPooler,M=h.fourArgumentPooler,T=/\/(?!\/)/g;o.prototype.destructor=function(){this.func=null,this.context=null,this.count=0},h.addPoolingTo(o,v),s.prototype.destructor=function(){this.result=null,this.keyPrefix=null,this.func=null,this.context=null,this.count=0},h.addPoolingTo(s,M);var b={forEach:a,map:c,mapIntoWithKeyPrefixInternal:l,count:p,toArray:f};e.exports=b},function(e,t,n){"use strict";function r(e,t){var n=x.hasOwnProperty(t)?x[t]:null;A.hasOwnProperty(t)&&(n!==T.OVERRIDE_BASE?g(!1):void 0),e.hasOwnProperty(t)&&(n!==T.DEFINE_MANY&&n!==T.DEFINE_MANY_MERGED?g(!1):void 0)}function o(e,t){if(t){"function"==typeof t?g(!1):void 0,p.isValidElement(t)?g(!1):void 0;var n=e.prototype;t.hasOwnProperty(M)&&E.mixins(e,t.mixins);for(var o in t)if(t.hasOwnProperty(o)&&o!==M){var i=t[o];if(r(n,o),E.hasOwnProperty(o))E[o](e,i);else{var a=x.hasOwnProperty(o),l=n.hasOwnProperty(o),c="function"==typeof i,d=c&&!a&&!l&&t.autobind!==!1;if(d)n.__reactAutoBindMap||(n.__reactAutoBindMap={}),n.__reactAutoBindMap[o]=i,n[o]=i;else if(l){var f=x[o];!a||f!==T.DEFINE_MANY_MERGED&&f!==T.DEFINE_MANY?g(!1):void 0,f===T.DEFINE_MANY_MERGED?n[o]=s(n[o],i):f===T.DEFINE_MANY&&(n[o]=u(n[o],i))}else n[o]=i}}}}function i(e,t){if(t)for(var n in t){var r=t[n];if(t.hasOwnProperty(n)){var o=n in E;o?g(!1):void 0;var i=n in e;i?g(!1):void 0,e[n]=r}}}function a(e,t){e&&t&&"object"==typeof e&&"object"==typeof t?void 0:g(!1);for(var n in t)t.hasOwnProperty(n)&&(void 0!==e[n]?g(!1):void 0,e[n]=t[n]);return e}function s(e,t){return function(){var n=e.apply(this,arguments),r=t.apply(this,arguments);if(null==n)return r;if(null==r)return n;var o={};return a(o,n),a(o,r),o}}function u(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}}function l(e,t){var n=t.bind(e);return n}function c(e){for(var t in e.__reactAutoBindMap)if(e.__reactAutoBindMap.hasOwnProperty(t)){var n=e.__reactAutoBindMap[t];e[t]=l(e,n)}}var d=n(187),p=n(13),f=(n(65),n(64),n(202)),h=n(3),m=n(55),g=n(2),y=n(71),v=n(28),M=(n(4),v({mixins:null})),T=y({DEFINE_ONCE:null,DEFINE_MANY:null,OVERRIDE_BASE:null,DEFINE_MANY_MERGED:null}),b=[],x={mixins:T.DEFINE_MANY,statics:T.DEFINE_MANY,propTypes:T.DEFINE_MANY,contextTypes:T.DEFINE_MANY,childContextTypes:T.DEFINE_MANY,getDefaultProps:T.DEFINE_MANY_MERGED,getInitialState:T.DEFINE_MANY_MERGED,getChildContext:T.DEFINE_MANY_MERGED,render:T.DEFINE_ONCE,componentWillMount:T.DEFINE_MANY,componentDidMount:T.DEFINE_MANY,componentWillReceiveProps:T.DEFINE_MANY,shouldComponentUpdate:T.DEFINE_ONCE,componentWillUpdate:T.DEFINE_MANY,componentDidUpdate:T.DEFINE_MANY,componentWillUnmount:T.DEFINE_MANY,updateComponent:T.OVERRIDE_BASE},E={displayName:function(e,t){e.displayName=t},mixins:function(e,t){if(t)for(var n=0;n"+s+""},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=""+e;if(n!==this._stringText){this._stringText=n;var o=a.getNode(this._rootNodeID);r.updateTextContent(o,n)}}},unmountComponent:function(){i.unmountIDFromEnvironment(this._rootNodeID)}}),e.exports=c},function(e,t,n){"use strict";function r(){this.reinitializeTransaction()}var o=n(18),i=n(67),a=n(3),s=n(21),u={initialize:s,close:function(){p.isBatchingUpdates=!1}},l={initialize:s,close:o.flushBatchedUpdates.bind(o)},c=[l,u];a(r.prototype,i.Mixin,{getTransactionWrappers:function(){return c}});var d=new r,p={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,o,i){var a=p.isBatchingUpdates;p.isBatchingUpdates=!0,a?e(t,n,r,o,i):d.perform(e,null,t,n,r,o,i)}};e.exports=p},function(e,t,n){"use strict";function r(){if(!N){N=!0,y.EventEmitter.injectReactEventListener(g),y.EventPluginHub.injectEventPluginOrder(s),y.EventPluginHub.injectInstanceHandle(v),y.EventPluginHub.injectMount(M),y.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:E,EnterLeaveEventPlugin:u,ChangeEventPlugin:i,SelectEventPlugin:b,BeforeInputEventPlugin:o}),y.NativeComponent.injectGenericComponentClass(h),y.NativeComponent.injectTextComponentClass(m),y.Class.injectMixin(d),y.DOMProperty.injectDOMPropertyConfig(c),y.DOMProperty.injectDOMPropertyConfig(A),y.EmptyComponent.injectEmptyComponent("noscript"),y.Updates.injectReconcileTransaction(T),y.Updates.injectBatchingStrategy(f),y.RootIndex.injectCreateReactRootIndex(l.canUseDOM?a.createReactRootIndex:x.createReactRootIndex),y.Component.injectEnvironment(p)}}var o=n(379),i=n(381),a=n(382),s=n(384),u=n(385),l=n(9),c=n(388),d=n(390),p=n(97),f=n(192),h=n(394),m=n(191),g=n(402),y=n(403),v=n(43),M=n(11),T=n(407),b=n(413),x=n(414),E=n(415),A=n(412),N=!1;e.exports={inject:r}},function(e,t,n){"use strict";function r(){if(d.current){var e=d.current.getName();if(e)return" Check the render method of ` + "`" + `"+e+"` + "`" + `."}return""}function o(e,t){if(e._store&&!e._store.validated&&null==e.key){e._store.validated=!0;i("uniqueKey",e,t)}}function i(e,t,n){var o=r();if(!o){var i="string"==typeof n?n:n.displayName||n.name;i&&(o=" Check the top-level render call using <"+i+">.")}var a=h[e]||(h[e]={});if(a[o])return null;a[o]=!0;var s={parentOrOwner:o,url:" See https://fb.me/react-warning-keys for more information.",childOwner:null};return t&&t._owner&&t._owner!==d.current&&(s.childOwner=" It was passed a child from "+t._owner.getName()+"."),s}function a(e,t){if("object"==typeof e)if(Array.isArray(e))for(var n=0;n/,i={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(e){var t=r(e);return e.replace(o," "+i.CHECKSUM_ATTR_NAME+'="'+t+'"$&')},canReuseMarkup:function(e,t){var n=t.getAttribute(i.CHECKSUM_ATTR_NAME);n=n&&parseInt(n,10);var o=r(e);return o===n}};e.exports=i},function(e,t,n){"use strict";var r=n(71),o=r({INSERT_MARKUP:null,MOVE_EXISTING:null,REMOVE_NODE:null,SET_MARKUP:null,TEXT_CONTENT:null});e.exports=o},function(e,t,n){"use strict";function r(e){if("function"==typeof e.type)return e.type;var t=e.type,n=d[t];return null==n&&(d[t]=n=l(t)),n}function o(e){return c?void 0:u(!1),new c(e.type,e.props)}function i(e){return new p(e)}function a(e){return e instanceof p}var s=n(3),u=n(2),l=null,c=null,d={},p=null,f={injectGenericComponentClass:function(e){c=e},injectTextComponentClass:function(e){p=e},injectComponentClasses:function(e){s(d,e)}},h={getComponentClassForElement:r,createInternalComponent:o,createInstanceForText:i,isTextComponent:a,injection:f};e.exports=h},function(e,t,n){"use strict";function r(e,t){}var o=(n(4),{isMounted:function(e){return!1},enqueueCallback:function(e,t){},enqueueForceUpdate:function(e){r(e,"forceUpdate")},enqueueReplaceState:function(e,t){r(e,"replaceState")},enqueueSetState:function(e,t){r(e,"setState")},enqueueSetProps:function(e,t){r(e,"setProps")},enqueueReplaceProps:function(e,t){r(e,"replaceProps")}});e.exports=o},function(e,t,n){"use strict";function r(e){function t(t,n,r,o,i,a){if(o=o||x,a=a||r,null==n[r]){var s=M[i];return t?new Error("Required "+s+" ` + "`" + `"+a+"` + "`" + ` was not specified in "+("` + "`" + `"+o+"` + "`" + `.")):null}return e(n,r,o,i,a)}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}function o(e){function t(t,n,r,o,i){var a=t[n],s=m(a);if(s!==e){var u=M[o],l=g(a);return new Error("Invalid "+u+" ` + "`" + `"+i+"` + "`" + ` of type "+("` + "`" + `"+l+"` + "`" + ` supplied to ` + "`" + `"+r+"` + "`" + `, expected ")+("` + "`" + `"+e+"` + "`" + `."))}return null}return r(t)}function i(){return r(T.thatReturns(null))}function a(e){function t(t,n,r,o,i){var a=t[n];if(!Array.isArray(a)){var s=M[o],u=m(a);return new Error("Invalid "+s+" ` + "`" + `"+i+"` + "`" + ` of type "+("` + "`" + `"+u+"` + "`" + ` supplied to ` + "`" + `"+r+"` + "`" + `, expected an array."))}for(var l=0;l>"}var v=n(13),M=n(64),T=n(21),b=n(106),x="<>",E={array:o("array"),bool:o("boolean"),func:o("function"),number:o("number"),object:o("object"),string:o("string"),any:i(),arrayOf:a,element:s(),instanceOf:u,node:p(),objectOf:c,oneOf:l,oneOfType:d,shape:f};e.exports=E},function(e,t){"use strict";var n={injectCreateReactRootIndex:function(e){r.createReactRootIndex=e}},r={createReactRootIndex:null,injection:n};e.exports=r},function(e,t){"use strict";var n={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(e){n.currentScrollLeft=e.x,n.currentScrollTop=e.y}};e.exports=n},function(e,t,n){"use strict";function r(e,t){if(null==t?o(!1):void 0,null==e)return t;var n=Array.isArray(e),r=Array.isArray(t);return n&&r?(e.push.apply(e,t),e):n?(e.push(t),e):r?[e].concat(t):[e,t]}var o=n(2);e.exports=r},function(e,t){"use strict";var n=function(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)};e.exports=n},function(e,t,n){"use strict";function r(){return!i&&o.canUseDOM&&(i="textContent"in document.documentElement?"textContent":"innerText"),i}var o=n(9),i=null;e.exports=r},function(e,t){"use strict";function n(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&r[e.type]||"textarea"===t)}var r={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};e.exports=n},function(e,t,n){"use strict";var r=n(21),o={listen:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!1),{remove:function(){e.removeEventListener(t,n,!1)}}):e.attachEvent?(e.attachEvent("on"+t,n),{remove:function(){e.detachEvent("on"+t,n)}}):void 0},capture:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!0),{remove:function(){e.removeEventListener(t,n,!0)}}):{remove:r}},registerDefault:function(){}};e.exports=o},function(e,t,n){"use strict";function r(e,t){var n=!0;e:for(;n;){var r=e,i=t;if(n=!1,r&&i){if(r===i)return!0;if(o(r))return!1;if(o(i)){e=r,t=i.parentNode,n=!0;continue e}return r.contains?r.contains(i):r.compareDocumentPosition?!!(16&r.compareDocumentPosition(i)):!1}return!1}}var o=n(441);e.exports=r},function(e,t){"use strict";function n(e){try{e.focus()}catch(t){}}e.exports=n},function(e,t){"use strict";function n(){if("undefined"==typeof document)return null;try{return document.activeElement||document.body}catch(e){return document.body}}e.exports=n},function(e,t,n){"use strict";function r(e){return a?void 0:i(!1),p.hasOwnProperty(e)||(e="*"),s.hasOwnProperty(e)||("*"===e?a.innerHTML="":a.innerHTML="<"+e+">",s[e]=!a.firstChild),s[e]?p[e]:null}var o=n(9),i=n(2),a=o.canUseDOM?document.createElement("div"):null,s={},u=[1,'"],l=[1,"","
"],c=[3,"","
"],d=[1,'',""],p={"*":[1,"?
","
"],area:[1,"",""],col:[2,"","
"],legend:[1,"
","
"],param:[1,"",""],tr:[2,"","
"],optgroup:u,option:u,caption:l,colgroup:l,tbody:l,tfoot:l,thead:l,td:c,th:c},f=["circle","clipPath","defs","ellipse","g","image","line","linearGradient","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","text","tspan"];f.forEach(function(e){p[e]=d,s[e]=!0}),e.exports=r},function(e,t){"use strict";function n(e,t){if(e===t)return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),o=Object.keys(t);if(n.length!==o.length)return!1;for(var i=r.bind(t),a=0;an;n++)t[n]=arguments[n];return function(e){return function(n,r,o){var a=e(n,r,o),u=a.dispatch,l=[],c={getState:a.getState,dispatch:function(e){return u(e)}};return l=t.map(function(e){return e(c)}),u=s["default"].apply(void 0,l)(a.dispatch),i({},a,{dispatch:u})}}}t.__esModule=!0;var i=Object.assign||function(e){for(var t=1;tn;n++)t[n]=arguments[n];return function(){if(0===t.length)return arguments[0];var e=t[t.length-1],n=t.slice(0,-1);return n.reduceRight(function(e,t){return t(e)},e.apply(void 0,arguments))}}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){"use strict";function n(e){if(!e||"object"!=typeof e)return!1;var t="function"==typeof e.constructor?Object.getPrototypeOf(e):Object.prototype;if(null===t)return!0;var n=t.constructor;return"function"==typeof n&&n instanceof n&&r(n)===o}t.__esModule=!0,t["default"]=n;var r=function(e){return Function.prototype.toString.call(e)},o=r(Object);e.exports=t["default"]},function(e,t){"use strict";function n(e,t){return Object.keys(e).reduce(function(n,r){return n[r]=t(e[r],r),n},{})}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){"use strict";function n(e){"undefined"!=typeof console&&"function"==typeof console.error&&console.error(e);try{throw new Error(e)}catch(t){}}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){e.exports="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmlld2JveD0iMCAwIDE2MCAyNTYiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzMwOTIiCiAgIGhlaWdodD0iMjE4LjE0ODQ0IgogICB3aWR0aD0iMTM3Ij4KICA8ZGVmcwogICAgIGlkPSJkZWZzMzA5NCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEzMDk3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuOTk5OTk5NjcsLTk4Mi44NTE0OSkiCiAgICAgaWQ9ImxheWVyMSI+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMTEyNTg2LDAsMCwxLjAxMTI1ODYsNS40NjAzNzMyLC0xMy4yMjM3MTQpIgogICAgICAgaWQ9Imc0MTQ0Ij4KICAgICAgPGcKICAgICAgICAgaWQ9Imc0MTQwIj4KICAgICAgICA8ZwogICAgICAgICAgIHN0eWxlPSJpbWFnZS1yZW5kZXJpbmc6YXV0byIKICAgICAgICAgICBpZD0ibWluaW8iCiAgICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMDAwMDIzLDAsMCwwLjk5OTk5Nzk5LC03MzkuMzE2NDYsMjk1LjIyNjkpIj4KICAgICAgICAgIDx0aXRsZQogICAgICAgICAgICAgaWQ9InRpdGxlMzMzNyI+TWluaW8gTG9nbzwvdGl0bGU+CiAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgZD0ibSA4MDMuNDI5MDMsODAxLjgwODEzIGMgMTIuNDA4MDIsNC4xNzA2NyAyNy4wNDk5LDkuMTE2NjUgMzcuOTUxODYsMTIuODA5MDYgLTEyLjAxMjk1LC0yMS4yMDY4MyAtMjcuODQzMDUsLTM0LjExNjg3IC0zNy45NTE4NiwtNDAuNzg1NzggbCAwLDI3Ljk3NjcyIHogbSAwLDkzLjcyMTEzIC0xNC4yMjMwMyw4Ljk2MjE3IDAsLTkyLjQ1ODY0IGMgLTEuNTI5ODUsLTAuNTEzOSAtMi45Nzk0OCwtMC45OTgxIC00LjMzNDA1LC0xLjQ1MjU5IC0xOS45ODU5MywtNi42NzE4OSAtMzIuNzIwNywtMTcuOTU3MDMgLTM1Ljg1MTY4LC0zMS43NzkwNCAtMi41NDU3NywtMTEuMjEzODYgMS41NTA2NCwtMjMuMDIxODQgMTEuMjQ2NTQsLTMyLjM5NjkxIDguODQ5MjksLTguNTUyMjUgMjEuMjI3NjEsLTE4LjM5OTY0IDMxLjE3MzA0LC0yNi4zMTYxOSAzLjYwMzI5LC0yLjg2NjU4IDYuNzMxMjksLTUuMzE3MyA5LjIwMjgsLTcuMzk2NjkgMi4zMTQwNiwtMS45Mzk3NyAxLjYxNTk4LC00Ljk1NDg4IDAuNTcwMzMsLTYuMjE0NDEgLTEuNzQwNzMsLTIuMDkxMjcgLTQuNjE5MjEsLTEuNzQ2NjkgLTYuNTYxOTUsLTAuMzIzNzkgLTAuMTAzOTgsMC4wODAyIC01LjY1NTk1LDQuNDA4MzIgLTUuNjU1OTUsNC40MDgzMiBsIC04LjU4MTk1LC0xMS41NzAzMyBjIDAsMCA1LjYwODQzLC00LjE0MDk2IDUuODIyMywtNC4zMDEzNyA4LjM5Nzc3LC02LjE1NSAxOS41NDAzNCwtNC45ODc1OCAyNS45MjQwNiwyLjcxNTA5IDMuMTkwMzksMy44NDA5MyA0LjY4NDU5LDguNjk3NzkgNC4yMDkyOSwxMy42NzA1MSAtMC40NzIzMiw0Ljk1NDkgLTIuODQ1NzksOS40MzE1MyAtNi42ODA3OCwxMi41OTIyIC0yLjU4NDM5LDIuMTI5ODggLTUuNzM5MTIsNC42NDI5OCAtOS4zOTI5MSw3LjU0NTIyIC05LjcwNzc5LDcuNzI2NDEgLTIxLjc4OTA1LDE3LjMzOTE1IC0zMC4xNDIyNiwyNS40MTkwNyAtNi4xMjUzLDUuOTIzMyAtOC43MDY3MSwxMi42NzgzNCAtNy4yNjg5NiwxOS4wMjM0NSAxLjk4NzMsOC43NTQyNCAxMS4zMzI2OCwxNi4zNDEwNSAyNi4zMjIxMywyMS4zNzkxMSBsIDAsLTQ2LjIyNDg2IGMgNDAuMjk1NjMsMTMuNjIyOTggNjguNzYyNDgsNjEuMjIzMjEgNzguMjA1ODksODcuNjQwMzkgMCwwIC00MS43NjMwOCwtMTQuMTU3NjggLTYzLjk4Mjg2LC0yMS42NDA1MSBsIDAsNzguNzE5OCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNmOGY4Zjg7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IiAvPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgIGQ9Im0gODAzLjQyOTAzLDgyNi4xMjUxMyAtMTQuMjIzMDMsLTQuNzgyNjEgMCwtOS4zMDk3MyAxNC4yMjMwMyw0Ljc3NjY3IDAsOS4zMTU2NyIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNjZGNjY2E7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBpZD0icGF0aDUwIiAvPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgIGQ9Im0gNzM0Ljc1NTY2LDc0NS4wNjE1NSBjIC0wLjIzNDY5LDAuMTY2MzYgLTAuNTQ5NTYsMC4xNDg1MyAtMC43MzA3NywtMC4wNzQzIC0wLjE3ODIzLC0wLjIyNTc2IC0wLjEzMDYzLC0wLjUzNzY2IDAuMDgwMiwtMC43MzM3MyA0LjkzMTEzLC00LjUxNTI1IDI0LjQ1NjYxLC0yMy44Njg0NCA0Ni4zMDgwNSwtNDUuMjYyNCBsIDguNTgxOTMsMTEuNTcwMzMgYyAwLDAgLTUzLjU0MTM1LDM0LjAxMjg4IC01NC4yMzk0MiwzNC41MDAwNyIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNmMTQ2MjE7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiCiAgICAgICAgICAgICBpZD0icGF0aDUyIiAvPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg=="; -},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t,n){function r(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function o(e,t,n){if(e&&l(e)&&e instanceof r)return e;var o=new r;return o.parse(e,t,n),o}function i(e){return u(e)&&(e=o(e)),e instanceof r?e.format():r.prototype.format.call(e)}function a(e,t){return o(e,!1,!0).resolve(t)}function s(e,t){return e?o(e,!1,!0).resolveObject(t):t}function u(e){return"string"==typeof e}function l(e){return"object"==typeof e&&null!==e}function c(e){return null===e}function d(e){return null==e}var p=n(461);t.parse=o,t.resolve=a,t.resolveObject=s,t.format=i,t.Url=r;var f=/^([a-z0-9.+-]+:)/i,h=/:[0-9]*$/,m=["<",">",'"',"` + "`" + `"," ","\r","\n"," "],g=["{","}","|","\\","^","` + "`" + `"].concat(m),y=["'"].concat(g),v=["%","/","?",";","#"].concat(y),M=["/","?","#"],T=255,b=/^[a-z0-9A-Z_-]{0,63}$/,x=/^([a-z0-9A-Z_-]{0,63})(.*)$/,E={javascript:!0,"javascript:":!0},A={javascript:!0,"javascript:":!0},N={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},w=n(464);r.prototype.parse=function(e,t,n){if(!u(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var r=e;r=r.trim();var o=f.exec(r);if(o){o=o[0];var i=o.toLowerCase();this.protocol=i,r=r.substr(o.length)}if(n||o||r.match(/^\/\/[^@\/]+@[^@\/]+/)){var a="//"===r.substr(0,2);!a||o&&A[o]||(r=r.substr(2),this.slashes=!0)}if(!A[o]&&(a||o&&!N[o])){for(var s=-1,l=0;lc)&&(s=c)}var d,h;h=-1===s?r.lastIndexOf("@"):r.lastIndexOf("@",s),-1!==h&&(d=r.slice(0,h),r=r.slice(h+1),this.auth=decodeURIComponent(d)),s=-1;for(var l=0;lc)&&(s=c)}-1===s&&(s=r.length),this.host=r.slice(0,s),r=r.slice(s),this.parseHost(),this.hostname=this.hostname||"";var m="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!m)for(var g=this.hostname.split(/\./),l=0,I=g.length;I>l;l++){var C=g[l];if(C&&!C.match(b)){for(var D="",S=0,k=C.length;k>S;S++)D+=C.charCodeAt(S)>127?"x":C[S];if(!D.match(b)){var L=g.slice(0,l),O=g.slice(l+1),P=C.match(x);P&&(L.push(P[1]),O.unshift(P[2])),O.length&&(r="/"+O.join(".")+r),this.hostname=L.join(".");break}}}if(this.hostname.length>T?this.hostname="":this.hostname=this.hostname.toLowerCase(),!m){for(var j=this.hostname.split("."),z=[],l=0;ll;l++){var B=y[l],W=encodeURIComponent(B);W===B&&(W=escape(B)),r=r.split(B).join(W)}var F=r.indexOf("#");-1!==F&&(this.hash=r.substr(F),r=r.slice(0,F));var V=r.indexOf("?");if(-1!==V?(this.search=r.substr(V),this.query=r.substr(V+1),t&&(this.query=w.parse(this.query)),r=r.slice(0,V)):t&&(this.search="",this.query={}),r&&(this.pathname=r),N[i]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var U=this.pathname||"",R=this.search||"";this.path=U+R}return this.href=this.format(),this},r.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",r=this.hash||"",o=!1,i="";this.host?o=e+this.host:this.hostname&&(o=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(o+=":"+this.port)),this.query&&l(this.query)&&Object.keys(this.query).length&&(i=w.stringify(this.query));var a=this.search||i&&"?"+i||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||N[t])&&o!==!1?(o="//"+(o||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):o||(o=""),r&&"#"!==r.charAt(0)&&(r="#"+r),a&&"?"!==a.charAt(0)&&(a="?"+a),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),a=a.replace("#","%23"),t+o+n+a+r},r.prototype.resolve=function(e){return this.resolveObject(o(e,!1,!0)).format()},r.prototype.resolveObject=function(e){if(u(e)){var t=new r;t.parse(e,!1,!0),e=t}var n=new r;if(Object.keys(this).forEach(function(e){n[e]=this[e]},this),n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol)return Object.keys(e).forEach(function(t){"protocol"!==t&&(n[t]=e[t])}),N[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n;if(e.protocol&&e.protocol!==n.protocol){if(!N[e.protocol])return Object.keys(e).forEach(function(t){n[t]=e[t]}),n.href=n.format(),n;if(n.protocol=e.protocol,e.host||A[e.protocol])n.pathname=e.pathname;else{for(var o=(e.pathname||"").split("/");o.length&&!(e.host=o.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==o[0]&&o.unshift(""),o.length<2&&o.unshift(""),n.pathname=o.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var i=n.pathname||"",a=n.search||"";n.path=i+a}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var s=n.pathname&&"/"===n.pathname.charAt(0),l=e.host||e.pathname&&"/"===e.pathname.charAt(0),p=l||s||n.host&&e.pathname,f=p,h=n.pathname&&n.pathname.split("/")||[],o=e.pathname&&e.pathname.split("/")||[],m=n.protocol&&!N[n.protocol];if(m&&(n.hostname="",n.port=null,n.host&&(""===h[0]?h[0]=n.host:h.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===o[0]?o[0]=e.host:o.unshift(e.host)),e.host=null),p=p&&(""===o[0]||""===h[0])),l)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,h=o;else if(o.length)h||(h=[]),h.pop(),h=h.concat(o),n.search=e.search,n.query=e.query;else if(!d(e.search)){if(m){n.hostname=n.host=h.shift();var g=n.host&&n.host.indexOf("@")>0?n.host.split("@"):!1;g&&(n.auth=g.shift(),n.host=n.hostname=g.shift())}return n.search=e.search,n.query=e.query,c(n.pathname)&&c(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!h.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var y=h.slice(-1)[0],v=(n.host||e.host)&&("."===y||".."===y)||""===y,M=0,T=h.length;T>=0;T--)y=h[T],"."==y?h.splice(T,1):".."===y?(h.splice(T,1),M++):M&&(h.splice(T,1),M--);if(!p&&!f)for(;M--;M)h.unshift("..");!p||""===h[0]||h[0]&&"/"===h[0].charAt(0)||h.unshift(""),v&&"/"!==h.join("/").substr(-1)&&h.push("");var b=""===h[0]||h[0]&&"/"===h[0].charAt(0);if(m){n.hostname=n.host=b?"":h.length?h.shift():"";var g=n.host&&n.host.indexOf("@")>0?n.host.split("@"):!1;g&&(n.auth=g.shift(),n.host=n.hostname=g.shift())}return p=p||n.host&&h.length,p&&!b&&h.unshift(""),h.length?n.pathname=h.join("/"):(n.pathname=null,n.path=null),c(n.pathname)&&c(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},r.prototype.parseHost=function(){var e=this.host,t=h.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){return _.LoggedIn()?void 0:(F.dispatch(O.setLoginRedirectPath(location.pathname)),void t(k.minioBrowserPrefix+"/login"))}function a(e,t){_.LoggedIn()&&t(""+k.minioBrowserPrefix)}function s(){2>G&&setTimeout(function(){document.querySelector(".page-load").classList.add("pl-"+G),G++,s()},Z[G])}n(451);var u=n(1),l=o(u),c=n(12),d=o(c),p=n(445),f=o(p),h=n(113),m=o(h),g=n(216),y=o(g),v=n(170),M=o(v),T=n(171),b=o(T),x=n(89),E=o(x),A=n(167),N=o(A),w=n(347),I=o(w),C=n(165),D=o(C),S=n(46),k=(o(S),n(45)),L=n(44),O=r(L),P=n(229),j=o(P),z=n(226),R=o(z),U=n(225),Y=o(U),B=n(116),W=o(B);window.Web=W["default"];var F=(0,y["default"])(f["default"])(m["default"])(j["default"]),V=(0,D["default"])(function(e){return e})(Y["default"]),H=(0,D["default"])(function(e){return e})(R["default"]),_=new W["default"](window.location.protocol+"//"+window.location.host+k.minioBrowserPrefix+"/webrpc",F.dispatch);"localhost:8080"===window.location.host&&(_=new W["default"]("http://localhost:9000"+k.minioBrowserPrefix+"/webrpc",F.dispatch)),window.web=_,F.dispatch(O.setWeb(_));var Q=function(e){return l["default"].createElement("div",null,e.children)};d["default"].render(l["default"].createElement(I["default"],{store:F,web:_},l["default"].createElement(b["default"],{history:E["default"]},l["default"].createElement(M["default"],{path:"/",component:Q},l["default"].createElement(M["default"],{path:"minio",component:Q},l["default"].createElement(N["default"],{component:V,onEnter:i}),l["default"].createElement(M["default"],{path:"login",component:H,onEnter:a}),l["default"].createElement(M["default"],{path:":bucket",component:V,onEnter:i}),l["default"].createElement(M["default"],{path:":bucket/*",component:V,onEnter:i}))))),document.getElementById("root"));var Z=[10,400],G=0;s(),localStorage.newlyUpdated&&(F.dispatch(O.showAlert({type:"success",message:"Updated to the latest UI Version."})),delete localStorage.newlyUpdated)},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n-1})))}},{key:"selectPrefix",value:function(e,t){var n=this.props,r=(n.dispatch,n.currentPath),o=(n.web,n.currentBucket);if(e.preventDefault(),t.endsWith("/")||""===t){if(t===r)return;h["default"].push(Z.pathJoin(o,t))}else window.location=window.location.origin+"/minio/download/"+o+"/"+t+"?token="+localStorage.token}},{key:"makeBucket",value:function(e){e.preventDefault();var t=this.refs.makeBucketRef.value;this.refs.makeBucketRef.value="";var n=this.props,r=n.web,o=n.dispatch;this.hideMakeBucketModal(),r.MakeBucket({bucketName:t}).then(function(){o(_.addBucket(t)),o(_.selectBucket(t))})["catch"](function(e){return o(_.showAlert({type:"danger",message:e.message}))})}},{key:"hideMakeBucketModal",value:function(){var e=this.props.dispatch;e(_.hideMakeBucketModal())}},{key:"showMakeBucketModal",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.showMakeBucketModal())}},{key:"showAbout",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.showAbout())}},{key:"hideAbout",value:function(){var e=this.props.dispatch;e(_.hideAbout())}},{key:"uploadFile",value:function(e){e.preventDefault();var t=this.props,n=t.dispatch,r=t.upload;if(r.inProgress)return void n(_.showAlert({type:"danger",message:"An upload already in progress"}));var o=e.target.files[0];e.target.value=null,this.xhr=new XMLHttpRequest,n(_.uploadFile(o,this.xhr))}},{key:"removeObject",value:function(e,t){var n=this.props,r=n.web,o=n.dispatch,i=n.currentBucket,a=n.currentPath;r.RemoveObject({bucketName:i,objectName:a+t.name}).then(function(){return o(_.selectPrefix(a))})["catch"](function(e){return o(_.showAlert({type:"danger",message:e.message}))})}},{key:"uploadAbort",value:function(e){e.preventDefault();var t=this.props.dispatch;this.xhr.abort(),t(_.setUpload({inProgress:!1,percent:0})),this.hideAbortModal(e)}},{key:"showAbortModal",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.setShowAbortModal(!0))}},{key:"hideAbortModal",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.setShowAbortModal(!1))}},{key:"hideAlert",value:function(){var e=this.props.dispatch;e(_.hideAlert())}},{key:"dataType",value:function(e,t){return X.getDataType(e,t)}},{key:"sortObjectsByName",value:function(e){var t=this.props,n=t.dispatch,r=t.objects,o=t.sortNameOrder;n(_.setObjects(Z.sortObjectsByName(r,!o))),n(_.setSortNameOrder(!o))}},{key:"sortObjectsBySize",value:function(){var e=this.props,t=e.dispatch,n=e.objects,r=e.sortSizeOrder;t(_.setObjects(Z.sortObjectsBySize(n,!r))),t(_.setSortSizeOrder(!r))}},{key:"sortObjectsByDate",value:function(){var e=this.props,t=e.dispatch,n=e.objects,r=e.sortDateOrder;t(_.setObjects(Z.sortObjectsByDate(n,!r))),t(_.setSortDateOrder(!r))}},{key:"logout",value:function(e){var t=this.props.web;e.preventDefault(),t.Logout(),h["default"].push(q.minioBrowserPrefix+"/login")}},{key:"landingPage",value:function(e){e.preventDefault(),this.props.dispatch(_.selectBucket(this.props.buckets[0]))}},{key:"fullScreen",value:function(e){e.preventDefault();var t=document.documentElement;t.requestFullscreen&&t.requestFullscreen(),t.mozRequestFullScreen&&t.mozRequestFullScreen(),t.webkitRequestFullscreen&&t.webkitRequestFullscreen(),t.msRequestFullscreen&&t.msRequestFullscreen()}},{key:"toggleSidebar",value:function(e){this.props.dispatch(_.setSidebarStatus(e))}},{key:"hideSidebar",value:function(e){var t=e||window.event,n=t.srcElement||t.target;3===n.nodeType&&(n=n.parentNode);var r=n.id;"mh-trigger"!==r&&this.props.dispatch(_.setSidebarStatus(!1))}},{key:"showSettings",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.showSettings()),web.GetAuth().then(function(e){t(_.setSettings({accessKey:e.accessKey,secretKey:e.secretKey}))})}},{key:"hideSettings",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.setSettings({accessKey:"",secretKey:"",secretKeyVisible:!1})),t(_.hideSettings())}},{key:"accessKeyChange",value:function(e){var t=this.props.dispatch;t(_.setSettings({accessKey:e.target.value}))}},{key:"secretKeyChange",value:function(e){var t=this.props.dispatch;t(_.setSettings({secretKey:e.target.value}))}},{key:"secretKeyVisible",value:function(e){var t=this.props.dispatch;t(_.setSettings({secretKeyVisible:e}))}},{key:"generateAuth",value:function(e){e.preventDefault();var t=this.props.dispatch;web.GenerateAuth().then(function(e){t(_.setSettings({secretKeyVisible:!0})),t(_.setSettings({accessKey:e.accessKey,secretKey:e.secretKey}))})}},{key:"setAuth",value:function(e){e.preventDefault();var t=this.props,n=t.web,r=t.dispatch,o=document.getElementById("accessKey").value,i=document.getElementById("secretKey").value;n.SetAuth({accessKey:o,secretKey:i}).then(function(e){r(_.setSettings({accessKey:"",secretKey:"",secretKeyVisible:!1})),r(_.hideSettings()),r(_.showAlert({type:"success",message:"Changed credentials"}))})["catch"](function(e){r(_.setSettings({accessKey:"",secretKey:"",secretKeyVisible:!1})),r(_.hideSettings()),r(_.showAlert({type:"danger",message:e.message}))})}},{key:"render",value:function(){var e=this.props.diskInfo,t=e.total,n=e.free,r=this.props,o=r.showMakeBucketModal,i=r.showAbortModal,a=r.upload,s=r.alert,u=r.sortNameOrder,l=r.sortSizeOrder,d=r.sortDateOrder,f=r.showAbout,h=r.showSettings,m=r.settings,g=this.props.serverInfo,y=g.version,M=g.memory,T=g.platform,b=g.runtime,E=this.props.sidebarStatus,N="",I=a.loaded/a.total*100;a.inProgress&&(N=c["default"].createElement("div",{className:"alert progress animated fadeInUp alert-info"},c["default"].createElement("button",{type:"button",className:"close",onClick:this.showAbortModal.bind(this)},c["default"].createElement("span",null,"×")),c["default"].createElement("div",{className:"text-center"},c["default"].createElement("small",null,a.filename)),c["default"].createElement(C["default"],{now:I}),c["default"].createElement("div",{className:"text-center"},c["default"].createElement("small",null,v["default"].filesize(a.loaded)," (",I.toFixed(2)," %)"))));var D=c["default"].createElement(S["default"],{className:(0,p["default"])({alert:!0,animated:!0,fadeInDown:s.show,fadeOutUp:!s.show}),bsStyle:s.type,onDismiss:this.hideAlert.bind(this)},c["default"].createElement("div",{className:"text-center"},s.message));s.message||(D="");var k="",O=(0,p["default"])({"abort-upload":!0}),j=(0,p["default"])({fa:!0,"fa-stop":!0}),z=(0,p["default"])({fa:!0,"fa-play":!0});i&&(k=c["default"].createElement(ee,{baseClass:O,text:"Abort the upload in progress?",okText:"Abort",okIcon:j,cancelText:"Continue",cancelIcon:z,okHandler:this.uploadAbort.bind(this),cancelHandler:this.hideAbortModal.bind(this)}));var R=(c["default"].createElement(P["default"],{id:"tt-sign-out"},"Sign out"),c["default"].createElement(P["default"],{id:"tt-upload-file"},"Upload file")),Y=c["default"].createElement(P["default"],{id:"tt-create-bucket"},"Create bucket"),B=t-n,F=B/t*100+"%";return c["default"].createElement("div",{className:(0,p["default"])({"file-explorer":!0,toggled:E})},k,c["default"].createElement(K,{landingPage:this.landingPage.bind(this),searchBuckets:this.searchBuckets.bind(this),selectBucket:this.selectBucket.bind(this),clickOutside:this.hideSidebar.bind(this)}),c["default"].createElement("div",{className:"fe-body"},D,c["default"].createElement("header",{className:"mobile-header hidden-lg hidden-md"},c["default"].createElement("div",{id:"mh-trigger",className:"mh-trigger "+(0,p["default"])({"mht-toggled":E}),onClick:this.toggleSidebar.bind(this,!E)},c["default"].createElement("div",{className:"mht-lines"},c["default"].createElement("div",{className:"top"}),c["default"].createElement("div",{className:"center"}),c["default"].createElement("div",{className:"bottom"}))),c["default"].createElement("img",{className:"mh-logo",src:V["default"],alt:""})),c["default"].createElement("header",{className:"fe-header"},c["default"].createElement($,{selectPrefix:this.selectPrefix.bind(this)}),c["default"].createElement("div",{className:"feh-usage"},c["default"].createElement("div",{className:"fehu-chart"},c["default"].createElement("div",{style:{width:F}})),c["default"].createElement("ul",null,c["default"].createElement("li",null,"Used: ",v["default"].filesize(t-n)),c["default"].createElement("li",{className:"pull-right"},"Free: ",v["default"].filesize(t-B)))),c["default"].createElement("ul",{className:"feh-actions"},c["default"].createElement(te,null),c["default"].createElement("li",null,c["default"].createElement(U["default"],{pullRight:!0,id:"top-right-menu"},c["default"].createElement(U["default"].Toggle,{noCaret:!0},c["default"].createElement("i",{className:"fa fa-reorder"})),c["default"].createElement(U["default"].Menu,{className:"dm-right"},c["default"].createElement("li",null,c["default"].createElement("a",{target:"_blank",href:"https://github.com/minio/miniobrowser"},"Github ",c["default"].createElement("i",{className:"fa fa-github"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.fullScreen.bind(this)},"Fullscreen ",c["default"].createElement("i",{className:"fa fa-expand"}))),c["default"].createElement("li",null,c["default"].createElement("a",{target:"_blank",href:"https://gitter.im/minio/minio"},"Ask for help ",c["default"].createElement("i",{className:"fa fa-question-circle"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.showAbout.bind(this)},"About ",c["default"].createElement("i",{className:"fa fa-info-circle"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.showSettings.bind(this)},"Settings ",c["default"].createElement("i",{className:"fa fa-cog"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.logout.bind(this)},"Sign Out ",c["default"].createElement("i",{className:"fa fa-sign-out"})))))))),c["default"].createElement("div",{className:"feb-container"},c["default"].createElement("header",{className:"fesl-row","data-type":"folder"},c["default"].createElement("div",{className:"fesl-item fi-name",onClick:this.sortObjectsByName.bind(this),"data-sort":"name"},"Name",c["default"].createElement("i",{className:(0,p["default"])({"fesli-sort":!0,fa:!0,"fa-sort-alpha-desc":u,"fa-sort-alpha-asc":!u})})),c["default"].createElement("div",{className:"fesl-item fi-size",onClick:this.sortObjectsBySize.bind(this),"data-sort":"size"},"Size",c["default"].createElement("i",{className:(0,p["default"])({"fesli-sort":!0,fa:!0,"fa-sort-amount-desc":l,"fa-sort-amount-asc":!l})})),c["default"].createElement("div",{className:"fesl-item fi-modified",onClick:this.sortObjectsByDate.bind(this),"data-sort":"last-modified"},"Last Modified",c["default"].createElement("i",{className:(0,p["default"])({"fesli-sort":!0,fa:!0,"fa-sort-numeric-desc":d,"fa-sort-numeric-asc":!d})})))),c["default"].createElement("div",{className:"feb-container"},c["default"].createElement(J,{removeObject:this.removeObject.bind(this),dataType:this.dataType.bind(this),selectPrefix:this.selectPrefix.bind(this)})),N,c["default"].createElement(U["default"],{dropup:!0,className:"feb-actions",id:"fe-action-toggle"},c["default"].createElement(U["default"].Toggle,{noCaret:!0,className:"feba-toggle"},c["default"].createElement("span",null,c["default"].createElement("i",{className:"fa fa-plus"}))),c["default"].createElement(U["default"].Menu,null,c["default"].createElement(L["default"],{placement:"left",overlay:R},c["default"].createElement("a",{href:"#",className:"feba-btn feba-upload"},c["default"].createElement("input",{type:"file",onChange:this.uploadFile.bind(this),style:{display:"none"},id:"file-input"}),c["default"].createElement("label",{htmlFor:"file-input"},c["default"].createElement("i",{style:{cursor:"pointer"},className:"fa fa-cloud-upload"})))),c["default"].createElement(L["default"],{placement:"left",overlay:Y},c["default"].createElement("a",{href:"#",className:"feba-btn feba-bucket",onClick:this.showMakeBucketModal.bind(this)},c["default"].createElement("i",{className:"fa fa-hdd-o"}))))),c["default"].createElement(x["default"],{className:"feb-modal",animation:!1,show:o,onHide:this.hideMakeBucketModal.bind(this)},c["default"].createElement("button",{className:"close",onClick:this.hideMakeBucketModal.bind(this)},c["default"].createElement("span",null,"×")),c["default"].createElement(A["default"],null,c["default"].createElement("form",{onSubmit:this.makeBucket.bind(this)},c["default"].createElement("div",{className:"create-bucket"},c["default"].createElement("input",{type:"text",autofocus:!0,ref:"makeBucketRef",placeholder:"Bucket Name"}),c["default"].createElement("i",null))))),c["default"].createElement(x["default"],{className:"about-modal modal-dark",show:f,onHide:this.hideAbout.bind(this)},c["default"].createElement("div",{className:"am-inner"},c["default"].createElement("div",{className:"ami-item hidden-xs"},c["default"].createElement("a",{href:"https://minio.io",target:"_blank"},c["default"].createElement("img",{className:"amii-logo",src:V["default"],alt:""}))),c["default"].createElement("div",{className:"ami-item"},c["default"].createElement("ul",{className:"amii-list"},c["default"].createElement("li",null,c["default"].createElement("div",null,"Version"),c["default"].createElement("small",null,y)),c["default"].createElement("li",null,c["default"].createElement("div",null,"Memory"),c["default"].createElement("small",null,M)),c["default"].createElement("li",null,c["default"].createElement("div",null,"Platform"),c["default"].createElement("small",null,T)),c["default"].createElement("li",null,c["default"].createElement("div",null,"Runtime"),c["default"].createElement("small",null,b))),c["default"].createElement("span",{className:"amii-close",onClick:this.hideAbout.bind(this)},c["default"].createElement("i",{className:"fa fa-check"}))))),c["default"].createElement(x["default"],{className:"modal-dark",bsSize:"sm",show:h},c["default"].createElement(w["default"],null,"Change Password"),c["default"].createElement(A["default"],null,c["default"].createElement("div",{className:"p-relative",style:{paddingRight:"35px",marginBottom:"20px"}},c["default"].createElement(W["default"],{value:m.accessKey,onChange:this.accessKeyChange.bind(this),id:"accessKey",label:"Access Key",name:"accesskey",type:"text",spellCheck:"false",required:"required",autoComplete:"false",align:"ig-left"})),c["default"].createElement("div",{className:"p-relative"},c["default"].createElement(W["default"],{value:m.secretKey,onChange:this.secretKeyChange.bind(this),id:"secretKey",label:"Secret Key",name:"accesskey",type:m.secretKeyVisible?"text":"password",spellCheck:"false",required:"required",autoComplete:"false",align:"ig-left"}),c["default"].createElement("i",{onClick:this.secretKeyVisible.bind(this,!m.secretKeyVisible),className:"toggle-password fa fa-eye "+(m.secretKeyVisible?"toggled":"")})),c["default"].createElement("div",{className:"clearfix"}),c["default"].createElement("div",{className:"form-footer clearfix"},c["default"].createElement(L["default"],{placement:"bottom",overlay:c["default"].createElement(P["default"],{id:"tt-password-generate"},"Generate Keys")},c["default"].createElement("a",{href:"", -className:"ff-btn ff-key-gen",onClick:this.generateAuth.bind(this)},c["default"].createElement("i",{className:"fa fa-repeat"}))),c["default"].createElement("a",{href:"",className:"ff-btn",onClick:this.setAuth.bind(this)},c["default"].createElement("i",{className:"fa fa-check"})),c["default"].createElement("a",{href:"",className:"ff-btn",onClick:this.hideSettings.bind(this)},c["default"].createElement("i",{className:"fa fa-times"})))))))}}]),t}(c["default"].Component);t["default"]=ne},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n-1&&n.objects.splice(r,1),n.objects=[t.object].concat(o(n.objects));break;case a.SET_UPLOAD:n.upload=t.upload;break;case a.SET_ALERT:n.alert.alertTimeout&&clearTimeout(n.alert.alertTimeout),t.alert.show?n.alert=t.alert:n.alert=Object.assign({},n.alert,{show:!1});break;case a.SET_LOGIN_ERROR:n.loginError=!0;break;case a.SET_SHOW_ABORT_MODAL:n.showAbortModal=t.showAbortModal;break;case a.SHOW_ABOUT:n.showAbout=t.showAbout;break;case a.SET_SORT_NAME_ORDER:n.sortNameOrder=t.sortNameOrder;break;case a.SET_SORT_SIZE_ORDER:n.sortSizeOrder=t.sortSizeOrder;break;case a.SET_SORT_DATE_ORDER:n.sortDateOrder=t.sortDateOrder;break;case a.SET_LATEST_UI_VERSION:n.latestUiVersion=t.latestUiVersion;break;case a.SET_SIDEBAR_STATUS:n.sidebarStatus=t.sidebarStatus;break;case a.SET_LOGIN_REDIRECT_PATH:n.loginRedirectPath=t.path;case a.SET_LOAD_BUCKET:n.loadBucket=t.loadBucket;break;case a.SET_LOAD_PATH:n.loadPath=t.loadPath;break;case a.SHOW_SETTINGS:n.showSettings=t.showSettings;break;case a.SET_SETTINGS:n.settings=Object.assign({},n.settings,t.settings)}return n}},function(e,t,n){t=e.exports=n(231)(),t.push([e.id,'*,:after,:before{box-sizing:border-box}a{color:#337ab7;text-decoration:none}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#edecec;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:15px;text-align:left;background-color:#fff;border:1px solid transparent;border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:rgba(0,0,0,.08)}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:gray;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{text-decoration:none;color:#333;background-color:rgba(0,0,0,.05)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#333;text-decoration:none;outline:0;background-color:rgba(0,0,0,.075)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#e4e4e4}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid\\9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.modal,.modal-open{overflow:hidden}.modal{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translateY(-25%);transform:translateY(-25%);-webkit-transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0);transform:translate(0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid transparent;border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:transparent}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:35px 40px 0;border-bottom:1px solid transparent}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:transparent}.modal-body{position:relative;padding:35px 40px 30px}.modal-footer{padding:35px 40px 30px;text-align:right;border-top:1px solid transparent}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:500px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:Lato,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:13px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px}.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{left:5px}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}@-ms-viewport{width:device-width}.visible-lg,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translateY(-20px)}to{opacity:1;-webkit-transform:translateY(0)}}@keyframes fadeInDown{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px)}to{opacity:1;-webkit-transform:translateY(0)}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateY(0)}to{opacity:0;-webkit-transform:translateY(20px)}}@keyframes fadeOutDown{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(20px)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutUp{0%{opacity:1;-webkit-transform:translateY(0)}to{opacity:0;-webkit-transform:translateY(-20px)}}@keyframes fadeOutUp{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-20px)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@font-face{font-family:Lato;src:url('+n(459)+") format('woff2'),url("+n(458)+") format('woff');font-weight:400;font-style:normal}@font-face{font-family:FontAwesome;src:url("+n(457)+") format('woff'),url("+n(456)+'#fontawesomeregular) format(\'svg\');font-weight:400;font-style:normal}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.clearfix:after,.clearfix:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before{content:" ";display:table}.clearfix:after,.modal-footer:after,.modal-header:after{clear:both}.pull-right{float:right!important}.pull-left{float:left!important}.p-relative{position:relative}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-search:before{content:"\\F002"}.fa-check:before{content:"\\F00C"}.fa-file-o:before{content:"\\F016"}.fa-refresh:before{content:"\\F021"}.fa-question-circle:before{content:"\\F059"}.fa-info-circle:before{content:"\\F05A"}.fa-expand:before{content:"\\F065"}.fa-plus:before{content:"\\F067"}.fa-warning:before{content:"\\F071"}.fa-sign-in:before{content:"\\F08B"}.fa-sign-out:before{content:"\\F090"}.fa-github:before{content:"\\F09B"}.fa-hdd-o:before{content:"\\F0A0"}.fa-globe:before{content:"\\F0AC"}.fa-cloud-upload:before{content:"\\F0EE"}.fa-file-text-o:before{content:"\\F0F6"}.fa-reorder:before{content:"\\F0C9"}.fa-sort-alpha-asc:before{content:"\\F15D"}.fa-sort-alpha-desc:before{content:"\\F15E"}.fa-sort-amount-asc:before{content:"\\F160"}.fa-sort-amount-desc:before{content:"\\F161"}.fa-sort-numeric-asc:before{content:"\\F162"}.fa-sort-numeric-desc:before{content:"\\F163"}.fa-file-pdf-o:before{content:"\\F1C1"}.fa-file-word-o:before{content:"\\F1C2"}.fa-file-excel-o:before{content:"\\F1C3"}.fa-file-powerpoint-o:before{content:"\\F1C4"}.fa-file-image-o:before{content:"\\F1C5"}.fa-file-zip-o:before{content:"\\F1C6"}.fa-file-audio-o:before{content:"\\F1C7"}.fa-file-video-o:before{content:"\\F1C8"}.fa-file-code-o:before{content:"\\F1C9"}.fa-play:before{content:"\\F04B"}.fa-stop:before{content:"\\F04D"}.fa-cog:before{content:\'\\F013\'}.fa-times:before{content:\'\\F00D\'}.fa-question:before{content:\'\\F128\'}.fa-repeat:before{content:\'\\F01E\'}.fa-eye:before{content:\'\\F06E\'}*{-webkit-font-smoothing:antialiased}:active,:focus{outline:0}*,:after,:before{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:Lato,sans-serif;font-size:15px;line-height:1.42857143;color:gray;background-color:#edecec}body,html{min-height:100%;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;-webkit-transition:color;transition:color;-webkit-transition-duration:.3s;transition-duration:.3s}a,a:focus,a:hover{text-decoration:none}a:focus,a:hover{color:#23527c}.fe-h2{font-weight:400;margin:0;line-height:100%;font-size:24px}.alert{border:0;position:fixed;max-width:500px;margin:0;box-shadow:0 4px 5px rgba(0,0,0,.1);color:#fff;width:100%;right:20px;border-radius:3px;z-index:12;padding:17px 50px 17px 17px}.alert:not(.progress){top:20px}@media (min-width:768px){.alert:not(.progress){left:50%;margin-left:-250px}}.alert.progress{bottom:20px;right:20px}.alert.alert-danger{background:#f55d5d}.alert.alert-success{background:#37d672}.alert.alert-info{background:#2196f3}@media (max-width:767px){.alert{left:20px;width:calc(100% - 40px);max-width:100%}}.alert .progress{margin:10px 10px 8px 0;height:5px;box-shadow:none;border-radius:1px;background-color:#1d82d2;border-radius:2px;overflow:hidden}.alert .progress-bar{box-shadow:none;background-color:#fff;height:100%}.alert .close{position:absolute;right:15px;top:15px}.more{display:block;color:hsla(0,0%,100%,.7);font-size:13px;margin-top:2px}.more:hover{color:#fff}.modal-header{color:hsla(0,0%,100%,.4);font-size:13px;text-transform:uppercase}.modal-header small{display:block;text-transform:none;font-size:12px;margin-top:3px;color:hsla(0,0%,100%,.2)}.modal-content{border-radius:3px;box-shadow:0 4px 5px rgba(0,0,0,.1)}.modal-dark .modal-content{background-color:#32393f;box-shadow:0 2px 13px rgba(0,0,0,.5)}.dropdown-menu{padding:15px 0;top:0;margin-top:-1px}.dropdown-menu>li>a{padding:8px 20px;font-size:15px}.dropdown-menu>li>a>i{width:20px}.dm-right>li>a{text-align:right}.close{right:19px;font-weight:400;opacity:1;font-size:18px;position:absolute;text-align:center;top:16px;z-index:1;padding:0;border:0;background-color:transparent}.close span{width:25px;height:25px;background:hsla(0,0%,100%,.18);display:block;border-radius:50%;line-height:24px;text-shadow:none;color:#fff}.close:focus span,.close:hover span{background-color:hsla(0,0%,100%,.25);color:#fff}.input-group{position:relative}.input-group:not(:last-child){margin-bottom:20px}.ig-label{position:absolute;text-align:center;bottom:7px;left:0;width:100%;color:hsla(0,0%,100%,.55);font-size:15px;transition:all .15s;-webkit-transition:all;transition:all;-webkit-transition-duration:.15s;transition-duration:.15s;padding:2px 0 3px;border-radius:2px;font-weight:400}.ig-helpers{position:relative;z-index:1}.ig-helpers i:first-child{height:1px;width:100%;background:#42494e;display:block}.ig-helpers i:last-child{position:absolute;height:1px;width:100%;background:#fff;left:0;bottom:0;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:scale(0);transform:scale(0)}.ig-helpers i:after,.ig-helpers i:before{position:absolute;bottom:1px;background:hsla(0,0%,100%,.2);width:1px;height:10px}.ig-helpers i:before{left:0}.ig-helpers i:after{right:0}.ig-text{width:100%;height:50px;border:0;background:transparent;text-align:center;color:#fff;position:relative;z-index:1}.ig-text:focus+label+.ig-helpers i:last-child{-webkit-transform:scale(1);transform:scale(1)}.ig-text:focus+label+.ig-helpers i:last-child:after,.ig-text:focus+label+.ig-helpers i:last-child:before{width:1px;background:#fff}.ig-text:focus+.ig-label,.ig-text:valid+.ig-label{bottom:40px;font-size:13px;z-index:1;color:hsla(0,0%,100%,.3)}.ig-left .ig-label,.ig-left .ig-text{text-align:left}.ig-error .ig-label{color:#e23f3f}.ig-error .ig-helpers i:first-child,.ig-error .ig-helpers i:first-child:after,.ig-error .ig-helpers i:first-child:before{background:rgba(226,63,63,.43)}.ig-error .ig-helpers i:last-child,.ig-error .ig-helpers i:last-child:after,.ig-error .ig-helpers i:last-child:before{background:#e23f3f!important}.ig-error:after{content:"\\F05A";font-family:FontAwesome;position:absolute;top:17px;right:9px;font-size:20px;color:#d33d3e}.form-footer{margin:15px -6px 0;text-align:center}.ff-btn{display:inline-block;width:40px;height:40px;line-height:36px;color:#fff;border:1px solid #fff;border-radius:50%;font-size:15px;margin:0 6px;opacity:.3;text-align:center;-webkit-transition:all;transition:all;-webkit-transition-duration:.25s;transition-duration:.25s}.ff-btn:focus,.ff-btn:hover{color:#fff;opacity:.6}.login{height:100vh;min-height:500px;background:#32393f;text-align:center}.login:before{height:calc(100% - 110px);width:1px;content:""}.l-wrap,.login:before{display:inline-block;vertical-align:middle}.l-wrap{width:80%;max-width:500px;margin-top:-50px}.l-wrap.toggled{display:inline-block}.l-wrap .input-group:not(:last-child){margin-bottom:40px}.l-footer{height:110px;padding:0 50px}.lf-logo{float:right}.lf-logo img{width:40px}.lf-server{float:left;color:hsla(0,0%,100%,.4);font-size:20px;font-weight:400;padding-top:40px}@media (max-width:768px){.lf-logo,.lf-server{float:none;display:block;text-align:center;width:100%}.lf-logo{margin-bottom:5px}.lf-server{font-size:15px}}.lw-btn{width:50px;height:50px;border:1px solid #fff;display:inline-block;border-radius:50%;font-size:22px;color:#fff;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;opacity:.3;background-color:transparent;line-height:45px;padding:0}.lw-btn:hover{color:#fff;opacity:.8;border-color:#fff}.lw-btn i{display:block;width:100%;padding-left:3px}input:-webkit-autofill{-webkit-box-shadow:0 0 0 50px #32393f inset!important;-webkit-text-fill-color:#fff!important}.fe-header{padding:45px 55px 20px}@media (min-width:992px){.fe-header{position:relative}}@media (max-width:667px){.fe-header{padding:25px 30px 20px}}.fe-header h2{font-size:17px;margin-bottom:0}.fe-header h2>span{margin-bottom:7px;display:inline-block}.fe-header h2>span>a{color:#589fdc}.fe-header h2>span>a:hover{color:#4984b7}.fe-header h2>span:not(:first-child):before{content:\'/\';margin:0 4px;color:#c1c1c1}.fe-header p{color:#bdbdbd;margin-top:7px}.feh-usage{margin-top:12px;max-width:285px}@media (max-width:667px){.feh-usage{max-width:100%;font-size:12px}}.feh-usage>ul{color:#bdbdbd;margin-top:7px;list-style:none;padding:0}.feh-usage>ul>li{padding-right:0;display:inline-block}.feh-usage>ul>li:first-child{color:#2ed2ff}.fehu-chart{height:5px;background:#eee;position:relative;border-radius:2px;overflow:hidden}.fehu-chart>div{position:absolute;left:0;height:100%;background:#2ed2ff}.feh-actions{list-style:none;padding:0;margin:0;position:absolute;right:35px;top:30px;z-index:11}@media (max-width:991px){.feh-actions{top:7px;right:10px;position:fixed}}.feh-actions>li{display:inline-block;text-align:right;vertical-align:top;line-height:100%}.feh-actions>li>.btn-group>button,.feh-actions>li>a{display:block;height:45px;min-width:45px;text-align:center;border-radius:50%;padding:0;border:0;background:none}@media (min-width:992px){.feh-actions>li>.btn-group>button,.feh-actions>li>a{color:#7b7b7b;font-size:21px;line-height:45px;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s}.feh-actions>li>.btn-group>button:hover,.feh-actions>li>a:hover{background:rgba(0,0,0,.09)}}@media (max-width:991px){.feh-actions>li>.btn-group>button,.feh-actions>li>a{color:#eaeaea;font-size:16px}.feh-actions>li>.btn-group>button .fa-reorder:before,.feh-actions>li>a .fa-reorder:before{content:\'\\F142\'}}.feha-search{position:relative}.feha-search:before{color:gray;font-family:fontAwesome;content:\'\\F002\';position:absolute;top:14px;font-size:18px;left:20px}.feha-search input[type=text]{border:0;width:350px;background:#f3f3f3;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;padding:15px 20px 17px 55px;border-radius:2px;margin:0 0 15px}.feha-search input[type=text]::-webkit-input-placeholder{color:gray}.feha-search input[type=text]::-moz-placeholder{color:gray}.feha-search input[type=text]:-ms-input-placeholder{color:gray}.feha-search input[type=text]:focus{box-shadow:0 0 1px -2px #eaeaea;background:#eaeaea;width:500px}@media (max-width:767px){.about-modal{text-align:center}.about-modal .modal-dialog{max-width:400px;width:90%;margin:20px auto 0}}.am-inner{display:flex;flex-direction:row;align-items:center;min-height:350px;position:relative}@media (min-width:768px){.am-inner:before{content:\'\';width:150px;height:100%;top:0;left:0;position:absolute;background-color:#23282c}}.ami-item:first-child{width:150px;text-align:center}.ami-item:last-child{flex:4;padding:30px}.amii-logo{width:70px;position:relative}.amii-list{list-style:none;padding:0}.amii-list>li{margin-bottom:15px}.amii-list>li div{color:hsla(0,0%,100%,.8);text-transform:uppercase;font-size:14px}.amii-list>li small{font-size:13px;color:hsla(0,0%,100%,.4)}.amii-close{width:40px;height:40px;display:inline-block;border:1px solid #fff;border-radius:50%;line-height:37px;font-size:17px;color:#fff;margin-top:10px;opacity:.4;-webkit-transition:opacity;transition:opacity;-webkit-transition-duration:.3s;transition-duration:.3s;text-align:center;cursor:pointer}.amii-close:hover{opacity:.8;color:#fff}@media (max-width:991px){.mobile-header{background-color:#23282c;padding:10px 10px 9px;text-align:center;position:fixed;z-index:10;box-shadow:0 0 10px rgba(0,0,0,.65);left:0;top:0;width:100%}.mobile-header .mh-logo{height:35px;position:relative;top:4px}.mh-trigger{width:41px;height:41px;cursor:pointer;float:left;position:relative;text-align:center}.mh-trigger:after,.mh-trigger:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%}.mh-trigger:after{z-index:1}.mh-trigger:before{background:hsla(0,0%,100%,.1);-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:scale(0);transform:scale(0)}.mht-toggled:before{-webkit-transform:scale(1);transform:scale(1)}.mht-toggled .mht-lines{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.mht-toggled .mht-lines>div.top{width:12px;transform:translateX(8px) translateY(1px) rotate(45deg);-webkit-transform:translateX(8px) translateY(1px) rotate(45deg)}.mht-toggled .mht-lines>div.bottom{width:12px;transform:translateX(8px) translateY(-1px) rotate(-45deg);-webkit-transform:translateX(8px) translateY(-1px) rotate(-45deg)}.mht-lines,.mht-lines>div{-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s}.mht-lines{width:18px;height:12px;display:inline-block;margin-top:14px}.mht-lines>div{background-color:#eaeaea;width:18px;height:2px}.mht-lines>div.center{margin:3px 0}}.ff-key-gen{position:relative;border-color:#ffc500;color:#ffc500;opacity:.4}.ff-key-gen:hover{opacity:.7;color:#ffc500}.ff-key-gen .fa-refresh{font-size:11px;position:absolute;bottom:16px;left:18px}.ff-key-gen .fa-key{position:relative;left:-3px;top:3px}.toggle-password{position:absolute;bottom:0;right:0;width:30px;height:25px;border:1px solid #42494e;border-radius:0;line-height:25px;text-align:center;font-size:12px;cursor:pointer;z-index:10}.toggle-password:hover{background:hsla(0,0%,100%,.02)}.toggle-password.active,.toggle-password.toggled{background:#42494e}.fe-sidebar{width:300px;background-color:#32393f;position:fixed;height:100%;overflow:hidden;color:#fff;padding:35px}@media (min-width:992px){.fe-sidebar{-webkit-transform:translateZ(0);transform:translateZ(0)}}@media (max-width:991px){.fe-sidebar{padding-top:85px;z-index:9;box-shadow:0 0 10px rgba(0,0,0,.65);-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:translate3d(-300px,0,0);transform:translate3d(-300px,0,0)}.fe-sidebar.toggled{-webkit-transform:translateZ(0);transform:translateZ(0)}}.fe-sidebar a{color:hsla(0,0%,100%,.58)}.fe-sidebar a:hover{color:#fff}.fes-header{margin-bottom:40px}.fes-header h2,.fes-header img{float:left}.fes-header h2{margin:13px 0 0 10px}.fes-header img{width:32px}.fesl-search{position:relative;margin-bottom:20px}.fesl-search:before{color:hsla(0,0%,100%,.4);font-family:fontAwesome;content:\'\\F002\';top:1px;font-size:15px;position:absolute;left:0}.fesl-search>i{position:absolute;left:0;bottom:0;content:"";height:1px;width:0;background:#fff;-webkit-transition:all;transition:all;-webkit-transition-duration:.25s;transition-duration:.25s}.fesl-search input[type=text]{width:100%;color:#fff;background:transparent;border:0;border-bottom:1px solid hsla(0,0%,100%,.1);padding:0 2px 10px 25px;font-size:14px}.fesl-search input[type=text]::-moz-placeholder{color:hsla(0,0%,100%,.4);opacity:1}.fesl-search input[type=text]:-ms-input-placeholder{color:hsla(0,0%,100%,.4)}.fesl-search input[type=text]::-webkit-input-placeholder{color:hsla(0,0%,100%,.4)}.fesl-search input[type=text]:focus+i{width:100%}.fesl-inner{height:calc(100vh - 260px);overflow:auto;padding:0;margin:0 -35px}.fesl-inner li{position:relative}.fesl-inner li>a{display:block;padding:10px 40px 12px 35px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fesl-inner li>a:before{font-family:FontAwesome;content:\'\\F0A0\';font-size:17px;margin-right:15px;position:relative;top:1px;opacity:.8;filter:alpha(opacity=80)}.fesl-inner li.active>a{background-color:rgba(0,0,0,.2);color:#fff}.fesl-inner li:not(.active)>a:hover{background-color:rgba(0,0,0,.1)}.fesl-inner ul{list-style:none;padding:0;margin:0}.fesl-inner:hover .scrollbar-vertical{opacity:1}.scrollbar-vertical{position:absolute;right:5px;width:4px;height:100%;opacity:0;-webkit-transition:opacity;transition:opacity;-webkit-transition-duration:.3s;transition-duration:.3s}.scrollbar-vertical div{border-radius:1px!important;background-color:#6a6a6a!important}.fes-host{position:fixed;left:0;bottom:0;z-index:1;background:#32393f;color:hsla(0,0%,100%,.4);font-size:20px;font-weight:400;width:300px;padding:20px 20px 20px 34px}.fes-host>i{margin-right:10px}.fesl-row{padding-right:40px;padding-top:5px;padding-bottom:5px;position:relative}@media (min-width:668px){.fesl-row{display:flex;flex-flow:row;justify-content:space-between}}.fesl-row:after,.fesl-row:before{content:" ";display:table}.fesl-row:after{clear:both}@media (min-width:668px){header.fesl-row{margin-bottom:20px;border-bottom:1px solid #f0f0f0;padding-left:40px}header.fesl-row .fesl-item,header.fesl-row .fesli-sort{-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s}header.fesl-row .fesl-item{cursor:pointer;color:#bdbdbd;font-weight:500;margin-bottom:-5px}header.fesl-row .fesl-item>.fesli-sort{float:right;margin:4px 0 0;opacity:0;filter:alpha(opacity=0);color:#32393f;font-size:14px}header.fesl-row .fesl-item:hover{background:#f5f5f5;color:#32393f}header.fesl-row .fesl-item:hover>.fesli-sort{opacity:.5;filter:alpha(opacity=50)}}@media (max-width:667px){header.fesl-row{display:none}}div.fesl-row{padding-left:85px;border-bottom:1px solid transparent;cursor:default}@media (max-width:667px){div.fesl-row{padding-left:75px;padding-right:20px}}div.fesl-row:nth-child(even){background-color:#f7f7f7}div.fesl-row.context-menu-active,div.fesl-row.ui-selected,div.fesl-row.ui-selecting{background-color:#03a9f4;color:#fff}div.fesl-row.context-menu-active .fesl-item:before,div.fesl-row.context-menu-active a,div.fesl-row.ui-selected .fesl-item:before,div.fesl-row.ui-selected a,div.fesl-row.ui-selecting .fesl-item:before,div.fesl-row.ui-selecting a{color:#fff}div.fesl-row.ui-selected:nth-child(even){background-color:#03a9f4}div.fesl-row[data-type]:before{font-family:fontAwesome;width:35px;height:35px;vertical-align:top;text-align:center;line-height:35px;position:absolute;border-radius:50%;font-size:16px;left:50px;top:9px;color:#fff}@media (max-width:667px){div.fesl-row[data-type]:before{left:25px}}@media (max-width:667px){div.fesl-row[data-type=folder] .fesl-item.fi-name{padding-top:10px;padding-bottom:7px}div.fesl-row[data-type=folder] .fesl-item.fi-modified,div.fesl-row[data-type=folder] .fesl-item.fi-size{display:none}}div.fesl-row[data-type=folder]:before{content:\'\\F114\';background-color:#2dd3fb}div.fesl-row[data-type=pdf]:before{content:"\\F1C1";background-color:#fb766d}div.fesl-row[data-type=zip]:before{content:"\\F1C6";background-color:#374952}div.fesl-row[data-type=audio]:before{content:"\\F1C7";background-color:#009688}div.fesl-row[data-type=code]:before{content:"\\F1C9";background-color:#997867}div.fesl-row[data-type=excel]:before{content:"\\F1C3";background-color:#64c866}div.fesl-row[data-type=image]:before{content:"\\F1C5";background-color:#d24ce9}div.fesl-row[data-type=video]:before{content:"\\F1C8";background-color:#fdc206}div.fesl-row[data-type=other]:before{content:"\\F016";background-color:#8a8a8a}div.fesl-row[data-type=text]:before{content:"\\F0F6";background-color:#8a8a8a}div.fesl-row[data-type=doc]:before{content:"\\F1C2";background-color:#2196f5}div.fesl-row[data-type=presentation]:before{content:"\\F1C4";background-color:#fba220}div.fesl-row.fesl-loading:before{content:\'\'}.fesl-item{padding:10px 15px;color:gray;display:block;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.fesl-item a{color:gray}@media (min-width:668px){.fesl-item.fi-name{flex:3}.fesl-item.fi-size{width:140px}.fesl-item.fi-modified{width:190px}}@media (max-width:667px){.fesl-item{padding:0}.fesl-item.fi-name{width:100%;margin-bottom:3px}.fesl-item.fi-modified,.fesl-item.fi-size{font-size:12px;color:#b5b5b5}.fesl-item.fi-size{float:left}.fesl-item.fi-modified{float:right}}.create-bucket{position:relative}.create-bucket input[type=text]{width:100%;border:0;color:#fff;background:transparent;text-align:center;height:40px}.create-bucket input[type=text]::-moz-placeholder{color:#fff;opacity:1}.create-bucket input[type=text]:-ms-input-placeholder{color:#fff}.create-bucket input[type=text]::-webkit-input-placeholder{color:#fff}.create-bucket input[type=text]:focus+i:before{-webkit-transform:scale(1)!important;transform:scale(1)!important}.create-bucket i,.create-bucket i:before{position:absolute;height:1px;width:100%;left:0;bottom:0}.create-bucket i{background-color:hsla(0,0%,100%,.44)}.create-bucket i:before{background:#fff;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;content:\'\';-webkit-transform:scale(0);transform:scale(0);z-index:1}.file-explorer{background-color:#fff;position:relative;height:100%}.file-explorer.toggled{height:100vh;overflow:hidden}.fe-body{min-height:100vh;overflow:auto}@media (min-width:992px){.fe-body{padding:0 0 40px 300px}}@media (max-width:991px){.fe-body{padding:75px 0 40px}}.feb-actions{position:fixed;bottom:30px;right:30px}.feb-actions .dropdown-menu{min-width:55px;width:55px;text-align:center;background:transparent;box-shadow:none;margin:0}.feb-actions.open .feba-btn{-webkit-transform:scale(1);transform:scale(1)}.feb-actions.open .feba-btn:first-child{-webkit-animation-name:feba-btn-anim;animation-name:feba-btn-anim;-webkit-animation-duration:.3s;animation-duration:.3s}.feb-actions.open .feba-btn:last-child{-webkit-animation-name:feba-btn-anim;animation-name:feba-btn-anim;-webkit-animation-duration:.1s;animation-duration:.1s}.feb-actions.open .feba-toggle{background:#d23327}.feb-actions.open .feba-toggle>span{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.feba-toggle{width:55px;height:55px;line-height:55px;border-radius:50%;background:#f44336;box-shadow:0 3px 6px rgba(0,0,0,.35);display:inline-block;text-align:center;border:0;padding:0}.feba-toggle span{display:inline-block;height:100%;width:100%}.feba-toggle i{color:#fff;font-size:17px;line-height:58px}.feba-toggle,.feba-toggle>span{-webkit-transition:all;transition:all;-webkit-transition-duration:.25s;transition-duration:.25s;-webkit-backface-visibility:hidden;backface-visibility:hidden}.feba-btn{width:40px;margin-top:10px;height:40px;border-radius:50%;text-align:center;display:inline-block;line-height:40px;box-shadow:0 3px 4px rgba(0,0,0,.15);-webkit-transform:scale(0);transform:scale(0);position:relative}.feba-btn,.feba-btn:focus,.feba-btn:hover{color:#fff}.feba-btn label{width:100%;height:100%;position:absolute;left:0;top:0;cursor:pointer}.feba-bucket{background:#ff9800}.feba-upload{background:#ffc107}@-webkit-keyframes feba-btn-anim{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes feba-btn-anim{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}.feb-modal .modal-content{background-color:#ff9800}.feb-modal .modal-dialog{-webkit-animation-name:fadeIn;animation-name:fadeIn;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both;width:330px;position:fixed;right:25px;bottom:95px;margin:0;height:110px}.feb-modal .modal-content{width:100%;height:100%}.abort-upload .modal-dialog{margin:0;width:100%;height:100%}.abort-upload .modal-content{width:510px;height:105px;position:fixed;right:19px;bottom:17px;background-color:#f55d5d;color:#fff;text-align:center}.abort-upload .cm-text{margin-bottom:10px;font-size:14px;margin-top:4px}.abort-upload .cmf-btn{border:0;background:hsla(0,0%,100%,.2);margin:0 5px;border-radius:2px;color:#fff;font-size:13px;padding:7px 12px;position:relative}.abort-upload .cmf-btn>i{font-size:11px;margin-right:8px;position:relative;top:-1px}.abort-upload .cmf-btn:hover{background-color:hsla(0,0%,100%,.3)}.l-bucket,.l-listing{width:23px;height:23px;-webkit-animation-name:zoomIn;animation-name:zoomIn;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.l-bucket>i,.l-listing>i{border-width:2px}.l-bucket{left:31px;top:10px}.l-bucket>i{background-color:#32393f;border-top-color:hsla(0,0%,100%,.1);border-right-color:hsla(0,0%,100%,.1);border-bottom-color:hsla(0,0%,100%,.1)}.active .l-bucket>i{background-color:#282e32}.l-listing{left:56px;top:15px}.l-listing>i{border-top-color:hsla(0,0%,100%,.4);border-right-color:hsla(0,0%,100%,.4);border-bottom-color:hsla(0,0%,100%,.4)}@media (max-width:667px){.l-listing{left:31px}}.ie-warning{background-color:#ff5252;width:100%;height:100%;position:fixed;left:0;top:0;text-align:center}.ie-warning:before{width:1px;content:\'\';height:100%}.ie-warning .iw-inner,.ie-warning:before{display:inline-block;vertical-align:middle}.iw-inner{width:470px;height:300px;background-color:#fff;border-radius:5px;padding:40px;position:relative}.iw-inner ul{list-style:none;padding:0;margin:0;width:230px;margin-left:80px;margin-top:16px}.iw-inner ul>li{float:left}.iw-inner ul>li>a{display:block;padding:10px 15px 7px;font-size:14px;margin:0 1px;border-radius:3px}.iw-inner ul>li>a:hover{background:#eee}.iw-inner ul>li>a img{height:40px;margin-bottom:5px}.iwi-icon{color:#ff5252;font-size:40px;display:block;line-height:100%;margin-bottom:15px}.iwi-skip{position:absolute;left:0;bottom:-35px;width:100%;color:hsla(0,0%,100%,.6);cursor:pointer}.iwi-skip:hover{color:#fff}',""]); -},function(e,t){e.exports=function(){var e=[];return e.toString=function(){for(var e=[],t=0;t1&&(n=n.charAt(0)):n=" ",r=void 0===r?"left":"right","right"===r)for(;e.length4&&21>e?"th":{1:"st",2:"nd",3:"rd"}[e%10]||"th"},w:function(){return n.getDay()},z:function(){return(c.L()?a[c.n()]:i[c.n()])+c.j()-1},W:function(){var e=c.z()-c.N()+1.5;return o.pad(1+Math.floor(Math.abs(e)/7)+(e%7>3.5?1:0),2,"0")},F:function(){return l[n.getMonth()]},m:function(){return o.pad(c.n(),2,"0")},M:function(){return c.F().slice(0,3)},n:function(){return n.getMonth()+1},t:function(){return new Date(c.Y(),c.n(),0).getDate()},L:function(){return 1===new Date(c.Y(),1,29).getMonth()?1:0},o:function(){var e=c.n(),t=c.W();return c.Y()+(12===e&&9>t?-1:1===e&&t>9)},Y:function(){return n.getFullYear()},y:function(){return String(c.Y()).slice(-2)},a:function(){return n.getHours()>11?"pm":"am"},A:function(){return c.a().toUpperCase()},B:function(){var e=n.getTime()/1e3,t=e%86400+3600;0>t&&(t+=86400);var r=t/86.4%1e3;return 0>e?Math.ceil(r):Math.floor(r)},g:function(){return c.G()%12||12},G:function(){return n.getHours()},h:function(){return o.pad(c.g(),2,"0")},H:function(){return o.pad(c.G(),2,"0")},i:function(){return o.pad(n.getMinutes(),2,"0")},s:function(){return o.pad(n.getSeconds(),2,"0")},u:function(){return o.pad(1e3*n.getMilliseconds(),6,"0")},O:function(){var e=n.getTimezoneOffset(),t=Math.abs(e);return(e>0?"-":"+")+o.pad(100*Math.floor(t/60)+t%60,4,"0")},P:function(){var e=c.O();return e.substr(0,3)+":"+e.substr(3,2)},Z:function(){return 60*-n.getTimezoneOffset()},c:function(){return"Y-m-d\\TH:i:sP".replace(r,s)},r:function(){return"D, d M Y H:i:s O".replace(r,s)},U:function(){return n.getTime()/1e3||0}};return e.replace(r,s)},o.numberFormat=function(e,t,n,r){t=isNaN(t)?2:Math.abs(t),n=void 0===n?".":n,r=void 0===r?",":r;var o=0>e?"-":"";e=Math.abs(+e||0);var i=parseInt(e.toFixed(t),10)+"",a=i.length>3?i.length%3:0;return o+(a?i.substr(0,a)+r:"")+i.substr(a).replace(/(\d{3})(?=\d)/g,"$1"+r)+(t?n+Math.abs(e-i).toFixed(t).slice(2):"")},o.naturalDay=function(e,t){e=void 0===e?o.time():e,t=void 0===t?"Y-m-d":t;var n=86400,r=new Date,i=new Date(r.getFullYear(),r.getMonth(),r.getDate()).getTime()/1e3;return i>e&&e>=i-n?"yesterday":e>=i&&i+n>e?"today":e>=i+n&&i+2*n>e?"tomorrow":o.date(t,e)},o.relativeTime=function(e){e=void 0===e?o.time():e;var t=o.time(),n=t-e;if(2>n&&n>-2)return(n>=0?"just ":"")+"now";if(60>n&&n>-60)return n>=0?Math.floor(n)+" seconds ago":"in "+Math.floor(-n)+" seconds";if(120>n&&n>-120)return n>=0?"about a minute ago":"in about a minute";if(3600>n&&n>-3600)return n>=0?Math.floor(n/60)+" minutes ago":"in "+Math.floor(-n/60)+" minutes";if(7200>n&&n>-7200)return n>=0?"about an hour ago":"in about an hour";if(86400>n&&n>-86400)return n>=0?Math.floor(n/3600)+" hours ago":"in "+Math.floor(-n/3600)+" hours";var r=172800;if(r>n&&n>-r)return n>=0?"1 day ago":"in 1 day";var i=2505600;if(i>n&&n>-i)return n>=0?Math.floor(n/86400)+" days ago":"in "+Math.floor(-n/86400)+" days";var a=5184e3;if(a>n&&n>-a)return n>=0?"about a month ago":"in about a month";var s=parseInt(o.date("Y",t),10),u=parseInt(o.date("Y",e),10),l=12*s+parseInt(o.date("n",t),10),c=12*u+parseInt(o.date("n",e),10),d=l-c;if(12>d&&d>-12)return d>=0?d+" months ago":"in "+-d+" months";var p=s-u;return 2>p&&p>-2?p>=0?"a year ago":"in a year":p>=0?p+" years ago":"in "+-p+" years"},o.ordinal=function(e){e=parseInt(e,10),e=isNaN(e)?0:e;var t=0>e?"-":"";e=Math.abs(e);var n=e%100;return t+e+(n>4&&21>n?"th":{1:"st",2:"nd",3:"rd"}[e%10]||"th")},o.filesize=function(e,t,n,r,i,a){return t=void 0===t?1024:t,0>=e?"0 bytes":(t>e&&void 0===n&&(n=0),void 0===a&&(a=" "),o.intword(e,["bytes","KB","MB","GB","TB","PB"],t,n,r,i,a))},o.intword=function(e,t,n,r,i,a,s){var u,l;t=t||["","K","M","B","T"],l=t.length-1,n=n||1e3,r=isNaN(r)?2:Math.abs(r),i=i||".",a=a||",",s=s||"";for(var c=0;c

"),e=e.replace(/\n/g,"
"),"

"+e+"

"},o.nl2br=function(e){return e.replace(/(\r\n|\n|\r)/g,"
")},o.truncatechars=function(e,t){return e.length<=t?e:e.substr(0,t)+"…"},o.truncatewords=function(e,t){var n=e.split(" ");return n.lengtht.documentElement.clientHeight;return{modalStyles:{paddingRight:r&&!o?y["default"]():void 0,paddingLeft:!r&&o?y["default"]():void 0}}}});H.Body=k["default"],H.Header=O["default"],H.Title=j["default"],H.Footer=R["default"],H.Dialog=D["default"],H.TRANSITION_DURATION=300,H.BACKDROP_TRANSITION_DURATION=150,t["default"]=f.bsSizes([m.Sizes.LARGE,m.Sizes.SMALL],f.bsClass("modal",H)),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(6)["default"],o=n(5)["default"];t.__esModule=!0;var i=n(1),a=o(i),s=n(7),u=o(s),l=n(10),c=o(l),d=n(35),p=a["default"].createClass({displayName:"ModalDialog",propTypes:{dialogClassName:a["default"].PropTypes.string},render:function(){var e=r({display:"block"},this.props.style),t=c["default"].prefix(this.props),n=c["default"].getClassSet(this.props);return delete n[t],n[c["default"].prefix(this.props,"dialog")]=!0,a["default"].createElement("div",r({},this.props,{title:null,tabIndex:"-1",role:"dialog",style:e,className:u["default"](this.props.className,t)}),a["default"].createElement("div",{className:u["default"](this.props.dialogClassName,n)},a["default"].createElement("div",{className:c["default"].prefix(this.props,"content"),role:"document"},this.props.children)))}});t["default"]=l.bsSizes([d.Sizes.LARGE,d.Sizes.SMALL],l.bsClass("modal",p)),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(15)["default"],o=n(14)["default"],i=n(6)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(1),u=a(s),l=n(7),c=a(l),d=n(10),p=a(d),f=function(e){function t(){o(this,t),e.apply(this,arguments)}return r(t,e),t.prototype.render=function(){return u["default"].createElement("div",i({},this.props,{className:c["default"](this.props.className,p["default"].prefix(this.props,"footer"))}),this.props.children)},t}(u["default"].Component);f.propTypes={bsClass:u["default"].PropTypes.string},f.defaultProps={bsClass:"modal"},t["default"]=d.bsClass("modal",f),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(15)["default"],o=n(14)["default"],i=n(6)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(1),u=a(s),l=n(7),c=a(l),d=n(10),p=a(d),f=function(e){function t(){o(this,t),e.apply(this,arguments)}return r(t,e),t.prototype.render=function(){return u["default"].createElement("h4",i({},this.props,{className:c["default"](this.props.className,p["default"].prefix(this.props,"title"))}),this.props.children)},t}(u["default"].Component);t["default"]=d.bsClass("modal",f),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(15)["default"],o=n(14)["default"],i=n(6)["default"],a=n(37)["default"],s=n(5)["default"];t.__esModule=!0;var u=n(1),l=s(u),c=n(323),d=s(c),p=n(59),f=s(p),h=n(120),m=s(h),g=n(7),y=s(g),v=function(e){function t(){o(this,t),e.apply(this,arguments)}return r(t,e),t.prototype.render=function(){var e=this.props,t=e.children,n=e.animation,r=a(e,["children","animation"]);return n===!0&&(n=m["default"]),n===!1&&(n=null),n||(t=u.cloneElement(t,{className:y["default"]("in",t.props.className)})),l["default"].createElement(d["default"],i({},r,{transition:n}),t)},t}(l["default"].Component);v.propTypes=i({},d["default"].propTypes,{show:l["default"].PropTypes.bool,rootClose:l["default"].PropTypes.bool,onHide:l["default"].PropTypes.func,animation:l["default"].PropTypes.oneOfType([l["default"].PropTypes.bool,f["default"]]),onEnter:l["default"].PropTypes.func,onEntering:l["default"].PropTypes.func,onEntered:l["default"].PropTypes.func,onExit:l["default"].PropTypes.func,onExiting:l["default"].PropTypes.func,onExited:l["default"].PropTypes.func}),v.defaultProps={animation:m["default"],rootClose:!1,show:!1},t["default"]=v,e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t){return Array.isArray(t)?t.indexOf(e)>=0:e===t}var o=n(6)["default"],i=n(72)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(49),u=a(s),l=n(154),c=a(l),d=n(1),p=a(d),f=n(12),h=a(f),m=n(60),g=(a(m),n(244)),y=a(g),v=n(36),M=a(v),T=p["default"].createClass({displayName:"OverlayTrigger",propTypes:o({},y["default"].propTypes,{trigger:p["default"].PropTypes.oneOfType([p["default"].PropTypes.oneOf(["click","hover","focus"]),p["default"].PropTypes.arrayOf(p["default"].PropTypes.oneOf(["click","hover","focus"]))]),delay:p["default"].PropTypes.number,delayShow:p["default"].PropTypes.number,delayHide:p["default"].PropTypes.number,defaultOverlayShown:p["default"].PropTypes.bool,overlay:p["default"].PropTypes.node.isRequired,onBlur:p["default"].PropTypes.func,onClick:p["default"].PropTypes.func,onFocus:p["default"].PropTypes.func,onMouseEnter:p["default"].PropTypes.func,onMouseLeave:p["default"].PropTypes.func,target:function(){},onHide:function(){},show:function(){}}),getDefaultProps:function(){return{defaultOverlayShown:!1,trigger:["hover","focus"]}},getInitialState:function(){return{isOverlayShown:this.props.defaultOverlayShown}},show:function(){this.setState({isOverlayShown:!0})},hide:function(){this.setState({isOverlayShown:!1})},toggle:function(){this.state.isOverlayShown?this.hide():this.show()},componentWillMount:function(){this.handleMouseOver=this.handleMouseOverOut.bind(null,this.handleDelayedShow),this.handleMouseOut=this.handleMouseOverOut.bind(null,this.handleDelayedHide)},componentDidMount:function(){this._mountNode=document.createElement("div"),this.renderOverlay()},renderOverlay:function(){h["default"].unstable_renderSubtreeIntoContainer(this,this._overlay,this._mountNode)},componentWillUnmount:function(){h["default"].unmountComponentAtNode(this._mountNode),this._mountNode=null,clearTimeout(this._hoverShowDelay),clearTimeout(this._hoverHideDelay)},componentDidUpdate:function(){this._mountNode&&this.renderOverlay()},getOverlayTarget:function(){return h["default"].findDOMNode(this)},getOverlay:function(){var e=o({},c["default"](this.props,i(y["default"].propTypes)),{show:this.state.isOverlayShown,onHide:this.hide,target:this.getOverlayTarget,onExit:this.props.onExit,onExiting:this.props.onExiting,onExited:this.props.onExited,onEnter:this.props.onEnter,onEntering:this.props.onEntering,onEntered:this.props.onEntered}),t=d.cloneElement(this.props.overlay,{placement:e.placement,container:e.container});return p["default"].createElement(y["default"],e,t)},render:function(){var e=p["default"].Children.only(this.props.children),t=e.props,n={"aria-describedby":this.props.overlay.props.id};return this._overlay=this.getOverlay(),n.onClick=M["default"](t.onClick,this.props.onClick),r("click",this.props.trigger)&&(n.onClick=M["default"](this.toggle,n.onClick)),r("hover",this.props.trigger)&&(n.onMouseOver=M["default"](this.handleMouseOver,this.props.onMouseOver,t.onMouseOver),n.onMouseOut=M["default"](this.handleMouseOut,this.props.onMouseOut,t.onMouseOut)),r("focus",this.props.trigger)&&(n.onFocus=M["default"](this.handleDelayedShow,this.props.onFocus,t.onFocus),n.onBlur=M["default"](this.handleDelayedHide,this.props.onBlur,t.onBlur)),d.cloneElement(e,n)},handleDelayedShow:function(){var e=this;if(null!=this._hoverHideDelay)return clearTimeout(this._hoverHideDelay),void(this._hoverHideDelay=null);if(!this.state.isOverlayShown&&null==this._hoverShowDelay){var t=null!=this.props.delayShow?this.props.delayShow:this.props.delay;return t?void(this._hoverShowDelay=setTimeout(function(){e._hoverShowDelay=null,e.show()},t)):void this.show()}},handleDelayedHide:function(){var e=this;if(null!=this._hoverShowDelay)return clearTimeout(this._hoverShowDelay),void(this._hoverShowDelay=null);if(this.state.isOverlayShown&&null==this._hoverHideDelay){var t=null!=this.props.delayHide?this.props.delayHide:this.props.delay;return t?void(this._hoverHideDelay=setTimeout(function(){e._hoverHideDelay=null,e.hide()},t)):void this.hide()}},handleMouseOverOut:function(e,t){var n=t.currentTarget,r=t.relatedTarget||t.nativeEvent.toElement;(!r||r!==n&&!u["default"](n,r))&&e(t)}});t["default"]=T,e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t,n){if(e[t]){var r=function(){var r=void 0,o=void 0;return c["default"].Children.forEach(e[t],function(e){e.type!==T&&(o=e.type.displayName?e.type.displayName:e.type,r=new Error("Children of "+n+" can contain only ProgressBar components. Found "+o))}),{v:r}}();if("object"==typeof r)return r.v}}var o=n(15)["default"],i=n(14)["default"],a=n(6)["default"],s=n(37)["default"],u=n(5)["default"];t.__esModule=!0;var l=n(1),c=u(l),d=n(238),p=u(d),f=n(10),h=u(f),m=n(35),g=n(7),y=u(g),v=n(47),M=u(v),T=function(e){function t(){i(this,t),e.apply(this,arguments)}return o(t,e),t.prototype.getPercentage=function(e,t,n){var r=1e3;return Math.round((e-t)/(n-t)*100*r)/r},t.prototype.render=function(){if(this.props.isChild)return this.renderProgressBar();var e=void 0;return e=this.props.children?M["default"].map(this.props.children,this.renderChildBar):this.renderProgressBar(),c["default"].createElement("div",a({},this.props,{className:y["default"](this.props.className,"progress"),min:null,max:null,label:null,"aria-valuetext":null}),e)},t.prototype.renderChildBar=function(e,t){return l.cloneElement(e,{isChild:!0,key:e.key?e.key:t})},t.prototype.renderProgressBar=function(){var e,t=this.props,n=t.className,r=t.label,o=t.now,i=t.min,u=t.max,l=s(t,["className","label","now","min","max"]),d=this.getPercentage(o,i,u);"string"==typeof r&&(r=this.renderLabel(d)),this.props.srOnly&&(r=c["default"].createElement("span",{className:"sr-only"},r));var p=y["default"](n,h["default"].getClassSet(this.props),(e={active:this.props.active},e[h["default"].prefix(this.props,"striped")]=this.props.active||this.props.striped,e));return c["default"].createElement("div",a({},l,{className:p,role:"progressbar",style:{width:d+"%"},"aria-valuenow":this.props.now,"aria-valuemin":this.props.min,"aria-valuemax":this.props.max}),r)},t.prototype.renderLabel=function(e){var t=this.props.interpolateClass||p["default"];return c["default"].createElement(t,{now:this.props.now,min:this.props.min,max:this.props.max,percent:e,bsStyle:this.props.bsStyle},this.props.label)},t}(c["default"].Component);T.propTypes=a({},T.propTypes,{min:l.PropTypes.number,now:l.PropTypes.number,max:l.PropTypes.number,label:l.PropTypes.node,srOnly:l.PropTypes.bool,striped:l.PropTypes.bool,active:l.PropTypes.bool,children:r,className:c["default"].PropTypes.string,interpolateClass:l.PropTypes.node,isChild:l.PropTypes.bool}),T.defaultProps=a({},T.defaultProps,{min:0,max:100,active:!1,isChild:!1,srOnly:!1,striped:!1}),t["default"]=f.bsStyles(m.State.values(),f.bsClass("progress-bar",T)),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(6)["default"],o=n(5)["default"];t.__esModule=!0;var i=n(1),a=o(i),s=n(7),u=o(s),l=n(10),c=o(l),d=n(163),p=o(d),f=a["default"].createClass({ +},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t,n){function r(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function o(e,t,n){if(e&&l(e)&&e instanceof r)return e;var o=new r;return o.parse(e,t,n),o}function i(e){return u(e)&&(e=o(e)),e instanceof r?e.format():r.prototype.format.call(e)}function a(e,t){return o(e,!1,!0).resolve(t)}function s(e,t){return e?o(e,!1,!0).resolveObject(t):t}function u(e){return"string"==typeof e}function l(e){return"object"==typeof e&&null!==e}function c(e){return null===e}function d(e){return null==e}var p=n(461);t.parse=o,t.resolve=a,t.resolveObject=s,t.format=i,t.Url=r;var f=/^([a-z0-9.+-]+:)/i,h=/:[0-9]*$/,m=["<",">",'"',"` + "`" + `"," ","\r","\n"," "],g=["{","}","|","\\","^","` + "`" + `"].concat(m),y=["'"].concat(g),v=["%","/","?",";","#"].concat(y),M=["/","?","#"],T=255,b=/^[a-z0-9A-Z_-]{0,63}$/,x=/^([a-z0-9A-Z_-]{0,63})(.*)$/,E={javascript:!0,"javascript:":!0},A={javascript:!0,"javascript:":!0},N={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},w=n(464);r.prototype.parse=function(e,t,n){if(!u(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var r=e;r=r.trim();var o=f.exec(r);if(o){o=o[0];var i=o.toLowerCase();this.protocol=i,r=r.substr(o.length)}if(n||o||r.match(/^\/\/[^@\/]+@[^@\/]+/)){var a="//"===r.substr(0,2);!a||o&&A[o]||(r=r.substr(2),this.slashes=!0)}if(!A[o]&&(a||o&&!N[o])){for(var s=-1,l=0;lc)&&(s=c)}var d,h;h=-1===s?r.lastIndexOf("@"):r.lastIndexOf("@",s),-1!==h&&(d=r.slice(0,h),r=r.slice(h+1),this.auth=decodeURIComponent(d)),s=-1;for(var l=0;lc)&&(s=c)}-1===s&&(s=r.length),this.host=r.slice(0,s),r=r.slice(s),this.parseHost(),this.hostname=this.hostname||"";var m="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!m)for(var g=this.hostname.split(/\./),l=0,I=g.length;I>l;l++){var C=g[l];if(C&&!C.match(b)){for(var D="",S=0,k=C.length;k>S;S++)D+=C.charCodeAt(S)>127?"x":C[S];if(!D.match(b)){var L=g.slice(0,l),O=g.slice(l+1),P=C.match(x);P&&(L.push(P[1]),O.unshift(P[2])),O.length&&(r="/"+O.join(".")+r),this.hostname=L.join(".");break}}}if(this.hostname.length>T?this.hostname="":this.hostname=this.hostname.toLowerCase(),!m){for(var j=this.hostname.split("."),z=[],l=0;ll;l++){var B=y[l],W=encodeURIComponent(B);W===B&&(W=escape(B)),r=r.split(B).join(W)}var F=r.indexOf("#");-1!==F&&(this.hash=r.substr(F),r=r.slice(0,F));var V=r.indexOf("?");if(-1!==V?(this.search=r.substr(V),this.query=r.substr(V+1),t&&(this.query=w.parse(this.query)),r=r.slice(0,V)):t&&(this.search="",this.query={}),r&&(this.pathname=r),N[i]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var U=this.pathname||"",R=this.search||"";this.path=U+R}return this.href=this.format(),this},r.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",r=this.hash||"",o=!1,i="";this.host?o=e+this.host:this.hostname&&(o=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(o+=":"+this.port)),this.query&&l(this.query)&&Object.keys(this.query).length&&(i=w.stringify(this.query));var a=this.search||i&&"?"+i||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||N[t])&&o!==!1?(o="//"+(o||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):o||(o=""),r&&"#"!==r.charAt(0)&&(r="#"+r),a&&"?"!==a.charAt(0)&&(a="?"+a),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),a=a.replace("#","%23"),t+o+n+a+r},r.prototype.resolve=function(e){return this.resolveObject(o(e,!1,!0)).format()},r.prototype.resolveObject=function(e){if(u(e)){var t=new r;t.parse(e,!1,!0),e=t}var n=new r;if(Object.keys(this).forEach(function(e){n[e]=this[e]},this),n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol)return Object.keys(e).forEach(function(t){"protocol"!==t&&(n[t]=e[t])}),N[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n;if(e.protocol&&e.protocol!==n.protocol){if(!N[e.protocol])return Object.keys(e).forEach(function(t){n[t]=e[t]}),n.href=n.format(),n;if(n.protocol=e.protocol,e.host||A[e.protocol])n.pathname=e.pathname;else{for(var o=(e.pathname||"").split("/");o.length&&!(e.host=o.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==o[0]&&o.unshift(""),o.length<2&&o.unshift(""),n.pathname=o.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var i=n.pathname||"",a=n.search||"";n.path=i+a}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var s=n.pathname&&"/"===n.pathname.charAt(0),l=e.host||e.pathname&&"/"===e.pathname.charAt(0),p=l||s||n.host&&e.pathname,f=p,h=n.pathname&&n.pathname.split("/")||[],o=e.pathname&&e.pathname.split("/")||[],m=n.protocol&&!N[n.protocol];if(m&&(n.hostname="",n.port=null,n.host&&(""===h[0]?h[0]=n.host:h.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===o[0]?o[0]=e.host:o.unshift(e.host)),e.host=null),p=p&&(""===o[0]||""===h[0])),l)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,h=o;else if(o.length)h||(h=[]),h.pop(),h=h.concat(o),n.search=e.search,n.query=e.query;else if(!d(e.search)){if(m){n.hostname=n.host=h.shift();var g=n.host&&n.host.indexOf("@")>0?n.host.split("@"):!1;g&&(n.auth=g.shift(),n.host=n.hostname=g.shift())}return n.search=e.search,n.query=e.query,c(n.pathname)&&c(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!h.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var y=h.slice(-1)[0],v=(n.host||e.host)&&("."===y||".."===y)||""===y,M=0,T=h.length;T>=0;T--)y=h[T],"."==y?h.splice(T,1):".."===y?(h.splice(T,1),M++):M&&(h.splice(T,1),M--);if(!p&&!f)for(;M--;M)h.unshift("..");!p||""===h[0]||h[0]&&"/"===h[0].charAt(0)||h.unshift(""),v&&"/"!==h.join("/").substr(-1)&&h.push("");var b=""===h[0]||h[0]&&"/"===h[0].charAt(0);if(m){n.hostname=n.host=b?"":h.length?h.shift():"";var g=n.host&&n.host.indexOf("@")>0?n.host.split("@"):!1;g&&(n.auth=g.shift(),n.host=n.hostname=g.shift())}return p=p||n.host&&h.length,p&&!b&&h.unshift(""),h.length?n.pathname=h.join("/"):(n.pathname=null,n.path=null),c(n.pathname)&&c(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},r.prototype.parseHost=function(){var e=this.host,t=h.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){return _.LoggedIn()?void 0:(F.dispatch(O.setLoginRedirectPath(location.pathname)),void t(k.minioBrowserPrefix+"/login"))}function a(e,t){_.LoggedIn()&&t(""+k.minioBrowserPrefix)}function s(){2>Z&&setTimeout(function(){document.querySelector(".page-load").classList.add("pl-"+Z),Z++,s()},G[Z])}n(451);var u=n(1),l=o(u),c=n(12),d=o(c),p=n(445),f=o(p),h=n(113),m=o(h),g=n(216),y=o(g),v=n(170),M=o(v),T=n(171),b=o(T),x=n(89),E=o(x),A=n(167),N=o(A),w=n(347),I=o(w),C=n(165),D=o(C),S=n(46),k=(o(S),n(45)),L=n(44),O=r(L),P=n(229),j=o(P),z=n(226),R=o(z),U=n(225),Y=o(U),B=n(116),W=o(B);window.Web=W["default"];var F=(0,y["default"])(f["default"])(m["default"])(j["default"]),V=(0,D["default"])(function(e){return e})(Y["default"]),H=(0,D["default"])(function(e){return e})(R["default"]),_=new W["default"](window.location.protocol+"//"+window.location.host+k.minioBrowserPrefix+"/webrpc",F.dispatch);"localhost:8080"===window.location.host&&(_=new W["default"]("http://localhost:9000"+k.minioBrowserPrefix+"/webrpc",F.dispatch)),window.web=_,F.dispatch(O.setWeb(_));var Q=function(e){return l["default"].createElement("div",null,e.children)};d["default"].render(l["default"].createElement(I["default"],{store:F,web:_},l["default"].createElement(b["default"],{history:E["default"]},l["default"].createElement(M["default"],{path:"/",component:Q},l["default"].createElement(M["default"],{path:"minio",component:Q},l["default"].createElement(N["default"],{component:V,onEnter:i}),l["default"].createElement(M["default"],{path:"login",component:H,onEnter:a}),l["default"].createElement(M["default"],{path:":bucket",component:V,onEnter:i}),l["default"].createElement(M["default"],{path:":bucket/*",component:V,onEnter:i}))))),document.getElementById("root"));var G=[10,400],Z=0;s(),localStorage.newlyUpdated&&(F.dispatch(O.showAlert({type:"success",message:"Updated to the latest UI Version."})),delete localStorage.newlyUpdated)},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n-1})))}},{key:"selectPrefix",value:function(e,t){var n=this.props,r=(n.dispatch,n.currentPath),o=(n.web,n.currentBucket);if(e.preventDefault(),t.endsWith("/")||""===t){if(t===r)return;h["default"].push(G.pathJoin(o,t))}else window.location=window.location.origin+"/minio/download/"+o+"/"+t+"?token="+localStorage.token}},{key:"makeBucket",value:function(e){e.preventDefault();var t=this.refs.makeBucketRef.value;this.refs.makeBucketRef.value="";var n=this.props,r=n.web,o=n.dispatch;this.hideMakeBucketModal(),r.MakeBucket({bucketName:t}).then(function(){o(_.addBucket(t)),o(_.selectBucket(t))})["catch"](function(e){return o(_.showAlert({type:"danger",message:e.message}))})}},{key:"hideMakeBucketModal",value:function(){var e=this.props.dispatch;e(_.hideMakeBucketModal())}},{key:"showMakeBucketModal",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.showMakeBucketModal())}},{key:"showAbout",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.showAbout())}},{key:"hideAbout",value:function(){var e=this.props.dispatch;e(_.hideAbout())}},{key:"uploadFile",value:function(e){e.preventDefault();var t=this.props,n=t.dispatch,r=t.upload;if(r.inProgress)return void n(_.showAlert({type:"danger",message:"An upload already in progress"}));var o=e.target.files[0];e.target.value=null,this.xhr=new XMLHttpRequest,n(_.uploadFile(o,this.xhr))}},{key:"removeObject",value:function(e,t){var n=this.props,r=n.web,o=n.dispatch,i=n.currentBucket,a=n.currentPath;r.RemoveObject({bucketName:i,objectName:a+t.name}).then(function(){return o(_.selectPrefix(a))})["catch"](function(e){return o(_.showAlert({type:"danger",message:e.message}))})}},{key:"uploadAbort",value:function(e){e.preventDefault();var t=this.props.dispatch;this.xhr.abort(),t(_.setUpload({inProgress:!1,percent:0})),this.hideAbortModal(e)}},{key:"showAbortModal",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.setShowAbortModal(!0))}},{key:"hideAbortModal",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.setShowAbortModal(!1))}},{key:"hideAlert",value:function(){var e=this.props.dispatch;e(_.hideAlert())}},{key:"dataType",value:function(e,t){return X.getDataType(e,t)}},{key:"sortObjectsByName",value:function(e){var t=this.props,n=t.dispatch,r=t.objects,o=t.sortNameOrder;n(_.setObjects(G.sortObjectsByName(r,!o))),n(_.setSortNameOrder(!o))}},{key:"sortObjectsBySize",value:function(){var e=this.props,t=e.dispatch,n=e.objects,r=e.sortSizeOrder;t(_.setObjects(G.sortObjectsBySize(n,!r))),t(_.setSortSizeOrder(!r))}},{key:"sortObjectsByDate",value:function(){var e=this.props,t=e.dispatch,n=e.objects,r=e.sortDateOrder;t(_.setObjects(G.sortObjectsByDate(n,!r))),t(_.setSortDateOrder(!r))}},{key:"logout",value:function(e){var t=this.props.web;e.preventDefault(),t.Logout(),h["default"].push(q.minioBrowserPrefix+"/login")}},{key:"landingPage",value:function(e){e.preventDefault(),this.props.dispatch(_.selectBucket(this.props.buckets[0]))}},{key:"fullScreen",value:function(e){e.preventDefault();var t=document.documentElement;t.requestFullscreen&&t.requestFullscreen(),t.mozRequestFullScreen&&t.mozRequestFullScreen(),t.webkitRequestFullscreen&&t.webkitRequestFullscreen(),t.msRequestFullscreen&&t.msRequestFullscreen()}},{key:"toggleSidebar",value:function(e){this.props.dispatch(_.setSidebarStatus(e))}},{key:"hideSidebar",value:function(e){var t=e||window.event,n=t.srcElement||t.target;3===n.nodeType&&(n=n.parentNode);var r=n.id;"mh-trigger"!==r&&this.props.dispatch(_.setSidebarStatus(!1))}},{key:"showSettings",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.showSettings()),web.GetAuth().then(function(e){t(_.setSettings({accessKey:e.accessKey,secretKey:e.secretKey}))})}},{key:"hideSettings",value:function(e){e.preventDefault();var t=this.props.dispatch;t(_.setSettings({accessKey:"",secretKey:"",secretKeyVisible:!1})),t(_.hideSettings())}},{key:"accessKeyChange",value:function(e){var t=this.props.dispatch;t(_.setSettings({accessKey:e.target.value}))}},{key:"secretKeyChange",value:function(e){var t=this.props.dispatch;t(_.setSettings({secretKey:e.target.value}))}},{key:"secretKeyVisible",value:function(e){var t=this.props.dispatch;t(_.setSettings({secretKeyVisible:e}))}},{key:"generateAuth",value:function(e){e.preventDefault();var t=this.props.dispatch;web.GenerateAuth().then(function(e){t(_.setSettings({secretKeyVisible:!0})),t(_.setSettings({accessKey:e.accessKey,secretKey:e.secretKey}))})}},{key:"setAuth",value:function(e){e.preventDefault();var t=this.props,n=t.web,r=t.dispatch,o=document.getElementById("accessKey").value,i=document.getElementById("secretKey").value;n.SetAuth({accessKey:o,secretKey:i}).then(function(e){r(_.setSettings({accessKey:"",secretKey:"",secretKeyVisible:!1})),r(_.hideSettings()),r(_.showAlert({type:"success",message:"Changed credentials"}))})["catch"](function(e){r(_.setSettings({accessKey:"",secretKey:"",secretKeyVisible:!1})),r(_.hideSettings()),r(_.showAlert({type:"danger",message:e.message}))})}},{key:"render",value:function(){var e=this.props.storageInfo,t=e.total,n=e.free,r=this.props,o=r.showMakeBucketModal,i=r.showAbortModal,a=r.upload,s=r.alert,u=r.sortNameOrder,l=r.sortSizeOrder,d=r.sortDateOrder,f=r.showAbout,h=r.showSettings,m=r.settings,g=this.props.serverInfo,y=g.version,M=g.memory,T=g.platform,b=g.runtime,E=this.props.sidebarStatus,N="",I=a.loaded/a.total*100;a.inProgress&&(N=c["default"].createElement("div",{className:"alert progress animated fadeInUp alert-info"},c["default"].createElement("button",{type:"button",className:"close",onClick:this.showAbortModal.bind(this)},c["default"].createElement("span",null,"×")),c["default"].createElement("div",{className:"text-center"},c["default"].createElement("small",null,a.filename)),c["default"].createElement(C["default"],{now:I}),c["default"].createElement("div",{className:"text-center"},c["default"].createElement("small",null,v["default"].filesize(a.loaded)," (",I.toFixed(2)," %)"))));var D=c["default"].createElement(S["default"],{className:(0,p["default"])({alert:!0,animated:!0,fadeInDown:s.show,fadeOutUp:!s.show}),bsStyle:s.type,onDismiss:this.hideAlert.bind(this)},c["default"].createElement("div",{className:"text-center"},s.message));s.message||(D="");var k="",O=(0,p["default"])({"abort-upload":!0}),j=(0,p["default"])({fa:!0,"fa-stop":!0}),z=(0,p["default"])({fa:!0,"fa-play":!0});i&&(k=c["default"].createElement(ee,{baseClass:O,text:"Abort the upload in progress?",okText:"Abort",okIcon:j,cancelText:"Continue",cancelIcon:z,okHandler:this.uploadAbort.bind(this),cancelHandler:this.hideAbortModal.bind(this)}));var R=(c["default"].createElement(P["default"],{id:"tt-sign-out"},"Sign out"),c["default"].createElement(P["default"],{id:"tt-upload-file"},"Upload file")),Y=c["default"].createElement(P["default"],{id:"tt-create-bucket"},"Create bucket"),B=t-n,F=B/t*100+"%";return c["default"].createElement("div",{className:(0,p["default"])({"file-explorer":!0,toggled:E})},k,c["default"].createElement(K,{landingPage:this.landingPage.bind(this),searchBuckets:this.searchBuckets.bind(this),selectBucket:this.selectBucket.bind(this),clickOutside:this.hideSidebar.bind(this)}),c["default"].createElement("div",{className:"fe-body"},D,c["default"].createElement("header",{className:"mobile-header hidden-lg hidden-md"},c["default"].createElement("div",{id:"mh-trigger",className:"mh-trigger "+(0,p["default"])({"mht-toggled":E}),onClick:this.toggleSidebar.bind(this,!E)},c["default"].createElement("div",{className:"mht-lines"},c["default"].createElement("div",{className:"top"}),c["default"].createElement("div",{className:"center"}),c["default"].createElement("div",{className:"bottom"}))),c["default"].createElement("img",{className:"mh-logo",src:V["default"],alt:""})),c["default"].createElement("header",{className:"fe-header"},c["default"].createElement($,{selectPrefix:this.selectPrefix.bind(this)}),c["default"].createElement("div",{className:"feh-usage"},c["default"].createElement("div",{className:"fehu-chart"},c["default"].createElement("div",{style:{width:F}})),c["default"].createElement("ul",null,c["default"].createElement("li",null,"Used: ",v["default"].filesize(t-n)),c["default"].createElement("li",{className:"pull-right"},"Free: ",v["default"].filesize(t-B)))),c["default"].createElement("ul",{className:"feh-actions"},c["default"].createElement(te,null),c["default"].createElement("li",null,c["default"].createElement(U["default"],{pullRight:!0,id:"top-right-menu"},c["default"].createElement(U["default"].Toggle,{noCaret:!0},c["default"].createElement("i",{className:"fa fa-reorder"})),c["default"].createElement(U["default"].Menu,{className:"dm-right"},c["default"].createElement("li",null,c["default"].createElement("a",{target:"_blank",href:"https://github.com/minio/miniobrowser"},"Github ",c["default"].createElement("i",{className:"fa fa-github"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.fullScreen.bind(this)},"Fullscreen ",c["default"].createElement("i",{className:"fa fa-expand"}))),c["default"].createElement("li",null,c["default"].createElement("a",{target:"_blank",href:"https://gitter.im/minio/minio"},"Ask for help ",c["default"].createElement("i",{className:"fa fa-question-circle"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.showAbout.bind(this)},"About ",c["default"].createElement("i",{className:"fa fa-info-circle"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.showSettings.bind(this)},"Settings ",c["default"].createElement("i",{className:"fa fa-cog"}))),c["default"].createElement("li",null,c["default"].createElement("a",{href:"",onClick:this.logout.bind(this)},"Sign Out ",c["default"].createElement("i",{className:"fa fa-sign-out"})))))))),c["default"].createElement("div",{className:"feb-container"},c["default"].createElement("header",{className:"fesl-row","data-type":"folder"},c["default"].createElement("div",{className:"fesl-item fi-name",onClick:this.sortObjectsByName.bind(this),"data-sort":"name"},"Name",c["default"].createElement("i",{className:(0,p["default"])({"fesli-sort":!0,fa:!0,"fa-sort-alpha-desc":u,"fa-sort-alpha-asc":!u})})),c["default"].createElement("div",{className:"fesl-item fi-size",onClick:this.sortObjectsBySize.bind(this),"data-sort":"size"},"Size",c["default"].createElement("i",{className:(0,p["default"])({"fesli-sort":!0,fa:!0,"fa-sort-amount-desc":l,"fa-sort-amount-asc":!l})})),c["default"].createElement("div",{className:"fesl-item fi-modified",onClick:this.sortObjectsByDate.bind(this),"data-sort":"last-modified"},"Last Modified",c["default"].createElement("i",{className:(0,p["default"])({"fesli-sort":!0,fa:!0,"fa-sort-numeric-desc":d,"fa-sort-numeric-asc":!d})})))),c["default"].createElement("div",{className:"feb-container"},c["default"].createElement(J,{removeObject:this.removeObject.bind(this),dataType:this.dataType.bind(this),selectPrefix:this.selectPrefix.bind(this)})),N,c["default"].createElement(U["default"],{dropup:!0,className:"feb-actions",id:"fe-action-toggle"},c["default"].createElement(U["default"].Toggle,{noCaret:!0,className:"feba-toggle"},c["default"].createElement("span",null,c["default"].createElement("i",{className:"fa fa-plus"}))),c["default"].createElement(U["default"].Menu,null,c["default"].createElement(L["default"],{placement:"left",overlay:R},c["default"].createElement("a",{href:"#",className:"feba-btn feba-upload"},c["default"].createElement("input",{type:"file",onChange:this.uploadFile.bind(this),style:{display:"none"},id:"file-input"}),c["default"].createElement("label",{htmlFor:"file-input"},c["default"].createElement("i",{style:{cursor:"pointer"},className:"fa fa-cloud-upload"})))),c["default"].createElement(L["default"],{placement:"left",overlay:Y},c["default"].createElement("a",{href:"#",className:"feba-btn feba-bucket",onClick:this.showMakeBucketModal.bind(this)},c["default"].createElement("i",{className:"fa fa-hdd-o"}))))),c["default"].createElement(x["default"],{className:"feb-modal",animation:!1,show:o,onHide:this.hideMakeBucketModal.bind(this)},c["default"].createElement("button",{className:"close",onClick:this.hideMakeBucketModal.bind(this)},c["default"].createElement("span",null,"×")),c["default"].createElement(A["default"],null,c["default"].createElement("form",{onSubmit:this.makeBucket.bind(this)},c["default"].createElement("div",{className:"create-bucket"},c["default"].createElement("input",{type:"text",autofocus:!0,ref:"makeBucketRef",placeholder:"Bucket Name"}),c["default"].createElement("i",null))))),c["default"].createElement(x["default"],{className:"about-modal modal-dark",show:f,onHide:this.hideAbout.bind(this)},c["default"].createElement("div",{className:"am-inner"},c["default"].createElement("div",{className:"ami-item hidden-xs"},c["default"].createElement("a",{href:"https://minio.io",target:"_blank"},c["default"].createElement("img",{className:"amii-logo",src:V["default"],alt:""}))),c["default"].createElement("div",{className:"ami-item"},c["default"].createElement("ul",{className:"amii-list"},c["default"].createElement("li",null,c["default"].createElement("div",null,"Version"),c["default"].createElement("small",null,y)),c["default"].createElement("li",null,c["default"].createElement("div",null,"Memory"),c["default"].createElement("small",null,M)),c["default"].createElement("li",null,c["default"].createElement("div",null,"Platform"),c["default"].createElement("small",null,T)),c["default"].createElement("li",null,c["default"].createElement("div",null,"Runtime"),c["default"].createElement("small",null,b))),c["default"].createElement("span",{className:"amii-close",onClick:this.hideAbout.bind(this)},c["default"].createElement("i",{className:"fa fa-check"}))))),c["default"].createElement(x["default"],{className:"modal-dark",bsSize:"sm",show:h},c["default"].createElement(w["default"],null,"Change Password"),c["default"].createElement(A["default"],null,c["default"].createElement("div",{className:"p-relative",style:{paddingRight:"35px",marginBottom:"20px"}},c["default"].createElement(W["default"],{value:m.accessKey,onChange:this.accessKeyChange.bind(this),id:"accessKey",label:"Access Key",name:"accesskey",type:"text",spellCheck:"false",required:"required",autoComplete:"false",align:"ig-left"})),c["default"].createElement("div",{className:"p-relative"},c["default"].createElement(W["default"],{value:m.secretKey,onChange:this.secretKeyChange.bind(this),id:"secretKey",label:"Secret Key",name:"accesskey",type:m.secretKeyVisible?"text":"password",spellCheck:"false",required:"required",autoComplete:"false",align:"ig-left"}),c["default"].createElement("i",{onClick:this.secretKeyVisible.bind(this,!m.secretKeyVisible),className:"toggle-password fa fa-eye "+(m.secretKeyVisible?"toggled":"")})),c["default"].createElement("div",{className:"clearfix"}),c["default"].createElement("div",{className:"form-footer clearfix"},c["default"].createElement(L["default"],{placement:"bottom",overlay:c["default"].createElement(P["default"],{id:"tt-password-generate" +},"Generate Keys")},c["default"].createElement("a",{href:"",className:"ff-btn ff-key-gen",onClick:this.generateAuth.bind(this)},c["default"].createElement("i",{className:"fa fa-repeat"}))),c["default"].createElement("a",{href:"",className:"ff-btn",onClick:this.setAuth.bind(this)},c["default"].createElement("i",{className:"fa fa-check"})),c["default"].createElement("a",{href:"",className:"ff-btn",onClick:this.hideSettings.bind(this)},c["default"].createElement("i",{className:"fa fa-times"})))))))}}]),t}(c["default"].Component);t["default"]=ne},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n-1&&n.objects.splice(r,1),n.objects=[t.object].concat(o(n.objects));break;case a.SET_UPLOAD:n.upload=t.upload;break;case a.SET_ALERT:n.alert.alertTimeout&&clearTimeout(n.alert.alertTimeout),t.alert.show?n.alert=t.alert:n.alert=Object.assign({},n.alert,{show:!1});break;case a.SET_LOGIN_ERROR:n.loginError=!0;break;case a.SET_SHOW_ABORT_MODAL:n.showAbortModal=t.showAbortModal;break;case a.SHOW_ABOUT:n.showAbout=t.showAbout;break;case a.SET_SORT_NAME_ORDER:n.sortNameOrder=t.sortNameOrder;break;case a.SET_SORT_SIZE_ORDER:n.sortSizeOrder=t.sortSizeOrder;break;case a.SET_SORT_DATE_ORDER:n.sortDateOrder=t.sortDateOrder;break;case a.SET_LATEST_UI_VERSION:n.latestUiVersion=t.latestUiVersion;break;case a.SET_SIDEBAR_STATUS:n.sidebarStatus=t.sidebarStatus;break;case a.SET_LOGIN_REDIRECT_PATH:n.loginRedirectPath=t.path;case a.SET_LOAD_BUCKET:n.loadBucket=t.loadBucket;break;case a.SET_LOAD_PATH:n.loadPath=t.loadPath;break;case a.SHOW_SETTINGS:n.showSettings=t.showSettings;break;case a.SET_SETTINGS:n.settings=Object.assign({},n.settings,t.settings)}return n}},function(e,t,n){t=e.exports=n(231)(),t.push([e.id,'*,:after,:before{box-sizing:border-box}a{color:#337ab7;text-decoration:none}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#edecec;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:15px;text-align:left;background-color:#fff;border:1px solid transparent;border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:rgba(0,0,0,.08)}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:gray;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{text-decoration:none;color:#333;background-color:rgba(0,0,0,.05)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#333;text-decoration:none;outline:0;background-color:rgba(0,0,0,.075)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#e4e4e4}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid\\9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.modal,.modal-open{overflow:hidden}.modal{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translateY(-25%);transform:translateY(-25%);-webkit-transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0);transform:translate(0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid transparent;border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:transparent}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:30px 35px 0;border-bottom:1px solid transparent}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:transparent}.modal-body{position:relative;padding:30px 35px 25px}.modal-footer{padding:30px 35px 25px;text-align:right;border-top:1px solid transparent}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:500px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:Lato,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:13px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px}.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{left:5px}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}@-ms-viewport{width:device-width}.visible-lg,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translateY(-20px)}to{opacity:1;-webkit-transform:translateY(0)}}@keyframes fadeInDown{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(20px)}to{opacity:1;-webkit-transform:translateY(0)}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateY(0)}to{opacity:0;-webkit-transform:translateY(20px)}}@keyframes fadeOutDown{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(20px)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutUp{0%{opacity:1;-webkit-transform:translateY(0)}to{opacity:0;-webkit-transform:translateY(-20px)}}@keyframes fadeOutUp{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-20px)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@font-face{font-family:Lato;src:url('+n(459)+") format('woff2'),url("+n(458)+") format('woff');font-weight:400;font-style:normal}@font-face{font-family:FontAwesome;src:url("+n(457)+") format('woff'),url("+n(456)+'#fontawesomeregular) format(\'svg\');font-weight:400;font-style:normal}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.clearfix:after,.clearfix:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before{content:" ";display:table}.clearfix:after,.modal-footer:after,.modal-header:after{clear:both}.pull-right{float:right!important}.pull-left{float:left!important}.p-relative{position:relative}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-search:before{content:"\\F002"}.fa-check:before{content:"\\F00C"}.fa-file-o:before{content:"\\F016"}.fa-refresh:before{content:"\\F021"}.fa-question-circle:before{content:"\\F059"}.fa-info-circle:before{content:"\\F05A"}.fa-expand:before{content:"\\F065"}.fa-plus:before{content:"\\F067"}.fa-warning:before{content:"\\F071"}.fa-sign-in:before{content:"\\F08B"}.fa-sign-out:before{content:"\\F090"}.fa-github:before{content:"\\F09B"}.fa-hdd-o:before{content:"\\F0A0"}.fa-globe:before{content:"\\F0AC"}.fa-cloud-upload:before{content:"\\F0EE"}.fa-file-text-o:before{content:"\\F0F6"}.fa-reorder:before{content:"\\F0C9"}.fa-sort-alpha-asc:before{content:"\\F15D"}.fa-sort-alpha-desc:before{content:"\\F15E"}.fa-sort-amount-asc:before{content:"\\F160"}.fa-sort-amount-desc:before{content:"\\F161"}.fa-sort-numeric-asc:before{content:"\\F162"}.fa-sort-numeric-desc:before{content:"\\F163"}.fa-file-pdf-o:before{content:"\\F1C1"}.fa-file-word-o:before{content:"\\F1C2"}.fa-file-excel-o:before{content:"\\F1C3"}.fa-file-powerpoint-o:before{content:"\\F1C4"}.fa-file-image-o:before{content:"\\F1C5"}.fa-file-zip-o:before{content:"\\F1C6"}.fa-file-audio-o:before{content:"\\F1C7"}.fa-file-video-o:before{content:"\\F1C8"}.fa-file-code-o:before{content:"\\F1C9"}.fa-play:before{content:"\\F04B"}.fa-stop:before{content:"\\F04D"}.fa-cog:before{content:\'\\F013\'}.fa-times:before{content:\'\\F00D\'}.fa-question:before{content:\'\\F128\'}.fa-repeat:before{content:\'\\F01E\'}.fa-eye:before{content:\'\\F06E\'}*{-webkit-font-smoothing:antialiased}:active,:focus{outline:0}*,:after,:before{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:Lato,sans-serif;font-size:15px;line-height:1.42857143;color:gray;background-color:#edecec}body,html{min-height:100%;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;-webkit-transition:color;transition:color;-webkit-transition-duration:.3s;transition-duration:.3s}a,a:focus,a:hover{text-decoration:none}a:focus,a:hover{color:#23527c}.fe-h2{font-weight:400;margin:0;line-height:100%;font-size:24px}.alert{border:0;position:fixed;max-width:500px;margin:0;box-shadow:0 4px 5px rgba(0,0,0,.1);color:#fff;width:100%;right:20px;border-radius:3px;z-index:12;padding:17px 50px 17px 17px}.alert:not(.progress){top:20px}@media (min-width:768px){.alert:not(.progress){left:50%;margin-left:-250px}}.alert.progress{bottom:20px;right:20px}.alert.alert-danger{background:#f55d5d}.alert.alert-success{background:#37d672}.alert.alert-info{background:#2196f3}@media (max-width:767px){.alert{left:20px;width:calc(100% - 40px);max-width:100%}}.alert .progress{margin:10px 10px 8px 0;height:5px;box-shadow:none;border-radius:1px;background-color:#1d82d2;border-radius:2px;overflow:hidden}.alert .progress-bar{box-shadow:none;background-color:#fff;height:100%}.alert .close{position:absolute;right:15px;top:15px}.more{display:block;color:hsla(0,0%,100%,.7);font-size:13px;margin-top:2px}.more:hover{color:#fff}.modal-header{color:hsla(0,0%,100%,.4);font-size:13px;text-transform:uppercase}.modal-header small{display:block;text-transform:none;font-size:12px;margin-top:3px;color:hsla(0,0%,100%,.2)}.modal-content{border-radius:3px;box-shadow:0 4px 5px rgba(0,0,0,.1)}.modal-dark .modal-content{background-color:#32393f;box-shadow:0 2px 13px rgba(0,0,0,.5)}.dropdown-menu{padding:15px 0;top:0;margin-top:-1px}.dropdown-menu>li>a{padding:8px 20px;font-size:15px}.dropdown-menu>li>a>i{width:20px}.dm-right>li>a{text-align:right}.close{right:19px;font-weight:400;opacity:1;font-size:18px;position:absolute;text-align:center;top:16px;z-index:1;padding:0;border:0;background-color:transparent}.close span{width:25px;height:25px;background:hsla(0,0%,100%,.18);display:block;border-radius:50%;line-height:24px;text-shadow:none;color:#fff}.close:focus span,.close:hover span{background-color:hsla(0,0%,100%,.25);color:#fff}.input-group{position:relative}.input-group:not(:last-child){margin-bottom:20px}.ig-label{position:absolute;text-align:center;bottom:7px;left:0;width:100%;color:hsla(0,0%,100%,.55);font-size:15px;transition:all .15s;-webkit-transition:all;transition:all;-webkit-transition-duration:.15s;transition-duration:.15s;padding:2px 0 3px;border-radius:2px;font-weight:400}.ig-helpers{position:relative;z-index:1}.ig-helpers i:first-child{height:1px;width:100%;background:#42494e;display:block}.ig-helpers i:last-child{position:absolute;height:1px;width:100%;background:#fff;left:0;bottom:0;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:scale(0);transform:scale(0)}.ig-helpers i:after,.ig-helpers i:before{position:absolute;bottom:1px;background:hsla(0,0%,100%,.2);width:1px;height:10px}.ig-helpers i:before{left:0}.ig-helpers i:after{right:0}.ig-text{width:100%;height:50px;border:0;background:transparent;text-align:center;color:#fff;position:relative;z-index:1}.ig-text:focus+label+.ig-helpers i:last-child{-webkit-transform:scale(1);transform:scale(1)}.ig-text:focus+label+.ig-helpers i:last-child:after,.ig-text:focus+label+.ig-helpers i:last-child:before{width:1px;background:#fff}.ig-text:focus+.ig-label,.ig-text:valid+.ig-label{bottom:40px;font-size:13px;z-index:1;color:hsla(0,0%,100%,.3)}.ig-left .ig-label,.ig-left .ig-text{text-align:left}.ig-error .ig-label{color:#e23f3f}.ig-error .ig-helpers i:first-child,.ig-error .ig-helpers i:first-child:after,.ig-error .ig-helpers i:first-child:before{background:rgba(226,63,63,.43)}.ig-error .ig-helpers i:last-child,.ig-error .ig-helpers i:last-child:after,.ig-error .ig-helpers i:last-child:before{background:#e23f3f!important}.ig-error:after{content:"\\F05A";font-family:FontAwesome;position:absolute;top:17px;right:9px;font-size:20px;color:#d33d3e}.form-footer{margin:15px -6px 0;text-align:center}.ff-btn{display:inline-block;width:40px;height:40px;line-height:36px;color:#fff;border:1px solid #fff;border-radius:50%;font-size:15px;margin:0 6px;opacity:.3;text-align:center;-webkit-transition:all;transition:all;-webkit-transition-duration:.25s;transition-duration:.25s}.ff-btn:focus,.ff-btn:hover{color:#fff;opacity:.6}.login{height:100vh;min-height:500px;background:#32393f;text-align:center}.login:before{height:calc(100% - 110px);width:1px;content:""}.l-wrap,.login:before{display:inline-block;vertical-align:middle}.l-wrap{width:80%;max-width:500px;margin-top:-50px}.l-wrap.toggled{display:inline-block}.l-wrap .input-group:not(:last-child){margin-bottom:40px}.l-footer{height:110px;padding:0 50px}.lf-logo{float:right}.lf-logo img{width:40px}.lf-server{float:left;color:hsla(0,0%,100%,.4);font-size:20px;font-weight:400;padding-top:40px}@media (max-width:768px){.lf-logo,.lf-server{float:none;display:block;text-align:center;width:100%}.lf-logo{margin-bottom:5px}.lf-server{font-size:15px}}.lw-btn{width:50px;height:50px;border:1px solid #fff;display:inline-block;border-radius:50%;font-size:22px;color:#fff;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;opacity:.3;background-color:transparent;line-height:45px;padding:0}.lw-btn:hover{color:#fff;opacity:.8;border-color:#fff}.lw-btn i{display:block;width:100%;padding-left:3px}input:-webkit-autofill{-webkit-box-shadow:0 0 0 50px #32393f inset!important;-webkit-text-fill-color:#fff!important}.fe-header{padding:45px 55px 20px}@media (min-width:992px){.fe-header{position:relative}}@media (max-width:667px){.fe-header{padding:25px 30px 20px}}.fe-header h2{font-size:17px;margin-bottom:0}.fe-header h2>span{margin-bottom:7px;display:inline-block}.fe-header h2>span>a{color:#589fdc}.fe-header h2>span>a:hover{color:#4984b7}.fe-header h2>span:not(:first-child):before{content:\'/\';margin:0 4px;color:#c1c1c1}.fe-header p{color:#bdbdbd;margin-top:7px}.feh-usage{margin-top:12px;max-width:285px}@media (max-width:667px){.feh-usage{max-width:100%;font-size:12px}}.feh-usage>ul{color:#bdbdbd;margin-top:7px;list-style:none;padding:0}.feh-usage>ul>li{padding-right:0;display:inline-block}.feh-usage>ul>li:first-child{color:#2ed2ff}.fehu-chart{height:5px;background:#eee;position:relative;border-radius:2px;overflow:hidden}.fehu-chart>div{position:absolute;left:0;height:100%;background:#2ed2ff}.feh-actions{list-style:none;padding:0;margin:0;position:absolute;right:35px;top:30px;z-index:11}@media (max-width:991px){.feh-actions{top:7px;right:10px;position:fixed}}.feh-actions>li{display:inline-block;text-align:right;vertical-align:top;line-height:100%}.feh-actions>li>.btn-group>button,.feh-actions>li>a{display:block;height:45px;min-width:45px;text-align:center;border-radius:50%;padding:0;border:0;background:none}@media (min-width:992px){.feh-actions>li>.btn-group>button,.feh-actions>li>a{color:#7b7b7b;font-size:21px;line-height:45px;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s}.feh-actions>li>.btn-group>button:hover,.feh-actions>li>a:hover{background:rgba(0,0,0,.09)}}@media (max-width:991px){.feh-actions>li>.btn-group>button,.feh-actions>li>a{color:#eaeaea;font-size:16px}.feh-actions>li>.btn-group>button .fa-reorder:before,.feh-actions>li>a .fa-reorder:before{content:\'\\F142\'}}.feha-search{position:relative}.feha-search:before{color:gray;font-family:fontAwesome;content:\'\\F002\';position:absolute;top:14px;font-size:18px;left:20px}.feha-search input[type=text]{border:0;width:350px;background:#f3f3f3;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;padding:15px 20px 17px 55px;border-radius:2px;margin:0 0 15px}.feha-search input[type=text]::-webkit-input-placeholder{color:gray}.feha-search input[type=text]::-moz-placeholder{color:gray}.feha-search input[type=text]:-ms-input-placeholder{color:gray}.feha-search input[type=text]:focus{box-shadow:0 0 1px -2px #eaeaea;background:#eaeaea;width:500px}@media (max-width:767px){.about-modal{text-align:center}.about-modal .modal-dialog{max-width:400px;width:90%;margin:20px auto 0}}.am-inner{display:flex;flex-direction:row;align-items:center;min-height:350px;position:relative}@media (min-width:768px){.am-inner:before{content:\'\';width:150px;height:100%;top:0;left:0;position:absolute;background-color:#23282c}}.ami-item:first-child{width:150px;text-align:center}.ami-item:last-child{flex:4;padding:30px}.amii-logo{width:70px;position:relative}.amii-list{list-style:none;padding:0}.amii-list>li{margin-bottom:15px}.amii-list>li div{color:hsla(0,0%,100%,.8);text-transform:uppercase;font-size:14px}.amii-list>li small{font-size:13px;color:hsla(0,0%,100%,.4)}.amii-close{width:40px;height:40px;display:inline-block;border:1px solid #fff;border-radius:50%;line-height:37px;font-size:17px;color:#fff;margin-top:10px;opacity:.4;-webkit-transition:opacity;transition:opacity;-webkit-transition-duration:.3s;transition-duration:.3s;text-align:center;cursor:pointer}.amii-close:hover{opacity:.8;color:#fff}@media (max-width:991px){.mobile-header{background-color:#23282c;padding:10px 10px 9px;text-align:center;position:fixed;z-index:10;box-shadow:0 0 10px rgba(0,0,0,.65);left:0;top:0;width:100%}.mobile-header .mh-logo{height:35px;position:relative;top:4px}.mh-trigger{width:41px;height:41px;cursor:pointer;float:left;position:relative;text-align:center}.mh-trigger:after,.mh-trigger:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%}.mh-trigger:after{z-index:1}.mh-trigger:before{background:hsla(0,0%,100%,.1);-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:scale(0);transform:scale(0)}.mht-toggled:before{-webkit-transform:scale(1);transform:scale(1)}.mht-toggled .mht-lines{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.mht-toggled .mht-lines>div.top{width:12px;transform:translateX(8px) translateY(1px) rotate(45deg);-webkit-transform:translateX(8px) translateY(1px) rotate(45deg)}.mht-toggled .mht-lines>div.bottom{width:12px;transform:translateX(8px) translateY(-1px) rotate(-45deg);-webkit-transform:translateX(8px) translateY(-1px) rotate(-45deg)}.mht-lines,.mht-lines>div{-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s}.mht-lines{width:18px;height:12px;display:inline-block;margin-top:14px}.mht-lines>div{background-color:#eaeaea;width:18px;height:2px}.mht-lines>div.center{margin:3px 0}}.ff-key-gen{position:relative;border-color:#ffc500;color:#ffc500;opacity:.4}.ff-key-gen:hover{opacity:.7;color:#ffc500}.ff-key-gen .fa-refresh{font-size:11px;position:absolute;bottom:16px;left:18px}.ff-key-gen .fa-key{position:relative;left:-3px;top:3px}.toggle-password{position:absolute;bottom:0;right:0;width:30px;height:25px;border:1px solid #42494e;border-radius:0;line-height:25px;text-align:center;font-size:12px;cursor:pointer;z-index:10}.toggle-password:hover{background:hsla(0,0%,100%,.02)}.toggle-password.active,.toggle-password.toggled{background:#42494e}.fe-sidebar{width:300px;background-color:#32393f;position:fixed;height:100%;overflow:hidden;color:#fff;padding:35px}@media (min-width:992px){.fe-sidebar{-webkit-transform:translateZ(0);transform:translateZ(0)}}@media (max-width:991px){.fe-sidebar{padding-top:85px;z-index:9;box-shadow:0 0 10px rgba(0,0,0,.65);-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:translate3d(-300px,0,0);transform:translate3d(-300px,0,0)}.fe-sidebar.toggled{-webkit-transform:translateZ(0);transform:translateZ(0)}}.fe-sidebar a{color:hsla(0,0%,100%,.58)}.fe-sidebar a:hover{color:#fff}.fes-header{margin-bottom:40px}.fes-header h2,.fes-header img{float:left}.fes-header h2{margin:13px 0 0 10px}.fes-header img{width:32px}.fesl-search{position:relative;margin-bottom:20px}.fesl-search:before{color:hsla(0,0%,100%,.4);font-family:fontAwesome;content:\'\\F002\';top:1px;font-size:15px;position:absolute;left:0}.fesl-search>i{position:absolute;left:0;bottom:0;content:"";height:1px;width:0;background:#fff;-webkit-transition:all;transition:all;-webkit-transition-duration:.25s;transition-duration:.25s}.fesl-search input[type=text]{width:100%;color:#fff;background:transparent;border:0;border-bottom:1px solid hsla(0,0%,100%,.1);padding:0 2px 10px 25px;font-size:14px}.fesl-search input[type=text]::-moz-placeholder{color:hsla(0,0%,100%,.4);opacity:1}.fesl-search input[type=text]:-ms-input-placeholder{color:hsla(0,0%,100%,.4)}.fesl-search input[type=text]::-webkit-input-placeholder{color:hsla(0,0%,100%,.4)}.fesl-search input[type=text]:focus+i{width:100%}.fesl-inner{height:calc(100vh - 260px);overflow:auto;padding:0;margin:0 -35px}.fesl-inner li{position:relative}.fesl-inner li>a{display:block;padding:10px 40px 12px 35px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fesl-inner li>a:before{font-family:FontAwesome;content:\'\\F0A0\';font-size:17px;margin-right:15px;position:relative;top:1px;opacity:.8;filter:alpha(opacity=80)}.fesl-inner li.active>a{background-color:rgba(0,0,0,.2);color:#fff}.fesl-inner li:not(.active)>a:hover{background-color:rgba(0,0,0,.1)}.fesl-inner ul{list-style:none;padding:0;margin:0}.fesl-inner:hover .scrollbar-vertical{opacity:1}.scrollbar-vertical{position:absolute;right:5px;width:4px;height:100%;opacity:0;-webkit-transition:opacity;transition:opacity;-webkit-transition-duration:.3s;transition-duration:.3s}.scrollbar-vertical div{border-radius:1px!important;background-color:#6a6a6a!important}.fes-host{position:fixed;left:0;bottom:0;z-index:1;background:#32393f;color:hsla(0,0%,100%,.4);font-size:20px;font-weight:400;width:300px;padding:20px 20px 20px 34px}.fes-host>i{margin-right:10px}.fesl-row{padding-right:40px;padding-top:5px;padding-bottom:5px;position:relative}@media (min-width:668px){.fesl-row{display:flex;flex-flow:row;justify-content:space-between}}.fesl-row:after,.fesl-row:before{content:" ";display:table}.fesl-row:after{clear:both}@media (min-width:668px){header.fesl-row{margin-bottom:20px;border-bottom:1px solid #f0f0f0;padding-left:40px}header.fesl-row .fesl-item,header.fesl-row .fesli-sort{-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s}header.fesl-row .fesl-item{cursor:pointer;color:#bdbdbd;font-weight:500;margin-bottom:-5px}header.fesl-row .fesl-item>.fesli-sort{float:right;margin:4px 0 0;opacity:0;filter:alpha(opacity=0);color:#32393f;font-size:14px}header.fesl-row .fesl-item:hover{background:#f5f5f5;color:#32393f}header.fesl-row .fesl-item:hover>.fesli-sort{opacity:.5;filter:alpha(opacity=50)}}@media (max-width:667px){header.fesl-row{display:none}}div.fesl-row{padding-left:85px;border-bottom:1px solid transparent;cursor:default}@media (max-width:667px){div.fesl-row{padding-left:75px;padding-right:20px}}div.fesl-row:nth-child(even){background-color:#f7f7f7}div.fesl-row.context-menu-active,div.fesl-row.ui-selected,div.fesl-row.ui-selecting{background-color:#03a9f4;color:#fff}div.fesl-row.context-menu-active .fesl-item:before,div.fesl-row.context-menu-active a,div.fesl-row.ui-selected .fesl-item:before,div.fesl-row.ui-selected a,div.fesl-row.ui-selecting .fesl-item:before,div.fesl-row.ui-selecting a{color:#fff}div.fesl-row.ui-selected:nth-child(even){background-color:#03a9f4}div.fesl-row[data-type]:before{font-family:fontAwesome;width:35px;height:35px;vertical-align:top;text-align:center;line-height:35px;position:absolute;border-radius:50%;font-size:16px;left:50px;top:9px;color:#fff}@media (max-width:667px){div.fesl-row[data-type]:before{left:25px}}@media (max-width:667px){div.fesl-row[data-type=folder] .fesl-item.fi-name{padding-top:10px;padding-bottom:7px}div.fesl-row[data-type=folder] .fesl-item.fi-modified,div.fesl-row[data-type=folder] .fesl-item.fi-size{display:none}}div.fesl-row[data-type=folder]:before{content:\'\\F114\';background-color:#2dd3fb}div.fesl-row[data-type=pdf]:before{content:"\\F1C1";background-color:#fb766d}div.fesl-row[data-type=zip]:before{content:"\\F1C6";background-color:#374952}div.fesl-row[data-type=audio]:before{content:"\\F1C7";background-color:#009688}div.fesl-row[data-type=code]:before{content:"\\F1C9";background-color:#997867}div.fesl-row[data-type=excel]:before{content:"\\F1C3";background-color:#64c866}div.fesl-row[data-type=image]:before{content:"\\F1C5";background-color:#d24ce9}div.fesl-row[data-type=video]:before{content:"\\F1C8";background-color:#fdc206}div.fesl-row[data-type=other]:before{content:"\\F016";background-color:#8a8a8a}div.fesl-row[data-type=text]:before{content:"\\F0F6";background-color:#8a8a8a}div.fesl-row[data-type=doc]:before{content:"\\F1C2";background-color:#2196f5}div.fesl-row[data-type=presentation]:before{content:"\\F1C4";background-color:#fba220}div.fesl-row.fesl-loading:before{content:\'\'}.fesl-item{padding:10px 15px;color:gray;display:block;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.fesl-item a{color:gray}@media (min-width:668px){.fesl-item.fi-name{flex:3}.fesl-item.fi-size{width:140px}.fesl-item.fi-modified{width:190px}}@media (max-width:667px){.fesl-item{padding:0}.fesl-item.fi-name{width:100%;margin-bottom:3px}.fesl-item.fi-modified,.fesl-item.fi-size{font-size:12px;color:#b5b5b5}.fesl-item.fi-size{float:left}.fesl-item.fi-modified{float:right}}.create-bucket{position:relative}.create-bucket input[type=text]{width:100%;border:0;color:#fff;background:transparent;text-align:center;height:40px}.create-bucket input[type=text]::-moz-placeholder{color:#fff;opacity:1}.create-bucket input[type=text]:-ms-input-placeholder{color:#fff}.create-bucket input[type=text]::-webkit-input-placeholder{color:#fff}.create-bucket input[type=text]:focus+i:before{-webkit-transform:scale(1)!important;transform:scale(1)!important}.create-bucket i,.create-bucket i:before{position:absolute;height:1px;width:100%;left:0;bottom:0}.create-bucket i{background-color:hsla(0,0%,100%,.44)}.create-bucket i:before{background:#fff;-webkit-transition:all;transition:all;-webkit-transition-duration:.3s;transition-duration:.3s;content:\'\';-webkit-transform:scale(0);transform:scale(0);z-index:1}.file-explorer{background-color:#fff;position:relative;height:100%}.file-explorer.toggled{height:100vh;overflow:hidden}.fe-body{min-height:100vh;overflow:auto}@media (min-width:992px){.fe-body{padding:0 0 40px 300px}}@media (max-width:991px){.fe-body{padding:75px 0 40px}}.feb-actions{position:fixed;bottom:30px;right:30px}.feb-actions .dropdown-menu{min-width:55px;width:55px;text-align:center;background:transparent;box-shadow:none;margin:0}.feb-actions.open .feba-btn{-webkit-transform:scale(1);transform:scale(1)}.feb-actions.open .feba-btn:first-child{-webkit-animation-name:feba-btn-anim;animation-name:feba-btn-anim;-webkit-animation-duration:.3s;animation-duration:.3s}.feb-actions.open .feba-btn:last-child{-webkit-animation-name:feba-btn-anim;animation-name:feba-btn-anim;-webkit-animation-duration:.1s;animation-duration:.1s}.feb-actions.open .feba-toggle{background:#d23327}.feb-actions.open .feba-toggle>span{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.feba-toggle{width:55px;height:55px;line-height:55px;border-radius:50%;background:#f44336;box-shadow:0 3px 6px rgba(0,0,0,.35);display:inline-block;text-align:center;border:0;padding:0}.feba-toggle span{display:inline-block;height:100%;width:100%}.feba-toggle i{color:#fff;font-size:17px;line-height:58px}.feba-toggle,.feba-toggle>span{-webkit-transition:all;transition:all;-webkit-transition-duration:.25s;transition-duration:.25s;-webkit-backface-visibility:hidden;backface-visibility:hidden}.feba-btn{width:40px;margin-top:10px;height:40px;border-radius:50%;text-align:center;display:inline-block;line-height:40px;box-shadow:0 3px 4px rgba(0,0,0,.15);-webkit-transform:scale(0);transform:scale(0);position:relative}.feba-btn,.feba-btn:focus,.feba-btn:hover{color:#fff}.feba-btn label{width:100%;height:100%;position:absolute;left:0;top:0;cursor:pointer}.feba-bucket{background:#ff9800}.feba-upload{background:#ffc107}@-webkit-keyframes feba-btn-anim{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes feba-btn-anim{0%{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}.feb-modal .modal-content{background-color:#ff9800}.feb-modal .modal-dialog{-webkit-animation-name:fadeIn;animation-name:fadeIn;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both;width:330px;position:fixed;right:25px;bottom:95px;margin:0;height:110px}.feb-modal .modal-content{width:100%;height:100%}.abort-upload .modal-dialog{margin:0;width:100%;height:100%}.abort-upload .modal-content{width:510px;position:fixed;right:19px;bottom:17px;background-color:#f55d5d;color:#fff;text-align:center}.abort-upload .cm-text{margin-bottom:10px;font-size:14px}.abort-upload .cmf-btn{border:0;background:hsla(0,0%,100%,.2);margin:0 5px;border-radius:2px;color:#fff;font-size:13px;padding:7px 12px;position:relative}.abort-upload .cmf-btn>i{font-size:11px;margin-right:8px;position:relative;top:-1px}.abort-upload .cmf-btn:hover{background-color:hsla(0,0%,100%,.3)}.l-bucket,.l-listing{width:23px;height:23px;-webkit-animation-name:zoomIn;animation-name:zoomIn;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.l-bucket>i,.l-listing>i{border-width:2px}.l-bucket{left:31px;top:10px}.l-bucket>i{background-color:#32393f;border-top-color:hsla(0,0%,100%,.1);border-right-color:hsla(0,0%,100%,.1);border-bottom-color:hsla(0,0%,100%,.1)}.active .l-bucket>i{background-color:#282e32}.l-listing{left:56px;top:15px}.l-listing>i{border-top-color:hsla(0,0%,100%,.4);border-right-color:hsla(0,0%,100%,.4);border-bottom-color:hsla(0,0%,100%,.4)}@media (max-width:667px){.l-listing{left:31px}}.ie-warning{background-color:#ff5252;width:100%;height:100%;position:fixed;left:0;top:0;text-align:center}.ie-warning:before{width:1px;content:\'\';height:100%}.ie-warning .iw-inner,.ie-warning:before{display:inline-block;vertical-align:middle}.iw-inner{width:470px;height:300px;background-color:#fff;border-radius:5px;padding:40px;position:relative}.iw-inner ul{list-style:none;padding:0;margin:0;width:230px;margin-left:80px;margin-top:16px}.iw-inner ul>li{float:left}.iw-inner ul>li>a{display:block;padding:10px 15px 7px;font-size:14px;margin:0 1px;border-radius:3px}.iw-inner ul>li>a:hover{background:#eee}.iw-inner ul>li>a img{height:40px;margin-bottom:5px}.iwi-icon{color:#ff5252;font-size:40px;display:block;line-height:100%;margin-bottom:15px}.iwi-skip{position:absolute;left:0;bottom:-35px;width:100%;color:hsla(0,0%,100%,.6);cursor:pointer}.iwi-skip:hover{color:#fff}',""]); +},function(e,t){e.exports=function(){var e=[];return e.toString=function(){for(var e=[],t=0;t1&&(n=n.charAt(0)):n=" ",r=void 0===r?"left":"right","right"===r)for(;e.length4&&21>e?"th":{1:"st",2:"nd",3:"rd"}[e%10]||"th"},w:function(){return n.getDay()},z:function(){return(c.L()?a[c.n()]:i[c.n()])+c.j()-1},W:function(){var e=c.z()-c.N()+1.5;return o.pad(1+Math.floor(Math.abs(e)/7)+(e%7>3.5?1:0),2,"0")},F:function(){return l[n.getMonth()]},m:function(){return o.pad(c.n(),2,"0")},M:function(){return c.F().slice(0,3)},n:function(){return n.getMonth()+1},t:function(){return new Date(c.Y(),c.n(),0).getDate()},L:function(){return 1===new Date(c.Y(),1,29).getMonth()?1:0},o:function(){var e=c.n(),t=c.W();return c.Y()+(12===e&&9>t?-1:1===e&&t>9)},Y:function(){return n.getFullYear()},y:function(){return String(c.Y()).slice(-2)},a:function(){return n.getHours()>11?"pm":"am"},A:function(){return c.a().toUpperCase()},B:function(){var e=n.getTime()/1e3,t=e%86400+3600;0>t&&(t+=86400);var r=t/86.4%1e3;return 0>e?Math.ceil(r):Math.floor(r)},g:function(){return c.G()%12||12},G:function(){return n.getHours()},h:function(){return o.pad(c.g(),2,"0")},H:function(){return o.pad(c.G(),2,"0")},i:function(){return o.pad(n.getMinutes(),2,"0")},s:function(){return o.pad(n.getSeconds(),2,"0")},u:function(){return o.pad(1e3*n.getMilliseconds(),6,"0")},O:function(){var e=n.getTimezoneOffset(),t=Math.abs(e);return(e>0?"-":"+")+o.pad(100*Math.floor(t/60)+t%60,4,"0")},P:function(){var e=c.O();return e.substr(0,3)+":"+e.substr(3,2)},Z:function(){return 60*-n.getTimezoneOffset()},c:function(){return"Y-m-d\\TH:i:sP".replace(r,s)},r:function(){return"D, d M Y H:i:s O".replace(r,s)},U:function(){return n.getTime()/1e3||0}};return e.replace(r,s)},o.numberFormat=function(e,t,n,r){t=isNaN(t)?2:Math.abs(t),n=void 0===n?".":n,r=void 0===r?",":r;var o=0>e?"-":"";e=Math.abs(+e||0);var i=parseInt(e.toFixed(t),10)+"",a=i.length>3?i.length%3:0;return o+(a?i.substr(0,a)+r:"")+i.substr(a).replace(/(\d{3})(?=\d)/g,"$1"+r)+(t?n+Math.abs(e-i).toFixed(t).slice(2):"")},o.naturalDay=function(e,t){e=void 0===e?o.time():e,t=void 0===t?"Y-m-d":t;var n=86400,r=new Date,i=new Date(r.getFullYear(),r.getMonth(),r.getDate()).getTime()/1e3;return i>e&&e>=i-n?"yesterday":e>=i&&i+n>e?"today":e>=i+n&&i+2*n>e?"tomorrow":o.date(t,e)},o.relativeTime=function(e){e=void 0===e?o.time():e;var t=o.time(),n=t-e;if(2>n&&n>-2)return(n>=0?"just ":"")+"now";if(60>n&&n>-60)return n>=0?Math.floor(n)+" seconds ago":"in "+Math.floor(-n)+" seconds";if(120>n&&n>-120)return n>=0?"about a minute ago":"in about a minute";if(3600>n&&n>-3600)return n>=0?Math.floor(n/60)+" minutes ago":"in "+Math.floor(-n/60)+" minutes";if(7200>n&&n>-7200)return n>=0?"about an hour ago":"in about an hour";if(86400>n&&n>-86400)return n>=0?Math.floor(n/3600)+" hours ago":"in "+Math.floor(-n/3600)+" hours";var r=172800;if(r>n&&n>-r)return n>=0?"1 day ago":"in 1 day";var i=2505600;if(i>n&&n>-i)return n>=0?Math.floor(n/86400)+" days ago":"in "+Math.floor(-n/86400)+" days";var a=5184e3;if(a>n&&n>-a)return n>=0?"about a month ago":"in about a month";var s=parseInt(o.date("Y",t),10),u=parseInt(o.date("Y",e),10),l=12*s+parseInt(o.date("n",t),10),c=12*u+parseInt(o.date("n",e),10),d=l-c;if(12>d&&d>-12)return d>=0?d+" months ago":"in "+-d+" months";var p=s-u;return 2>p&&p>-2?p>=0?"a year ago":"in a year":p>=0?p+" years ago":"in "+-p+" years"},o.ordinal=function(e){e=parseInt(e,10),e=isNaN(e)?0:e;var t=0>e?"-":"";e=Math.abs(e);var n=e%100;return t+e+(n>4&&21>n?"th":{1:"st",2:"nd",3:"rd"}[e%10]||"th")},o.filesize=function(e,t,n,r,i,a){return t=void 0===t?1024:t,0>=e?"0 bytes":(t>e&&void 0===n&&(n=0),void 0===a&&(a=" "),o.intword(e,["bytes","KB","MB","GB","TB","PB"],t,n,r,i,a))},o.intword=function(e,t,n,r,i,a,s){var u,l;t=t||["","K","M","B","T"],l=t.length-1,n=n||1e3,r=isNaN(r)?2:Math.abs(r),i=i||".",a=a||",",s=s||"";for(var c=0;c

"),e=e.replace(/\n/g,"
"),"

"+e+"

"},o.nl2br=function(e){return e.replace(/(\r\n|\n|\r)/g,"
")},o.truncatechars=function(e,t){return e.length<=t?e:e.substr(0,t)+"…"},o.truncatewords=function(e,t){var n=e.split(" ");return n.lengtht.documentElement.clientHeight;return{modalStyles:{paddingRight:r&&!o?y["default"]():void 0,paddingLeft:!r&&o?y["default"]():void 0}}}});H.Body=k["default"],H.Header=O["default"],H.Title=j["default"],H.Footer=R["default"],H.Dialog=D["default"],H.TRANSITION_DURATION=300,H.BACKDROP_TRANSITION_DURATION=150,t["default"]=f.bsSizes([m.Sizes.LARGE,m.Sizes.SMALL],f.bsClass("modal",H)),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(6)["default"],o=n(5)["default"];t.__esModule=!0;var i=n(1),a=o(i),s=n(7),u=o(s),l=n(10),c=o(l),d=n(35),p=a["default"].createClass({displayName:"ModalDialog",propTypes:{dialogClassName:a["default"].PropTypes.string},render:function(){var e=r({display:"block"},this.props.style),t=c["default"].prefix(this.props),n=c["default"].getClassSet(this.props);return delete n[t],n[c["default"].prefix(this.props,"dialog")]=!0,a["default"].createElement("div",r({},this.props,{title:null,tabIndex:"-1",role:"dialog",style:e,className:u["default"](this.props.className,t)}),a["default"].createElement("div",{className:u["default"](this.props.dialogClassName,n)},a["default"].createElement("div",{className:c["default"].prefix(this.props,"content"),role:"document"},this.props.children)))}});t["default"]=l.bsSizes([d.Sizes.LARGE,d.Sizes.SMALL],l.bsClass("modal",p)),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(15)["default"],o=n(14)["default"],i=n(6)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(1),u=a(s),l=n(7),c=a(l),d=n(10),p=a(d),f=function(e){function t(){o(this,t),e.apply(this,arguments)}return r(t,e),t.prototype.render=function(){return u["default"].createElement("div",i({},this.props,{className:c["default"](this.props.className,p["default"].prefix(this.props,"footer"))}),this.props.children)},t}(u["default"].Component);f.propTypes={bsClass:u["default"].PropTypes.string},f.defaultProps={bsClass:"modal"},t["default"]=d.bsClass("modal",f),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(15)["default"],o=n(14)["default"],i=n(6)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(1),u=a(s),l=n(7),c=a(l),d=n(10),p=a(d),f=function(e){function t(){o(this,t),e.apply(this,arguments)}return r(t,e),t.prototype.render=function(){return u["default"].createElement("h4",i({},this.props,{className:c["default"](this.props.className,p["default"].prefix(this.props,"title"))}),this.props.children)},t}(u["default"].Component);t["default"]=d.bsClass("modal",f),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(15)["default"],o=n(14)["default"],i=n(6)["default"],a=n(37)["default"],s=n(5)["default"];t.__esModule=!0;var u=n(1),l=s(u),c=n(323),d=s(c),p=n(59),f=s(p),h=n(120),m=s(h),g=n(7),y=s(g),v=function(e){function t(){o(this,t),e.apply(this,arguments)}return r(t,e),t.prototype.render=function(){var e=this.props,t=e.children,n=e.animation,r=a(e,["children","animation"]);return n===!0&&(n=m["default"]),n===!1&&(n=null),n||(t=u.cloneElement(t,{className:y["default"]("in",t.props.className)})),l["default"].createElement(d["default"],i({},r,{transition:n}),t)},t}(l["default"].Component);v.propTypes=i({},d["default"].propTypes,{show:l["default"].PropTypes.bool,rootClose:l["default"].PropTypes.bool,onHide:l["default"].PropTypes.func,animation:l["default"].PropTypes.oneOfType([l["default"].PropTypes.bool,f["default"]]),onEnter:l["default"].PropTypes.func,onEntering:l["default"].PropTypes.func,onEntered:l["default"].PropTypes.func,onExit:l["default"].PropTypes.func,onExiting:l["default"].PropTypes.func,onExited:l["default"].PropTypes.func}),v.defaultProps={animation:m["default"],rootClose:!1,show:!1},t["default"]=v,e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t){return Array.isArray(t)?t.indexOf(e)>=0:e===t}var o=n(6)["default"],i=n(72)["default"],a=n(5)["default"];t.__esModule=!0;var s=n(49),u=a(s),l=n(154),c=a(l),d=n(1),p=a(d),f=n(12),h=a(f),m=n(60),g=(a(m),n(244)),y=a(g),v=n(36),M=a(v),T=p["default"].createClass({displayName:"OverlayTrigger",propTypes:o({},y["default"].propTypes,{trigger:p["default"].PropTypes.oneOfType([p["default"].PropTypes.oneOf(["click","hover","focus"]),p["default"].PropTypes.arrayOf(p["default"].PropTypes.oneOf(["click","hover","focus"]))]),delay:p["default"].PropTypes.number,delayShow:p["default"].PropTypes.number,delayHide:p["default"].PropTypes.number,defaultOverlayShown:p["default"].PropTypes.bool,overlay:p["default"].PropTypes.node.isRequired,onBlur:p["default"].PropTypes.func,onClick:p["default"].PropTypes.func,onFocus:p["default"].PropTypes.func,onMouseEnter:p["default"].PropTypes.func,onMouseLeave:p["default"].PropTypes.func,target:function(){},onHide:function(){},show:function(){}}),getDefaultProps:function(){return{defaultOverlayShown:!1,trigger:["hover","focus"]}},getInitialState:function(){return{isOverlayShown:this.props.defaultOverlayShown}},show:function(){this.setState({isOverlayShown:!0})},hide:function(){this.setState({isOverlayShown:!1})},toggle:function(){this.state.isOverlayShown?this.hide():this.show()},componentWillMount:function(){this.handleMouseOver=this.handleMouseOverOut.bind(null,this.handleDelayedShow),this.handleMouseOut=this.handleMouseOverOut.bind(null,this.handleDelayedHide)},componentDidMount:function(){this._mountNode=document.createElement("div"),this.renderOverlay()},renderOverlay:function(){h["default"].unstable_renderSubtreeIntoContainer(this,this._overlay,this._mountNode)},componentWillUnmount:function(){h["default"].unmountComponentAtNode(this._mountNode),this._mountNode=null,clearTimeout(this._hoverShowDelay),clearTimeout(this._hoverHideDelay)},componentDidUpdate:function(){this._mountNode&&this.renderOverlay()},getOverlayTarget:function(){return h["default"].findDOMNode(this)},getOverlay:function(){var e=o({},c["default"](this.props,i(y["default"].propTypes)),{show:this.state.isOverlayShown,onHide:this.hide,target:this.getOverlayTarget,onExit:this.props.onExit,onExiting:this.props.onExiting,onExited:this.props.onExited,onEnter:this.props.onEnter,onEntering:this.props.onEntering,onEntered:this.props.onEntered}),t=d.cloneElement(this.props.overlay,{placement:e.placement,container:e.container});return p["default"].createElement(y["default"],e,t)},render:function(){var e=p["default"].Children.only(this.props.children),t=e.props,n={"aria-describedby":this.props.overlay.props.id};return this._overlay=this.getOverlay(),n.onClick=M["default"](t.onClick,this.props.onClick),r("click",this.props.trigger)&&(n.onClick=M["default"](this.toggle,n.onClick)),r("hover",this.props.trigger)&&(n.onMouseOver=M["default"](this.handleMouseOver,this.props.onMouseOver,t.onMouseOver),n.onMouseOut=M["default"](this.handleMouseOut,this.props.onMouseOut,t.onMouseOut)),r("focus",this.props.trigger)&&(n.onFocus=M["default"](this.handleDelayedShow,this.props.onFocus,t.onFocus),n.onBlur=M["default"](this.handleDelayedHide,this.props.onBlur,t.onBlur)),d.cloneElement(e,n)},handleDelayedShow:function(){var e=this;if(null!=this._hoverHideDelay)return clearTimeout(this._hoverHideDelay),void(this._hoverHideDelay=null);if(!this.state.isOverlayShown&&null==this._hoverShowDelay){var t=null!=this.props.delayShow?this.props.delayShow:this.props.delay;return t?void(this._hoverShowDelay=setTimeout(function(){e._hoverShowDelay=null,e.show()},t)):void this.show()}},handleDelayedHide:function(){var e=this;if(null!=this._hoverShowDelay)return clearTimeout(this._hoverShowDelay),void(this._hoverShowDelay=null);if(this.state.isOverlayShown&&null==this._hoverHideDelay){var t=null!=this.props.delayHide?this.props.delayHide:this.props.delay;return t?void(this._hoverHideDelay=setTimeout(function(){e._hoverHideDelay=null,e.hide()},t)):void this.hide()}},handleMouseOverOut:function(e,t){var n=t.currentTarget,r=t.relatedTarget||t.nativeEvent.toElement;(!r||r!==n&&!u["default"](n,r))&&e(t)}});t["default"]=T,e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t,n){if(e[t]){var r=function(){var r=void 0,o=void 0;return c["default"].Children.forEach(e[t],function(e){e.type!==T&&(o=e.type.displayName?e.type.displayName:e.type,r=new Error("Children of "+n+" can contain only ProgressBar components. Found "+o))}),{v:r}}();if("object"==typeof r)return r.v}}var o=n(15)["default"],i=n(14)["default"],a=n(6)["default"],s=n(37)["default"],u=n(5)["default"];t.__esModule=!0;var l=n(1),c=u(l),d=n(238),p=u(d),f=n(10),h=u(f),m=n(35),g=n(7),y=u(g),v=n(47),M=u(v),T=function(e){function t(){i(this,t),e.apply(this,arguments)}return o(t,e),t.prototype.getPercentage=function(e,t,n){var r=1e3;return Math.round((e-t)/(n-t)*100*r)/r},t.prototype.render=function(){if(this.props.isChild)return this.renderProgressBar();var e=void 0;return e=this.props.children?M["default"].map(this.props.children,this.renderChildBar):this.renderProgressBar(),c["default"].createElement("div",a({},this.props,{className:y["default"](this.props.className,"progress"),min:null,max:null,label:null,"aria-valuetext":null}),e)},t.prototype.renderChildBar=function(e,t){return l.cloneElement(e,{isChild:!0,key:e.key?e.key:t})},t.prototype.renderProgressBar=function(){var e,t=this.props,n=t.className,r=t.label,o=t.now,i=t.min,u=t.max,l=s(t,["className","label","now","min","max"]),d=this.getPercentage(o,i,u);"string"==typeof r&&(r=this.renderLabel(d)),this.props.srOnly&&(r=c["default"].createElement("span",{className:"sr-only"},r));var p=y["default"](n,h["default"].getClassSet(this.props),(e={active:this.props.active},e[h["default"].prefix(this.props,"striped")]=this.props.active||this.props.striped,e));return c["default"].createElement("div",a({},l,{className:p,role:"progressbar",style:{width:d+"%"},"aria-valuenow":this.props.now,"aria-valuemin":this.props.min,"aria-valuemax":this.props.max}),r)},t.prototype.renderLabel=function(e){var t=this.props.interpolateClass||p["default"];return c["default"].createElement(t,{now:this.props.now,min:this.props.min,max:this.props.max,percent:e,bsStyle:this.props.bsStyle},this.props.label)},t}(c["default"].Component);T.propTypes=a({},T.propTypes,{min:l.PropTypes.number,now:l.PropTypes.number,max:l.PropTypes.number,label:l.PropTypes.node,srOnly:l.PropTypes.bool,striped:l.PropTypes.bool,active:l.PropTypes.bool,children:r,className:c["default"].PropTypes.string,interpolateClass:l.PropTypes.node,isChild:l.PropTypes.bool}),T.defaultProps=a({},T.defaultProps,{min:0,max:100,active:!1,isChild:!1,srOnly:!1,striped:!1}),t["default"]=f.bsStyles(m.State.values(),f.bsClass("progress-bar",T)),e.exports=t["default"]},function(e,t,n){"use strict";var r=n(6)["default"],o=n(5)["default"];t.__esModule=!0;var i=n(1),a=o(i),s=n(7),u=o(s),l=n(10),c=o(l),d=n(163),p=o(d),f=a["default"].createClass({ displayName:"Tooltip",propTypes:{id:p["default"](a["default"].PropTypes.oneOfType([a["default"].PropTypes.string,a["default"].PropTypes.number])),placement:a["default"].PropTypes.oneOf(["top","right","bottom","left"]),positionLeft:a["default"].PropTypes.number,positionTop:a["default"].PropTypes.number,arrowOffsetLeft:a["default"].PropTypes.oneOfType([a["default"].PropTypes.number,a["default"].PropTypes.string]),arrowOffsetTop:a["default"].PropTypes.oneOfType([a["default"].PropTypes.number,a["default"].PropTypes.string]),title:a["default"].PropTypes.node},getDefaultProps:function(){return{bsClass:"tooltip",placement:"right"}},render:function(){var e,t=(e={},e[c["default"].prefix(this.props)]=!0,e[this.props.placement]=!0,e),n=r({left:this.props.positionLeft,top:this.props.positionTop},this.props.style),o={left:this.props.arrowOffsetLeft,top:this.props.arrowOffsetTop};return a["default"].createElement("div",r({role:"tooltip"},this.props,{className:u["default"](this.props.className,t),style:n}),a["default"].createElement("div",{className:c["default"].prefix(this.props,"arrow"),style:o}),a["default"].createElement("div",{className:c["default"].prefix(this.props,"inner")},this.props.children))}});t["default"]=f,e.exports=t["default"]},function(e,t,n){"use strict";var r=n(5)["default"];t.__esModule=!0;var o=n(162),i=n(249),a=r(i);t["default"]={requiredRoles:function(){for(var e=arguments.length,t=Array(e),n=0;e>n;n++)t[n]=arguments[n];return o.createChainableTypeChecker(function(e,n,r){var o=void 0,i=a["default"](e.children),s=function(e,t){return e===t.props.bsRole};return t.every(function(e){return i.some(function(t){return s(e,t)})?!0:(o=e,!1)}),o?new Error("(children) "+r+" - Missing a required child with bsRole: "+o+". "+(r+" must have at least one child of each of the following bsRoles: "+t.join(", "))):void 0})},exclusiveRoles:function(){for(var e=arguments.length,t=Array(e),n=0;e>n;n++)t[n]=arguments[n];return o.createChainableTypeChecker(function(e,n,r){var o=a["default"](e.children),i=void 0;return t.every(function(e){var t=o.filter(function(t){return t.props.bsRole===e});return t.length>1?(i=e,!1):!0}),i?new Error("(children) "+r+" - Duplicate children detected of bsRole: "+i+". Only one child each allowed with the following bsRoles: "+t.join(", ")):void 0})}},e.exports=t["default"]},function(e,t,n){"use strict";function r(e){var t=[];return void 0===e?t:(a["default"].forEach(e,function(e){t.push(e)}),t)}var o=n(5)["default"];t.__esModule=!0,t["default"]=r;var i=n(47),a=o(i);e.exports=t["default"]},function(e,t,n){e.exports={"default":n(254),__esModule:!0}},function(e,t,n){n(264),e.exports=n(48).Object.assign},function(e,t,n){var r=n(74);e.exports=function(e,t){return r.create(e,t)}},function(e,t,n){n(265),e.exports=n(48).Object.keys},function(e,t,n){n(266),e.exports=n(48).Object.setPrototypeOf},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(128);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){var r=n(257);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==r(e)?e.split(""):Object(e)}},function(e,t,n){var r=n(74),o=n(129),i=n(260);e.exports=n(127)(function(){var e=Object.assign,t={},n={},r=Symbol(),o="abcdefghijklmnopqrst";return t[r]=7,o.split("").forEach(function(e){n[e]=e}),7!=e({},t)[r]||Object.keys(e({},n)).join("")!=o})?function(e,t){for(var n=o(e),a=arguments,s=a.length,u=1,l=r.getKeys,c=r.getSymbols,d=r.isEnum;s>u;)for(var p,f=i(a[u++]),h=c?l(f).concat(c(f)):l(f),m=h.length,g=0;m>g;)d.call(f,p=h[g++])&&(n[p]=f[p]);return n}:Object.assign},function(e,t,n){var r=n(73),o=n(48),i=n(127);e.exports=function(e,t){var n=(o.Object||{})[e]||Object[e],a={};a[e]=t(n),r(r.S+r.F*i(function(){n(1)}),"Object",a)}},function(e,t,n){var r=n(74).getDesc,o=n(128),i=n(256),a=function(e,t){if(i(e),!o(t)&&null!==t)throw TypeError(t+": can't set as prototype!")};e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,t,o){try{o=n(126)(Function.call,r(Object.prototype,"__proto__").set,2),o(e,[]),t=!(e instanceof Array)}catch(i){t=!0}return function(e,n){return a(e,n),t?e.__proto__=n:o(e,n),e}}({},!1):void 0),check:a}},function(e,t,n){var r=n(73);r(r.S+r.F,"Object",{assign:n(261)})},function(e,t,n){var r=n(129);n(262)("keys",function(e){return function(t){return e(r(t))}})},function(e,t,n){var r=n(73);r(r.S,"Object",{setPrototypeOf:n(263).set})},function(e,t,n){"use strict";var r=n(131);e.exports=function(e,t){e.classList?e.classList.add(t):r(e)||(e.className=e.className+" "+t)}},function(e,t,n){"use strict";e.exports={addClass:n(267),removeClass:n(269),hasClass:n(131)}},function(e,t){"use strict";e.exports=function(e,t){e.classList?e.classList.remove(t):e.className=e.className.replace(new RegExp("(^|\\s)"+t+"(?:\\s|$)","g"),"$1").replace(/\s+/g," ").replace(/^\s*|\s*$/g,"")}},function(e,t,n){"use strict";var r=n(49),o=n(274);e.exports=function(e,t){return function(n){var i=n.currentTarget,a=n.target,s=o(i,e);s.some(function(e){return r(e,a)})&&t.call(this,n)}}},function(e,t,n){"use strict";var r=n(75),o=n(132),i=n(270);e.exports={on:r,off:o,filter:i}},function(e,t,n){"use strict";function r(e){return e.nodeName&&e.nodeName.toLowerCase()}function o(e){for(var t=(0,s["default"])(e),n=e&&e.offsetParent;n&&"html"!==r(e)&&"static"===(0,l["default"])(n,"position");)n=n.offsetParent;return n||t.documentElement}var i=n(57);t.__esModule=!0,t["default"]=o;var a=n(38),s=i.interopRequireDefault(a),u=n(76),l=i.interopRequireDefault(u);e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e.nodeName&&e.nodeName.toLowerCase()}function o(e,t){var n,o={top:0,left:0};return"fixed"===(0,m["default"])(e,"position")?n=e.getBoundingClientRect():(t=t||(0,l["default"])(e),n=(0,s["default"])(e),"html"!==r(t)&&(o=(0,s["default"])(t)),o.top+=parseInt((0,m["default"])(t,"borderTopWidth"),10)-(0,d["default"])(t)||0,o.left+=parseInt((0,m["default"])(t,"borderLeftWidth"),10)-(0,f["default"])(t)||0),i._extends({},n,{top:n.top-o.top-(parseInt((0,m["default"])(e,"marginTop"),10)||0),left:n.left-o.left-(parseInt((0,m["default"])(e,"marginLeft"),10)||0)})}var i=n(57);t.__esModule=!0,t["default"]=o;var a=n(133),s=i.interopRequireDefault(a),u=n(272),l=i.interopRequireDefault(u),c=n(134),d=i.interopRequireDefault(c),p=n(275),f=i.interopRequireDefault(p),h=n(76),m=i.interopRequireDefault(h);e.exports=t["default"]},function(e,t){"use strict";var n=/^[\w-]*$/,r=Function.prototype.bind.call(Function.prototype.call,[].slice);e.exports=function(e,t){var o,i="#"===t[0],a="."===t[0],s=i||a?t.slice(1):t,u=n.test(s);return u?i?(e=e.getElementById?e:document,(o=e.getElementById(s))?[o]:[]):r(e.getElementsByClassName&&a?e.getElementsByClassName(s):e.getElementsByTagName(t)):r(e.querySelectorAll(t))}},function(e,t,n){"use strict";var r=n(56);e.exports=function(e,t){var n=r(e);return void 0===t?n?"pageXOffset"in n?n.pageXOffset:n.document.documentElement.scrollLeft:e.scrollLeft:void(n?n.scrollTo(t,"pageYOffset"in n?n.pageYOffset:n.document.documentElement.scrollTop):e.scrollLeft=t)}},function(e,t,n){"use strict";var r=n(57),o=n(135),i=r.interopRequireDefault(o),a=/^(top|right|bottom|left)$/,s=/^([+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|))(?!px)[a-z%]+$/i;e.exports=function(e){if(!e)throw new TypeError("No Element passed to ` + "`" + `getComputedStyle()` + "`" + `");var t=e.ownerDocument;return"defaultView"in t?t.defaultView.opener?e.ownerDocument.defaultView.getComputedStyle(e,null):window.getComputedStyle(e,null):{getPropertyValue:function(t){var n=e.style;t=(0,i["default"])(t),"float"==t&&(t="styleFloat");var r=e.currentStyle[t]||null;if(null==r&&n&&n[t]&&(r=n[t]),s.test(r)&&!a.test(t)){var o=n.left,u=e.runtimeStyle,l=u&&u.left;l&&(u.left=e.currentStyle.left),n.left="fontSize"===t?"1em":r,r=n.pixelLeft+"px",n.left=o,l&&(u.left=l)}return r}}}},function(e,t){"use strict";e.exports=function(e,t){return"removeProperty"in e.style?e.style.removeProperty(t):e.style.removeAttribute(t)}},function(e,t,n){"use strict";function r(){var e,t="",n={O:"otransitionend",Moz:"transitionend",Webkit:"webkitTransitionEnd",ms:"MSTransitionEnd"},r=document.createElement("div");for(var o in n)if(l.call(n,o)&&void 0!==r.style[o+"TransitionProperty"]){t="-"+o.toLowerCase()+"-",e=n[o];break}return e||void 0===r.style.transitionProperty||(e="transitionend"),{end:e,prefix:t}}var o,i,a,s,u=n(29),l=Object.prototype.hasOwnProperty,c="transform",d={};u&&(d=r(),c=d.prefix+c,a=d.prefix+"transition-property",i=d.prefix+"transition-duration",s=d.prefix+"transition-delay",o=d.prefix+"transition-timing-function"),e.exports={transform:c,end:d.end,property:a,timing:o,delay:s,duration:i}},function(e,t){"use strict";var n=/-(.)/g;e.exports=function(e){return e.replace(n,function(e,t){return t.toUpperCase()})}},function(e,t){"use strict";var n=/([A-Z])/g;e.exports=function(e){return e.replace(n,"-$1").toLowerCase()}},function(e,t,n){"use strict";var r=n(280),o=/^ms-/;e.exports=function(e){return r(e).replace(o,"-ms-")}},function(e,t){function n(e){var t=e?e.length:0;return t?e[t-1]:void 0}e.exports=n},function(e,t,n){var r=n(291),o=n(309),i=o(r);e.exports=i},function(e,t,n){(function(t){function r(e){var t=e?e.length:0;for(this.data={hash:s(null),set:new a};t--;)this.push(e[t])}var o=n(305),i=n(58),a=i(t,"Set"),s=i(Object,"create");r.prototype.push=o,e.exports=r}).call(t,function(){return this}())},function(e,t){function n(e,t){for(var n=-1,r=e.length;++n=s?a(t):null,p=t.length;d&&(l=i,c=!1,t=d);e:for(;++ut&&(t=-t>o?0:o+t),n=void 0===n||n>o?o:+n||0,0>n&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var i=Array(o);++r-1?n[l]:void 0}return i(n,r,e)}}var o=n(289),i=n(292),a=n(293),s=n(24);e.exports=r},function(e,t,n){function r(e,t,n,r,i,a,s){var u=-1,l=e.length,c=t.length;if(l!=c&&!(i&&c>l))return!1;for(;++u=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}t.__esModule=!0;var i=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}t.__esModule=!0;var s=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}t.__esModule=!0;var s=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}function s(){}t.__esModule=!0;var u=Object.assign||function(e){for(var t=1;tn;n++)t[n]=arguments[n];return t.filter(function(e){return null!=e}).reduce(function(e,t){if("function"!=typeof t)throw new Error("Invalid Argument Type, must only provide functions, undefined, or null.");return null===e?t:function(){for(var n=arguments.length,r=Array(n),o=0;n>o;o++)r[o]=arguments[o];e.apply(this,r),t.apply(this,r)}},null)}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t){"use strict";function n(e,t){t&&(e?t.setAttribute("aria-hidden","true"):t.removeAttribute("aria-hidden"))}function r(e,t){s(e,t,function(e){return n(!0,e)})}function o(e,t){s(e,t,function(e){return n(!1,e)})}t.__esModule=!0,t.ariaHidden=n,t.hideSiblings=r,t.showSiblings=o;var i=["template","script","style"],a=function(e){var t=e.nodeType,n=e.tagName;return 1===t&&-1===i.indexOf(n.toLowerCase())},s=function(e,t,n){t=[].concat(t),[].forEach.call(e.children,function(e){-1===t.indexOf(e)&&a(e)&&n(e)})}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t,n,r){var o=h.getContainerDimensions(n),i=o.scroll,a=o.height,s=e-r-i,u=e+r-i+t;return 0>s?-s:u>a?a-u:0}function i(e,t,n,r){var o=h.getContainerDimensions(n),i=o.width,a=e-r,s=e+r+t;return 0>a?-a:s>i?i-s:0}t.__esModule=!0;var a=n(50),s=r(a),u=n(133),l=r(u),c=n(273),d=r(c),p=n(134),f=r(p),h={getContainerDimensions:function(e){var t=void 0,n=void 0,r=void 0;if("BODY"===e.tagName)t=window.innerWidth,n=window.innerHeight,r=f["default"](s["default"](e).documentElement)||f["default"](e);else{var o=l["default"](e);t=o.width,n=o.height,r=f["default"](e)}return{width:t,height:n,scroll:r}},getPosition:function(e,t){var n="BODY"===t.tagName?l["default"](e):d["default"](e,t);return n},calcOverlayPosition:function(e,t,n,r,a){var s=h.getPosition(n,r),u=l["default"](t),c=u.height,d=u.width,p=void 0,f=void 0,m=void 0,g=void 0;if("left"===e||"right"===e){f=s.top+(s.height-c)/2,p="left"===e?s.left-d:s.left+s.width;var y=o(f,c,r,a);f+=y,g=50*(1-2*y/c)+"%",m=void 0}else{if("top"!==e&&"bottom"!==e)throw new Error('calcOverlayPosition(): No such placement of "'+e+'" found.');p=s.left+(s.width-d)/2,f="top"===e?s.top-c:s.top+s.height;var v=i(p,d,r,a);p+=v,m=50*(1-2*v/d)+"%",g=void 0}return{positionLeft:p,positionTop:f,arrowOffsetLeft:m,arrowOffsetTop:g}}};t["default"]=h,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){return function(n,r,o){return null!=n[r]&&a["default"](!1,'"'+r+'" property of "'+o+'" has been deprecated.\n'+t),e(n,r,o)}}t.__esModule=!0,t["default"]=o;var i=n(60),a=r(i);e.exports=t["default"]},function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function a(e,t){function n(r,o){function a(e,n){var r=d.getLinkName(e),i=this.props[o[e]];r&&u(this.props,r)&&!i&&(i=this.props[r].requestChange);for(var a=arguments.length,s=Array(a>2?a-2:0),l=2;a>l;l++)s[l-2]=arguments[l];t(this,e,i,n,s)}function u(e,t){return void 0!==e[t]}var c,p=arguments.length<=2||void 0===arguments[2]?[]:arguments[2],f=r.displayName||r.name||"Component",h=d.getType(r).propTypes;c=d.uncontrolledPropTypes(o,h,f),p=d.transform(p,function(e,t){e[t]=function(){var e;return(e=this.refs.inner)[t].apply(e,arguments)}},{});var m=l["default"].createClass(s({displayName:"Uncontrolled("+f+")",mixins:e,propTypes:c},p,{componentWillMount:function(){var e=this.props,t=Object.keys(o);this._values=d.transform(t,function(t,n){t[n]=e[d.defaultKey(n)]},{})},componentWillReceiveProps:function(e){var t=this,n=this.props,r=Object.keys(o);r.forEach(function(r){void 0===d.getValue(e,r)&&void 0!==d.getValue(n,r)&&(t._values[r]=e[d.defaultKey(r)])})},render:function(){var e=this,t={},n=this.props,c=(n.valueLink,n.checkedLink,i(n,["valueLink","checkedLink"]));return d.each(o,function(n,r){var o=d.getLinkName(r),i=e.props[r];o&&!u(e.props,r)&&u(e.props,o)&&(i=e.props[o].value),t[r]=void 0!==i?i:e._values[r],t[n]=a.bind(e,r)}),t=s({},c,t,{ref:"inner"}),l["default"].createElement(r,t)}}));return m.ControlledComponent=r,m.deferControlTo=function(e,t,r){return void 0===t&&(t={}),n(e,s({},o,t),r)},m}return n}t.__esModule=!0;var s=Object.assign||function(e){for(var t=1;t=13?e:e.type}function s(e,t){var n=l(t);return n&&!u(e,t)&&u(e,n)?e[n].value:e[t]}function u(e,t){return void 0!==e[t]}function l(e){return"value"===e?"valueLink":"checked"===e?"checkedLink":null}function c(e){return"default"+e.charAt(0).toUpperCase()+e.substr(1)}function d(e,t,n){return function(){for(var r=arguments.length,o=Array(r),i=0;r>i;i++)o[i]=arguments[i];t&&t.call.apply(t,[e].concat(o)),n&&n.call.apply(n,[e].concat(o))}}function p(e,t,n){return f(e,t.bind(null,n=n||(Array.isArray(e)?[]:{}))),n}function f(e,t,n){if(Array.isArray(e))return e.forEach(t,n);for(var r in e)h(e,r)&&t.call(n,e[r],r,e)}function h(e,t){return e?Object.prototype.hasOwnProperty.call(e,t):!1}t.__esModule=!0,t.customPropType=o,t.uncontrolledPropTypes=i,t.getType=a,t.getValue=s,t.getLinkName=l,t.defaultKey=c,t.chain=d,t.transform=p,t.each=f,t.has=h;var m=n(1),g=r(m),y=n(137),v=(r(y),g["default"].version.split(".").map(parseFloat));t.version=v},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){var t=e.style,n=o(e,["style"]),r=c({},t,{height:6,right:2,bottom:2,left:2,borderRadius:3});return p["default"].createElement("div",c({style:r},n))}function a(e){var t=e.style,n=o(e,["style"]),r=c({},t,{width:6,right:2,bottom:2,top:2,borderRadius:3});return p["default"].createElement("div",c({style:r},n))}function s(e){var t=e.style,n=o(e,["style"]),r=c({},t,{cursor:"pointer",borderRadius:"inherit",backgroundColor:"rgba(0,0,0,.2)"});return p["default"].createElement("div",c({style:r},n))}function u(e){var t=e.style,n=o(e,["style"]),r=c({},t,{cursor:"pointer",borderRadius:"inherit",backgroundColor:"rgba(0,0,0,.2)"});return p["default"].createElement("div",c({style:r},n))}function l(e){var t=e.style,n=o(e,["style"]),r=c({},t);return p["default"].createElement("div",c({style:r},n))}var c=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}var i=Object.assign||function(e){for(var t=1;t: Component has no static height. This can happen if the root element has no CSS or is displayed as inline block."),i||console.error(": Horizontal bar has no height. Make sure you set the height property if you use a custom render method like ` + "`" + `renderScrollbarHorizontal` + "`" + `"),a||console.error(": Vertical bar has no width. Make sure you set the width property if you use a custom render method like ` + "`" + `renderScrollbarVertical` + "`" + `"))}},scrollTop:function(){var e=arguments.length<=0||void 0===arguments[0]?0:arguments[0],t=this.refs.view;t.scrollTop=e},scrollToTop:function(){var e=this.refs.view;e.scrollTop=0},scrollToBottom:function(){var e=this.refs.view;e.scrollTop=e.scrollHeight},scrollLeft:function(){var e=arguments.length<=0||void 0===arguments[0]?0:arguments[0],t=this.refs.view;t.scrollLeft=e},scrollToLeft:function(){var e=this.refs.view;e.scrollLeft=0},scrollToRight:function(){var e=this.refs.view;e.scrollLeft=e.scrollWidth},addListeners:function(){"undefined"!=typeof document&&(this.refs.view.addEventListener("scroll",this.handleScroll),this.refs.barVertical.addEventListener("mousedown",this.handleVerticalTrackMouseDown),this.refs.barHorizontal.addEventListener("mousedown",this.handleHorizontalTrackMouseDown),this.refs.thumbVertical.addEventListener("mousedown",this.handleVerticalThumbMouseDown),this.refs.thumbHorizontal.addEventListener("mousedown",this.handleHorizontalThumbMouseDown),document.addEventListener("mouseup",this.handleDocumentMouseUp),window.addEventListener("resize",this.handleWindowResize))},removeListeners:function(){"undefined"!=typeof document&&(this.refs.view.removeEventListener("scroll",this.handleScroll),this.refs.barVertical.removeEventListener("mousedown",this.handleVerticalTrackMouseDown),this.refs.barHorizontal.removeEventListener("mousedown",this.handleHorizontalTrackMouseDown),this.refs.thumbVertical.removeEventListener("mousedown",this.handleVerticalThumbMouseDown),this.refs.thumbHorizontal.removeEventListener("mousedown",this.handleHorizontalThumbMouseDown),document.removeEventListener("mouseup",this.handleDocumentMouseUp),window.removeEventListener("resize",this.handleWindowResize))},handleScroll:function(e){var t=this.props.onScroll;this.update(function(n){t&&t(e,n)})},handleVerticalTrackMouseDown:function(e){var t=this.refs,n=t.thumbVertical,r=t.barVertical,o=t.view,i=Math.abs(e.target.getBoundingClientRect().top-e.clientY),a=n.offsetHeight/2,s=100*(i-a)/r.offsetHeight;o.scrollTop=s*o.scrollHeight/100},handleHorizontalTrackMouseDown:function(){var e=this.refs,t=e.thumbHorizontal,n=e.barHorizontal,r=e.view,o=Math.abs(event.target.getBoundingClientRect().left-event.clientX),i=t.offsetWidth/2,a=100*(o-i)/n.offsetWidth;r.scrollLeft=a*r.scrollWidth/100},handleVerticalThumbMouseDown:function(e){this.handleDragStart(e);var t=e.currentTarget,n=e.clientY;this.prevPageY=t.offsetHeight-(n-t.getBoundingClientRect().top)},handleHorizontalThumbMouseDown:function(e){this.handleDragStart(e);var t=e.currentTarget,n=e.clientX;this.prevPageX=t.offsetWidth-(n-t.getBoundingClientRect().left)},handleDocumentMouseUp:function(){this.handleDragEnd()},handleDocumentMouseMove:function(e){if(this.cursorDown===!1)return!1;if(this.prevPageY){var t=this.refs,n=t.barVertical,r=t.thumbVertical,o=t.view,i=-1*(n.getBoundingClientRect().top-e.clientY),a=r.offsetHeight-this.prevPageY,s=100*(i-a)/n.offsetHeight;return o.scrollTop=s*o.scrollHeight/100,!1}if(this.prevPageX){var u=this.refs,l=u.barHorizontal,c=u.thumbHorizontal,o=u.view,i=-1*(l.getBoundingClientRect().left-e.clientX),a=c.offsetWidth-this.prevPageX,s=100*(i-a)/l.offsetWidth;return o.scrollLeft=s*o.scrollWidth/100,!1}},handleWindowResize:function(){this.update()},handleDragStart:function(e){document&&(e.stopImmediatePropagation(),this.cursorDown=!0,(0,l["default"])(document.body,y.disableSelectStyle),document.addEventListener("mousemove",this.handleDocumentMouseMove),document.onselectstart=g["default"])},handleDragEnd:function(){document&&(this.cursorDown=!1,this.prevPageX=this.prevPageY=0,(0,l["default"])(document.body,y.resetDisableSelectStyle),document.removeEventListener("mousemove",this.handleDocumentMouseMove),document.onselectstart=void 0)},raf:function(e){var t=this;this.timer&&s["default"].cancel(this.timer),this.timer=(0,s["default"])(function(){t.timer=void 0,e()})},update:function(e){var t=this.refs,n=t.thumbHorizontal,r=t.thumbVertical,i=this.getInnerSizePercentage(),a=i.widthPercentageInner,s=i.heightPercentageInner,u=this.getPosition(),c=u.x,d=u.y,p=o(u,["x","y"]);this.raf(function(){if((0,h["default"])()>0){var t={width:100>a?a+"%":0,transform:"translateX("+c+"%)"},o={height:100>s?s+"%":0,transform:"translateY("+d+"%)"};(0,l["default"])(n,t),(0,l["default"])(r,o)}"function"==typeof e&&e(p)})},render:function(){var e=(0,h["default"])(),t=this.props,n=t.style,r=t.renderScrollbarHorizontal,a=t.renderScrollbarVertical,s=t.renderThumbHorizontal,u=t.renderThumbVertical,l=t.renderView,p=(t.onScroll,t.children),f=o(t,["style","renderScrollbarHorizontal","renderScrollbarVertical","renderThumbHorizontal","renderThumbVertical","renderView","onScroll","children"]),m=i({position:"relative",overflow:"hidden",width:"100%",height:"100%"},n),g=e>0?i({},y.scrollbarsVisibleViewStyle,{right:-e,bottom:-e}):y.scrollbarsInvisibleViewStyle,v=e>0?y.defaultScrollbarHorizontalStyle:i({},y.defaultScrollbarHorizontalStyle,{display:"none"}),M=e>0?y.defaultScrollbarVerticalStyle:i({},y.defaultScrollbarVerticalStyle,{display:"none"});return d["default"].createElement("div",i({},f,{style:m}),(0,c.cloneElement)(l({style:g}),{ref:"view"},p),(0,c.cloneElement)(r({style:v}),{ref:"barHorizontal"},(0,c.cloneElement)(s({style:y.defaultThumbHorizontalStyle}),{ref:"thumbHorizontal"})),(0,c.cloneElement)(a({style:M}),{ref:"barVertical"},(0,c.cloneElement)(u({style:y.defaultThumbVerticalStyle}),{ref:"thumbVertical"})))}})},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.defaultThumbHorizontalStyle={position:"relative",display:"block",height:"100%"},t.defaultThumbVerticalStyle={position:"relative",display:"block",width:"100%"},t.defaultScrollbarHorizontalStyle={position:"absolute"},t.defaultScrollbarVerticalStyle={position:"absolute"},t.scrollbarsVisibleViewStyle={position:"absolute",top:0,left:0,overflow:"scroll",WebkitOverflowScrolling:"touch"},t.scrollbarsInvisibleViewStyle={position:"relative",width:"100%",height:"100%",overflow:"scroll",WebkitOverflowScrolling:"touch"},t.disableSelectStyle={"-webkit-touch-callout":"none","-webkit-user-select":"none","-khtml-user-select":"none","-moz-user-select":"none","-ms-user-select":"none","user-select":"none"},t.resetDisableSelectStyle={"-webkit-touch-callout":"","-webkit-user-select":"","-khtml-user-select":"","-moz-user-select":"","-ms-user-select":"","user-select":""}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(){if(s!==!1)return s;if("undefined"!=typeof document){var e=document.createElement("div");(0,a["default"])(e,{width:100,height:100,position:"absolute",top:-9999,overflow:"scroll","-ms-overflow-style":"scrollbar"}),document.body.appendChild(e),s=e.offsetWidth-e.clientWidth,document.body.removeChild(e)}else s=0;return s}Object.defineProperty(t,"__esModule",{value:!0}),t["default"]=o;var i=n(164),a=r(i),s=!1},function(e,t){"use strict";function n(){return!1}Object.defineProperty(t,"__esModule",{value:!0}),t["default"]=n},function(e,t){var n={animationIterationCount:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridColumn:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,stopOpacity:!0,strokeDashoffset:!0,strokeOpacity:!0,strokeWidth:!0};e.exports=function(e,t){return"number"!=typeof t||n[e]?t:t+"px"}},function(e,t){var n=null,r=["Webkit","Moz","O","ms"];e.exports=function(e){n||(n=document.createElement("div"));var t=n.style;if(e in t)return e;for(var o=e.charAt(0).toUpperCase()+e.slice(1),i=r.length;i>=0;i--){var a=r[i]+o;if(a in t)return a}return!1}},function(e,t,n){function r(e){return o(e).replace(/\s(\w)/g,function(e,t){return t.toUpperCase()})}var o=n(342);e.exports=r},function(e,t,n){function r(e){return o(e).replace(/[\W_]+(.|$)/g,function(e,t){return t?" "+t:""})}var o=n(343);e.exports=r},function(e,t){function n(e){return i.test(e)?e.toLowerCase():(s.test(e)&&(e=r(e)),a.test(e)&&(e=o(e)),e.toLowerCase())}function r(e){return e.replace(u,function(e,t){return t?" "+t:""})}function o(e){return e.replace(l,function(e,t,n){return t+" "+n.toLowerCase().split("").join(" ")})}e.exports=n;var i=/\s/,a=/[a-z][A-Z]/,s=/[\W_]/,u=/[\W_]+(.|$)/g,l=/(.)([A-Z]+)/g},function(e,t,n){for(var r=n(345),o="undefined"==typeof window?{}:window,i=["moz","webkit"],a="AnimationFrame",s=o["request"+a],u=o["cancel"+a]||o["cancelRequest"+a],l=0;lt;++t)e[t].onLeave&&e[t].onLeave.call(e[t])}t.__esModule=!0,t.runEnterHooks=a,t.runLeaveHooks=s;var u=n(87),l=n(8);r(l)},function(e,t,n){"use strict";function r(e,t,n){if(!e.path)return!1;var r=i.getParamNames(e.path);return r.some(function(e){return t.params[e]!==n.params[e]})}function o(e,t){var n=e&&e.routes,o=t.routes,i=void 0,a=void 0;return n?(i=n.filter(function(n){return-1===o.indexOf(n)||r(n,e,t)}),i.reverse(),a=o.filter(function(e){return-1===n.indexOf(e)||-1!==i.indexOf(e)})):(i=[],a=o),{leaveRoutes:i,enterRoutes:a}}t.__esModule=!0;var i=n(40);t["default"]=o,e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t,n){t.component||t.components?n(null,t.component||t.components):t.getComponent?t.getComponent(e,n):t.getComponents?t.getComponents(e,n):n()}function o(e,t){i.mapAsync(e.routes,function(t,n,o){r(e.location,t,o)},t)}t.__esModule=!0;var i=n(87);t["default"]=o,e.exports=t["default"]},function(e,t,n){"use strict";function r(e,t){var n={};if(!e.path)return n;var r=o.getParamNames(e.path);for(var i in t)t.hasOwnProperty(i)&&-1!==r.indexOf(i)&&(n[i]=t[i]);return n}t.__esModule=!0; var o=n(40);t["default"]=r,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(179),i=r(o),a=n(174),s=r(a);t["default"]=s["default"](i["default"]),e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(171),i=r(o);t.Router=i["default"];var a=n(168),s=r(a);t.Link=s["default"];var u=n(354),l=r(u);t.IndexLink=l["default"];var c=n(355),d=r(c);t.IndexRedirect=d["default"];var p=n(167),f=r(p);t.IndexRoute=f["default"];var h=n(169),m=r(h);t.Redirect=m["default"];var g=n(170),y=r(g);t.Route=y["default"];var v=n(353),M=r(v);t.History=M["default"];var T=n(356),b=r(T);t.Lifecycle=b["default"];var x=n(357),E=r(x);t.RouteContext=E["default"];var A=n(368),N=r(A);t.useRoutes=N["default"];var w=n(26);t.createRoutes=w.createRoutes;var I=n(88),C=r(I);t.RouterContext=C["default"];var D=n(358),S=r(D);t.RoutingContext=S["default"];var k=n(31),L=r(k);t.PropTypes=L["default"];var O=n(366),P=r(O);t.match=P["default"];var j=n(176),z=r(j);t.useRouterHistory=z["default"];var R=n(40);t.formatPattern=R.formatPattern;var U=n(89),Y=r(U);t.browserHistory=Y["default"];var B=n(363),W=r(B);t.hashHistory=W["default"];var F=n(173),V=r(F);t.createMemoryHistory=V["default"]},function(e,t,n){"use strict";function r(e,t){if(e==t)return!0;if(null==e||null==t)return!1;if(Array.isArray(e))return Array.isArray(t)&&e.length===t.length&&e.every(function(e,n){return r(e,t[n])});if("object"==typeof e){for(var n in e)if(e.hasOwnProperty(n))if(void 0===e[n]){if(void 0!==t[n])return!1}else{if(!t.hasOwnProperty(n))return!1;if(!r(e[n],t[n]))return!1}return!0}return String(e)===String(t)}function o(e,t,n){return e.every(function(e,r){return String(t[r])===String(n[e])})}function i(e,t,n){for(var r=e,i=[],a=[],s=0,u=t.length;u>s;++s){var c=t[s],d=c.path||"";if("/"===d.charAt(0)&&(r=e,i=[],a=[]),null!==r){var p=l.matchPattern(d,r);r=p.remainingPathname,i=[].concat(i,p.paramNames),a=[].concat(a,p.paramValues)}if(""===r&&c.path&&o(i,a,n))return s}return null}function a(e,t,n,r){var o=i(e,t,n);return null===o?!1:r?t.slice(o+1).every(function(e){return!e.path}):!0}function s(e,t){return null==t?null==e:null==e?!0:r(e,t)}function u(e,t,n,r,o){var i=e.pathname,u=e.query;return null==n?!1:a(i,r,o,t)?s(u,n.query):!1}t.__esModule=!0,t["default"]=u;var l=n(40);e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e,t){var n=e.history,r=e.routes,i=e.location,s=o(e,["history","routes","location"]);n||i?void 0:u["default"](!1),n=n?n:c["default"](s);var l=p["default"](n,f.createRoutes(r)),d=void 0;i?i=n.createLocation(i):d=n.listen(function(e){i=e});var m=h.createRouterObject(n,l);n=h.createRoutingHistory(n,l),l.match(i,function(e,r,o){t(e,r,o&&a({},o,{history:n,router:m,matchContext:{history:n,transitionManager:l,router:m}})),d&&d()})}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e){return function(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=t.routes,r=o(t,["routes"]),i=u["default"](e)(r),s=c["default"](i,n);return a({},i,s)}}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;ti?t.call(this,i++,o,r):r.apply(this,arguments))}var i=0,a=!1;o()}t.__esModule=!0,t.loopAsync=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(){function e(e){e=e||window.history.state||{};var t=d.getWindowPath(),n=e,r=n.key,o=void 0;r?o=p.readState(r):(o=null,r=M.createKey(),y&&window.history.replaceState(i({},e,{key:r}),null,t));var a=l.parsePath(t);return M.createLocation(i({},a,{state:o}),void 0,r)}function t(t){function n(t){void 0!==t.state&&r(e(t.state))}var r=t.transitionTo;return d.addEventListener(window,"popstate",n),function(){d.removeEventListener(window,"popstate",n)}}function n(e){var t=e.basename,n=e.pathname,r=e.search,o=e.hash,i=e.state,a=e.action,s=e.key;if(a!==u.POP){p.saveState(s,i);var l=(t||"")+n+r+o,c={key:s};if(a===u.PUSH){if(v)return window.location.href=l,!1;window.history.pushState(c,null,l)}else{if(v)return window.location.replace(l),!1;window.history.replaceState(c,null,l)}}}function r(e){1===++T&&(b=t(M));var n=M.listenBefore(e);return function(){n(),0===--T&&b()}}function o(e){1===++T&&(b=t(M));var n=M.listen(e);return function(){n(),0===--T&&b()}}function a(e){1===++T&&(b=t(M)),M.registerTransitionHook(e)}function f(e){M.unregisterTransitionHook(e),0===--T&&b()}var m=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];c.canUseDOM?void 0:s["default"](!1);var g=m.forceRefresh,y=d.supportsHistory(),v=!y||g,M=h["default"](i({},m,{getCurrentLocation:e,finishTransition:n,saveState:p.saveState})),T=0,b=void 0;return i({},M,{listenBefore:r,listen:o,registerTransitionHook:a,unregisterTransitionHook:f})}t.__esModule=!0;var i=Object.assign||function(e){for(var t=1;t=0&&t=0&&g0&&"number"!=typeof e[0]?!1:!0:!1}function i(e,t,n){var i,c;if(r(e)||r(t))return!1;if(e.prototype!==t.prototype)return!1;if(u(e))return u(t)?(e=a.call(e),t=a.call(t),l(e,t,n)):!1;if(o(e)){if(!o(t))return!1;if(e.length!==t.length)return!1;for(i=0;i=0;i--)if(d[i]!=p[i])return!1;for(i=d.length-1;i>=0;i--)if(c=d[i],!l(e[c],t[c],n))return!1;return typeof e==typeof t}var a=Array.prototype.slice,s=n(375),u=n(374),l=e.exports=function(e,t,n){return n||(n={}),e===t?!0:e instanceof Date&&t instanceof Date?e.getTime()===t.getTime():!e||!t||"object"!=typeof e&&"object"!=typeof t?n.strict?e===t:e==t:i(e,t,n)}},function(e,t){function n(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function r(e){return e&&"object"==typeof e&&"number"==typeof e.length&&Object.prototype.hasOwnProperty.call(e,"callee")&&!Object.prototype.propertyIsEnumerable.call(e,"callee")||!1}var o="[object Arguments]"==function(){return Object.prototype.toString.call(arguments)}();t=e.exports=o?n:r,t.supported=n,t.unsupported=r},function(e,t){function n(e){var t=[];for(var n in e)t.push(n);return t}t=e.exports="function"==typeof Object.keys?Object.keys:n,t.shim=n},function(e,t,n){"use strict";var r=n(377);t.extract=function(e){return e.split("?")[1]||""},t.parse=function(e){return"string"!=typeof e?{}:(e=e.trim().replace(/^(\?|#|&)/,""),e?e.split("&").reduce(function(e,t){var n=t.replace(/\+/g," ").split("="),r=n.shift(),o=n.length>0?n.join("="):void 0;return r=decodeURIComponent(r),o=void 0===o?null:decodeURIComponent(o),e.hasOwnProperty(r)?Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]:e[r]=o,e},{}):{})},t.stringify=function(e){return e?Object.keys(e).sort().map(function(t){var n=e[t];return void 0===n?"":null===n?t:Array.isArray(n)?n.sort().map(function(e){return r(t)+"="+r(e)}).join("&"):r(t)+"="+r(n)}).filter(function(e){return e.length>0}).join("&"):""}},function(e,t){"use strict";e.exports=function(e){return encodeURIComponent(e).replace(/[!'()*]/g,function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()})}},function(e,t,n){"use strict";var r=n(11),o=n(102),i=n(212),a={componentDidMount:function(){this.props.autoFocus&&i(o(this))}},s={Mixin:a,focusDOMComponent:function(){i(r.getNode(this._rootNodeID))}};e.exports=s},function(e,t,n){"use strict";function r(){var e=window.opera;return"object"==typeof e&&"function"==typeof e.version&&parseInt(e.version(),10)<=12}function o(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function i(e){switch(e){case C.topCompositionStart:return D.compositionStart;case C.topCompositionEnd:return D.compositionEnd;case C.topCompositionUpdate:return D.compositionUpdate}}function a(e,t){return e===C.topKeyDown&&t.keyCode===b}function s(e,t){switch(e){case C.topKeyUp:return-1!==T.indexOf(t.keyCode);case C.topKeyDown:return t.keyCode!==b;case C.topKeyPress:case C.topMouseDown:case C.topBlur:return!0;default:return!1}}function u(e){var t=e.detail;return"object"==typeof t&&"data"in t?t.data:null}function l(e,t,n,r,o){var l,c;if(x?l=i(e):k?s(e,r)&&(l=D.compositionEnd):a(e,r)&&(l=D.compositionStart),!l)return null;N&&(k||l!==D.compositionStart?l===D.compositionEnd&&k&&(c=k.getData()):k=g.getPooled(t));var d=y.getPooled(l,n,r,o);if(c)d.data=c;else{var p=u(r);null!==p&&(d.data=p)}return h.accumulateTwoPhaseDispatches(d),d}function c(e,t){switch(e){case C.topCompositionEnd:return u(t);case C.topKeyPress:var n=t.which;return n!==w?null:(S=!0,I);case C.topTextInput:var r=t.data;return r===I&&S?null:r;default:return null}}function d(e,t){if(k){if(e===C.topCompositionEnd||s(e,t)){var n=k.getData();return g.release(k),k=null,n}return null}switch(e){case C.topPaste:return null;case C.topKeyPress:return t.which&&!o(t)?String.fromCharCode(t.which):null;case C.topCompositionEnd:return N?null:t.data;default:return null}}function p(e,t,n,r,o){var i;if(i=A?c(e,r):d(e,r),!i)return null;var a=v.getPooled(D.beforeInput,n,r,o);return a.data=i,h.accumulateTwoPhaseDispatches(a),a}var f=n(22),h=n(52),m=n(9),g=n(387),y=n(417),v=n(420),M=n(28),T=[9,13,27,32],b=229,x=m.canUseDOM&&"CompositionEvent"in window,E=null;m.canUseDOM&&"documentMode"in document&&(E=document.documentMode);var A=m.canUseDOM&&"TextEvent"in window&&!E&&!r(),N=m.canUseDOM&&(!x||E&&E>8&&11>=E),w=32,I=String.fromCharCode(w),C=f.topLevelTypes,D={beforeInput:{phasedRegistrationNames:{bubbled:M({onBeforeInput:null}),captured:M({onBeforeInputCapture:null})},dependencies:[C.topCompositionEnd,C.topKeyPress,C.topTextInput,C.topPaste]},compositionEnd:{phasedRegistrationNames:{bubbled:M({onCompositionEnd:null}),captured:M({onCompositionEndCapture:null})},dependencies:[C.topBlur,C.topCompositionEnd,C.topKeyDown,C.topKeyPress,C.topKeyUp,C.topMouseDown]},compositionStart:{phasedRegistrationNames:{bubbled:M({onCompositionStart:null}),captured:M({onCompositionStartCapture:null})},dependencies:[C.topBlur,C.topCompositionStart,C.topKeyDown,C.topKeyPress,C.topKeyUp,C.topMouseDown]},compositionUpdate:{phasedRegistrationNames:{bubbled:M({onCompositionUpdate:null}),captured:M({onCompositionUpdateCapture:null})},dependencies:[C.topBlur,C.topCompositionUpdate,C.topKeyDown,C.topKeyPress,C.topKeyUp,C.topMouseDown]}},S=!1,k=null,L={eventTypes:D,extractEvents:function(e,t,n,r,o){return[l(e,t,n,r,o),p(e,t,n,r,o)]}};e.exports=L},function(e,t,n){"use strict";var r=n(182),o=n(9),i=n(17),a=(n(434),n(425)),s=n(439),u=n(443),l=(n(4),u(function(e){return s(e)})),c=!1,d="cssFloat";if(o.canUseDOM){var p=document.createElement("div").style;try{p.font=""}catch(f){c=!0}void 0===document.documentElement.style.cssFloat&&(d="styleFloat")}var h={createMarkupForStyles:function(e){var t="";for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];null!=r&&(t+=l(n)+":",t+=a(n,r)+";")}return t||null},setValueForStyles:function(e,t){var n=e.style;for(var o in t)if(t.hasOwnProperty(o)){var i=a(o,t[o]);if("float"===o&&(o=d),i)n[o]=i;else{var s=c&&r.shorthandPropertyExpansions[o];if(s)for(var u in s)n[u]="";else n[o]=""}}}};i.measureMethods(h,"CSSPropertyOperations",{setValueForStyles:"setValueForStyles"}),e.exports=h},function(e,t,n){"use strict";function r(e){var t=e.nodeName&&e.nodeName.toLowerCase();return"select"===t||"input"===t&&"file"===e.type}function o(e){var t=E.getPooled(D.change,k,e,A(e));T.accumulateTwoPhaseDispatches(t),x.batchedUpdates(i,t)}function i(e){M.enqueueEvents(e),M.processEventQueue(!1)}function a(e,t){S=e,k=t,S.attachEvent("onchange",o)}function s(){S&&(S.detachEvent("onchange",o),S=null,k=null)}function u(e,t,n){return e===C.topChange?n:void 0}function l(e,t,n){e===C.topFocus?(s(),a(t,n)):e===C.topBlur&&s()}function c(e,t){S=e,k=t,L=e.value,O=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(S,"value",z),S.attachEvent("onpropertychange",p)}function d(){S&&(delete S.value,S.detachEvent("onpropertychange",p),S=null,k=null,L=null,O=null)}function p(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==L&&(L=t,o(e))}}function f(e,t,n){return e===C.topInput?n:void 0}function h(e,t,n){e===C.topFocus?(d(),c(t,n)):e===C.topBlur&&d()}function m(e,t,n){return e!==C.topSelectionChange&&e!==C.topKeyUp&&e!==C.topKeyDown||!S||S.value===L?void 0:(L=S.value,k)}function g(e){return e.nodeName&&"input"===e.nodeName.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)}function y(e,t,n){return e===C.topClick?n:void 0}var v=n(22),M=n(51),T=n(52),b=n(9),x=n(18),E=n(34),A=n(105),N=n(108),w=n(209),I=n(28),C=v.topLevelTypes,D={change:{phasedRegistrationNames:{bubbled:I({onChange:null}),captured:I({onChangeCapture:null})},dependencies:[C.topBlur,C.topChange,C.topClick,C.topFocus,C.topInput,C.topKeyDown,C.topKeyUp,C.topSelectionChange]}},S=null,k=null,L=null,O=null,P=!1;b.canUseDOM&&(P=N("change")&&(!("documentMode"in document)||document.documentMode>8));var j=!1;b.canUseDOM&&(j=N("input")&&(!("documentMode"in document)||document.documentMode>9));var z={get:function(){return O.get.call(this)},set:function(e){L=""+e,O.set.call(this,e)}},R={eventTypes:D,extractEvents:function(e,t,n,o,i){var a,s;if(r(t)?P?a=u:s=l:w(t)?j?a=f:(a=m,s=h):g(t)&&(a=y),a){var c=a(e,t,n);if(c){var d=E.getPooled(D.change,c,o,i);return d.type="change",T.accumulateTwoPhaseDispatches(d),d}}s&&s(e,t,n)}};e.exports=R},function(e,t){"use strict";var n=0,r={createReactRootIndex:function(){return n++}};e.exports=r},function(e,t,n){"use strict";function r(e){return e.substring(1,e.indexOf(" "))}var o=n(9),i=n(436),a=n(21),s=n(214),u=n(2),l=/^(<[^ \/>]+)/,c="data-danger-index",d={dangerouslyRenderMarkup:function(e){o.canUseDOM?void 0:u(!1);for(var t,n={},d=0;de&&n[e]===o[e];e++);var a=r-e;for(t=1;a>=t&&n[r-t]===o[i-t];t++);var s=t>1?1-t:void 0;return this._fallbackText=o.slice(e,s),this._fallbackText}}),o.addPoolingTo(r),e.exports=r},function(e,t,n){"use strict";var r,o=n(42),i=n(9),a=o.injection.MUST_USE_ATTRIBUTE,s=o.injection.MUST_USE_PROPERTY,u=o.injection.HAS_BOOLEAN_VALUE,l=o.injection.HAS_SIDE_EFFECTS,c=o.injection.HAS_NUMERIC_VALUE,d=o.injection.HAS_POSITIVE_NUMERIC_VALUE,p=o.injection.HAS_OVERLOADED_BOOLEAN_VALUE;if(i.canUseDOM){var f=document.implementation;r=f&&f.hasFeature&&f.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")}var h={isCustomAttribute:RegExp.prototype.test.bind(/^(data|aria)-[a-z_][a-z\d_.\-]*$/),Properties:{accept:null,acceptCharset:null,accessKey:null,action:null,allowFullScreen:a|u,allowTransparency:a,alt:null,async:u,autoComplete:null,autoPlay:u,capture:a|u,cellPadding:null,cellSpacing:null,charSet:a,challenge:a,checked:s|u,classID:a,className:r?a:s,cols:a|d,colSpan:null,content:null,contentEditable:null,contextMenu:a,controls:s|u,coords:null,crossOrigin:null,data:null,dateTime:a,"default":u,defer:u,dir:null,disabled:a|u,download:p,draggable:null,encType:null,form:a,formAction:a,formEncType:a,formMethod:a,formNoValidate:u,formTarget:a,frameBorder:a,headers:null,height:a,hidden:a|u,high:null,href:null,hrefLang:null,htmlFor:null,httpEquiv:null,icon:null,id:s,inputMode:a,integrity:null,is:a,keyParams:a,keyType:a,kind:null,label:null,lang:null,list:a,loop:s|u,low:null,manifest:a,marginHeight:null,marginWidth:null,max:null,maxLength:a,media:a,mediaGroup:null,method:null,min:null,minLength:a,multiple:s|u,muted:s|u,name:null,nonce:a,noValidate:u,open:u,optimum:null,pattern:null,placeholder:null,poster:null,preload:null,radioGroup:null,readOnly:s|u,rel:null,required:u,reversed:u,role:a,rows:a|d,rowSpan:null,sandbox:null,scope:null,scoped:u,scrolling:null,seamless:a|u,selected:s|u,shape:null,size:a|d,sizes:a,span:d,spellCheck:null,src:null,srcDoc:s,srcLang:null,srcSet:a,start:c,step:null,style:null,summary:null,tabIndex:null,target:null,title:null,type:null,useMap:null,value:s|l,width:a,wmode:a,wrap:null,about:a,datatype:a,inlist:a,prefix:a,property:a,resource:a,"typeof":a,vocab:a,autoCapitalize:a,autoCorrect:a,autoSave:null,color:null,itemProp:a,itemScope:a|u,itemType:a,itemID:a,itemRef:a,results:null,security:a,unselectable:a},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMPropertyNames:{autoComplete:"autocomplete",autoFocus:"autofocus",autoPlay:"autoplay",autoSave:"autosave",encType:"encoding",hrefLang:"hreflang",radioGroup:"radiogroup",spellCheck:"spellcheck",srcDoc:"srcdoc",srcSet:"srcset"}};e.exports=h},function(e,t,n){"use strict";var r=n(188),o=n(399),i=n(404),a=n(3),s=n(426),u={};a(u,i),a(u,{findDOMNode:s("findDOMNode","ReactDOM","react-dom",r,r.findDOMNode),render:s("render","ReactDOM","react-dom",r,r.render),unmountComponentAtNode:s("unmountComponentAtNode","ReactDOM","react-dom",r,r.unmountComponentAtNode),renderToString:s("renderToString","ReactDOMServer","react-dom/server",o,o.renderToString),renderToStaticMarkup:s("renderToStaticMarkup","ReactDOMServer","react-dom/server",o,o.renderToStaticMarkup)}),u.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=r,u.__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=o,e.exports=u},function(e,t,n){"use strict";var r=(n(53),n(102)),o=(n(4),"_getDOMNodeDidWarn"),i={getDOMNode:function(){return this.constructor[o]=!0,r(this)}};e.exports=i},function(e,t,n){"use strict";function r(e,t,n){var r=void 0===e[n];null!=t&&r&&(e[n]=i(t,null))}var o=n(33),i=n(107),a=n(110),s=n(111),u=(n(4),{instantiateChildren:function(e,t,n){if(null==e)return null;var o={};return s(e,r,o),o},updateChildren:function(e,t,n,r){if(!t&&!e)return null;var s;for(s in t)if(t.hasOwnProperty(s)){var u=e&&e[s],l=u&&u._currentElement,c=t[s];if(null!=u&&a(l,c))o.receiveComponent(u,c,n,r),t[s]=u;else{u&&o.unmountComponent(u,s);var d=i(c,null);t[s]=d}}for(s in e)!e.hasOwnProperty(s)||t&&t.hasOwnProperty(s)||o.unmountComponent(e[s]);return t},unmountChildren:function(e){for(var t in e)if(e.hasOwnProperty(t)){var n=e[t];o.unmountComponent(n)}}});e.exports=u},function(e,t,n){"use strict";function r(e){var t=e._currentElement._owner||null;if(t){var n=t.getName();if(n)return" Check the render method of ` + "`" + `"+n+"` + "`" + `."}return""}function o(e){}var i=n(98),a=n(23),s=n(13),u=n(53),l=n(17),c=n(65),d=(n(64),n(33)),p=n(100),f=n(3),h=n(55),m=n(2),g=n(110);n(4);o.prototype.render=function(){var e=u.get(this)._currentElement.type;return e(this.props,this.context,this.updater)};var y=1,v={construct:function(e){this._currentElement=e,this._rootNodeID=null,this._instance=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._topLevelWrapper=null,this._pendingCallbacks=null},mountComponent:function(e,t,n){this._context=n,this._mountOrder=y++,this._rootNodeID=e;var r,i,a=this._processProps(this._currentElement.props),l=this._processContext(n),c=this._currentElement.type,f="prototype"in c;f&&(r=new c(a,l,p)),(!f||null===r||r===!1||s.isValidElement(r))&&(i=r,r=new o(c)),r.props=a,r.context=l,r.refs=h,r.updater=p,this._instance=r,u.set(r,this);var g=r.state;void 0===g&&(r.state=g=null),"object"!=typeof g||Array.isArray(g)?m(!1):void 0,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,r.componentWillMount&&(r.componentWillMount(),this._pendingStateQueue&&(r.state=this._processPendingState(r.props,r.context))),void 0===i&&(i=this._renderValidatedComponent()),this._renderedComponent=this._instantiateReactComponent(i);var v=d.mountComponent(this._renderedComponent,e,t,this._processChildContext(n));return r.componentDidMount&&t.getReactMountReady().enqueue(r.componentDidMount,r),v},unmountComponent:function(){var e=this._instance;e.componentWillUnmount&&e.componentWillUnmount(),d.unmountComponent(this._renderedComponent),this._renderedComponent=null,this._instance=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=null,this._topLevelWrapper=null,u.remove(e)},_maskContext:function(e){var t=null,n=this._currentElement.type,r=n.contextTypes;if(!r)return h;t={};for(var o in r)t[o]=e[o];return t},_processContext:function(e){var t=this._maskContext(e);return t},_processChildContext:function(e){var t=this._currentElement.type,n=this._instance,r=n.getChildContext&&n.getChildContext();if(r){"object"!=typeof t.childContextTypes?m(!1):void 0;for(var o in r)o in t.childContextTypes?void 0:m(!1);return f({},e,r)}return e},_processProps:function(e){return e},_checkPropTypes:function(e,t,n){var o=this.getName();for(var i in e)if(e.hasOwnProperty(i)){var a;try{"function"!=typeof e[i]?m(!1):void 0,a=e[i](t,i,o,n)}catch(s){a=s}if(a instanceof Error){r(this);n===c.prop}}},receiveComponent:function(e,t,n){var r=this._currentElement,o=this._context;this._pendingElement=null,this.updateComponent(t,r,e,o,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement&&d.receiveComponent(this,this._pendingElement||this._currentElement,e,this._context),(null!==this._pendingStateQueue||this._pendingForceUpdate)&&this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context)},updateComponent:function(e,t,n,r,o){var i,a=this._instance,s=this._context===o?a.context:this._processContext(o);t===n?i=n.props:(i=this._processProps(n.props),a.componentWillReceiveProps&&a.componentWillReceiveProps(i,s));var u=this._processPendingState(i,s),l=this._pendingForceUpdate||!a.shouldComponentUpdate||a.shouldComponentUpdate(i,u,s);l?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,i,u,s,e,o)):(this._currentElement=n,this._context=o,a.props=i,a.state=u,a.context=s)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,o=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;if(o&&1===r.length)return r[0];for(var i=f({},o?r[0]:n.state),a=o?1:0;a=0||null!=t.is}function g(e){h(e),this._tag=e.toLowerCase(),this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._rootNodeID=null,this._wrapperState=null,this._topLevelWrapper=null,this._nodeWithLegacyProperties=null}var y=n(378),v=n(380),M=n(42),T=n(95),b=n(22),x=n(63),E=n(97),A=n(393),N=n(396),w=n(397),I=n(190),C=n(400),D=n(11),S=n(405),k=n(17),L=n(100),O=n(3),P=n(68),j=n(69),z=n(2),R=(n(108),n(28)),U=n(70),Y=n(109),B=(n(215),n(112),n(4),x.deleteListener),W=x.listenTo,F=x.registrationNameModules,V={string:!0,number:!0},H=R({children:null}),_=R({style:null}),Q=R({__html:null}),Z=1,G={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"},X={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},q={listing:!0,pre:!0,textarea:!0},K=(O({menuitem:!0},X),/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/),J={},$={}.hasOwnProperty;g.displayName="ReactDOMComponent",g.Mixin={construct:function(e){this._currentElement=e},mountComponent:function(e,t,n){this._rootNodeID=e;var r=this._currentElement.props;switch(this._tag){case"iframe":case"img":case"form":case"video":case"audio":this._wrapperState={listeners:null},t.getReactMountReady().enqueue(d,this);break;case"button":r=A.getNativeProps(this,r,n);break;case"input":N.mountWrapper(this,r,n),r=N.getNativeProps(this,r,n);break;case"option":w.mountWrapper(this,r,n),r=w.getNativeProps(this,r,n);break;case"select":I.mountWrapper(this,r,n),r=I.getNativeProps(this,r,n),n=I.processChildContext(this,r,n);break;case"textarea":C.mountWrapper(this,r,n),r=C.getNativeProps(this,r,n)}u(this,r);var o;if(t.useCreateElement){var i=n[D.ownerDocumentContextKey],a=i.createElement(this._currentElement.type);T.setAttributeForID(a,this._rootNodeID),D.getID(a),this._updateDOMProperties({},r,t,a),this._createInitialChildren(t,r,n,a),o=a}else{var s=this._createOpenTagMarkupAndPutListeners(t,r),l=this._createContentMarkup(t,r,n);o=!l&&X[this._tag]?s+"/>":s+">"+l+""}switch(this._tag){case"input":t.getReactMountReady().enqueue(p,this);case"button":case"select":case"textarea":r.autoFocus&&t.getReactMountReady().enqueue(y.focusDOMComponent,this)}return o},_createOpenTagMarkupAndPutListeners:function(e,t){var n="<"+this._currentElement.type;for(var r in t)if(t.hasOwnProperty(r)){var o=t[r];if(null!=o)if(F.hasOwnProperty(r))o&&l(this._rootNodeID,r,o,e);else{r===_&&(o&&(o=this._previousStyleCopy=O({},t.style)),o=v.createMarkupForStyles(o));var i=null;null!=this._tag&&m(this._tag,t)?r!==H&&(i=T.createMarkupForCustomAttribute(r,o)):i=T.createMarkupForProperty(r,o),i&&(n+=" "+i)}}if(e.renderToStaticMarkup)return n;var a=T.createMarkupForID(this._rootNodeID);return n+" "+a},_createContentMarkup:function(e,t,n){var r="",o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&(r=o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)r=j(i);else if(null!=a){var s=this.mountChildren(a,e,n);r=s.join("")}}return q[this._tag]&&"\n"===r.charAt(0)?"\n"+r:r},_createInitialChildren:function(e,t,n,r){var o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&U(r,o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)Y(r,i);else if(null!=a)for(var s=this.mountChildren(a,e,n),u=0;ut.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart("character",n),o.setEndPoint("EndToStart",o),o.moveEnd("character",r-n),o.select()}function s(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),i="undefined"==typeof t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var s=l(e,o),u=l(e,i);if(s&&u){var d=document.createRange();d.setStart(s.node,s.offset),n.removeAllRanges(),o>i?(n.addRange(d),n.extend(u.node,u.offset)):(d.setEnd(u.node,u.offset),n.addRange(d))}}}var u=n(9),l=n(429),c=n(208),d=u.canUseDOM&&"selection"in document&&!("getSelection"in window),p={getOffsets:d?o:i,setOffsets:d?a:s};e.exports=p},function(e,t,n){"use strict";var r=n(193),o=n(410),i=n(101);r.inject();var a={renderToString:o.renderToString,renderToStaticMarkup:o.renderToStaticMarkup,version:i};e.exports=a},function(e,t,n){"use strict";function r(){this._rootNodeID&&c.updateWrapper(this)}function o(e){var t=this._currentElement.props,n=i.executeOnChange(t,e);return s.asap(r,this),n}var i=n(96),a=n(99),s=n(18),u=n(3),l=n(2),c=(n(4),{getNativeProps:function(e,t,n){null!=t.dangerouslySetInnerHTML?l(!1):void 0;var r=u({},t,{defaultValue:void 0,value:void 0,children:e._wrapperState.initialValue,onChange:e._wrapperState.onChange});return r},mountWrapper:function(e,t){var n=t.defaultValue,r=t.children;null!=r&&(null!=n?l(!1):void 0,Array.isArray(r)&&(r.length<=1?void 0:l(!1),r=r[0]),n=""+r),null==n&&(n="");var a=i.getValue(t);e._wrapperState={initialValue:""+(null!=a?a:n),onChange:o.bind(e)}},updateWrapper:function(e){var t=e._currentElement.props,n=i.getValue(t);null!=n&&a.updatePropertyByID(e._rootNodeID,"value",""+n)}});e.exports=c},function(e,t,n){"use strict";function r(e){o.enqueueEvents(e),o.processEventQueue(!1)}var o=n(51),i={handleTopLevel:function(e,t,n,i,a){var s=o.extractEvents(e,t,n,i,a);r(s)}};e.exports=i},function(e,t,n){"use strict";function r(e){var t=p.getID(e),n=d.getReactRootIDFromNodeID(t),r=p.findReactContainerForID(n),o=p.getFirstReactDOM(r);return o}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){a(e)}function a(e){for(var t=p.getFirstReactDOM(m(e.nativeEvent))||window,n=t;n;)e.ancestors.push(n),n=r(n);for(var o=0;o=0||null!=t.is}function g(e){h(e),this._tag=e.toLowerCase(),this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._rootNodeID=null,this._wrapperState=null,this._topLevelWrapper=null,this._nodeWithLegacyProperties=null}var y=n(378),v=n(380),M=n(42),T=n(95),b=n(22),x=n(63),E=n(97),A=n(393),N=n(396),w=n(397),I=n(190),C=n(400),D=n(11),S=n(405),k=n(17),L=n(100),O=n(3),P=n(68),j=n(69),z=n(2),R=(n(108),n(28)),U=n(70),Y=n(109),B=(n(215),n(112),n(4),x.deleteListener),W=x.listenTo,F=x.registrationNameModules,V={string:!0,number:!0},H=R({children:null}),_=R({style:null}),Q=R({__html:null}),G=1,Z={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"},X={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},q={listing:!0,pre:!0,textarea:!0},K=(O({menuitem:!0},X),/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/),J={},$={}.hasOwnProperty;g.displayName="ReactDOMComponent",g.Mixin={construct:function(e){this._currentElement=e},mountComponent:function(e,t,n){this._rootNodeID=e;var r=this._currentElement.props;switch(this._tag){case"iframe":case"img":case"form":case"video":case"audio":this._wrapperState={listeners:null},t.getReactMountReady().enqueue(d,this);break;case"button":r=A.getNativeProps(this,r,n);break;case"input":N.mountWrapper(this,r,n),r=N.getNativeProps(this,r,n);break;case"option":w.mountWrapper(this,r,n),r=w.getNativeProps(this,r,n);break;case"select":I.mountWrapper(this,r,n),r=I.getNativeProps(this,r,n),n=I.processChildContext(this,r,n);break;case"textarea":C.mountWrapper(this,r,n),r=C.getNativeProps(this,r,n)}u(this,r);var o;if(t.useCreateElement){var i=n[D.ownerDocumentContextKey],a=i.createElement(this._currentElement.type);T.setAttributeForID(a,this._rootNodeID),D.getID(a),this._updateDOMProperties({},r,t,a),this._createInitialChildren(t,r,n,a),o=a}else{var s=this._createOpenTagMarkupAndPutListeners(t,r),l=this._createContentMarkup(t,r,n);o=!l&&X[this._tag]?s+"/>":s+">"+l+""}switch(this._tag){case"input":t.getReactMountReady().enqueue(p,this);case"button":case"select":case"textarea":r.autoFocus&&t.getReactMountReady().enqueue(y.focusDOMComponent,this)}return o},_createOpenTagMarkupAndPutListeners:function(e,t){var n="<"+this._currentElement.type;for(var r in t)if(t.hasOwnProperty(r)){var o=t[r];if(null!=o)if(F.hasOwnProperty(r))o&&l(this._rootNodeID,r,o,e);else{r===_&&(o&&(o=this._previousStyleCopy=O({},t.style)),o=v.createMarkupForStyles(o));var i=null;null!=this._tag&&m(this._tag,t)?r!==H&&(i=T.createMarkupForCustomAttribute(r,o)):i=T.createMarkupForProperty(r,o),i&&(n+=" "+i)}}if(e.renderToStaticMarkup)return n;var a=T.createMarkupForID(this._rootNodeID);return n+" "+a},_createContentMarkup:function(e,t,n){var r="",o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&(r=o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)r=j(i);else if(null!=a){var s=this.mountChildren(a,e,n);r=s.join("")}}return q[this._tag]&&"\n"===r.charAt(0)?"\n"+r:r},_createInitialChildren:function(e,t,n,r){var o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&U(r,o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)Y(r,i);else if(null!=a)for(var s=this.mountChildren(a,e,n),u=0;ut.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart("character",n),o.setEndPoint("EndToStart",o),o.moveEnd("character",r-n),o.select()}function s(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),i="undefined"==typeof t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var s=l(e,o),u=l(e,i);if(s&&u){var d=document.createRange();d.setStart(s.node,s.offset),n.removeAllRanges(),o>i?(n.addRange(d),n.extend(u.node,u.offset)):(d.setEnd(u.node,u.offset),n.addRange(d))}}}var u=n(9),l=n(429),c=n(208),d=u.canUseDOM&&"selection"in document&&!("getSelection"in window),p={getOffsets:d?o:i,setOffsets:d?a:s};e.exports=p},function(e,t,n){"use strict";var r=n(193),o=n(410),i=n(101);r.inject();var a={renderToString:o.renderToString,renderToStaticMarkup:o.renderToStaticMarkup,version:i};e.exports=a},function(e,t,n){"use strict";function r(){this._rootNodeID&&c.updateWrapper(this)}function o(e){var t=this._currentElement.props,n=i.executeOnChange(t,e);return s.asap(r,this),n}var i=n(96),a=n(99),s=n(18),u=n(3),l=n(2),c=(n(4),{getNativeProps:function(e,t,n){null!=t.dangerouslySetInnerHTML?l(!1):void 0;var r=u({},t,{defaultValue:void 0,value:void 0,children:e._wrapperState.initialValue,onChange:e._wrapperState.onChange});return r},mountWrapper:function(e,t){var n=t.defaultValue,r=t.children;null!=r&&(null!=n?l(!1):void 0,Array.isArray(r)&&(r.length<=1?void 0:l(!1),r=r[0]),n=""+r),null==n&&(n="");var a=i.getValue(t);e._wrapperState={initialValue:""+(null!=a?a:n),onChange:o.bind(e)}},updateWrapper:function(e){var t=e._currentElement.props,n=i.getValue(t);null!=n&&a.updatePropertyByID(e._rootNodeID,"value",""+n)}});e.exports=c},function(e,t,n){"use strict";function r(e){o.enqueueEvents(e),o.processEventQueue(!1)}var o=n(51),i={handleTopLevel:function(e,t,n,i,a){var s=o.extractEvents(e,t,n,i,a);r(s)}};e.exports=i},function(e,t,n){"use strict";function r(e){var t=p.getID(e),n=d.getReactRootIDFromNodeID(t),r=p.findReactContainerForID(n),o=p.getFirstReactDOM(r);return o}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){a(e)}function a(e){for(var t=p.getFirstReactDOM(m(e.nativeEvent))||window,n=t;n;)e.ancestors.push(n),n=r(n);for(var o=0;oo;){for(;oo;o++)n+=t+=e.charCodeAt(o);return t%=r,n%=r,t|n<<16}var r=65521;e.exports=n},function(e,t,n){"use strict";function r(e,t){var n=null==t||"boolean"==typeof t||""===t;if(n)return"";var r=isNaN(t);return r||0===t||i.hasOwnProperty(e)&&i[e]?""+t:("string"==typeof t&&(t=t.trim()),t+"px")}var o=n(182),i=o.isUnitlessNumber;e.exports=r},function(e,t,n){"use strict";function r(e,t,n,r,o){return o}n(3),n(4);e.exports=r},function(e,t,n){"use strict";function r(e,t,n){var r=e,o=void 0===r[n];o&&null!=t&&(r[n]=t)}function o(e){if(null==e)return e;var t={};return i(e,r,t),t}var i=n(111);n(4);e.exports=o},function(e,t,n){"use strict";function r(e){if(e.key){var t=i[e.key]||e.key;if("Unidentified"!==t)return t}if("keypress"===e.type){var n=o(e);return 13===n?"Enter":String.fromCharCode(n)}return"keydown"===e.type||"keyup"===e.type?a[e.keyCode]||"Unidentified":""}var o=n(103),i={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},a={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};e.exports=r},function(e,t){"use strict";function n(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function r(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function o(e,t){for(var o=n(e),i=0,a=0;o;){if(3===o.nodeType){if(a=i+o.textContent.length,t>=i&&a>=t)return{node:o,offset:t-i};i=a}o=n(r(o))}}e.exports=o},function(e,t,n){"use strict";function r(e){return o.isValidElement(e)?void 0:i(!1),e}var o=n(13),i=n(2);e.exports=r},function(e,t,n){"use strict";function r(e){return'"'+o(e)+'"'}var o=n(69);e.exports=r},function(e,t,n){"use strict";var r=n(11);e.exports=r.renderSubtreeIntoContainer},function(e,t){"use strict";function n(e){return e.replace(r,function(e,t){return t.toUpperCase()})}var r=/-(.)/g;e.exports=n},function(e,t,n){"use strict";function r(e){return o(e.replace(i,"ms-"))}var o=n(433),i=/^-ms-/;e.exports=r},function(e,t,n){"use strict";function r(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"length"in e&&!("setInterval"in e)&&"number"!=typeof e.nodeType&&(Array.isArray(e)||"callee"in e||"item"in e)}function o(e){return r(e)?Array.isArray(e)?e.slice():i(e):[e]}var i=n(444);e.exports=o},function(e,t,n){"use strict";function r(e){var t=e.match(c);return t&&t[1].toLowerCase()}function o(e,t){var n=l;l?void 0:u(!1);var o=r(e),i=o&&s(o);if(i){n.innerHTML=i[1]+e+i[2];for(var c=i[0];c--;)n=n.lastChild}else n.innerHTML=e;var d=n.getElementsByTagName("script");d.length&&(t?void 0:u(!1),a(d).forEach(t));for(var p=a(n.childNodes);n.lastChild;)n.removeChild(n.lastChild);return p}var i=n(9),a=n(435),s=n(214),u=n(2),l=i.canUseDOM?document.createElement("div"):null,c=/^\s*<(\w+)/;e.exports=o},function(e,t){"use strict";function n(e){return e===window?{x:window.pageXOffset||document.documentElement.scrollLeft,y:window.pageYOffset||document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}e.exports=n},function(e,t){"use strict";function n(e){return e.replace(r,"-$1").toLowerCase()}var r=/([A-Z])/g;e.exports=n},function(e,t,n){"use strict";function r(e){return o(e).replace(i,"-ms-")}var o=n(438),i=/^ms-/;e.exports=r},function(e,t){"use strict";function n(e){return!(!e||!("function"==typeof Node?e instanceof Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}e.exports=n},function(e,t,n){"use strict";function r(e){return o(e)&&3==e.nodeType}var o=n(440);e.exports=r},function(e,t){"use strict";function n(e,t,n){if(!e)return null;var o={};for(var i in e)r.call(e,i)&&(o[i]=t.call(n,e[i],i,e));return o}var r=Object.prototype.hasOwnProperty;e.exports=n},function(e,t){"use strict";function n(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}e.exports=n},function(e,t,n){"use strict";function r(e){var t=e.length;if(Array.isArray(e)||"object"!=typeof e&&"function"!=typeof e?o(!1):void 0,"number"!=typeof t?o(!1):void 0,0===t||t-1 in e?void 0:o(!1),e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(n){}for(var r=Array(t),i=0;t>i;i++)r[i]=e[i];return r}var o=n(2);e.exports=r},function(e,t){"use strict";function n(e){var t=e.dispatch,n=e.getState;return function(e){return function(r){return"function"==typeof r?r(t,n):e(r)}}}e.exports=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){return function(){return t(e.apply(void 0,arguments))}}function i(e,t){if("function"==typeof e)return o(e,t);if("object"!=typeof e||null===e||void 0===e)throw new Error("bindActionCreators expected an object or a function, instead received "+(null===e?"null":typeof e)+'. Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');return s["default"](e,function(e){return o(e,t)})}t.__esModule=!0,t["default"]=i;var a=n(219),s=r(a);e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){var n=t&&t.type,r=n&&'"'+n.toString()+'"'||"an action";return'Reducer "'+e+'" returned undefined handling '+r+". To ignore an action, you must explicitly return the previous state."}function i(e){Object.keys(e).forEach(function(t){var n=e[t],r=n(void 0,{type:s.ActionTypes.INIT});if("undefined"==typeof r)throw new Error('Reducer "'+t+'" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined.');var o="@@redux/PROBE_UNKNOWN_ACTION_"+Math.random().toString(36).substring(7).split("").join(".");if("undefined"==typeof n(void 0,{type:o}))throw new Error('Reducer "'+t+'" returned undefined when probed with a random type. '+("Don't try to handle "+s.ActionTypes.INIT+' or other actions in "redux/*" ')+"namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined.")})}function a(e){var t,n=p["default"](e,function(e){return"function"==typeof e});try{i(n)}catch(r){t=r}return function(e,r){if(void 0===e&&(e={}),t)throw t;var i=!1,a=c["default"](n,function(t,n){var a=e[n],s=t(a,r);if("undefined"==typeof s){var u=o(n,r);throw new Error(u)}return i=i||s!==a,s});return i?a:e}}t.__esModule=!0,t["default"]=a;var s=n(113),u=n(218),l=(r(u),n(219)),c=r(l),d=n(449),p=r(d),f=n(220);r(f);e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(113),i=r(o),a=n(447),s=r(a),u=n(446),l=r(u),c=n(216),d=r(c),p=n(217),f=r(p),h=n(220);r(h);t.createStore=i["default"],t.combineReducers=s["default"],t.bindActionCreators=l["default"],t.applyMiddleware=d["default"],t.compose=f["default"]},function(e,t){"use strict";function n(e,t){return Object.keys(e).reduce(function(n,r){return t(e[r])&&(n[r]=e[r]),n},{})}t.__esModule=!0,t["default"]=n,e.exports=t["default"]},function(e,t,n){function r(e,t){for(var n=0;n=0&&M.splice(t,1)}function s(e){var t=document.createElement("style");return t.type="text/css",i(e,t),t}function u(e){var t=document.createElement("link");return t.rel="stylesheet",i(e,t),t}function l(e,t){var n,r,o;if(t.singleton){var i=v++;n=y||(y=s(t)),r=c.bind(null,n,i,!1),o=c.bind(null,n,i,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=u(t),r=p.bind(null,n),o=function(){a(n),n.href&&URL.revokeObjectURL(n.href)}):(n=s(t),r=d.bind(null,n),o=function(){a(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}function c(e,t,n,r){var o=n?"":r.css;if(e.styleSheet)e.styleSheet.cssText=T(t,o);else{var i=document.createTextNode(o),a=e.childNodes;a[t]&&e.removeChild(a[t]),a.length?e.insertBefore(i,a[t]):e.appendChild(i)}}function d(e,t){var n=t.css,r=t.media;t.sourceMap;if(r&&e.setAttribute("media",r),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}function p(e,t){var n=t.css,r=(t.media,t.sourceMap);r&&(n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var o=new Blob([n],{type:"text/css"}),i=e.href;e.href=URL.createObjectURL(o),i&&URL.revokeObjectURL(i)}var f={},h=function(e){var t;return function(){return"undefined"==typeof t&&(t=e.apply(this,arguments)),t}},m=h(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),g=h(function(){return document.head||document.getElementsByTagName("head")[0]}),y=null,v=0,M=[];e.exports=function(e,t){t=t||{},"undefined"==typeof t.singleton&&(t.singleton=m()),"undefined"==typeof t.insertAt&&(t.insertAt="bottom");var n=o(e);return r(n,t),function(e){for(var i=[],a=0;a=400){var s="cannot "+t.method+" "+t.url+" ("+a.status+")";e=new i(s),e.status=a.status,e.body=a.body,e.res=a,r(e)}else o?r(new i(o)):n(a)})})},o.prototype.then=function(){var e=this.promise();return e.then.apply(e,arguments)}},function(e,t,n){function r(){}function o(e){var t={}.toString.call(e);switch(t){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}}function i(e){return e===Object(e)}function a(e){if(!i(e))return e;var t=[];for(var n in e)null!=e[n]&&s(t,n,e[n]);return t.join("&")}function s(e,t,n){return Array.isArray(n)?n.forEach(function(n){s(e,t,n)}):void e.push(encodeURIComponent(t)+"="+encodeURIComponent(n))}function u(e){for(var t,n,r={},o=e.split("&"),i=0,a=o.length;a>i;++i)n=o[i],t=n.split("="),r[decodeURIComponent(t[0])]=decodeURIComponent(t[1]);return r}function l(e){var t,n,r,o,i=e.split(/\r?\n/),a={};i.pop();for(var s=0,u=i.length;u>s;++s)n=i[s],t=n.indexOf(":"),r=n.slice(0,t).toLowerCase(),o=T(n.slice(t+1)),a[r]=o;return a}function c(e){return/[\/+]json\b/.test(e)}function d(e){return e.split(/ *; */).shift()}function p(e){return M(e.split(/ *; */),function(e,t){var n=t.split(/ *= */),r=n.shift(),o=n.shift();return r&&o&&(e[r]=o),e},{})}function f(e,t){t=t||{},this.req=e,this.xhr=this.req.xhr,this.text="HEAD"!=this.req.method&&(""===this.xhr.responseType||"text"===this.xhr.responseType)||"undefined"==typeof this.xhr.responseType?this.xhr.responseText:null,this.statusText=this.req.xhr.statusText,this.setStatusProperties(this.xhr.status),this.header=this.headers=l(this.xhr.getAllResponseHeaders()),this.header["content-type"]=this.xhr.getResponseHeader("content-type"),this.setHeaderProperties(this.header),this.body="HEAD"!=this.req.method?this.parseBody(this.text?this.text:this.xhr.response):null}function h(e,t){var n=this;v.call(this),this._query=this._query||[],this.method=e,this.url=t,this.header={},this._header={},this.on("end",function(){var e=null,t=null;try{t=new f(n)}catch(r){return e=new Error("Parser is unable to parse the response"),e.parse=!0,e.original=r,e.rawResponse=n.xhr&&n.xhr.responseText?n.xhr.responseText:null,n.callback(e)}if(n.emit("response",t),e)return n.callback(e,t);if(t.status>=200&&t.status<300)return n.callback(e,t);var o=new Error(t.statusText||"Unsuccessful HTTP response");o.original=e,o.response=t,o.status=t.status,n.callback(o,t)})}function m(e,t){return"function"==typeof t?new h("GET",e).end(t):1==arguments.length?new h("GET",e):new h(e,t)}function g(e,t){var n=m("DELETE",e);return t&&n.end(t),n}var y,v=n(454),M=n(455);y="undefined"!=typeof window?window:"undefined"!=typeof self?self:this,m.getXHR=function(){if(!(!y.XMLHttpRequest||y.location&&"file:"==y.location.protocol&&y.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(e){}return!1};var T="".trim?function(e){return e.trim()}:function(e){return e.replace(/(^\s*|\s*$)/g,"")};m.serializeObject=a,m.parseString=u,m.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"},m.serialize={"application/x-www-form-urlencoded":a,"application/json":JSON.stringify},m.parse={"application/x-www-form-urlencoded":u,"application/json":JSON.parse},f.prototype.get=function(e){return this.header[e.toLowerCase()]},f.prototype.setHeaderProperties=function(e){var t=this.header["content-type"]||"";this.type=d(t);var n=p(t);for(var r in n)this[r]=n[r]},f.prototype.parseBody=function(e){var t=m.parse[this.type];return t&&e&&(e.length||e instanceof Object)?t(e):null},f.prototype.setStatusProperties=function(e){1223===e&&(e=204);var t=e/100|0;this.status=this.statusCode=e,this.statusType=t,this.info=1==t,this.ok=2==t,this.clientError=4==t,this.serverError=5==t,this.error=4==t||5==t?this.toError():!1,this.accepted=202==e,this.noContent=204==e,this.badRequest=400==e,this.unauthorized=401==e,this.notAcceptable=406==e,this.notFound=404==e,this.forbidden=403==e},f.prototype.toError=function(){var e=this.req,t=e.method,n=e.url,r="cannot "+t+" "+n+" ("+this.status+")",o=new Error(r);return o.status=this.status,o.method=t,o.url=n,o},m.Response=f,v(h.prototype),h.prototype.use=function(e){return e(this),this},h.prototype.timeout=function(e){return this._timeout=e,this},h.prototype.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},h.prototype.abort=function(){return this.aborted?void 0:(this.aborted=!0,this.xhr.abort(),this.clearTimeout(),this.emit("abort"),this)},h.prototype.set=function(e,t){if(i(e)){for(var n in e)this.set(n,e[n]);return this}return this._header[e.toLowerCase()]=t,this.header[e]=t,this},h.prototype.unset=function(e){return delete this._header[e.toLowerCase()],delete this.header[e],this},h.prototype.getHeader=function(e){return this._header[e.toLowerCase()]},h.prototype.type=function(e){return this.set("Content-Type",m.types[e]||e),this},h.prototype.parse=function(e){return this._parser=e,this},h.prototype.accept=function(e){return this.set("Accept",m.types[e]||e),this},h.prototype.auth=function(e,t){var n=btoa(e+":"+t);return this.set("Authorization","Basic "+n),this},h.prototype.query=function(e){return"string"!=typeof e&&(e=a(e)),e&&this._query.push(e),this},h.prototype.field=function(e,t){return this._formData||(this._formData=new y.FormData),this._formData.append(e,t),this},h.prototype.attach=function(e,t,n){return this._formData||(this._formData=new y.FormData),this._formData.append(e,t,n||t.name),this},h.prototype.send=function(e){var t=i(e),n=this.getHeader("Content-Type");if(t&&i(this._data))for(var r in e)this._data[r]=e[r];else"string"==typeof e?(n||this.type("form"),n=this.getHeader("Content-Type"),"application/x-www-form-urlencoded"==n?this._data=this._data?this._data+"&"+e:e:this._data=(this._data||"")+e):this._data=e;return!t||o(e)?this:(n||this.type("json"),this)},h.prototype.callback=function(e,t){var n=this._callback;this.clearTimeout(),n(e,t)},h.prototype.crossDomainError=function(){var e=new Error("Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.");e.crossDomain=!0,e.status=this.status,e.method=this.method,e.url=this.url,this.callback(e)},h.prototype.timeoutError=function(){var e=this._timeout,t=new Error("timeout of "+e+"ms exceeded");t.timeout=e,this.callback(t)},h.prototype.withCredentials=function(){return this._withCredentials=!0,this},h.prototype.end=function(e){var t=this,n=this.xhr=m.getXHR(),i=this._query.join("&"),a=this._timeout,s=this._formData||this._data;this._callback=e||r,n.onreadystatechange=function(){if(4==n.readyState){var e;try{e=n.status}catch(r){e=0}if(0==e){if(t.timedout)return t.timeoutError();if(t.aborted)return;return t.crossDomainError()}t.emit("end")}};var u=function(e){e.total>0&&(e.percent=e.loaded/e.total*100),e.direction="download",t.emit("progress",e)};this.hasListeners("progress")&&(n.onprogress=u);try{n.upload&&this.hasListeners("progress")&&(n.upload.onprogress=u)}catch(l){}if(a&&!this._timer&&(this._timer=setTimeout(function(){t.timedout=!0,t.abort()},a)),i&&(i=m.serializeObject(i),this.url+=~this.url.indexOf("?")?"&"+i:"?"+i),n.open(this.method,this.url,!0),this._withCredentials&&(n.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof s&&!o(s)){var d=this.getHeader("Content-Type"),p=this._parser||m.serialize[d?d.split(";")[0]:""];!p&&c(d)&&(p=m.serialize["application/json"]),p&&(s=p(s))}for(var f in this.header)null!=this.header[f]&&n.setRequestHeader(f,this.header[f]);return this.emit("request",this),n.send("undefined"!=typeof s?s:null),this},h.prototype.then=function(e,t){return this.end(function(n,r){n?t(n):e(r)})},m.Request=h,m.get=function(e,t,n){var r=m("GET",e);return"function"==typeof t&&(n=t,t=null),t&&r.query(t),n&&r.end(n),r},m.head=function(e,t,n){var r=m("HEAD",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},m.del=g,m["delete"]=g,m.patch=function(e,t,n){var r=m("PATCH",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},m.post=function(e,t,n){var r=m("POST",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},m.put=function(e,t,n){var r=m("PUT",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},e.exports=m},function(e,t){function n(e){return e?r(e):void 0}function r(e){for(var t in n.prototype)e[t]=n.prototype[t];return e}e.exports=n,n.prototype.on=n.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},n.prototype.once=function(e,t){function n(){this.off(e,n),t.apply(this,arguments)}return n.fn=t,this.on(e,n),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks["$"+e];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var r,o=0;or;++r)n[r].apply(this,t)}return this},n.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},n.prototype.hasListeners=function(e){return!!this.listeners(e).length}},function(e,t){e.exports=function(e,t,n){for(var r=0,o=e.length,i=3==arguments.length?n:e[r++];o>r;)i=t.call(null,i,e[r],++r,e);return i}},function(e,t){e.exports="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxtZXRhZGF0YT5Db3B5cmlnaHQgKEMpIDIwMTYgYnkgb3JpZ2luYWwgYXV0aG9ycyBAIGZvbnRlbGxvLmNvbTwvbWV0YWRhdGE+CjxkZWZzPgo8Zm9udCBpZD0iZm9udGF3ZXNvbWUtd2ViZm9udCIgaG9yaXotYWR2LXg9IjEwMDAiID4KPGZvbnQtZmFjZSBmb250LWZhbWlseT0iZm9udGF3ZXNvbWUtd2ViZm9udCIgZm9udC13ZWlnaHQ9IjQwMCIgZm9udC1zdHJldGNoPSJub3JtYWwiIHVuaXRzLXBlci1lbT0iMTAwMCIgYXNjZW50PSI4NTAiIGRlc2NlbnQ9Ii0xNTAiIC8+CjxtaXNzaW5nLWdseXBoIGhvcml6LWFkdi14PSIxMDAwIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMTIiIHVuaWNvZGU9IiYjeGYwMDI7IiBkPSJNNjQzIDM4NnEwIDEwMy03MyAxNzZ0LTE3NyA3NC0xNzctNzQtNzMtMTc2IDczLTE3NyAxNzctNzMgMTc3IDczIDczIDE3N3ogbTI4Ni00NjVxMC0yOS0yMi01MHQtNTAtMjFxLTMwIDAtNTAgMjFsLTE5MSAxOTFxLTEwMC02OS0yMjMtNjktODAgMC0xNTMgMzF0LTEyNSA4NC04NCAxMjUtMzEgMTUzIDMxIDE1MiA4NCAxMjYgMTI1IDg0IDE1MyAzMSAxNTMtMzEgMTI1LTg0IDg0LTEyNiAzMS0xNTJxMC0xMjMtNjktMjIzbDE5MS0xOTFxMjEtMjEgMjEtNTF6IiBob3Jpei1hZHYteD0iOTI4LjYiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJjaGVjayIgdW5pY29kZT0iJiN4ZjAwYzsiIGQ9Ik05MzMgNTM0cTAtMjItMTYtMzhsLTQwNC00MDQtNzYtNzZxLTE2LTE1LTM4LTE1dC0zOCAxNWwtNzYgNzYtMjAyIDIwMnEtMTUgMTYtMTUgMzh0MTUgMzhsNzYgNzZxMTYgMTYgMzggMTZ0MzgtMTZsMTY0LTE2NSAzNjYgMzY3cTE2IDE2IDM4IDE2dDM4LTE2bDc2LTc2cTE2LTE1IDE2LTM4eiIgaG9yaXotYWR2LXg9IjEwMDAiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0zNiIgdW5pY29kZT0iJiN4ZjAwZDsiIGQ9Ik03MjQgMTEycTAtMjItMTUtMzhsLTc2LTc2cS0xNi0xNS0zOC0xNXQtMzggMTVsLTE2NCAxNjUtMTY0LTE2NXEtMTYtMTUtMzgtMTV0LTM4IDE1bC03NiA3NnEtMTYgMTYtMTYgMzh0MTYgMzhsMTY0IDE2NC0xNjQgMTY0cS0xNiAxNi0xNiAzOHQxNiAzOGw3NiA3NnExNiAxNiAzOCAxNnQzOC0xNmwxNjQtMTY0IDE2NCAxNjRxMTYgMTYgMzggMTZ0MzgtMTZsNzYtNzZxMTUtMTUgMTUtMzh0LTE1LTM4bC0xNjQtMTY0IDE2NC0xNjRxMTUtMTUgMTUtMzh6IiBob3Jpei1hZHYteD0iNzg1LjciIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0zNSIgdW5pY29kZT0iJiN4ZjAxMzsiIGQ9Ik01NzEgMzUwcTAgNTktNDEgMTAxdC0xMDEgNDItMTAxLTQyLTQyLTEwMSA0Mi0xMDEgMTAxLTQyIDEwMSA0MiA0MSAxMDF6IG0yODYgNjF2LTEyNHEwLTctNC0xM3QtMTEtN2wtMTA0LTE2cS0xMC0zMC0yMS01MSAxOS0yNyA1OS03NyA2LTYgNi0xM3QtNS0xM3EtMTUtMjEtNTUtNjF0LTUzLTM5cS03IDAtMTQgNWwtNzcgNjBxLTI1LTEzLTUxLTIxLTktNzYtMTYtMTA0LTQtMTYtMjAtMTZoLTEyNHEtOCAwLTE0IDV0LTYgMTJsLTE2IDEwM3EtMjcgOS01MCAyMWwtNzktNjBxLTYtNS0xNC01LTggMC0xNCA2LTcwIDY0LTkyIDk0LTQgNS00IDEzIDAgNiA1IDEyIDggMTIgMjggMzd0MzAgNDBxLTE1IDI4LTIzIDU1bC0xMDIgMTVxLTcgMS0xMSA3dC01IDEzdjEyNHEwIDcgNSAxM3QxMCA3bDEwNCAxNnE4IDI1IDIyIDUxLTIzIDMyLTYwIDc3LTYgNy02IDE0IDAgNSA1IDEyIDE1IDIwIDU1IDYwdDUzIDQwcTcgMCAxNS01bDc3LTYwcTI0IDEzIDUwIDIxIDkgNzYgMTcgMTA0IDMgMTYgMjAgMTZoMTI0cTcgMCAxMy01dDctMTJsMTUtMTAzcTI4LTkgNTEtMjBsNzkgNTlxNSA1IDEzIDUgNyAwIDE0LTUgNzItNjcgOTItOTUgNC01IDQtMTIgMC03LTQtMTMtOS0xMi0yOS0zN3QtMzAtNDBxMTUtMjggMjMtNTRsMTAyLTE2cTctMSAxMi03dDQtMTN6IiBob3Jpei1hZHYteD0iODU3LjEiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0yIiB1bmljb2RlPSImI3hmMDE2OyIgZD0iTTgxOSA2MzhxMTYtMTYgMjctNDJ0MTEtNTB2LTY0MnEwLTIzLTE1LTM4dC0zOC0xNmgtNzUwcS0yMyAwLTM4IDE2dC0xNiAzOHY4OTJxMCAyMyAxNiAzOHQzOCAxNmg1MDBxMjIgMCA0OS0xMXQ0Mi0yN3ogbS0yNDggMTM2di0yMTBoMjEwcS01IDE3LTEyIDIzbC0xNzUgMTc1cS02IDctMjMgMTJ6IG0yMTUtODUzdjU3MmgtMjMycS0yMyAwLTM4IDE2dC0xNiAzN3YyMzNoLTQyOXYtODU4aDcxNXoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTMyIiB1bmljb2RlPSImI3hmMDIxOyIgZD0iTTg0MyAyNjFxMC0zIDAtNC0zNi0xNTAtMTUwLTI0M3QtMjY3LTkzcS04MSAwLTE1NyAzMXQtMTM2IDg4bC03Mi03MnEtMTEtMTEtMjUtMTF0LTI1IDExLTExIDI1djI1MHEwIDE0IDExIDI1dDI1IDExaDI1MHExNCAwIDI1LTExdDEwLTI1LTEwLTI1bC03Ny03N3E0MC0zNiA5MC01N3QxMDUtMjBxNzQgMCAxMzkgMzd0MTA0IDk5cTYgMTAgMzAgNjYgNCAxMyAxNiAxM2gxMDdxOCAwIDEzLTZ0NS0xMnogbTE0IDQ0NnYtMjUwcTAtMTQtMTAtMjV0LTI2LTExaC0yNTBxLTE0IDAtMjUgMTF0LTEwIDI1IDEwIDI1bDc3IDc3cS04MiA3Ny0xOTQgNzctNzUgMC0xNDAtMzd0LTEwNC05OXEtNi0xMC0yOS02Ni01LTEzLTE3LTEzaC0xMTFxLTcgMC0xMyA2dC01IDEydjRxMzYgMTUwIDE1MSAyNDN0MjY4IDkzcTgxIDAgMTU4LTMxdDEzNy04OGw3MiA3MnExMSAxMSAyNSAxMXQyNi0xMSAxMC0yNXoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTMzIiB1bmljb2RlPSImI3hmMDRiOyIgZD0iTTc3MiAzMzNsLTc0MS00MTJxLTEzLTctMjItMnQtOSAyMHY4MjJxMCAxNCA5IDIwdDIyLTJsNzQxLTQxMnExMy03IDEzLTE3dC0xMy0xN3oiIGhvcml6LWFkdi14PSI3ODUuNyIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTM0IiB1bmljb2RlPSImI3hmMDRkOyIgZD0iTTg1NyA3NDN2LTc4NnEwLTE0LTEwLTI1dC0yNi0xMWgtNzg1cS0xNSAwLTI1IDExdC0xMSAyNXY3ODZxMCAxNCAxMSAyNXQyNSAxMWg3ODVxMTUgMCAyNi0xMXQxMC0yNXoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTUiIHVuaWNvZGU9IiYjeGYwNTk7IiBkPSJNNTAwIDgydjEwN3EwIDgtNSAxM3QtMTMgNWgtMTA3cS04IDAtMTMtNXQtNS0xM3YtMTA3cTAtOCA1LTEzdDEzLTVoMTA3cTggMCAxMyA1dDUgMTN6IG0xNDMgMzc1cTAgNDktMzEgOTF0LTc3IDY1LTk1IDIzcS0xMzYgMC0yMDctMTE5LTktMTMgNC0yNGw3NC01NXE0LTQgMTAtNCA5IDAgMTQgNyAzMCAzOCA0OCA1MSAxOSAxNCA0OCAxNCAyNyAwIDQ4LTE1dDIxLTMzcTAtMjEtMTEtMzR0LTM4LTI1cS0zNS0xNS02NS00OHQtMjktNzB2LTIwcTAtOCA1LTEzdDEzLTVoMTA3cTggMCAxMyA1dDUgMTNxMCAxMCAxMiAyN3QzMCAyOHExOCAxMCAyOCAxNnQyNSAxOSAyNSAyNyAxNiAzNCA3IDQ1eiBtMjE0LTEwN3EwLTExNy01Ny0yMTV0LTE1Ni0xNTYtMjE1LTU4LTIxNiA1OC0xNTUgMTU2LTU4IDIxNSA1OCAyMTUgMTU1IDE1NiAyMTYgNTggMjE1LTU4IDE1Ni0xNTYgNTctMjE1eiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtNCIgdW5pY29kZT0iJiN4ZjA1YTsiIGQ9Ik01NzEgODJ2ODlxMCA4LTUgMTN0LTEyIDVoLTU0djI4NnEwIDgtNSAxM3QtMTMgNWgtMTc4cS04IDAtMTMtNXQtNS0xM3YtODlxMC04IDUtMTN0MTMtNWg1M3YtMTc5aC01M3EtOCAwLTEzLTV0LTUtMTN2LTg5cTAtOCA1LTEzdDEzLTVoMjUwcTcgMCAxMiA1dDUgMTN6IG0tNzEgNTAwdjg5cTAgOC01IDEzdC0xMyA1aC0xMDdxLTggMC0xMy01dC01LTEzdi04OXEwLTggNS0xM3QxMy01aDEwN3E4IDAgMTMgNXQ1IDEzeiBtMzU3LTIzMnEwLTExNy01Ny0yMTV0LTE1Ni0xNTYtMjE1LTU4LTIxNiA1OC0xNTUgMTU2LTU4IDIxNSA1OCAyMTUgMTU1IDE1NiAyMTYgNTggMjE1LTU4IDE1Ni0xNTYgNTctMjE1eiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMyIgdW5pY29kZT0iJiN4ZjA2NTsiIGQ9Ik00MjEgMjYxcTAtNy01LTEzbC0xODUtMTg1IDgwLTgxcTEwLTEwIDEwLTI1dC0xMC0yNS0yNS0xMWgtMjUwcS0xNSAwLTI1IDExdC0xMSAyNXYyNTBxMCAxNSAxMSAyNXQyNSAxMSAyNS0xMWw4MC04MCAxODYgMTg1cTUgNiAxMiA2dDEzLTZsNjQtNjNxNS02IDUtMTN6IG00MzYgNDgydi0yNTBxMC0xNS0xMC0yNXQtMjYtMTEtMjUgMTFsLTgwIDgwLTE4NS0xODVxLTYtNi0xMy02dC0xMyA2bC02NCA2NHEtNSA1LTUgMTJ0NSAxM2wxODYgMTg1LTgxIDgxcS0xMCAxMC0xMCAyNXQxMCAyNSAyNSAxMWgyNTBxMTUgMCAyNi0xMXQxMC0yNXoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTEiIHVuaWNvZGU9IiYjeGYwNjc7IiBkPSJNNzg2IDQzOXYtMTA3cTAtMjItMTYtMzh0LTM4LTE1aC0yMzJ2LTIzM3EwLTIyLTE2LTM3dC0zOC0xNmgtMTA3cS0yMiAwLTM4IDE2dC0xNSAzN3YyMzNoLTIzMnEtMjMgMC0zOCAxNXQtMTYgMzh2MTA3cTAgMjMgMTYgMzh0MzggMTZoMjMydjIzMnEwIDIyIDE1IDM4dDM4IDE2aDEwN3EyMyAwIDM4LTE2dDE2LTM4di0yMzJoMjMycTIzIDAgMzgtMTZ0MTYtMzh6IiBob3Jpei1hZHYteD0iNzg1LjciIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC02IiB1bmljb2RlPSImI3hmMDcxOyIgZD0iTTU3MSA4M3YxMDZxMCA4LTUgMTN0LTEyIDVoLTEwOHEtNyAwLTEyLTV0LTUtMTN2LTEwNnEwLTggNS0xM3QxMi02aDEwOHE3IDAgMTIgNnQ1IDEzeiBtLTEgMjA4bDEwIDI1N3EwIDYtNSAxMC03IDYtMTQgNmgtMTIycS02IDAtMTQtNi01LTQtNS0xMmw5LTI1NXEwLTUgNi05dDEzLTNoMTAzcTggMCAxNCAzdDUgOXogbS03IDUyMmw0MjgtNzg2cTIwLTM1LTEtNzAtOS0xNy0yNi0yNnQtMzUtMTBoLTg1OHEtMTggMC0zNSAxMHQtMjYgMjZxLTIxIDM1LTEgNzBsNDI5IDc4NnE5IDE3IDI2IDI3dDM2IDEwIDM2LTEwIDI3LTI3eiIgaG9yaXotYWR2LXg9IjEwMDAiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC04IiB1bmljb2RlPSImI3hmMDhiOyIgZD0iTTM1NyA0NnEwLTIgMS0xMXQwLTE0LTItMTQtNS0xMS0xMi0zaC0xNzhxLTY3IDAtMTE0IDQ3dC00NyAxMTR2MzkycTAgNjcgNDcgMTE0dDExNCA0N2gxNzhxOCAwIDEzLTV0NS0xM3EwLTIgMS0xMXQwLTE1LTItMTMtNS0xMS0xMi0zaC0xNzhxLTM3IDAtNjMtMjZ0LTI3LTY0di0zOTJxMC0zNyAyNy02M3Q2My0yN2gxNjAgNyA3dDYgMCA3LTIgNC0zIDQtNSAxLTh6IG01MTggMzA0cTAtMTQtMTEtMjVsLTMwMy0zMDRxLTExLTEwLTI1LTEwdC0yNSAxMC0xMSAyNXYxNjFoLTI1MHEtMTQgMC0yNSAxMXQtMTEgMjV2MjE0cTAgMTUgMTEgMjV0MjUgMTFoMjUwdjE2MXEwIDE0IDExIDI1dDI1IDEwIDI1LTEwbDMwMy0zMDRxMTEtMTAgMTEtMjV6IiBob3Jpei1hZHYteD0iOTI4LjYiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC03IiB1bmljb2RlPSImI3hmMDkwOyIgZD0iTTY2MSAzNTBxMC0xNC0xMS0yNWwtMzAzLTMwNHEtMTEtMTAtMjYtMTB0LTI1IDEwLTEwIDI1djE2MWgtMjUwcS0xNSAwLTI1IDExdC0xMSAyNXYyMTRxMCAxNSAxMSAyNXQyNSAxMWgyNTB2MTYxcTAgMTQgMTAgMjV0MjUgMTAgMjYtMTBsMzAzLTMwNHExMS0xMCAxMS0yNXogbTE5NiAxOTZ2LTM5MnEwLTY3LTQ3LTExNHQtMTE0LTQ3aC0xNzhxLTcgMC0xMyA1dC01IDEzcTAgMi0xIDExdDAgMTUgMiAxMyA1IDExIDEyIDNoMTc4cTM3IDAgNjQgMjd0MjYgNjN2MzkycTAgMzctMjYgNjR0LTY0IDI2aC0xNjAtNi04dC02IDAtNiAyLTUgMy00IDUtMSA4cTAgMi0xIDExdDAgMTUgMiAxMyA1IDExIDEyIDNoMTc4cTY3IDAgMTE0LTQ3dDQ3LTExNHoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTkiIHVuaWNvZGU9IiYjeGYwOWI7IiBkPSJNODU3IDM1MHEwLTE0MC04Mi0yNTJ0LTIxMS0xNTVxLTE1LTMtMjIgNHQtNyAxN3YxMThxMCA1NC0yOSA3OSAzMiAzIDU3IDEwdDUzIDIyIDQ1IDM3IDMwIDU4IDExIDg0cTAgNjgtNDQgMTE1IDIxIDUxLTQgMTE0LTE2IDUtNDYtNnQtNTEtMjVsLTIxLTEzcS01MiAxNS0xMDcgMTV0LTEwOC0xNXEtOCA2LTIzIDE1dC00NyAyMi00OCA3cS0yNC02My00LTExNC00NC00Ny00NC0xMTUgMC00NyAxMi04M3QyOS01OSA0NS0zNyA1Mi0yMiA1Ny0xMHEtMjItMjAtMjctNTgtMTItNS0yNS04dC0zMi0zLTM2IDEyLTMxIDM1cS0xMSAxOC0yNyAyOXQtMjggMTRsLTExIDFxLTEyIDAtMTYtMnQtMy03IDUtOCA3LTZsNC0zcTEyLTYgMjQtMjF0MTgtMjlsNi0xM3E3LTIxIDI0LTM0dDM3LTE3IDM5LTMgMzEgMWwxMyAzcTAtMjIgMC01MHQxLTMwcTAtMTAtOC0xN3QtMjItNHEtMTI5IDQzLTIxMSAxNTV0LTgyIDI1MnEwIDExNyA1OCAyMTV0MTU1IDE1NiAyMTYgNTggMjE1LTU4IDE1Ni0xNTYgNTctMjE1eiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMTAiIHVuaWNvZGU9IiYjeGYwYTA7IiBkPSJNNTgwIDE3MXEwLTE4LTEzLTMxdC0zMS0xMy0zMiAxMy0xMyAzMSAxMyAzMiAzMiAxMyAzMS0xMyAxMy0zMnogbTE0MyAwcTAtMTgtMTMtMzF0LTMxLTEzLTMyIDEzLTEzIDMxIDEzIDMyIDMyIDEzIDMxLTEzIDEzLTMyeiBtNjMtODl2MTc5cTAgNy02IDEydC0xMiA2aC02NzlxLTcgMC0xMi02dC02LTEydi0xNzlxMC03IDYtMTJ0MTItNmg2NzlxNyAwIDEyIDZ0NiAxMnogbS02ODcgMjY4aDY1OWwtODggMjY5cS0yIDctOSAxMnQtMTQgNWgtNDM3cS03IDAtMTQtNXQtOS0xMnogbTc1OC04OXYtMTc5cTAtMzctMjYtNjN0LTYzLTI2aC02NzlxLTM2IDAtNjMgMjZ0LTI2IDYzdjE3OXEwIDE0IDkgNDJsMTEwIDMzOHE5IDI5IDM1IDQ4dDU2IDE4aDQzN3EzMSAwIDU2LTE4dDM1LTQ4bDExMC0zMzhxOS0yOCA5LTQyeiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMTEiIHVuaWNvZGU9IiYjeGYwYWM7IiBkPSJNNDI5IDc3OXExMTYgMCAyMTUtNTh0MTU2LTE1NiA1Ny0yMTUtNTctMjE1LTE1Ni0xNTYtMjE1LTU4LTIxNiA1OC0xNTUgMTU2LTU4IDIxNSA1OCAyMTUgMTU1IDE1NiAyMTYgNTh6IG0xNTMtMjkxcS0yLTEtNi01dC03LTZxMSAwIDIgM3QzIDYgMiA0cTMgNCAxMiA4IDggNCAyOSA3IDE5IDUgMjktNi0xIDEgNSA3dDggN3EyIDEgOCAzdDkgNGwxIDEycS03LTEtMTAgNHQtMyAxMnEwLTItNC01IDAgNC0yIDV0LTctMS01LTFxLTUgMi04IDV0LTUgOS0yIDhxLTEgMy01IDZ0LTUgNnEtMSAxLTIgM3QtMSA0LTMgMy0zIDEtNC0zLTQtNS0yLTNxLTIgMS00IDF0LTItMS0zLTEtMy0ycS0xLTItNC0ydC01LTFxOCAzLTEgNi01IDItOSAyIDYgMiA1IDZ0LTUgOGgzcS0xIDItNSA1dC0xMCA1LTcgM3EtNSAzLTE5IDV0LTE4IDFxLTMtNC0zLTZ0Mi04IDItN3ExLTMtMy03dC0zLTdxMC00IDctOXQ2LTEycS0yLTQtOS05dC05LTZxLTMtNS0xLTExdDYtOXExLTEgMS0ydC0yLTMtMy0yLTQtMmwtMS0xcS03LTMtMTIgM3QtNyAxNXEtNCAxNC05IDE3LTEzIDQtMTYtMS0zIDctMjMgMTUtMTQgNS0zMyAyIDQgMCAwIDgtNCA5LTEwIDcgMSAzIDIgMTB0MCA3cTIgOCA3IDEzIDEgMSA0IDV0NSA3IDEgNHExOS0zIDI4IDYgMiAzIDYgOXQ2IDEwcTUgMyA4IDN0OC0zIDgtM3E4LTEgOCA2dC00IDExcTcgMCAyIDEwLTMgNC01IDUtNiAyLTE1LTMtNC0yIDItNC0xIDAtNi02dC05LTEwLTkgM3EwIDAtMyA3dC01IDhxLTUgMC05LTkgMSA1LTYgOXQtMTQgNHExMSA3LTQgMTUtNCAzLTEyIDN0LTExLTJxLTItNC0zLTd0My00IDYtMyA2LTIgNS0ycTgtNiA1LTgtMSAwLTUtMnQtNi0yLTQtMnEtMS0zIDAtOHQtMS04cS0zIDMtNSAxMHQtNCA5cTQtNS0xNC0zbC01IDBxLTMgMC05LTF0LTEyLTEtNyA1cS0zIDQgMCAxMSAwIDIgMiAxLTIgMi02IDV0LTYgNXEtMjUtOC01Mi0yMyAzIDAgNiAxIDMgMSA4IDR0NSAzcTE5IDcgMjQgNGwzIDJxNy05IDExLTE0LTQgMy0xNyAxLTExLTMtMTItNyA0LTYgMi0xMC0yIDItNiA2dC04IDYtOCAzcS05IDAtMTMtMS04MS00NS0xMzEtMTI0IDQtNCA3LTQgMi0xIDMtNXQxLTYgNiAxcTUtNCAyLTEwIDEgMCAyNS0xNSAxMC0xMCAxMS0xMiAyLTYtNS0xMC0xIDEtNSA1dC01IDJxLTItMyAwLTEwdDYtN3EtNCAwLTUtOXQtMi0yMCAwLTEzbDEtMXEtMi02IDMtMTl0MTItMTFxLTctMSAxMS0yNCAzLTQgNC01IDItMSA3LTR0OS02IDUtNXEyLTMgNi0xM3Q4LTEzcS0yLTMgNS0xMXQ2LTEzcS0xIDAtMi0xdC0xIDBxMi00IDktOHQ4LTdxMS0yIDEtNnQyLTYgNC0xcTIgMTEtMTMgMzUtOCAxMy05IDE2LTIgMi00IDh0LTIgOHExIDAgMyAwdDUtMiA0LTMgMS0xcS0xLTQgMS0xMHQ3LTEwIDEwLTExIDYtN3E0LTQgOC0xMXQwLThxNSAwIDExLTV0MTAtMTFxMy01IDQtMTV0My0xM3ExLTQgNS04dDctNWw5LTV0Ny0zcTMtMiAxMC02dDEyLTdxNi0yIDktMnQ4IDEgOCAycTggMSAxNi04dDEyLTEycTIwLTEwIDMwLTYtMSAwIDEtNHQ0LTkgNS04IDMtNXEzLTMgMTAtOHQxMC04cTQgMiA0IDUtMS01IDQtMTF0MTAtNnE4IDIgOCAxOC0xNy04LTI3IDEwIDAgMC0yIDN0LTIgNS0xIDQgMCA1IDIgMXE1IDAgNiAydC0xIDctMiA4cS0xIDQtNiAxMXQtNyA4cS0zLTUtOS00dC05IDVxMC0xLTEtM3QtMS00cS03IDAtOCAwIDEgMiAxIDEwdDIgMTNxMSAyIDMgNnQ1IDkgMiA3LTMgNS05IDFxLTExIDAtMTUtMTEtMS0yLTItNnQtMi02LTUtNHEtNC0yLTE0LTF0LTEzIDNxLTggNC0xMyAxNnQtNSAyMHEwIDYgMSAxNXQyIDE0LTMgMTRxMiAxIDUgNXQ1IDZxMiAxIDMgMXQzIDAgMiAxIDEgM3EwIDEtMiAyLTEgMS0yIDEgNC0xIDE2IDF0MTUtMXE5LTYgMTIgMSAwIDEtMSA2dDAgN3EzLTE1IDE2LTUgMi0xIDktM3Q5LTJxMi0xIDQtM3QzLTMgMyAwIDUgNHE1LTggNy0xMyA2LTIzIDEwLTI1IDQtMiA2LTF0MyA1IDAgOC0xIDdsLTEgNXYxMGwwIDRxLTggMi0xMCA3dDAgMTAgOSAxMHEwIDEgNCAydDkgNCA3IDRxMTIgMTEgOCAyMCA0IDAgNiA1IDAgMC0yIDJ0LTUgMi0yIDJxNSAyIDEgOCAzIDIgNCA3dDQgNXE1LTYgMTItMSA0IDUgMSA5IDIgNCAxMSA2dDEwIDVxNC0xIDUgMXQwIDcgMiA3cTIgMiA5IDV0NyAybDkgN3EyIDIgMCAyIDEwLTEgMTggNiA1IDYtNCAxMSAyIDQtMSA1dC05IDRxMiAwIDcgMHQ1IDFxOSA1LTMgOS0xMCAyLTI0LTd6IG0tOTEtNDkwcTExNSAyMSAxOTUgMTA2LTEgMi03IDJ0LTcgMnEtMTAgNC0xMyA1IDEgNC0xIDd0LTUgNS03IDUtNiA0cS0xIDEtNCAzdC00IDMtNCAyLTUgMi01LTFsLTItMXEtMiAwLTMtMXQtMy0yLTItMSAwLTJxLTEyIDEwLTIwIDEzLTMgMC02IDN0LTYgNC02IDAtNi0zcS0zLTMtNC05dC0xLTdxLTQgMyAwIDEwdDEgMTBxLTEgMy02IDJ0LTYtMi03LTUtNS0zLTQtMy01LTVxLTItMi00LTZ0LTItNnEtMSAyLTcgM3QtNSAzcTEtNSAyLTE5dDMtMjJxNC0xNy03LTI2LTE1LTE0LTE2LTIzLTItMTIgNy0xNCAwLTQtNS0xMnQtNC0xMnEwLTMgMi05eiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMzEiIHVuaWNvZGU9IiYjeGYwYzk7IiBkPSJNODU3IDEwMHYtNzFxMC0xNS0xMC0yNXQtMjYtMTFoLTc4NXEtMTUgMC0yNSAxMXQtMTEgMjV2NzFxMCAxNSAxMSAyNXQyNSAxMWg3ODVxMTUgMCAyNi0xMXQxMC0yNXogbTAgMjg2di03MnEwLTE0LTEwLTI1dC0yNi0xMGgtNzg1cS0xNSAwLTI1IDEwdC0xMSAyNXY3MnEwIDE0IDExIDI1dDI1IDEwaDc4NXExNSAwIDI2LTEwdDEwLTI1eiBtMCAyODV2LTcxcTAtMTQtMTAtMjV0LTI2LTExaC03ODVxLTE1IDAtMjUgMTF0LTExIDI1djcxcTAgMTUgMTEgMjZ0MjUgMTBoNzg1cTE1IDAgMjYtMTB0MTAtMjZ6IiBob3Jpei1hZHYteD0iODU3LjEiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaCIgdW5pY29kZT0iJiN4ZjBlZTsiIGQ9Ik03MTQgMzY4cTAgOC01IDEzbC0xOTYgMTk2cS01IDUtMTMgNXQtMTMtNWwtMTk2LTE5NnEtNS02LTUtMTMgMC04IDUtMTN0MTMtNWgxMjV2LTE5NnEwLTggNS0xM3QxMi01aDEwOHE3IDAgMTIgNXQ1IDEzdjE5NmgxMjVxOCAwIDEzIDV0NSAxM3ogbTM1Ny0xNjFxMC04OS02Mi0xNTF0LTE1Mi02M2gtNjA3cS0xMDMgMC0xNzcgNzN0LTczIDE3N3EwIDcyIDM5IDEzNHQxMDUgOTJxLTEgMTctMSAyNCAwIDExOCA4NCAyMDJ0MjAyIDg0cTg3IDAgMTU5LTQ5dDEwNS0xMjlxNDAgMzUgOTMgMzUgNTkgMCAxMDEtNDJ0NDItMTAxcTAtNDMtMjMtNzcgNzItMTcgMTE5LTc2dDQ2LTEzM3oiIGhvcml6LWFkdi14PSIxMDcxLjQiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0zMCIgdW5pY29kZT0iJiN4ZjBmNjsiIGQ9Ik04MTkgNjM4cTE2LTE2IDI3LTQydDExLTUwdi02NDJxMC0yMy0xNS0zOHQtMzgtMTZoLTc1MHEtMjMgMC0zOCAxNnQtMTYgMzh2ODkycTAgMjMgMTYgMzh0MzggMTZoNTAwcTIyIDAgNDktMTF0NDItMjd6IG0tMjQ4IDEzNnYtMjEwaDIxMHEtNSAxNy0xMiAyM2wtMTc1IDE3NXEtNiA3LTIzIDEyeiBtMjE1LTg1M3Y1NzJoLTIzMnEtMjMgMC0zOCAxNnQtMTYgMzd2MjMzaC00Mjl2LTg1OGg3MTV6IG0tNTcyIDQ4M3EwIDcgNSAxMnQxMyA1aDM5M3E4IDAgMTMtNXQ1LTEydi0zNnEwLTgtNS0xM3QtMTMtNWgtMzkzcS04IDAtMTMgNXQtNSAxM3YzNnogbTQxMS0xMjVxOCAwIDEzLTV0NS0xM3YtMzZxMC04LTUtMTN0LTEzLTVoLTM5M3EtOCAwLTEzIDV0LTUgMTN2MzZxMCA4IDUgMTN0MTMgNWgzOTN6IG0wLTE0M3E4IDAgMTMtNXQ1LTEzdi0zNnEwLTgtNS0xM3QtMTMtNWgtMzkzcS04IDAtMTMgNXQtNSAxM3YzNnEwIDggNSAxM3QxMyA1aDM5M3oiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTEzIiB1bmljb2RlPSImI3hmMTE0OyIgZD0iTTg1NyAxMTh2MzkzcTAgMjItMTUgMzh0LTM4IDE1aC0zOTNxLTIzIDAtMzggMTZ0LTE2IDM4djM2cTAgMjItMTUgMzh0LTM4IDE1aC0xNzlxLTIyIDAtMzgtMTV0LTE2LTM4di01MzZxMC0yMiAxNi0zOHQzOC0xNmg2NzlxMjIgMCAzOCAxNnQxNSAzOHogbTcyIDM5M3YtMzkzcTAtNTEtMzctODh0LTg4LTM3aC02NzlxLTUxIDAtODggMzd0LTM3IDg4djUzNnEwIDUxIDM3IDg4dDg4IDM3aDE3OXE1MSAwIDg4LTM3dDM3LTg4di0xOGgzNzVxNTEgMCA4OC0zN3QzNy04OHoiIGhvcml6LWFkdi14PSI5MjguNiIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTM3IiB1bmljb2RlPSImI3hmMTI4OyIgZD0iTTM5MyAxNDl2LTEzNHEwLTktNy0xNXQtMTUtN2gtMTM0cS05IDAtMTYgN3QtNyAxNXYxMzRxMCA5IDcgMTZ0MTYgNmgxMzRxOSAwIDE1LTZ0Ny0xNnogbTE3NiAzMzVxMC0zMC04LTU2dC0yMC00My0zMS0zMy0zMi0yNS0zNC0xOXEtMjMtMTMtMzgtMzd0LTE1LTM3cTAtMTAtNy0xOHQtMTYtOWgtMTM0cS04IDAtMTQgMTF0LTYgMjB2MjZxMCA0NiAzNyA4N3Q3OSA2MHEzMyAxNiA0NyAzMnQxNCA0MnEwIDI0LTI2IDQxdC02MCAxOHEtMzYgMC02MC0xNi0yMC0xNC02MC02NC03LTktMTctOS03IDAtMTQgNGwtOTEgNzBxLTggNi05IDE0dDMgMTZxODkgMTQ4IDI1OSAxNDggNDUgMCA5MC0xN3Q4MS00NiA1OS03MiAyMy04OHoiIGhvcml6LWFkdi14PSI1NzEuNCIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTE0IiB1bmljb2RlPSImI3hmMTQyOyIgZD0iTTIxNCAxNTR2LTEwOHEwLTIyLTE1LTM3dC0zOC0xNmgtMTA3cS0yMyAwLTM4IDE2dC0xNiAzN3YxMDhxMCAyMiAxNiAzOHQzOCAxNWgxMDdxMjIgMCAzOC0xNXQxNS0zOHogbTAgMjg1di0xMDdxMC0yMi0xNS0zOHQtMzgtMTVoLTEwN3EtMjMgMC0zOCAxNXQtMTYgMzh2MTA3cTAgMjMgMTYgMzh0MzggMTZoMTA3cTIyIDAgMzgtMTZ0MTUtMzh6IG0wIDI4NnYtMTA3cTAtMjItMTUtMzh0LTM4LTE2aC0xMDdxLTIzIDAtMzggMTZ0LTE2IDM4djEwN3EwIDIyIDE2IDM4dDM4IDE2aDEwN3EyMiAwIDM4LTE2dDE1LTM4eiIgaG9yaXotYWR2LXg9IjIxNC4zIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMTUiIHVuaWNvZGU9IiYjeGYxNWQ7IiBkPSJNNjY1IDYyMmg5OGwtNDAgMTIyLTYgMjZxLTIgOS0yIDExaC0ybC0xLTExcTAgMC0yLTEwdC01LTE2eiBtLTI1NC01NzZxMC02LTYtMTNsLTE3OC0xNzhxLTUtNS0xMy01LTYgMC0xMiA1bC0xNzkgMTc5cS04IDktNCAxOSA0IDExIDE3IDExaDEwN3Y3NjhxMCA4IDUgMTN0MTMgNWgxMDdxOCAwIDEzLTV0NS0xM3YtNzY4aDEwN3E4IDAgMTMtNXQ1LTEzeiBtNDY2LTY2di0xMzBoLTMyNnY1MGwyMDYgMjk1cTcgMTEgMTIgMTZsNiA1djFxLTEgMC0zIDB0LTUgMHEtNi0yLTE2LTJoLTEzMHYtNjRoLTY3djEyOGgzMTd2LTUwbC0yMDYtMjk2cS00LTQtMTItMTRsLTYtN3YtMWw4IDFxNSAyIDE2IDJoMTM5djY2aDY3eiBtNTAgNTAxdi02MGgtMTYxdjYwaDQybC0yNiA4MGgtMTM2bC0yNi04MGg0MnYtNjBoLTE2MHY2MGgzOWwxMjggMzY5aDkxbDEyOC0zNjloMzl6IiBob3Jpei1hZHYteD0iOTI4LjYiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0xNiIgdW5pY29kZT0iJiN4ZjE1ZTsiIGQ9Ik02NjUgNTFoOThsLTQwIDEyMi02IDI2cS0yIDktMiAxMWgtMmwtMS0xMXEwLTEtMi0xMHQtNS0xNnogbS0yNTQtNXEwLTYtNi0xM2wtMTc4LTE3OHEtNS01LTEzLTUtNiAwLTEyIDVsLTE3OSAxNzlxLTggOS00IDE5IDQgMTEgMTcgMTFoMTA3djc2OHEwIDggNSAxM3QxMyA1aDEwN3E4IDAgMTMtNXQ1LTEzdi03NjhoMTA3cTggMCAxMy01dDUtMTN6IG01MTYtMTM3di01OWgtMTYxdjU5aDQybC0yNiA4MGgtMTM2bC0yNi04MGg0MnYtNTloLTE2MHY1OWgzOWwxMjggMzcwaDkxbDEyOC0zNzBoMzl6IG0tNTAgNjQzdi0xMzFoLTMyNnY1MWwyMDYgMjk1cTcgMTAgMTIgMTVsNiA1djJxLTEgMC0zLTF0LTUgMHEtNi0yLTE2LTJoLTEzMHYtNjRoLTY3djEyOGgzMTd2LTUwbC0yMDYtMjk1cS00LTUtMTItMTVsLTYtNXYtMmw4IDJxNSAwIDE2IDBoMTM5djY3aDY3eiIgaG9yaXotYWR2LXg9IjkyOC42IiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMTciIHVuaWNvZGU9IiYjeGYxNjA7IiBkPSJNNDExIDQ2cTAtNi02LTEzbC0xNzgtMTc4cS01LTUtMTMtNS02IDAtMTIgNWwtMTc5IDE3OXEtOCA5LTQgMTkgNCAxMSAxNyAxMWgxMDd2NzY4cTAgOCA1IDEzdDEzIDVoMTA3cTggMCAxMy01dDUtMTN2LTc2OGgxMDdxOCAwIDEzLTV0NS0xM3ogbTU4OS03MXYtMTA3cTAtOC01LTEzdC0xMy01aC00NjRxLTggMC0xMyA1dC01IDEzdjEwN3EwIDggNSAxM3QxMyA1aDQ2NHE4IDAgMTMtNXQ1LTEzeiBtLTEwNyAyODZ2LTEwN3EwLTgtNS0xM3QtMTMtNWgtMzU3cS04IDAtMTMgNXQtNSAxM3YxMDdxMCA4IDUgMTN0MTMgNWgzNTdxOCAwIDEzLTV0NS0xM3ogbS0xMDcgMjg1di0xMDdxMC03LTUtMTJ0LTEzLTZoLTI1MHEtOCAwLTEzIDZ0LTUgMTJ2MTA3cTAgOCA1IDEzdDEzIDVoMjUwcTggMCAxMy01dDUtMTN6IG0tMTA3IDI4NnYtMTA3cTAtOC01LTEzdC0xMy01aC0xNDNxLTggMC0xMyA1dC01IDEzdjEwN3EwIDggNSAxM3QxMyA1aDE0M3E4IDAgMTMtNXQ1LTEzeiIgaG9yaXotYWR2LXg9IjEwMDAiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0xOCIgdW5pY29kZT0iJiN4ZjE2MTsiIGQ9Ik02NzktMjV2LTEwN3EwLTgtNS0xM3QtMTMtNWgtMTQzcS04IDAtMTMgNXQtNSAxM3YxMDdxMCA4IDUgMTN0MTMgNWgxNDNxOCAwIDEzLTV0NS0xM3ogbS0yNjggNzFxMC02LTYtMTNsLTE3OC0xNzhxLTUtNS0xMy01LTYgMC0xMiA1bC0xNzkgMTc5cS04IDktNCAxOSA0IDExIDE3IDExaDEwN3Y3NjhxMCA4IDUgMTN0MTMgNWgxMDdxOCAwIDEzLTV0NS0xM3YtNzY4aDEwN3E4IDAgMTMtNXQ1LTEzeiBtMzc1IDIxNXYtMTA3cTAtOC01LTEzdC0xMy01aC0yNTBxLTggMC0xMyA1dC01IDEzdjEwN3EwIDggNSAxM3QxMyA1aDI1MHE4IDAgMTMtNXQ1LTEzeiBtMTA3IDI4NXYtMTA3cTAtNy01LTEydC0xMy02aC0zNTdxLTggMC0xMyA2dC01IDEydjEwN3EwIDggNSAxM3QxMyA1aDM1N3E4IDAgMTMtNXQ1LTEzeiBtMTA3IDI4NnYtMTA3cTAtOC01LTEzdC0xMy01aC00NjRxLTggMC0xMyA1dC01IDEzdjEwN3EwIDggNSAxM3QxMyA1aDQ2NHE4IDAgMTMtNXQ1LTEzeiIgaG9yaXotYWR2LXg9IjEwMDAiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0xOSIgdW5pY29kZT0iJiN4ZjE2MjsiIGQ9Ik03NTEgMTE3cTAgMzYtMjQgNjV0LTU4IDMwcS0yOSAwLTQ2LTIxdC0xNy01MiAyMC01MyA1OC0yMnEyOCAwIDQ4IDE1dDE5IDM4eiBtLTM0MC03MXEwLTYtNi0xM2wtMTc4LTE3OHEtNS01LTEzLTUtNiAwLTEyIDVsLTE3OSAxNzlxLTggOS00IDE5IDQgMTEgMTcgMTFoMTA3djc2OHEwIDggNSAxM3QxMyA1aDEwN3E4IDAgMTMtNXQ1LTEzdi03NjhoMTA3cTggMCAxMy01dDUtMTN6IG00MTggMzlxMC0zNS03LTY4dC0yMy02NC0zOC01My01NS0zNi03MS0xNHEtMzUgMC02MCA5LTE0IDQtMjQgOGwyMiA2M3E5LTQgMTctNiAyMS03IDQyLTcgNDcgMCA3NSAzM3QzNyA4MWgtMXEtMTEtMTMtMzQtMjF0LTQ3LThxLTU5IDAtOTcgNDB0LTM3IDk3cTAgNTggNDAgOTl0MTAxIDQxcTY5IDAgMTE1LTUzdDQ1LTE0MXogbS0xNiA0MDB2LTY0aC0yNjJ2NjRoOTN2MjQxcTAgNCAwIDExdDEgOXY5aC0ybC0zLTdxLTUtNy0xNS0xN2wtMzUtMzItNDUgNDggMTA3IDEwM2g2OHYtMzY1aDkzeiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMjAiIHVuaWNvZGU9IiYjeGYxNjM7IiBkPSJNNzUxIDY4OXEwIDM1LTI0IDY1dC01OCAyOXEtMjkgMC00Ni0yMXQtMTctNTIgMjAtNTMgNTgtMjFxMjggMCA0OCAxNXQxOSAzOHogbS0zNDAtNjQzcTAtNi02LTEzbC0xNzgtMTc4cS01LTUtMTMtNS02IDAtMTIgNWwtMTc5IDE3OXEtOCA5LTQgMTkgNCAxMSAxNyAxMWgxMDd2NzY4cTAgOCA1IDEzdDEzIDVoMTA3cTggMCAxMy01dDUtMTN2LTc2OGgxMDdxOCAwIDEzLTV0NS0xM3ogbTQwMi0xMzJ2LTY0aC0yNjJ2NjRoOTN2MjQxcTAgNCAwIDEwdDEgMTB2OWgtMmwtMy03cS01LTctMTUtMTdsLTM1LTMzLTQ1IDQ4IDEwNyAxMDRoNjh2LTM2NWg5M3ogbTE2IDc0MnEwLTM0LTctNjd0LTIzLTY0LTM4LTUzLTU1LTM3LTcxLTE0cS0zNSAwLTYwIDktMTQgNS0yNCA5bDIyIDYzcTktNCAxNy02IDIxLTggNDItOCA0NyAwIDc1IDMzdDM3IDgxaC0xcS0xMS0xMy0zNC0yMHQtNDctOHEtNTkgMC05NyA0MHQtMzcgOTZxMCA1OSA0MCA5OXQxMDEgNDFxNjkgMCAxMTUtNTN0NDUtMTQxeiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMjkiIHVuaWNvZGU9IiYjeGYxYzE7IiBkPSJNODE5IDYzOHExNi0xNiAyNy00MnQxMS01MHYtNjQycTAtMjMtMTUtMzh0LTM4LTE2aC03NTBxLTIzIDAtMzggMTZ0LTE2IDM4djg5MnEwIDIzIDE2IDM4dDM4IDE2aDUwMHEyMiAwIDQ5LTExdDQyLTI3eiBtLTI0OCAxMzZ2LTIxMGgyMTBxLTUgMTctMTIgMjNsLTE3NSAxNzVxLTYgNy0yMyAxMnogbTIxNS04NTN2NTcyaC0yMzJxLTIzIDAtMzggMTZ0LTE2IDM3djIzM2gtNDI5di04NThoNzE1eiBtLTI4NyAzMzFxMTgtMTQgNDctMzEgMzMgNCA2NSA0IDgyIDAgOTktMjcgOS0xMyAxLTI5IDAtMS0xLTFsLTEtMnYwcS0zLTIxLTM5LTIxLTI3IDAtNjQgMTF0LTczIDI5cS0xMjMtMTMtMjE5LTQ2LTg1LTE0Ni0xMzUtMTQ2LTggMC0xNSA0bC0xNCA3cTAgMC0zIDItNiA2LTQgMjAgNSAyMyAzMiA1MXQ3MyA1NHE4IDUgMTMtMyAxLTEgMS0yIDI5IDQ3IDYwIDExMCAzOCA3NiA1OCAxNDYtMTMgNDYtMTcgODl0NCA3MXE2IDIyIDIzIDIyaDEyIDBxMTMgMCAyMC04IDEwLTEyIDUtMzgtMS0zLTItNCAwLTIgMC01di0xN3EtMS02OC04LTEwNyAzMS05MSA4Mi0xMzN6IG0tMzIxLTIyOXEyOSAxMyA3NiA4OC0yOS0yMi00OS00N3QtMjctNDF6IG0yMjIgNTEzcS05LTIzLTItNzMgMSA0IDQgMjQgMCAyIDQgMjQgMSAzIDMgNS0xIDAtMSAxdDAgMS0xIDFxMCAxMi03IDIwIDAtMSAwLTF2LTJ6IG0tNzAtMzY4cTc2IDMwIDE1OSA0NS0xIDAtNyA1dC05IDhxLTQzIDM3LTcxIDk4LTE1LTQ4LTQ2LTExMC0xNy0zMS0yNi00NnogbTM2MSA5cS0xMyAxMy03OCAxMyA0Mi0xNiA2OS0xNiA4IDAgMTAgMSAwIDAtMSAyeiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMjgiIHVuaWNvZGU9IiYjeGYxYzI7IiBkPSJNODE5IDYzOHExNi0xNiAyNy00MnQxMS01MHYtNjQycTAtMjMtMTUtMzh0LTM4LTE2aC03NTBxLTIzIDAtMzggMTZ0LTE2IDM4djg5MnEwIDIzIDE2IDM4dDM4IDE2aDUwMHEyMiAwIDQ5LTExdDQyLTI3eiBtLTI0OCAxMzZ2LTIxMGgyMTBxLTUgMTctMTIgMjNsLTE3NSAxNzVxLTYgNy0yMyAxMnogbTIxNS04NTN2NTcyaC0yMzJxLTIzIDAtMzggMTZ0LTE2IDM3djIzM2gtNDI5di04NThoNzE1eiBtLTY1NiA1MDB2LTU5aDM5bDkyLTM2OWg4OGw3MiAyNzFxNCAxMSA1IDI1IDIgOSAyIDE0aDJsMS0xNHExLTEgMi0xMXQzLTE0bDcyLTI3MWg4OWw5MSAzNjloMzl2NTloLTE2N3YtNTloNTBsLTU1LTI0NXEtMy0xMS00LTI1bC0xLTEyaC0zbC0xIDEycS0xIDItMiAxMXQtMyAxNGwtODEgMzA0aC02M2wtODEtMzA0cS0xLTUtMi0xM3QtMi0xMmwtMi0xMmgtMmwtMiAxMnEtMSAxNC0zIDI1bC01NiAyNDVoNTB2NTloLTE2N3oiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTI3IiB1bmljb2RlPSImI3hmMWMzOyIgZD0iTTgxOSA2MzhxMTYtMTYgMjctNDJ0MTEtNTB2LTY0MnEwLTIzLTE1LTM4dC0zOC0xNmgtNzUwcS0yMyAwLTM4IDE2dC0xNiAzOHY4OTJxMCAyMyAxNiAzOHQzOCAxNmg1MDBxMjIgMCA0OS0xMXQ0Mi0yN3ogbS0yNDggMTM2di0yMTBoMjEwcS01IDE3LTEyIDIzbC0xNzUgMTc1cS02IDctMjMgMTJ6IG0yMTUtODUzdjU3MmgtMjMycS0yMyAwLTM4IDE2dC0xNiAzN3YyMzNoLTQyOXYtODU4aDcxNXogbS01NDcgMTMxdi01OWgxNTd2NTloLTQybDU4IDkwcTMgNCA1IDl0NSA4IDIgMmgxcTAtMiAzLTYgMS0yIDItNHQzLTQgNC01bDYwLTkwaC00M3YtNTloMTYzdjU5aC0zOGwtMTA3IDE1MiAxMDggMTU4aDM4djU5aC0xNTZ2LTU5aDQxbC01Ny04OXEtMi00LTYtOXQtNS04bC0xLTFoLTFxMCAyLTMgNS0zIDYtOSAxM2wtNTkgODloNDJ2NTloLTE2MnYtNTloMzhsMTA2LTE1Mi0xMDktMTU4aC0zOHoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTI2IiB1bmljb2RlPSImI3hmMWM0OyIgZD0iTTgxOSA2MzhxMTYtMTYgMjctNDJ0MTEtNTB2LTY0MnEwLTIzLTE1LTM4dC0zOC0xNmgtNzUwcS0yMyAwLTM4IDE2dC0xNiAzOHY4OTJxMCAyMyAxNiAzOHQzOCAxNmg1MDBxMjIgMCA0OS0xMXQ0Mi0yN3ogbS0yNDggMTM2di0yMTBoMjEwcS01IDE3LTEyIDIzbC0xNzUgMTc1cS02IDctMjMgMTJ6IG0yMTUtODUzdjU3MmgtMjMycS0yMyAwLTM4IDE2dC0xNiAzN3YyMzNoLTQyOXYtODU4aDcxNXogbS01NTQgMTMxdi01OWgxODN2NTloLTUydjkzaDc2cTQzIDAgNjYgOSAzNyAxMiA2MCA0OHQyMiA4MnEwIDQ1LTIxIDc4dC01NiA0OXEtMjcgMTAtNzIgMTBoLTIwNnYtNTloNTJ2LTMxMGgtNTJ6IG0xOTcgMTU2aC02NnYxNTBoNjdxMjkgMCA0Ni0xMCAzMS0xOSAzMS02NCAwLTUwLTM0LTY3LTE4LTktNDQtOXoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTI1IiB1bmljb2RlPSImI3hmMWM1OyIgZD0iTTgxOSA2MzhxMTYtMTYgMjctNDJ0MTEtNTB2LTY0MnEwLTIzLTE1LTM4dC0zOC0xNmgtNzUwcS0yMyAwLTM4IDE2dC0xNiAzOHY4OTJxMCAyMyAxNiAzOHQzOCAxNmg1MDBxMjIgMCA0OS0xMXQ0Mi0yN3ogbS0yNDggMTM2di0yMTBoMjEwcS01IDE3LTEyIDIzbC0xNzUgMTc1cS02IDctMjMgMTJ6IG0yMTUtODUzdjU3MmgtMjMycS0yMyAwLTM4IDE2dC0xNiAzN3YyMzNoLTQyOXYtODU4aDcxNXogbS03MiAyNTB2LTE3OGgtNTcxdjEwN2wxMDcgMTA3IDcxLTcxIDIxNSAyMTR6IG0tNDY0IDEwOHEtNDUgMC03NiAzMXQtMzEgNzYgMzEgNzYgNzYgMzEgNzYtMzEgMzEtNzYtMzEtNzYtNzYtMzF6IiBob3Jpei1hZHYteD0iODU3LjEiIC8+CjxnbHlwaCBnbHlwaC1uYW1lPSJnbHlwaC0yNCIgdW5pY29kZT0iJiN4ZjFjNjsiIGQ9Ik0zNTcgNjM2djcxaC03MXYtNzFoNzF6IG03Mi03MnY3MmgtNzJ2LTcyaDcyeiBtLTcyLTcxdjcxaC03MXYtNzFoNzF6IG03Mi03MnY3MmgtNzJ2LTcyaDcyeiBtMzkwIDIxN3ExNi0xNiAyNy00MnQxMS01MHYtNjQycTAtMjMtMTUtMzh0LTM4LTE2aC03NTBxLTIzIDAtMzggMTZ0LTE2IDM4djg5MnEwIDIzIDE2IDM4dDM4IDE2aDUwMHEyMiAwIDQ5LTExdDQyLTI3eiBtLTI0OCAxMzZ2LTIxMGgyMTBxLTUgMTctMTIgMjNsLTE3NSAxNzVxLTYgNy0yMyAxMnogbTIxNS04NTN2NTcyaC0yMzJxLTIzIDAtMzggMTZ0LTE2IDM3djIzM2gtNzF2LTcyaC03MnY3MmgtMjg2di04NThoNzE1eiBtLTM1MCA0MDNsNjAtMTk1cTQtMTUgNC0yOSAwLTQ2LTQwLTc3dC0xMDMtMzAtMTAyIDMwLTQxIDc3cTAgMTQgNSAyOSAxMiAzNSA2NyAyMjF2NzFoNzF2LTcxaDQ0cTEzIDAgMjItN3QxMy0xOXogbS03OS0yNjBxMzAgMCA1MSAxMXQyMSAyNS0yMSAyNS01MSAxMS01MC0xMS0yMS0yNSAyMS0yNSA1MC0xMXoiIGhvcml6LWFkdi14PSI4NTcuMSIgLz4KPGdseXBoIGdseXBoLW5hbWU9ImdseXBoLTIzIiB1bmljb2RlPSImI3hmMWM3OyIgZD0iTTgxOSA2MzhxMTYtMTYgMjctNDJ0MTEtNTB2LTY0MnEwLTIzLTE1LTM4dC0zOC0xNmgtNzUwcS0yMyAwLTM4IDE2dC0xNiAzOHY4OTJxMCAyMyAxNiAzOHQzOCAxNmg1MDBxMjIgMCA0OS0xMXQ0Mi0yN3ogbS0yNDggMTM2di0yMTBoMjEwcS01IDE3LTEyIDIzbC0xNzUgMTc1cS02IDctMjMgMTJ6IG0yMTUtODUzdjU3MmgtMjMycS0yMyAwLTM4IDE2dC0xNiAzN3YyMzNoLTQyOXYtODU4aDcxNXogbS00NDAgNDU1cTExLTUgMTEtMTd2LTMwNHEwLTEyLTExLTE2LTQtMS03LTEtNiAwLTEyIDVsLTkzIDkzaC03M3EtOCAwLTEzIDV0LTUgMTN2MTA3cTAgOCA1IDEzdDEzIDVoNzNsOTMgOTNxOCA4IDE5IDR6IG0yMzMtMzg1cTE3IDAgMjggMTQgNzIgODggNzIgMjAydC03MiAyMDNxLTkgMTEtMjQgMTN0LTI3LThxLTExLTktMTMtMjR0OC0yNnE1Ni02OSA1Ni0xNTh0LTU2LTE1N3EtOS0xMi04LTI3dDEzLTIzcTEwLTkgMjMtOXogbS0xMTggODNxMTUgMCAyNiAxMSA0OSA1MiA0OSAxMjJ0LTQ5IDEyMnEtMTAgMTEtMjUgMTJ0LTI2LTEwLTExLTI1IDEwLTI2cTI5LTMyIDI5LTczdC0yOS03M3EtMTAtMTEtMTAtMjZ0MTEtMjVxMTItOSAyNS05eiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMjIiIHVuaWNvZGU9IiYjeGYxYzg7IiBkPSJNODE5IDYzOHExNi0xNiAyNy00MnQxMS01MHYtNjQycTAtMjMtMTUtMzh0LTM4LTE2aC03NTBxLTIzIDAtMzggMTZ0LTE2IDM4djg5MnEwIDIzIDE2IDM4dDM4IDE2aDUwMHEyMiAwIDQ5LTExdDQyLTI3eiBtLTI0OCAxMzZ2LTIxMGgyMTBxLTUgMTctMTIgMjNsLTE3NSAxNzVxLTYgNy0yMyAxMnogbTIxNS04NTN2NTcyaC0yMzJxLTIzIDAtMzggMTZ0LTE2IDM3djIzM2gtNDI5di04NThoNzE1eiBtLTM1NyA1MDBxMjkgMCA1MC0yMXQyMS01MHYtMjE0cTAtMjktMjEtNTB0LTUwLTIyaC0yMTVxLTI5IDAtNTAgMjJ0LTIxIDUwdjIxNHEwIDI5IDIxIDUwdDUwIDIxaDIxNXogbTI3NC0xcTExLTQgMTEtMTZ2LTMyMnEwLTEyLTExLTE3LTQtMS03LTEtNyAwLTEyIDVsLTE0OCAxNDl2NTBsMTQ4IDE0OHE1IDUgMTIgNSAzIDAgNy0xeiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZ2x5cGgtMjEiIHVuaWNvZGU9IiYjeGYxYzk7IiBkPSJNODE5IDYzOHExNi0xNiAyNy00MnQxMS01MHYtNjQycTAtMjMtMTUtMzh0LTM4LTE2aC03NTBxLTIzIDAtMzggMTZ0LTE2IDM4djg5MnEwIDIzIDE2IDM4dDM4IDE2aDUwMHEyMiAwIDQ5LTExdDQyLTI3eiBtLTI0OCAxMzZ2LTIxMGgyMTBxLTUgMTctMTIgMjNsLTE3NSAxNzVxLTYgNy0yMyAxMnogbTIxNS04NTN2NTcyaC0yMzJxLTIzIDAtMzggMTZ0LTE2IDM3djIzM2gtNDI5di04NThoNzE1eiBtLTUxOCA1MDBxNCA3IDEyIDd0MTMtM2wyOC0yMXE3LTUgNy0xMnQtMy0xM2wtMTAyLTEzNiAxMDItMTM2cTQtNiAzLTEzdC03LTEybC0yOC0yMXEtNi00LTEzLTR0LTEyIDdsLTEyNiAxNjhxLTggMTEgMCAyMnogbTQ0Ny0xNjdxOC0xMSAwLTIybC0xMjYtMTY4cS00LTYtMTEtN3QtMTQgNGwtMjggMjFxLTYgNS03IDEydDMgMTNsMTAyIDEzNi0xMDIgMTM2cS00IDYtMyAxM3Q3IDEybDI4IDIxcTYgNCAxNCAzdDExLTd6IG0tMzQ2LTI1OHEtNyAxLTExIDh0LTMgMTNsNzcgNDY0cTEgNyA3IDExdDE0IDNsMzUtNXE3LTIgMTEtOHQzLTEzbC03Ny00NjRxLTEtNy03LTExdC0xMy0zeiIgaG9yaXotYWR2LXg9Ijg1Ny4xIiAvPgo8Z2x5cGggZ2x5cGgtbmFtZT0iZXllIiB1bmljb2RlPSImI3hmMDZlOyIgZD0iTTkyOSAzMTRxLTg1IDEzMi0yMTMgMTk3IDM0LTU4IDM0LTEyNSAwLTEwNC03My0xNzd0LTE3Ny03My0xNzcgNzMtNzMgMTc3cTAgNjcgMzQgMTI1LTEyOC02NS0yMTMtMTk3IDc1LTExNCAxODctMTgydDI0Mi02OCAyNDIgNjggMTg3IDE4MnogbS00MDIgMjE1cTAgMTEtOCAxOXQtMTkgN3EtNzAgMC0xMjAtNTB0LTUwLTExOXEwLTExIDgtMTl0MTktOCAxOSA4IDggMTlxMCA0OCAzNCA4MnQ4MiAzNHExMSAwIDE5IDh0OCAxOXogbTQ3My0yMTVxMC0xOS0xMS0zOC03OC0xMjktMjEwLTIwNnQtMjc5LTc3LTI3OSA3Ny0yMTAgMjA2cS0xMSAxOS0xMSAzOHQxMSAzOXE3OCAxMjggMjEwIDIwNXQyNzkgNzggMjc5LTc4IDIxMC0yMDVxMTEtMjAgMTEtMzl6IiBob3Jpei1hZHYteD0iMTAwMCIgLz4KPGdseXBoIGdseXBoLW5hbWU9InJlcGVhdCIgdW5pY29kZT0iJiN4ZjAxZTsiIGQ9Ik04NTcgNzA3di0yNTBxMC0xNC0xMC0yNXQtMjYtMTFoLTI1MHEtMjMgMC0zMiAyMy0xMCAyMiA3IDM4bDc3IDc3cS04MiA3Ny0xOTQgNzctNTggMC0xMTEtMjN0LTkxLTYxLTYyLTkxLTIyLTExMSAyMi0xMTEgNjItOTEgOTEtNjEgMTExLTIzcTY2IDAgMTI1IDI5dDEwMCA4MnE0IDYgMTMgNyA4IDAgMTQtNWw3Ni03N3E1LTQgNi0xMXQtNS0xM3EtNjAtNzQtMTQ3LTExNHQtMTgyLTQxcS04NyAwLTE2NyAzNHQtMTM2IDkyLTkyIDEzNy0zNCAxNjYgMzQgMTY2IDkyIDEzNyAxMzYgOTIgMTY3IDM0cTgyIDAgMTU4LTMxdDEzNy04OGw3MiA3MnExNiAxOCAzOSA4IDIyLTkgMjItMzN6IiBob3Jpei1hZHYteD0iODU3LjEiIC8+CjwvZm9udD4KPC9kZWZzPgo8L3N2Zz4="; },function(e,t){e.exports="data:application/font-woff;base64,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"},function(e,t){e.exports="data:application/font-woff;base64,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"; },function(e,t){e.exports="data:application/octet-stream;base64,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"; },function(e,t){function n(){l=!1,a.length?u=a.concat(u):c=-1,u.length&&r()}function r(){if(!l){var e=setTimeout(n);l=!0;for(var t=u.length;t;){for(a=u,u=[];++c1)for(var n=1;n1&&(r=n[0]+"@",e=n[1]),e=e.replace(k,".");var o=e.split("."),i=s(o,t).join(".");return r+i}function l(e){for(var t,n,r=[],o=0,i=e.length;i>o;)t=e.charCodeAt(o++),t>=55296&&56319>=t&&i>o?(n=e.charCodeAt(o++),56320==(64512&n)?r.push(((1023&t)<<10)+(1023&n)+65536):(r.push(t),o--)):r.push(t);return r}function c(e){return s(e,function(e){var t="";return e>65535&&(e-=65536,t+=j(e>>>10&1023|55296),e=56320|1023&e),t+=j(e)}).join("")}function d(e){return 10>e-48?e-22:26>e-65?e-65:26>e-97?e-97:b}function p(e,t){return e+22+75*(26>e)-((0!=t)<<5)}function f(e,t,n){var r=0;for(e=n?P(e/N):e>>1,e+=P(e/t);e>O*E>>1;r+=b)e=P(e/O);return P(r+(O+1)*e/(e+A))}function h(e){var t,n,r,o,i,s,u,l,p,h,m=[],g=e.length,y=0,v=I,M=w;for(n=e.lastIndexOf(C),0>n&&(n=0),r=0;n>r;++r)e.charCodeAt(r)>=128&&a("not-basic"),m.push(e.charCodeAt(r));for(o=n>0?n+1:0;g>o;){for(i=y,s=1,u=b;o>=g&&a("invalid-input"),l=d(e.charCodeAt(o++)),(l>=b||l>P((T-y)/s))&&a("overflow"),y+=l*s,p=M>=u?x:u>=M+E?E:u-M,!(p>l);u+=b)h=b-p,s>P(T/h)&&a("overflow"),s*=h;t=m.length+1,M=f(y-i,t,0==i),P(y/t)>T-v&&a("overflow"),v+=P(y/t),y%=t,m.splice(y++,0,v)}return c(m)}function m(e){var t,n,r,o,i,s,u,c,d,h,m,g,y,v,M,A=[];for(e=l(e),g=e.length,t=I,n=0,i=w,s=0;g>s;++s)m=e[s],128>m&&A.push(j(m));for(r=o=A.length,o&&A.push(C);g>r;){for(u=T,s=0;g>s;++s)m=e[s],m>=t&&u>m&&(u=m);for(y=r+1,u-t>P((T-n)/y)&&a("overflow"),n+=(u-t)*y,t=u,s=0;g>s;++s)if(m=e[s],t>m&&++n>T&&a("overflow"),m==t){for(c=n,d=b;h=i>=d?x:d>=i+E?E:d-i,!(h>c);d+=b)M=c-h,v=b-h,A.push(j(p(h+M%v,0))),c=P(M/v);A.push(j(p(c,0))),i=f(n,y,r==o),n=0,++r}++n,++t}return A.join("")}function g(e){return u(e,function(e){return D.test(e)?h(e.slice(4).toLowerCase()):e})}function y(e){return u(e,function(e){return S.test(e)?"xn--"+m(e):e})}var v=("object"==typeof t&&t&&!t.nodeType&&t,"object"==typeof e&&e&&!e.nodeType&&e,"object"==typeof o&&o);(v.global===v||v.window===v||v.self===v)&&(i=v);var M,T=2147483647,b=36,x=1,E=26,A=38,N=700,w=72,I=128,C="-",D=/^xn--/,S=/[^\x20-\x7E]/,k=/[\x2E\u3002\uFF0E\uFF61]/g,L={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},O=b-x,P=Math.floor,j=String.fromCharCode;M={version:"1.3.2",ucs2:{decode:l,encode:c},decode:h,encode:m,toASCII:y,toUnicode:g},r=function(){return M}.call(t,n,t,e),!(void 0!==r&&(e.exports=r))}(this)}).call(t,n(222)(e),function(){return this}())},function(e,t){"use strict";function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,r,o){t=t||"&",r=r||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var s=1e3;o&&"number"==typeof o.maxKeys&&(s=o.maxKeys);var u=e.length;s>0&&u>s&&(u=s);for(var l=0;u>l;++l){var c,d,p,f,h=e[l].replace(a,"%20"),m=h.indexOf(r);m>=0?(c=h.substr(0,m),d=h.substr(m+1)):(c=h,d=""),p=decodeURIComponent(c),f=decodeURIComponent(d),n(i,p)?Array.isArray(i[p])?i[p].push(f):i[p]=[i[p],f]:i[p]=f}return i}},function(e,t){"use strict";var n=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,r,o){return t=t||"&",r=r||"=",null===e&&(e=void 0),"object"==typeof e?Object.keys(e).map(function(o){var i=encodeURIComponent(n(o))+r;return Array.isArray(e[o])?e[o].map(function(e){return i+encodeURIComponent(n(e))}).join(t):i+encodeURIComponent(n(e[o]))}).join(t):o?encodeURIComponent(n(o))+r+encodeURIComponent(n(e)):""}},function(e,t,n){"use strict";t.decode=t.parse=n(462),t.encode=t.stringify=n(463)}]);`) -func productionIndex_bundle20160422t025605zJsBytes() ([]byte, error) { - return _productionIndex_bundle20160422t025605zJs, nil +func productionIndex_bundle20160531t002805zJsBytes() ([]byte, error) { + return _productionIndex_bundle20160531t002805zJs, nil } -func productionIndex_bundle20160422t025605zJs() (*asset, error) { - bytes, err := productionIndex_bundle20160422t025605zJsBytes() +func productionIndex_bundle20160531t002805zJs() (*asset, error) { + bytes, err := productionIndex_bundle20160531t002805zJsBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "production/index_bundle-2016-04-22T02-56-05Z.js", size: 598240, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/index_bundle-2016-05-31T00-28-05Z.js", size: 598373, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -388,7 +388,7 @@ func productionLoaderCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/loader.css", size: 2246, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/loader.css", size: 2246, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -462,7 +462,7 @@ func productionLogoSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/logo.svg", size: 3079, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/logo.svg", size: 3079, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -479,7 +479,7 @@ func productionSafariPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "production/safari.png", size: 4971, mode: os.FileMode(420), modTime: time.Unix(1461293791, 0)} + info := bindataFileInfo{name: "production/safari.png", size: 4971, mode: os.FileMode(420), modTime: time.Unix(1464654517, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -540,7 +540,7 @@ var _bindata = map[string]func() (*asset, error){ "production/favicon.ico": productionFaviconIco, "production/firefox.png": productionFirefoxPng, "production/index.html": productionIndexHTML, - "production/index_bundle-2016-04-22T02-56-05Z.js": productionIndex_bundle20160422t025605zJs, + "production/index_bundle-2016-05-31T00-28-05Z.js": productionIndex_bundle20160531t002805zJs, "production/loader.css": productionLoaderCss, "production/logo.svg": productionLogoSvg, "production/safari.png": productionSafariPng, @@ -592,7 +592,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "favicon.ico": {productionFaviconIco, map[string]*bintree{}}, "firefox.png": {productionFirefoxPng, map[string]*bintree{}}, "index.html": {productionIndexHTML, map[string]*bintree{}}, - "index_bundle-2016-04-22T02-56-05Z.js": {productionIndex_bundle20160422t025605zJs, map[string]*bintree{}}, + "index_bundle-2016-05-31T00-28-05Z.js": {productionIndex_bundle20160531t002805zJs, map[string]*bintree{}}, "loader.css": {productionLoaderCss, map[string]*bintree{}}, "logo.svg": {productionLogoSvg, map[string]*bintree{}}, "safari.png": {productionSafariPng, map[string]*bintree{}}, @@ -653,6 +653,6 @@ func assetFS() *assetfs.AssetFS { panic("unreachable") } -var UIReleaseTag = "RELEASE.2016-04-22T02-56-05Z" -var UICommitID = "cbd963de7f523483c2f7dac6f1fb5cc9bb646843" -var UIVersion = "2016-04-22T02:56:05Z" +var UIReleaseTag = "RELEASE.2016-05-31T00-28-05Z" +var UICommitID = "66ed858bd3436d16ca4e08c738ba852935732438" +var UIVersion = "2016-05-31T00:28:05Z" diff --git a/vendor/vendor.json b/vendor/vendor.json index b66413b5d..950d32ccc 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -94,8 +94,8 @@ }, { "path": "github.com/minio/miniobrowser", - "revision": "16a35547d5b0aea8de96b74416929ab2e2d248cd", - "revisionTime": "2016-04-21T19:57:24-07:00" + "revision": "9c9fbc91e4b2e952048f9299c45d53ee0a0d0f2b", + "revisionTime": "2016-05-30T17:30:33-07:00" }, { "path": "github.com/mitchellh/go-homedir", diff --git a/web-handlers.go b/web-handlers.go index 909eeecc7..3249dcb0d 100644 --- a/web-handlers.go +++ b/web-handlers.go @@ -18,7 +18,6 @@ package main import ( "fmt" - "io" "net/http" "os" "path" @@ -98,6 +97,22 @@ func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, rep return nil } +// StorageInfoRep - contains storage usage statistics. +type StorageInfoRep struct { + StorageInfo StorageInfo `json:"storageInfo"` + UIVersion string `json:"uiVersion"` +} + +// StorageInfo - web call to gather storage usage statistics. +func (web *webAPIHandlers) StorageInfo(r *http.Request, args *GenericArgs, reply *StorageInfoRep) error { + if !isJWTReqAuthenticated(r) { + return &json2.Error{Message: "Unauthorized request"} + } + reply.UIVersion = miniobrowser.UIVersion + reply.StorageInfo = web.ObjectAPI.StorageInfo() + return nil +} + // MakeBucketArgs - make bucket args. type MakeBucketArgs struct { BucketName string `json:"bucketName"` @@ -127,10 +142,6 @@ type WebBucketInfo struct { Name string `json:"name"` // Date the bucket was created. CreationDate time.Time `json:"creationDate"` - // Total storage space where the bucket resides. - Total int64 `json:"total"` - // Free storage space where the bucket resides. - Free int64 `json:"free"` } // ListBuckets - list buckets api. @@ -148,8 +159,6 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re reply.Buckets = append(reply.Buckets, WebBucketInfo{ Name: bucket.Name, CreationDate: bucket.Created, - Total: bucket.Total, - Free: bucket.Free, }) } } @@ -373,12 +382,14 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) { // Add content disposition. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(object))) - objReader, err := web.ObjectAPI.GetObject(bucket, object, 0) + objInfo, err := web.ObjectAPI.GetObjectInfo(bucket, object) if err != nil { writeWebErrorResponse(w, err) return } - if _, err := io.Copy(w, objReader); err != nil { + offset := int64(0) + err = web.ObjectAPI.GetObject(bucket, object, offset, objInfo.Size, w) + if err != nil { /// No need to print error, response writer already written to. return } diff --git a/xl-erasure-v1-createfile.go b/xl-erasure-v1-createfile.go deleted file mode 100644 index 45da7becb..000000000 --- a/xl-erasure-v1-createfile.go +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "fmt" - "io" - slashpath "path" - "sync" - "time" -) - -// Erasure block size. -const erasureBlockSize = 4 * 1024 * 1024 // 4MiB. - -// cleanupCreateFileOps - cleans up all the temporary files and other -// temporary data upon any failure. -func (xl XL) cleanupCreateFileOps(volume, path string, writers ...io.WriteCloser) { - closeAndRemoveWriters(writers...) - for _, disk := range xl.storageDisks { - if err := disk.DeleteFile(volume, path); err != nil { - errorIf(err, "Unable to delete file.") - } - } -} - -// Close and remove writers if they are safeFile. -func closeAndRemoveWriters(writers ...io.WriteCloser) { - for _, writer := range writers { - if err := safeCloseAndRemove(writer); err != nil { - errorIf(err, "Failed to close writer.") - } - } -} - -// WriteErasure reads predefined blocks, encodes them and writes to -// configured storage disks. -func (xl XL) writeErasure(volume, path string, reader *io.PipeReader, wcloser *waitCloser) { - // Release the block writer upon function return. - defer wcloser.release() - - partsMetadata, errs := xl.getPartsMetadata(volume, path) - - // Convert errs into meaningful err to be sent upwards if possible - // based on total number of errors and read quorum. - err := xl.reduceError(errs) - if err != nil && err != errFileNotFound { - reader.CloseWithError(err) - return - } - - // List all the file versions on existing files. - versions := listFileVersions(partsMetadata, errs) - // Get highest file version. - higherVersion := highestInt(versions) - // Increment to have next higher version. - higherVersion++ - - writers := make([]io.WriteCloser, len(xl.storageDisks)) - - xlMetaV1FilePath := slashpath.Join(path, xlMetaV1File) - metadataWriters := make([]io.WriteCloser, len(xl.storageDisks)) - - // Save additional erasureMetadata. - modTime := time.Now().UTC() - - createFileError := 0 - for index, disk := range xl.storageDisks { - erasurePart := slashpath.Join(path, fmt.Sprintf("file.%d", index)) - var writer io.WriteCloser - writer, err = disk.CreateFile(volume, erasurePart) - if err != nil { - // Treat errFileNameTooLong specially - if err == errFileNameTooLong { - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - - createFileError++ - - // We can safely allow CreateFile errors up to len(xl.storageDisks) - xl.writeQuorum - // otherwise return failure. - if createFileError <= len(xl.storageDisks)-xl.writeQuorum { - continue - } - - // Remove previous temp writers for any failure. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(errWriteQuorum) - return - } - - // Create meta data file. - var metadataWriter io.WriteCloser - metadataWriter, err = disk.CreateFile(volume, xlMetaV1FilePath) - if err != nil { - createFileError++ - - // We can safely allow CreateFile errors up to - // len(xl.storageDisks) - xl.writeQuorum otherwise return failure. - if createFileError <= len(xl.storageDisks)-xl.writeQuorum { - continue - } - - // Remove previous temp writers for any failure. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(errWriteQuorum) - return - } - - writers[index] = writer - metadataWriters[index] = metadataWriter - } - - // Allocate 4MiB block size buffer for reading. - dataBuffer := make([]byte, erasureBlockSize) - var totalSize int64 // Saves total incoming stream size. - for { - // Read up to allocated block size. - var n int - n, err = io.ReadFull(reader, dataBuffer) - if err != nil { - // Any unexpected errors, close the pipe reader with error. - if err != io.ErrUnexpectedEOF && err != io.EOF { - // Remove all temp writers. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - } - // At EOF break out. - if err == io.EOF { - break - } - if n > 0 { - // Split the input buffer into data and parity blocks. - var dataBlocks [][]byte - dataBlocks, err = xl.ReedSolomon.Split(dataBuffer[0:n]) - if err != nil { - // Remove all temp writers. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - - // Encode parity blocks using data blocks. - err = xl.ReedSolomon.Encode(dataBlocks) - if err != nil { - // Remove all temp writers upon error. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - - var wg = &sync.WaitGroup{} - var wErrs = make([]error, len(writers)) - // Loop through and write encoded data to quorum disks. - for index, writer := range writers { - if writer == nil { - continue - } - wg.Add(1) - go func(index int, writer io.Writer) { - defer wg.Done() - encodedData := dataBlocks[index] - _, wErr := writers[index].Write(encodedData) - wErrs[index] = wErr - }(index, writer) - } - wg.Wait() - for _, wErr := range wErrs { - if wErr == nil { - continue - } - // Remove all temp writers upon error. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(wErr) - return - } - - // Update total written. - totalSize += int64(n) - } - } - - // Initialize metadata map, save all erasure related metadata. - metadata := xlMetaV1{} - metadata.Version = "1" - metadata.Stat.Size = totalSize - metadata.Stat.ModTime = modTime - metadata.Minio.Release = minioReleaseTag - if len(xl.storageDisks) > len(writers) { - // Save file.version only if we wrote to less disks than all - // storage disks. - metadata.Stat.Version = higherVersion - } - metadata.Erasure.DataBlocks = xl.DataBlocks - metadata.Erasure.ParityBlocks = xl.ParityBlocks - metadata.Erasure.BlockSize = erasureBlockSize - - // Write all the metadata. - // below case is not handled here - // Case: when storageDisks is 16 and write quorumDisks is 13, - // meta data write failure up to 2 can be considered. - // currently we fail for any meta data writes - for _, metadataWriter := range metadataWriters { - if metadataWriter == nil { - continue - } - - // Write metadata. - err = metadata.Write(metadataWriter) - if err != nil { - // Remove temporary files. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - } - - // Close all writers and metadata writers in routines. - for index, writer := range writers { - if writer == nil { - continue - } - // Safely wrote, now rename to its actual location. - if err = writer.Close(); err != nil { - // Remove all temp writers upon error. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - - if metadataWriters[index] == nil { - continue - } - // Safely wrote, now rename to its actual location. - if err = metadataWriters[index].Close(); err != nil { - // Remove all temp writers upon error. - xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) - reader.CloseWithError(err) - return - } - - } - - // Close the pipe reader and return. - reader.Close() - return -} - -// CreateFile - create a file. -func (xl XL) CreateFile(volume, path string) (writeCloser io.WriteCloser, err error) { - if !isValidVolname(volume) { - return nil, errInvalidArgument - } - if !isValidPath(path) { - return nil, errInvalidArgument - } - - // Initialize pipe for data pipe line. - pipeReader, pipeWriter := io.Pipe() - - // Initialize a new wait closer, implements both Write and Close. - wcloser := newWaitCloser(pipeWriter) - - // Start erasure encoding in routine, reading data block by block from pipeReader. - go xl.writeErasure(volume, path, pipeReader, wcloser) - - // Return the writer, caller should start writing to this. - return wcloser, nil -} diff --git a/xl-erasure-v1-healfile.go b/xl-erasure-v1-healfile.go deleted file mode 100644 index 7ea7ec001..000000000 --- a/xl-erasure-v1-healfile.go +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "errors" - "fmt" - "io" - slashpath "path" -) - -// healHeal - heals the file at path. -func (xl XL) healFile(volume string, path string) error { - totalBlocks := xl.DataBlocks + xl.ParityBlocks - needsHeal := make([]bool, totalBlocks) - var readers = make([]io.Reader, totalBlocks) - var writers = make([]io.WriteCloser, totalBlocks) - - // List all online disks to verify if we need to heal. - onlineDisks, metadata, heal, err := xl.listOnlineDisks(volume, path) - if err != nil { - return err - } - if !heal { - return nil - } - - for index, disk := range onlineDisks { - if disk == nil { - needsHeal[index] = true - continue - } - erasurePart := slashpath.Join(path, fmt.Sprintf("file.%d", index)) - // If disk.ReadFile returns error and we don't have read quorum it will be taken care as - // ReedSolomon.Reconstruct() will fail later. - var reader io.ReadCloser - offset := int64(0) - if reader, err = xl.storageDisks[index].ReadFile(volume, erasurePart, offset); err == nil { - readers[index] = reader - defer reader.Close() - } - } - - // create writers for parts where healing is needed. - for index, healNeeded := range needsHeal { - if !healNeeded { - continue - } - erasurePart := slashpath.Join(path, fmt.Sprintf("file.%d", index)) - writers[index], err = xl.storageDisks[index].CreateFile(volume, erasurePart) - if err != nil { - needsHeal[index] = false - safeCloseAndRemove(writers[index]) - continue - } - } - - // Check if there is atleast one part that needs to be healed. - atleastOneHeal := false - for _, healNeeded := range needsHeal { - if healNeeded { - atleastOneHeal = true - break - } - } - if !atleastOneHeal { - // Return if healing not needed anywhere. - return nil - } - - var totalLeft = metadata.Stat.Size - for totalLeft > 0 { - // Figure out the right blockSize. - var curBlockSize int64 - if metadata.Erasure.BlockSize < totalLeft { - curBlockSize = metadata.Erasure.BlockSize - } else { - curBlockSize = totalLeft - } - // Calculate the current block size. - curBlockSize = getEncodedBlockLen(curBlockSize, metadata.Erasure.DataBlocks) - enBlocks := make([][]byte, totalBlocks) - // Loop through all readers and read. - for index, reader := range readers { - // Initialize block slice and fill the data from each parts. - // ReedSolomon.Verify() expects that slice is not nil even if the particular - // part needs healing. - enBlocks[index] = make([]byte, curBlockSize) - if needsHeal[index] { - // Skip reading if the part needs healing. - continue - } - if reader == nil { - // If ReadFile() had returned error, do not read from this disk. - continue - } - _, err = io.ReadFull(reader, enBlocks[index]) - if err != nil && err != io.ErrUnexpectedEOF { - enBlocks[index] = nil - } - } - - // Check blocks if they are all zero in length. - if checkBlockSize(enBlocks) == 0 { - return errDataCorrupt - } - - // Verify the blocks. - ok, err := xl.ReedSolomon.Verify(enBlocks) - if err != nil { - closeAndRemoveWriters(writers...) - return err - } - - // Verification failed, blocks require reconstruction. - if !ok { - for index, healNeeded := range needsHeal { - if healNeeded { - // Reconstructs() reconstructs the parts if the array is nil. - enBlocks[index] = nil - } - } - err = xl.ReedSolomon.Reconstruct(enBlocks) - if err != nil { - closeAndRemoveWriters(writers...) - return err - } - // Verify reconstructed blocks again. - ok, err = xl.ReedSolomon.Verify(enBlocks) - if err != nil { - closeAndRemoveWriters(writers...) - return err - } - if !ok { - // Blocks cannot be reconstructed, corrupted data. - err = errors.New("Verification failed after reconstruction, data likely corrupted.") - closeAndRemoveWriters(writers...) - return err - } - } - for index, healNeeded := range needsHeal { - if !healNeeded { - continue - } - _, err := writers[index].Write(enBlocks[index]) - if err != nil { - safeCloseAndRemove(writers[index]) - continue - } - } - totalLeft = totalLeft - metadata.Erasure.BlockSize - } - - // After successful healing Close() the writer so that the temp - // files are committed to their location. - for _, writer := range writers { - if writer == nil { - continue - } - writer.Close() - } - - // Update the quorum metadata after heal. - errs := xl.updatePartsMetadata(volume, path, metadata, needsHeal) - for index, healNeeded := range needsHeal { - if healNeeded && errs[index] != nil { - return errs[index] - } - } - return nil -} diff --git a/xl-erasure-v1-metadata.go b/xl-erasure-v1-metadata.go deleted file mode 100644 index e5c29ff45..000000000 --- a/xl-erasure-v1-metadata.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/json" - "io" - "time" -) - -// A xlMetaV1 represents a metadata header mapping keys to sets of values. -type xlMetaV1 struct { - Version string `json:"version"` - Stat struct { - Size int64 `json:"size"` - ModTime time.Time `json:"modTime"` - Version int64 `json:"version"` - } `json:"stat"` - Erasure struct { - DataBlocks int `json:"data"` - ParityBlocks int `json:"parity"` - BlockSize int64 `json:"blockSize"` - } `json:"erasure"` - Minio struct { - Release string `json:"release"` - } `json:"minio"` -} - -// Write writes a metadata in wire format. -func (m xlMetaV1) Write(writer io.Writer) error { - metadataBytes, err := json.Marshal(m) - if err != nil { - return err - } - _, err = writer.Write(metadataBytes) - return err -} - -// xlMetaV1Decode - file metadata decode. -func xlMetaV1Decode(reader io.Reader) (metadata xlMetaV1, err error) { - decoder := json.NewDecoder(reader) - // Unmarshalling failed, file possibly corrupted. - if err = decoder.Decode(&metadata); err != nil { - return xlMetaV1{}, err - } - return metadata, nil -} diff --git a/xl-erasure-v1-readfile.go b/xl-erasure-v1-readfile.go deleted file mode 100644 index 987cd6143..000000000 --- a/xl-erasure-v1-readfile.go +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "errors" - "fmt" - "io" - slashpath "path" - "sync" -) - -// ReadFile - read file -func (xl XL) ReadFile(volume, path string, startOffset int64) (io.ReadCloser, error) { - // Input validation. - if !isValidVolname(volume) { - return nil, errInvalidArgument - } - if !isValidPath(path) { - return nil, errInvalidArgument - } - - onlineDisks, metadata, heal, err := xl.listOnlineDisks(volume, path) - if err != nil { - return nil, err - } - - if heal { - // Heal in background safely, since we already have read - // quorum disks. Let the reads continue. - go func() { - hErr := xl.healFile(volume, path) - errorIf(hErr, "Unable to heal file "+volume+"/"+path+".") - }() - } - - readers := make([]io.ReadCloser, len(xl.storageDisks)) - for index, disk := range onlineDisks { - if disk == nil { - continue - } - erasurePart := slashpath.Join(path, fmt.Sprintf("file.%d", index)) - // If disk.ReadFile returns error and we don't have read quorum it will be taken care as - // ReedSolomon.Reconstruct() will fail later. - var reader io.ReadCloser - offset := int64(0) - if reader, err = disk.ReadFile(volume, erasurePart, offset); err == nil { - readers[index] = reader - } - } - - // Initialize pipe. - pipeReader, pipeWriter := io.Pipe() - go func() { - var totalLeft = metadata.Stat.Size - // Read until the totalLeft. - for totalLeft > 0 { - // Figure out the right blockSize as it was encoded before. - var curBlockSize int64 - if metadata.Erasure.BlockSize < totalLeft { - curBlockSize = metadata.Erasure.BlockSize - } else { - curBlockSize = totalLeft - } - // Calculate the current encoded block size. - curEncBlockSize := getEncodedBlockLen(curBlockSize, metadata.Erasure.DataBlocks) - enBlocks := make([][]byte, len(xl.storageDisks)) - var wg = &sync.WaitGroup{} - // Loop through all readers and read. - for index, reader := range readers { - // Initialize shard slice and fill the data from each parts. - enBlocks[index] = make([]byte, curEncBlockSize) - if reader == nil { - continue - } - // Parallelize reading. - wg.Add(1) - go func(index int, reader io.Reader) { - defer wg.Done() - // Read the necessary blocks. - _, rErr := io.ReadFull(reader, enBlocks[index]) - if rErr != nil && rErr != io.ErrUnexpectedEOF { - readers[index] = nil - } - }(index, reader) - } - // Wait for the read routines to finish. - wg.Wait() - - // Check blocks if they are all zero in length. - if checkBlockSize(enBlocks) == 0 { - pipeWriter.CloseWithError(errDataCorrupt) - return - } - - // Verify the blocks. - var ok bool - ok, err = xl.ReedSolomon.Verify(enBlocks) - if err != nil { - pipeWriter.CloseWithError(err) - return - } - - // Verification failed, blocks require reconstruction. - if !ok { - for index, reader := range readers { - if reader == nil { - // Reconstruct expects missing blocks to be nil. - enBlocks[index] = nil - } - } - err = xl.ReedSolomon.Reconstruct(enBlocks) - if err != nil { - pipeWriter.CloseWithError(err) - return - } - // Verify reconstructed blocks again. - ok, err = xl.ReedSolomon.Verify(enBlocks) - if err != nil { - pipeWriter.CloseWithError(err) - return - } - if !ok { - // Blocks cannot be reconstructed, corrupted data. - err = errors.New("Verification failed after reconstruction, data likely corrupted.") - pipeWriter.CloseWithError(err) - return - } - } - - // Get all the data blocks. - dataBlocks := getDataBlocks(enBlocks, metadata.Erasure.DataBlocks, int(curBlockSize)) - - // Verify if the offset is right for the block, if not move to - // the next block. - - if startOffset > 0 { - startOffset = startOffset - int64(len(dataBlocks)) - // Start offset is greater than or equal to zero, skip the dataBlocks. - if startOffset >= 0 { - totalLeft = totalLeft - metadata.Erasure.BlockSize - continue - } - // Now get back the remaining offset if startOffset is negative. - startOffset = startOffset + int64(len(dataBlocks)) - } - - // Write safely the necessary blocks. - _, err = pipeWriter.Write(dataBlocks[int(startOffset):]) - if err != nil { - pipeWriter.CloseWithError(err) - return - } - - // Reset offset to '0' to read rest of the blocks. - startOffset = int64(0) - - // Save what's left after reading erasureBlockSize. - totalLeft = totalLeft - metadata.Erasure.BlockSize - } - - // Cleanly end the pipe after a successful decoding. - pipeWriter.Close() - - // Cleanly close all the underlying data readers. - for _, reader := range readers { - if reader == nil { - continue - } - reader.Close() - } - }() - - // Return the pipe for the top level caller to start reading. - return pipeReader, nil -} diff --git a/xl-erasure-v1-utils.go b/xl-erasure-v1-utils.go deleted file mode 100644 index ff505b143..000000000 --- a/xl-erasure-v1-utils.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -// getDataBlocks - fetches the data block only part of the input encoded blocks. -func getDataBlocks(enBlocks [][]byte, dataBlocks int, curBlockSize int) []byte { - var data []byte - for _, block := range enBlocks[:dataBlocks] { - data = append(data, block...) - } - data = data[:curBlockSize] - return data -} - -// checkBlockSize return the size of a single block. -// The first non-zero size is returned, -// or 0 if all blocks are size 0. -func checkBlockSize(blocks [][]byte) int { - for _, block := range blocks { - if len(block) != 0 { - return len(block) - } - } - return 0 -} - -// calculate the blockSize based on input length and total number of -// data blocks. -func getEncodedBlockLen(inputLen int64, dataBlocks int) (curBlockSize int64) { - curBlockSize = (inputLen + int64(dataBlocks) - 1) / int64(dataBlocks) - return curBlockSize -} diff --git a/xl-erasure-v1.go b/xl-erasure-v1.go deleted file mode 100644 index 5858dbe6e..000000000 --- a/xl-erasure-v1.go +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "errors" - "fmt" - "math/rand" - "os" - slashpath "path" - "strings" - - "path" - "sync" - - "github.com/klauspost/reedsolomon" -) - -const ( - // XL erasure metadata file. - xlMetaV1File = "file.json" -) - -// XL layer structure. -type XL struct { - ReedSolomon reedsolomon.Encoder // Erasure encoder/decoder. - DataBlocks int - ParityBlocks int - storageDisks []StorageAPI - readQuorum int - writeQuorum int -} - -// errUnexpected - returned for any unexpected error. -var errUnexpected = errors.New("Unexpected error - please report at https://github.com/minio/minio/issues") - -// newXL instantiate a new XL. -func newXL(disks []StorageAPI) (StorageAPI, error) { - // Initialize XL. - xl := &XL{} - - // Calculate data and parity blocks. - dataBlocks, parityBlocks := len(disks)/2, len(disks)/2 - - // Initialize reed solomon encoding. - rs, err := reedsolomon.New(dataBlocks, parityBlocks) - if err != nil { - return nil, err - } - - // Save the reedsolomon. - xl.DataBlocks = dataBlocks - xl.ParityBlocks = parityBlocks - xl.ReedSolomon = rs - - // Save all the initialized storage disks. - xl.storageDisks = disks - - // Figure out read and write quorum based on number of storage disks. - // Read quorum should be always N/2 + 1 (due to Vandermonde matrix - // erasure requirements) - xl.readQuorum = len(xl.storageDisks)/2 + 1 - - // Write quorum is assumed if we have total disks + 3 - // parity. (Need to discuss this again) - xl.writeQuorum = len(xl.storageDisks)/2 + 3 - if xl.writeQuorum > len(xl.storageDisks) { - xl.writeQuorum = len(xl.storageDisks) - } - - // Return successfully initialized. - return xl, nil -} - -// MakeVol - make a volume. -func (xl XL) MakeVol(volume string) error { - if !isValidVolname(volume) { - return errInvalidArgument - } - - // Err counters. - createVolErr := 0 // Count generic create vol errs. - volumeExistsErrCnt := 0 // Count all errVolumeExists errs. - - // Initialize sync waitgroup. - var wg = &sync.WaitGroup{} - - // Initialize list of errors. - var dErrs = make([]error, len(xl.storageDisks)) - - // Make a volume entry on all underlying storage disks. - for index, disk := range xl.storageDisks { - wg.Add(1) - // Make a volume inside a go-routine. - go func(index int, disk StorageAPI) { - defer wg.Done() - if disk == nil { - return - } - dErrs[index] = disk.MakeVol(volume) - }(index, disk) - } - - // Wait for all make vol to finish. - wg.Wait() - - // Loop through all the concocted errors. - for _, err := range dErrs { - if err == nil { - continue - } - // if volume already exists, count them. - if err == errVolumeExists { - volumeExistsErrCnt++ - continue - } - - // Update error counter separately. - createVolErr++ - } - // Return err if all disks report volume exists. - if volumeExistsErrCnt == len(xl.storageDisks) { - return errVolumeExists - } else if createVolErr > len(xl.storageDisks)-xl.writeQuorum { - // Return errWriteQuorum if errors were more than - // allowed write quorum. - return errWriteQuorum - } - return nil -} - -// DeleteVol - delete a volume. -func (xl XL) DeleteVol(volume string) error { - if !isValidVolname(volume) { - return errInvalidArgument - } - - // Collect if all disks report volume not found. - var volumeNotFoundErrCnt int - - var wg = &sync.WaitGroup{} - var dErrs = make([]error, len(xl.storageDisks)) - - // Remove a volume entry on all underlying storage disks. - for index, disk := range xl.storageDisks { - wg.Add(1) - // Delete volume inside a go-routine. - go func(index int, disk StorageAPI) { - defer wg.Done() - dErrs[index] = disk.DeleteVol(volume) - }(index, disk) - } - - // Wait for all the delete vols to finish. - wg.Wait() - - // Loop through concocted errors and return anything unusual. - for _, err := range dErrs { - if err != nil { - // We ignore error if errVolumeNotFound or errDiskNotFound - if err == errVolumeNotFound || err == errDiskNotFound { - volumeNotFoundErrCnt++ - continue - } - return err - } - } - // Return err if all disks report volume not found. - if volumeNotFoundErrCnt == len(xl.storageDisks) { - return errVolumeNotFound - } - return nil -} - -// ListVols - list volumes. -func (xl XL) ListVols() (volsInfo []VolInfo, err error) { - // Initialize sync waitgroup. - var wg = &sync.WaitGroup{} - - // Success vols map carries successful results of ListVols from each disks. - var successVols = make([][]VolInfo, len(xl.storageDisks)) - for index, disk := range xl.storageDisks { - wg.Add(1) // Add each go-routine to wait for. - go func(index int, disk StorageAPI) { - // Indicate wait group as finished. - defer wg.Done() - - // Initiate listing. - vlsInfo, _ := disk.ListVols() - successVols[index] = vlsInfo - }(index, disk) - } - - // For all the list volumes running in parallel to finish. - wg.Wait() - - // Loop through success vols and get aggregated usage values. - var vlsInfo []VolInfo - var total, free int64 - for _, vlsInfo = range successVols { - if len(vlsInfo) <= 1 { - continue - } - var vlInfo VolInfo - for _, vlInfo = range vlsInfo { - if vlInfo.Name == "" { - continue - } - break - } - free += vlInfo.Free - total += vlInfo.Total - } - - // Save the updated usage values back into the vols. - for _, vlInfo := range vlsInfo { - vlInfo.Free = free - vlInfo.Total = total - volsInfo = append(volsInfo, vlInfo) - } - - // NOTE: The assumption here is that volumes across all disks in - // readQuorum have consistent view i.e they all have same number - // of buckets. This is essentially not verified since healing - // should take care of this. - return volsInfo, nil -} - -// getAllVolInfo - list bucket volume info from all disks. -// Returns error slice indicating the failed volume stat operations. -func (xl XL) getAllVolInfo(volume string) ([]VolInfo, []error) { - // Create errs and volInfo slices of storageDisks size. - var errs = make([]error, len(xl.storageDisks)) - var volsInfo = make([]VolInfo, len(xl.storageDisks)) - - // Allocate a new waitgroup. - var wg = &sync.WaitGroup{} - for index, disk := range xl.storageDisks { - wg.Add(1) - // Stat volume on all the disks in a routine. - go func(index int, disk StorageAPI) { - defer wg.Done() - volInfo, err := disk.StatVol(volume) - if err != nil { - errs[index] = err - return - } - volsInfo[index] = volInfo - }(index, disk) - } - - // Wait for all the Stat operations to finish. - wg.Wait() - - // Return the concocted values. - return volsInfo, errs -} - -// listAllVolInfo - list all stat volume info from all disks. -// Returns -// - stat volume info for all online disks. -// - boolean to indicate if healing is necessary. -// - error if any. -func (xl XL) listAllVolInfo(volume string) ([]VolInfo, bool, error) { - volsInfo, errs := xl.getAllVolInfo(volume) - notFoundCount := 0 - for _, err := range errs { - if err == errVolumeNotFound { - notFoundCount++ - // If we have errors with file not found greater than allowed read - // quorum we return err as errFileNotFound. - if notFoundCount > len(xl.storageDisks)-xl.readQuorum { - return nil, false, errVolumeNotFound - } - } - } - - // Calculate online disk count. - onlineDiskCount := 0 - for index := range errs { - if errs[index] == nil { - onlineDiskCount++ - } - } - - var heal bool - // If online disks count is lesser than configured disks, most - // probably we need to heal the file, additionally verify if the - // count is lesser than readQuorum, if not we throw an error. - if onlineDiskCount < len(xl.storageDisks) { - // Online disks lesser than total storage disks, needs to be - // healed. unless we do not have readQuorum. - heal = true - // Verify if online disks count are lesser than readQuorum - // threshold, return an error if yes. - if onlineDiskCount < xl.readQuorum { - return nil, false, errReadQuorum - } - } - - // Return success. - return volsInfo, heal, nil -} - -// StatVol - get volume stat info. -func (xl XL) StatVol(volume string) (volInfo VolInfo, err error) { - if !isValidVolname(volume) { - return VolInfo{}, errInvalidArgument - } - - // List and figured out if we need healing. - volsInfo, heal, err := xl.listAllVolInfo(volume) - if err != nil { - return VolInfo{}, err - } - - // Heal for missing entries. - if heal { - go func() { - // Create volume if missing on disks. - for index, volInfo := range volsInfo { - if volInfo.Name != "" { - continue - } - // Volinfo name would be an empty string, create it. - xl.storageDisks[index].MakeVol(volume) - } - }() - } - - // Loop through all statVols, calculate the actual usage values. - var total, free int64 - for _, volInfo = range volsInfo { - if volInfo.Name == "" { - continue - } - free += volInfo.Free - total += volInfo.Total - } - // Update the aggregated values. - volInfo.Free = free - volInfo.Total = total - return volInfo, nil -} - -// isLeafDirectoryXL - check if a given path is leaf directory. i.e -// if it contains file xlMetaV1File -func isLeafDirectoryXL(disk StorageAPI, volume, leafPath string) (isLeaf bool) { - _, err := disk.StatFile(volume, path.Join(leafPath, xlMetaV1File)) - return err == nil -} - -// ListDir - return all the entries at the given directory path. -// If an entry is a directory it will be returned with a trailing "/". -func (xl XL) ListDir(volume, dirPath string) (entries []string, err error) { - if !isValidVolname(volume) { - return nil, errInvalidArgument - } - - // Count for list errors encountered. - var listErrCount = 0 - - // Loop through and return the first success entry based on the - // selected random disk. - for listErrCount < len(xl.storageDisks) { - // Choose a random disk on each attempt, do not hit the same disk all the time. - randIndex := rand.Intn(len(xl.storageDisks) - 1) - disk := xl.storageDisks[randIndex] // Pick a random disk. - // Initiate a list operation, if successful filter and return quickly. - if entries, err = disk.ListDir(volume, dirPath); err == nil { - for i, entry := range entries { - isLeaf := isLeafDirectoryXL(disk, volume, path.Join(dirPath, entry)) - isDir := strings.HasSuffix(entry, slashSeparator) - if isDir && isLeaf { - entries[i] = strings.TrimSuffix(entry, slashSeparator) - } - } - // We got the entries successfully return. - return entries, nil - } - listErrCount++ // Update list error count. - } - // Return error at the end. - return nil, err -} - -// Object API. - -// StatFile - stat a file -func (xl XL) StatFile(volume, path string) (FileInfo, error) { - if !isValidVolname(volume) { - return FileInfo{}, errInvalidArgument - } - if !isValidPath(path) { - return FileInfo{}, errInvalidArgument - } - - _, metadata, heal, err := xl.listOnlineDisks(volume, path) - if err != nil { - return FileInfo{}, err - } - - if heal { - // Heal in background safely, since we already have read quorum disks. - go func() { - hErr := xl.healFile(volume, path) - errorIf(hErr, "Unable to heal file "+volume+"/"+path+".") - }() - } - - // Return file info. - return FileInfo{ - Volume: volume, - Name: path, - Size: metadata.Stat.Size, - ModTime: metadata.Stat.ModTime, - Mode: os.FileMode(0644), - }, nil -} - -// deleteXLFiles - delete all XL backend files. -func (xl XL) deleteXLFiles(volume, path string) error { - errCount := 0 - // Update meta data file and remove part file - for index, disk := range xl.storageDisks { - erasureFilePart := slashpath.Join(path, fmt.Sprintf("file.%d", index)) - err := disk.DeleteFile(volume, erasureFilePart) - if err != nil { - errCount++ - - // We can safely allow DeleteFile errors up to len(xl.storageDisks) - xl.writeQuorum - // otherwise return failure. - if errCount <= len(xl.storageDisks)-xl.writeQuorum { - continue - } - - return err - } - - xlMetaV1FilePath := slashpath.Join(path, "file.json") - err = disk.DeleteFile(volume, xlMetaV1FilePath) - if err != nil { - errCount++ - - // We can safely allow DeleteFile errors up to len(xl.storageDisks) - xl.writeQuorum - // otherwise return failure. - if errCount <= len(xl.storageDisks)-xl.writeQuorum { - continue - } - - return err - } - } - // Return success. - return nil -} - -// DeleteFile - delete a file -func (xl XL) DeleteFile(volume, path string) error { - if !isValidVolname(volume) { - return errInvalidArgument - } - if !isValidPath(path) { - return errInvalidArgument - } - - // Delete all XL files. - return xl.deleteXLFiles(volume, path) -} - -// RenameFile - rename file. -func (xl XL) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) error { - // Validate inputs. - if !isValidVolname(srcVolume) { - return errInvalidArgument - } - if !isValidPath(srcPath) { - return errInvalidArgument - } - if !isValidVolname(dstVolume) { - return errInvalidArgument - } - if !isValidPath(dstPath) { - return errInvalidArgument - } - - // Initialize sync waitgroup. - var wg = &sync.WaitGroup{} - - // Initialize list of errors. - var errs = make([]error, len(xl.storageDisks)) - - // Rename file on all underlying storage disks. - for index, disk := range xl.storageDisks { - // Append "/" as srcPath and dstPath are either leaf-dirs or non-leaf-dris. - // If srcPath is an object instead of prefix we just rename the leaf-dir and - // not rename the part and metadata files separately. - wg.Add(1) - go func(index int, disk StorageAPI) { - defer wg.Done() - err := disk.RenameFile(srcVolume, retainSlash(srcPath), dstVolume, retainSlash(dstPath)) - if err != nil { - errs[index] = err - } - errs[index] = nil - }(index, disk) - } - - // Wait for all RenameFile to finish. - wg.Wait() - - // Gather err count. - var errCount = 0 - for _, err := range errs { - if err == nil { - continue - } - errCount++ - } - // We can safely allow RenameFile errors up to len(xl.storageDisks) - xl.writeQuorum - // otherwise return failure. Cleanup successful renames. - if errCount > len(xl.storageDisks)-xl.writeQuorum { - // Special condition if readQuorum exists, then return success. - if errCount <= len(xl.storageDisks)-xl.readQuorum { - return nil - } - // Ignore errors here, delete all successfully written files. - xl.deleteXLFiles(dstVolume, dstPath) - return errWriteQuorum - } - return nil -} diff --git a/xl-objects-multipart.go b/xl-objects-multipart.go deleted file mode 100644 index 6a8d6e081..000000000 --- a/xl-objects-multipart.go +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/json" - "fmt" - "io" - "path" - "strings" - "sync" - "time" -) - -// MultipartPartInfo Info of each part kept in the multipart metadata file after -// CompleteMultipartUpload() is called. -type MultipartPartInfo struct { - PartNumber int - ETag string - Size int64 -} - -// MultipartObjectInfo - contents of the multipart metadata file after -// CompleteMultipartUpload() is called. -type MultipartObjectInfo struct { - Parts []MultipartPartInfo - ModTime time.Time - Size int64 - MD5Sum string - ContentType string - ContentEncoding string - // Add more fields here. -} - -type byMultipartFiles []string - -func (files byMultipartFiles) Len() int { return len(files) } -func (files byMultipartFiles) Less(i, j int) bool { - first := strings.TrimSuffix(files[i], multipartSuffix) - second := strings.TrimSuffix(files[j], multipartSuffix) - return first < second -} -func (files byMultipartFiles) Swap(i, j int) { files[i], files[j] = files[j], files[i] } - -// GetPartNumberOffset - given an offset for the whole object, return the part and offset in that part. -func (m MultipartObjectInfo) GetPartNumberOffset(offset int64) (partIndex int, partOffset int64, err error) { - partOffset = offset - for i, part := range m.Parts { - partIndex = i - if partOffset < part.Size { - return - } - partOffset -= part.Size - } - // Offset beyond the size of the object - err = errUnexpected - return -} - -// getMultipartObjectMeta - incomplete meta file and extract meta information if any. -func getMultipartObjectMeta(storage StorageAPI, metaFile string) (meta map[string]string, err error) { - meta = make(map[string]string) - offset := int64(0) - objMetaReader, err := storage.ReadFile(minioMetaBucket, metaFile, offset) - if err != nil { - return nil, err - } - // Close the metadata reader. - defer objMetaReader.Close() - - decoder := json.NewDecoder(objMetaReader) - err = decoder.Decode(&meta) - if err != nil { - return nil, err - } - return meta, nil -} - -func partNumToPartFileName(partNum int) string { - return fmt.Sprintf("%.5d%s", partNum, multipartSuffix) -} - -// ListMultipartUploads - list multipart uploads. -func (xl xlObjects) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { - return listMultipartUploadsCommon(xl, bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) -} - -// NewMultipartUpload - initialize a new multipart upload, returns a unique id. -func (xl xlObjects) NewMultipartUpload(bucket, object string, meta map[string]string) (string, error) { - return newMultipartUploadCommon(xl.storage, bucket, object, meta) -} - -// PutObjectPart - writes the multipart upload chunks. -func (xl xlObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) { - return putObjectPartCommon(xl.storage, bucket, object, uploadID, partID, size, data, md5Hex) -} - -// ListObjectParts - list object parts. -func (xl xlObjects) ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { - return listObjectPartsCommon(xl.storage, bucket, object, uploadID, partNumberMarker, maxParts) -} - -// This function does the following check, suppose -// object is "a/b/c/d", stat makes sure that objects ""a/b/c"" -// "a/b" and "a" do not exist. -func (xl xlObjects) parentDirIsObject(bucket, parent string) error { - var stat func(string) error - stat = func(p string) error { - if p == "." { - return nil - } - _, err := xl.getObjectInfo(bucket, p) - if err == nil { - // If there is already a file at prefix "p" return error. - return errFileAccessDenied - } - if err == errFileNotFound { - // Check if there is a file as one of the parent paths. - return stat(path.Dir(p)) - } - return err - } - return stat(parent) -} - -func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (string, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return "", BucketNameInvalid{Bucket: bucket} - } - // Verify whether the bucket exists. - if !isBucketExist(xl.storage, bucket) { - return "", BucketNotFound{Bucket: bucket} - } - if !IsValidObjectName(object) { - return "", ObjectNameInvalid{ - Bucket: bucket, - Object: object, - } - } - if !isUploadIDExists(xl.storage, bucket, object, uploadID) { - return "", InvalidUploadID{UploadID: uploadID} - } - // Hold lock so that - // 1) no one aborts this multipart upload - // 2) no one does a parallel complete-multipart-upload on this multipart upload - nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) - - // Calculate s3 compatible md5sum for complete multipart. - s3MD5, err := completeMultipartMD5(parts...) - if err != nil { - return "", err - } - - var metadata = MultipartObjectInfo{} - var errs = make([]error, len(parts)) - - uploadIDIncompletePath := path.Join(mpartMetaPrefix, bucket, object, uploadID, incompleteFile) - objMeta, err := getMultipartObjectMeta(xl.storage, uploadIDIncompletePath) - if err != nil { - return "", toObjectErr(err, minioMetaBucket, uploadIDIncompletePath) - } - - // Waitgroup to wait for go-routines. - var wg = &sync.WaitGroup{} - - // Loop through all parts, validate them and then commit to disk. - for i, part := range parts { - // Construct part suffix. - partSuffix := fmt.Sprintf("%.5d.%s", part.PartNumber, part.ETag) - multipartPartFile := path.Join(mpartMetaPrefix, bucket, object, uploadID, partSuffix) - var fi FileInfo - fi, err = xl.storage.StatFile(minioMetaBucket, multipartPartFile) - if err != nil { - if err == errFileNotFound { - return "", InvalidPart{} - } - return "", err - } - // All parts except the last part has to be atleast 5MB. - if (i < len(parts)-1) && !isMinAllowedPartSize(fi.Size) { - return "", PartTooSmall{} - } - // Update metadata parts. - metadata.Parts = append(metadata.Parts, MultipartPartInfo{ - PartNumber: part.PartNumber, - ETag: part.ETag, - Size: fi.Size, - }) - metadata.Size += fi.Size - } - - // check if an object is present as one of the parent dir. - if err = xl.parentDirIsObject(bucket, path.Dir(object)); err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Save successfully calculated md5sum. - metadata.MD5Sum = s3MD5 - metadata.ContentType = objMeta["content-type"] - metadata.ContentEncoding = objMeta["content-encoding"] - - // Save modTime as well as the current time. - metadata.ModTime = time.Now().UTC() - - // Create temporary multipart meta file to write and then rename. - multipartMetaSuffix := fmt.Sprintf("%s.%s", uploadID, multipartMetaFile) - tempMultipartMetaFile := path.Join(tmpMetaPrefix, bucket, object, multipartMetaSuffix) - w, err := xl.storage.CreateFile(minioMetaBucket, tempMultipartMetaFile) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - encoder := json.NewEncoder(w) - err = encoder.Encode(&metadata) - if err != nil { - if err = safeCloseAndRemove(w); err != nil { - return "", toObjectErr(err, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - // Close the writer. - if err = w.Close(); err != nil { - if err = safeCloseAndRemove(w); err != nil { - return "", toObjectErr(err, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - - // Attempt a Rename of multipart meta file to final namespace. - multipartObjFile := path.Join(mpartMetaPrefix, bucket, object, uploadID, multipartMetaFile) - err = xl.storage.RenameFile(minioMetaBucket, tempMultipartMetaFile, minioMetaBucket, multipartObjFile) - if err != nil { - if derr := xl.storage.DeleteFile(minioMetaBucket, tempMultipartMetaFile); derr != nil { - return "", toObjectErr(err, minioMetaBucket, tempMultipartMetaFile) - } - return "", toObjectErr(err, bucket, multipartObjFile) - } - - // Loop through and atomically rename the parts to their actual location. - for index, part := range parts { - wg.Add(1) - go func(index int, part completePart) { - defer wg.Done() - partSuffix := fmt.Sprintf("%.5d.%s", part.PartNumber, part.ETag) - src := path.Join(mpartMetaPrefix, bucket, object, uploadID, partSuffix) - dst := path.Join(mpartMetaPrefix, bucket, object, uploadID, partNumToPartFileName(part.PartNumber)) - errs[index] = xl.storage.RenameFile(minioMetaBucket, src, minioMetaBucket, dst) - errorIf(errs[index], "Unable to rename file %s to %s.", src, dst) - }(index, part) - } - - // Wait for all the renames to finish. - wg.Wait() - - // Loop through errs list and return first error. - for _, err := range errs { - if err != nil { - return "", toObjectErr(err, bucket, object) - } - } - - // Delete the incomplete file place holder. - err = xl.storage.DeleteFile(minioMetaBucket, uploadIDIncompletePath) - if err != nil { - return "", toObjectErr(err, minioMetaBucket, uploadIDIncompletePath) - } - - // Hold write lock on the destination before rename - nsMutex.Lock(bucket, object) - defer nsMutex.Unlock(bucket, object) - - // Delete if an object already exists. - // FIXME: rename it to tmp file and delete only after - // the newly uploaded file is renamed from tmp location to - // the original location. - // Verify if the object is a multipart object. - if isMultipartObject(xl.storage, bucket, object) { - err = xl.deleteMultipartObject(bucket, object) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - return s3MD5, nil - } - err = xl.deleteObject(bucket, object) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - - uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) - if err = xl.storage.RenameFile(minioMetaBucket, uploadIDPath, bucket, object); err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Hold the lock so that two parallel complete-multipart-uploads do no - // leave a stale uploads.json behind. - nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) - defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) - - // Validate if there are other incomplete upload-id's present for - // the object, if yes do not attempt to delete 'uploads.json'. - var entries []string - if entries, err = xl.storage.ListDir(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)); err == nil { - if len(entries) > 1 { - return s3MD5, nil - } - } - - uploadsJSONPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) - err = xl.storage.DeleteFile(minioMetaBucket, uploadsJSONPath) - if err != nil { - return "", toObjectErr(err, minioMetaBucket, uploadsJSONPath) - } - - // Return md5sum. - return s3MD5, nil -} - -// AbortMultipartUpload - aborts a multipart upload. -func (xl xlObjects) AbortMultipartUpload(bucket, object, uploadID string) error { - return abortMultipartUploadCommon(xl.storage, bucket, object, uploadID) -} diff --git a/xl-objects.go b/xl-objects.go deleted file mode 100644 index 91e4758e9..000000000 --- a/xl-objects.go +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "crypto/md5" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "path" - "path/filepath" - "strings" - "sync" - - "github.com/minio/minio/pkg/mimedb" -) - -const ( - multipartSuffix = ".minio.multipart" - multipartMetaFile = "00000" + multipartSuffix - formatConfigFile = "format.json" -) - -// xlObjects - Implements fs object layer. -type xlObjects struct { - storage StorageAPI - listObjectMap map[listParams][]*treeWalker - listObjectMapMutex *sync.Mutex -} - -// errMaxDisks - returned for reached maximum of disks. -var errMaxDisks = errors.New("Number of disks are higher than supported maximum count '16'") - -// errMinDisks - returned for minimum number of disks. -var errMinDisks = errors.New("Number of disks are smaller than supported minimum count '8'") - -// errNumDisks - returned for odd number of disks. -var errNumDisks = errors.New("Number of disks should be multiples of '2'") - -const ( - // Maximum erasure blocks. - maxErasureBlocks = 16 - // Minimum erasure blocks. - minErasureBlocks = 8 -) - -func checkSufficientDisks(disks []string) error { - // Verify total number of disks. - totalDisks := len(disks) - if totalDisks > maxErasureBlocks { - return errMaxDisks - } - if totalDisks < minErasureBlocks { - return errMinDisks - } - - // isEven function to verify if a given number if even. - isEven := func(number int) bool { - return number%2 == 0 - } - - // Verify if we have even number of disks. - // only combination of 8, 10, 12, 14, 16 are supported. - if !isEven(totalDisks) { - return errNumDisks - } - - return nil -} - -// Depending on the disk type network or local, initialize storage layer. -func newStorageLayer(disk string) (storage StorageAPI, err error) { - if !strings.ContainsRune(disk, ':') || filepath.VolumeName(disk) != "" { - // Initialize filesystem storage API. - return newPosix(disk) - } - // Initialize rpc client storage API. - return newRPCClient(disk) -} - -// Initialize all storage disks to bootstrap. -func bootstrapDisks(disks []string) ([]StorageAPI, error) { - storageDisks := make([]StorageAPI, len(disks)) - for index, disk := range disks { - var err error - // Intentionally ignore disk not found errors while - // initializing POSIX, so that we have successfully - // initialized posix Storage. Subsequent calls to XL/Erasure - // will manage any errors related to disks. - storageDisks[index], err = newStorageLayer(disk) - if err != nil && err != errDiskNotFound { - return nil, err - } - } - return storageDisks, nil -} - -// newXLObjects - initialize new xl object layer. -func newXLObjects(disks []string) (ObjectLayer, error) { - if err := checkSufficientDisks(disks); err != nil { - return nil, err - } - - storageDisks, err := bootstrapDisks(disks) - if err != nil { - return nil, err - } - - // Initialize object layer - like creating minioMetaBucket, cleaning up tmp files etc. - initObjectLayer(storageDisks...) - - // Load saved XL format.json and validate. - newDisks, err := loadFormatXL(storageDisks) - if err != nil { - switch err { - case errUnformattedDisk: - // Save new XL format. - errSave := initFormatXL(storageDisks) - if errSave != nil { - return nil, errSave - } - newDisks = storageDisks - default: - // errCorruptedDisk - error. - return nil, fmt.Errorf("Unable to recognize backend format, %s", err) - } - } - - // FIXME: healFormatXL(newDisks) - - storage, err := newXL(newDisks) - if err != nil { - return nil, err - } - - // Return successfully initialized object layer. - return xlObjects{ - storage: storage, - listObjectMap: make(map[listParams][]*treeWalker), - listObjectMapMutex: &sync.Mutex{}, - }, nil -} - -/// Bucket operations - -// MakeBucket - make a bucket. -func (xl xlObjects) MakeBucket(bucket string) error { - nsMutex.Lock(bucket, "") - defer nsMutex.Unlock(bucket, "") - return makeBucket(xl.storage, bucket) -} - -// GetBucketInfo - get bucket info. -func (xl xlObjects) GetBucketInfo(bucket string) (BucketInfo, error) { - nsMutex.RLock(bucket, "") - defer nsMutex.RUnlock(bucket, "") - return getBucketInfo(xl.storage, bucket) -} - -// ListBuckets - list buckets. -func (xl xlObjects) ListBuckets() ([]BucketInfo, error) { - return listBuckets(xl.storage) -} - -// DeleteBucket - delete a bucket. -func (xl xlObjects) DeleteBucket(bucket string) error { - nsMutex.Lock(bucket, "") - nsMutex.Unlock(bucket, "") - return deleteBucket(xl.storage, bucket) -} - -/// Object Operations - -// GetObject - get an object. -func (xl xlObjects) GetObject(bucket, object string, startOffset int64) (io.ReadCloser, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return nil, BucketNameInvalid{Bucket: bucket} - } - // Verify if object is valid. - if !IsValidObjectName(object) { - return nil, ObjectNameInvalid{Bucket: bucket, Object: object} - } - nsMutex.RLock(bucket, object) - defer nsMutex.RUnlock(bucket, object) - if !isMultipartObject(xl.storage, bucket, object) { - _, err := xl.storage.StatFile(bucket, object) - if err == nil { - var reader io.ReadCloser - reader, err = xl.storage.ReadFile(bucket, object, startOffset) - if err != nil { - return nil, toObjectErr(err, bucket, object) - } - return reader, nil - } - return nil, toObjectErr(err, bucket, object) - } - fileReader, fileWriter := io.Pipe() - info, err := getMultipartObjectInfo(xl.storage, bucket, object) - if err != nil { - return nil, toObjectErr(err, bucket, object) - } - partIndex, offset, err := info.GetPartNumberOffset(startOffset) - if err != nil { - return nil, toObjectErr(err, bucket, object) - } - - // Hold a read lock once more which can be released after the following go-routine ends. - // We hold RLock once more because the current function would return before the go routine below - // executes and hence releasing the read lock (because of defer'ed nsMutex.RUnlock() call). - nsMutex.RLock(bucket, object) - go func() { - defer nsMutex.RUnlock(bucket, object) - for ; partIndex < len(info.Parts); partIndex++ { - part := info.Parts[partIndex] - r, err := xl.storage.ReadFile(bucket, pathJoin(object, partNumToPartFileName(part.PartNumber)), offset) - if err != nil { - fileWriter.CloseWithError(err) - return - } - // Reset offset to 0 as it would be non-0 only for the first loop if startOffset is non-0. - offset = 0 - if _, err = io.Copy(fileWriter, r); err != nil { - switch reader := r.(type) { - case *io.PipeReader: - reader.CloseWithError(err) - case io.ReadCloser: - reader.Close() - } - fileWriter.CloseWithError(err) - return - } - // Close the readerCloser that reads multiparts of an object from the xl storage layer. - // Not closing leaks underlying file descriptors. - r.Close() - } - fileWriter.Close() - }() - return fileReader, nil -} - -// Return the partsInfo of a special multipart object. -func getMultipartObjectInfo(storage StorageAPI, bucket, object string) (info MultipartObjectInfo, err error) { - offset := int64(0) - r, err := storage.ReadFile(bucket, pathJoin(object, multipartMetaFile), offset) - if err != nil { - return MultipartObjectInfo{}, err - } - decoder := json.NewDecoder(r) - err = decoder.Decode(&info) - if err != nil { - return MultipartObjectInfo{}, err - } - return info, nil -} - -// Return ObjectInfo. -func (xl xlObjects) getObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) { - objInfo.Bucket = bucket - objInfo.Name = object - // First see if the object was a simple-PUT upload. - fi, err := xl.storage.StatFile(bucket, object) - if err != nil { - if err != errFileNotFound { - return ObjectInfo{}, err - } - var info MultipartObjectInfo - // Check if the object was multipart upload. - info, err = getMultipartObjectInfo(xl.storage, bucket, object) - if err != nil { - return ObjectInfo{}, err - } - objInfo.Size = info.Size - objInfo.ModTime = info.ModTime - objInfo.MD5Sum = info.MD5Sum - objInfo.ContentType = info.ContentType - objInfo.ContentEncoding = info.ContentEncoding - } else { - metadata := make(map[string]string) - offset := int64(0) // To read entire content - r, err := xl.storage.ReadFile(bucket, pathJoin(object, "meta.json"), offset) - if err != nil { - return ObjectInfo{}, toObjectErr(err, bucket, object) - } - decoder := json.NewDecoder(r) - if err = decoder.Decode(&metadata); err != nil { - return ObjectInfo{}, toObjectErr(err, bucket, object) - } - contentType := metadata["content-type"] - if len(contentType) == 0 { - contentType = "application/octet-stream" - if objectExt := filepath.Ext(object); objectExt != "" { - content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))] - if ok { - contentType = content.ContentType - } - } - } - objInfo.Size = fi.Size - objInfo.IsDir = fi.Mode.IsDir() - objInfo.ModTime = fi.ModTime - objInfo.MD5Sum = metadata["md5Sum"] - objInfo.ContentType = contentType - objInfo.ContentEncoding = metadata["content-encoding"] - } - return objInfo, nil -} - -// GetObjectInfo - get object info. -func (xl xlObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return ObjectInfo{}, BucketNameInvalid{Bucket: bucket} - } - // Verify if object is valid. - if !IsValidObjectName(object) { - return ObjectInfo{}, ObjectNameInvalid{Bucket: bucket, Object: object} - } - nsMutex.RLock(bucket, object) - defer nsMutex.RUnlock(bucket, object) - info, err := xl.getObjectInfo(bucket, object) - if err != nil { - return ObjectInfo{}, toObjectErr(err, bucket, object) - } - return info, nil -} - -// PutObject - create an object. -func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return "", BucketNameInvalid{Bucket: bucket} - } - // Verify bucket exists. - if !isBucketExist(xl.storage, bucket) { - return "", BucketNotFound{Bucket: bucket} - } - if !IsValidObjectName(object) { - return "", ObjectNameInvalid{ - Bucket: bucket, - Object: object, - } - } - // No metadata is set, allocate a new one. - if metadata == nil { - metadata = make(map[string]string) - } - nsMutex.Lock(bucket, object) - defer nsMutex.Unlock(bucket, object) - - tempObj := path.Join(tmpMetaPrefix, bucket, object) - fileWriter, err := xl.storage.CreateFile(minioMetaBucket, tempObj) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Initialize md5 writer. - md5Writer := md5.New() - - // Instantiate a new multi writer. - multiWriter := io.MultiWriter(md5Writer, fileWriter) - - // Instantiate checksum hashers and create a multiwriter. - if size > 0 { - if _, err = io.CopyN(multiWriter, data, size); err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - } else { - if _, err = io.Copy(multiWriter, data); err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - } - - newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) - // Update the md5sum if not set with the newly calculated one. - if len(metadata["md5Sum"]) == 0 { - metadata["md5Sum"] = newMD5Hex - } - - // md5Hex representation. - md5Hex := metadata["md5Sum"] - if md5Hex != "" { - if newMD5Hex != md5Hex { - if err = safeCloseAndRemove(fileWriter); err != nil { - return "", toObjectErr(err, bucket, object) - } - return "", BadDigest{md5Hex, newMD5Hex} - } - } - - err = fileWriter.Close() - if err != nil { - if clErr := safeCloseAndRemove(fileWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - - // Check if an object is present as one of the parent dir. - if err = xl.parentDirIsObject(bucket, path.Dir(object)); err != nil { - return "", toObjectErr(err, bucket, object) - } - - // Delete if an object already exists. - // FIXME: rename it to tmp file and delete only after - // the newly uploaded file is renamed from tmp location to - // the original location. - // Verify if the object is a multipart object. - if isMultipartObject(xl.storage, bucket, object) { - err = xl.deleteMultipartObject(bucket, object) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - } else { - err = xl.deleteObject(bucket, object) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - } - - err = xl.storage.RenameFile(minioMetaBucket, tempObj, bucket, object) - if err != nil { - if dErr := xl.storage.DeleteFile(minioMetaBucket, tempObj); dErr != nil { - return "", toObjectErr(dErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - - tempMetaJSONFile := path.Join(tmpMetaPrefix, bucket, object, "meta.json") - metaWriter, err := xl.storage.CreateFile(minioMetaBucket, tempMetaJSONFile) - if err != nil { - return "", toObjectErr(err, bucket, object) - } - - encoder := json.NewEncoder(metaWriter) - err = encoder.Encode(&metadata) - if err != nil { - if clErr := safeCloseAndRemove(metaWriter); clErr != nil { - return "", toObjectErr(clErr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - if err = metaWriter.Close(); err != nil { - if err = safeCloseAndRemove(metaWriter); err != nil { - return "", toObjectErr(err, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - - metaJSONFile := path.Join(object, "meta.json") - err = xl.storage.RenameFile(minioMetaBucket, tempMetaJSONFile, bucket, metaJSONFile) - if err != nil { - if derr := xl.storage.DeleteFile(minioMetaBucket, tempMetaJSONFile); derr != nil { - return "", toObjectErr(derr, bucket, object) - } - return "", toObjectErr(err, bucket, object) - } - - // Return md5sum, successfully wrote object. - return newMD5Hex, nil -} - -// isMultipartObject - verifies if an object is special multipart file. -func isMultipartObject(storage StorageAPI, bucket, object string) bool { - _, err := storage.StatFile(bucket, pathJoin(object, multipartMetaFile)) - if err != nil { - if err == errFileNotFound { - return false - } - errorIf(err, "Failed to stat file "+bucket+pathJoin(object, multipartMetaFile)) - return false - } - return true -} - -// deleteMultipartObject - deletes only multipart object. -func (xl xlObjects) deleteMultipartObject(bucket, object string) error { - // Get parts info. - info, err := getMultipartObjectInfo(xl.storage, bucket, object) - if err != nil { - return err - } - // Range through all files and delete it. - var wg = &sync.WaitGroup{} - var errs = make([]error, len(info.Parts)) - for index, part := range info.Parts { - wg.Add(1) - // Start deleting parts in routine. - go func(index int, part MultipartPartInfo) { - defer wg.Done() - partFileName := partNumToPartFileName(part.PartNumber) - errs[index] = xl.storage.DeleteFile(bucket, pathJoin(object, partFileName)) - }(index, part) - } - // Wait for all the deletes to finish. - wg.Wait() - // Loop through and validate if any errors, if we are unable to remove any part return - // "unexpected" error as returning any other error might be misleading. For ex. - // if DeleteFile() had returned errFileNotFound and we return it, then client would see - // ObjectNotFound which is misleading. - for _, err := range errs { - if err != nil { - return errUnexpected - } - } - err = xl.storage.DeleteFile(bucket, pathJoin(object, multipartMetaFile)) - if err != nil { - return err - } - return nil -} - -// deleteObject - deletes a regular object. -func (xl xlObjects) deleteObject(bucket, object string) error { - metaJSONFile := path.Join(object, "meta.json") - // Ignore if meta.json file doesn't exist. - if err := xl.storage.DeleteFile(bucket, metaJSONFile); err != nil { - if err != errFileNotFound { - return err - } - } - if err := xl.storage.DeleteFile(bucket, object); err != nil { - if err != errFileNotFound { - return err - } - } - return nil -} - -// DeleteObject - delete the object. -func (xl xlObjects) DeleteObject(bucket, object string) error { - // Verify if bucket is valid. - if !IsValidBucketName(bucket) { - return BucketNameInvalid{Bucket: bucket} - } - if !IsValidObjectName(object) { - return ObjectNameInvalid{Bucket: bucket, Object: object} - } - nsMutex.Lock(bucket, object) - defer nsMutex.Unlock(bucket, object) - // Verify if the object is a multipart object. - if isMultipartObject(xl.storage, bucket, object) { - err := xl.deleteMultipartObject(bucket, object) - if err != nil { - return toObjectErr(err, bucket, object) - } - return nil - } - err := xl.deleteObject(bucket, object) - if err != nil { - return toObjectErr(err, bucket, object) - } - return nil -} - -// ListObjects - list all objects at prefix, delimited by '/'. -func (xl xlObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { - return listObjectsCommon(xl, bucket, prefix, marker, delimiter, maxKeys) -} diff --git a/xl-v1-bucket.go b/xl-v1-bucket.go new file mode 100644 index 000000000..26e8aa3ff --- /dev/null +++ b/xl-v1-bucket.go @@ -0,0 +1,249 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "sort" + "sync" +) + +/// Bucket operations + +// MakeBucket - make a bucket. +func (xl xlObjects) MakeBucket(bucket string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + + nsMutex.Lock(bucket, "") + defer nsMutex.Unlock(bucket, "") + + // Err counters. + createVolErr := 0 // Count generic create vol errs. + volumeExistsErrCnt := 0 // Count all errVolumeExists errs. + + // Initialize sync waitgroup. + var wg = &sync.WaitGroup{} + + // Initialize list of errors. + var dErrs = make([]error, len(xl.storageDisks)) + + // Make a volume entry on all underlying storage disks. + for index, disk := range xl.storageDisks { + if disk == nil { + dErrs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Make a volume inside a go-routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + err := disk.MakeVol(bucket) + if err != nil { + dErrs[index] = err + return + } + dErrs[index] = nil + }(index, disk) + } + + // Wait for all make vol to finish. + wg.Wait() + + // Look for specific errors and count them to be verified later. + for _, err := range dErrs { + if err == nil { + continue + } + // if volume already exists, count them. + if err == errVolumeExists { + volumeExistsErrCnt++ + continue + } + + // Update error counter separately. + createVolErr++ + } + + // Return err if all disks report volume exists. + if volumeExistsErrCnt > len(xl.storageDisks)-xl.readQuorum { + return toObjectErr(errVolumeExists, bucket) + } else if createVolErr > len(xl.storageDisks)-xl.writeQuorum { + // Return errXLWriteQuorum if errors were more than allowed write quorum. + return toObjectErr(errXLWriteQuorum, bucket) + } + return nil +} + +// getBucketInfo - returns the BucketInfo from one of the load balanced disks. +func (xl xlObjects) getBucketInfo(bucketName string) (bucketInfo BucketInfo, err error) { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + var volInfo VolInfo + volInfo, err = disk.StatVol(bucketName) + if err != nil { + // For some reason disk went offline pick the next one. + if err == errDiskNotFound { + continue + } + return BucketInfo{}, err + } + bucketInfo = BucketInfo{ + Name: volInfo.Name, + Created: volInfo.Created, + } + break + } + return bucketInfo, nil +} + +// Checks whether bucket exists. +func (xl xlObjects) isBucketExist(bucket string) bool { + nsMutex.RLock(bucket, "") + defer nsMutex.RUnlock(bucket, "") + + // Check whether bucket exists. + _, err := xl.getBucketInfo(bucket) + if err != nil { + if err == errVolumeNotFound { + return false + } + errorIf(err, "Stat failed on bucket "+bucket+".") + return false + } + return true +} + +// GetBucketInfo - returns BucketInfo for a bucket. +func (xl xlObjects) GetBucketInfo(bucket string) (BucketInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketInfo{}, BucketNameInvalid{Bucket: bucket} + } + nsMutex.RLock(bucket, "") + defer nsMutex.RUnlock(bucket, "") + bucketInfo, err := xl.getBucketInfo(bucket) + if err != nil { + return BucketInfo{}, toObjectErr(err, bucket) + } + return bucketInfo, nil +} + +// listBuckets - returns list of all buckets from a disk picked at random. +func (xl xlObjects) listBuckets() (bucketsInfo []BucketInfo, err error) { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + var volsInfo []VolInfo + volsInfo, err = disk.ListVols() + if err == nil { + // NOTE: The assumption here is that volumes across all disks in + // readQuorum have consistent view i.e they all have same number + // of buckets. This is essentially not verified since healing + // should take care of this. + var bucketsInfo []BucketInfo + for _, volInfo := range volsInfo { + // StorageAPI can send volume names which are incompatible + // with buckets, handle it and skip them. + if !IsValidBucketName(volInfo.Name) { + continue + } + bucketsInfo = append(bucketsInfo, BucketInfo{ + Name: volInfo.Name, + Created: volInfo.Created, + }) + } + return bucketsInfo, nil + } + break + } + return nil, err +} + +// ListBuckets - lists all the buckets, sorted by its name. +func (xl xlObjects) ListBuckets() ([]BucketInfo, error) { + bucketInfos, err := xl.listBuckets() + if err != nil { + return nil, toObjectErr(err) + } + // Sort by bucket name before returning. + sort.Sort(byBucketName(bucketInfos)) + return bucketInfos, nil +} + +// DeleteBucket - deletes a bucket. +func (xl xlObjects) DeleteBucket(bucket string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + + nsMutex.Lock(bucket, "") + defer nsMutex.Unlock(bucket, "") + + // Collect if all disks report volume not found. + var volumeNotFoundErrCnt int + + var wg = &sync.WaitGroup{} + var dErrs = make([]error, len(xl.storageDisks)) + + // Remove a volume entry on all underlying storage disks. + for index, disk := range xl.storageDisks { + if disk == nil { + dErrs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Delete volume inside a go-routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + err := disk.DeleteVol(bucket) + if err != nil { + dErrs[index] = err + return + } + dErrs[index] = nil + }(index, disk) + } + + // Wait for all the delete vols to finish. + wg.Wait() + + // Count the errors for known errors, return quickly if we found + // an unknown error. + for _, err := range dErrs { + if err != nil { + // We ignore error if errVolumeNotFound or errDiskNotFound + if err == errVolumeNotFound || err == errDiskNotFound { + volumeNotFoundErrCnt++ + continue + } + return toObjectErr(err, bucket) + } + } + + // Return errVolumeNotFound if all disks report volume not found. + if volumeNotFoundErrCnt == len(xl.storageDisks) { + return toObjectErr(errVolumeNotFound, bucket) + } + + return nil +} diff --git a/xl-v1-common.go b/xl-v1-common.go new file mode 100644 index 000000000..446e8ffa2 --- /dev/null +++ b/xl-v1-common.go @@ -0,0 +1,86 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import "path" + +// getLoadBalancedQuorumDisks - fetches load balanced sufficiently +// randomized quorum disk slice. +func (xl xlObjects) getLoadBalancedQuorumDisks() (disks []StorageAPI) { + // It is okay to have readQuorum disks. + return xl.getLoadBalancedDisks()[:xl.readQuorum-1] +} + +// getLoadBalancedDisks - fetches load balanced (sufficiently +// randomized) disk slice. +func (xl xlObjects) getLoadBalancedDisks() (disks []StorageAPI) { + // Based on the random shuffling return back randomized disks. + for _, i := range randInts(len(xl.storageDisks)) { + disks = append(disks, xl.storageDisks[i-1]) + } + return disks +} + +// This function does the following check, suppose +// object is "a/b/c/d", stat makes sure that objects ""a/b/c"" +// "a/b" and "a" do not exist. +func (xl xlObjects) parentDirIsObject(bucket, parent string) bool { + var isParentDirObject func(string) bool + isParentDirObject = func(p string) bool { + if p == "." { + return false + } + if xl.isObject(bucket, p) { + // If there is already a file at prefix "p" return error. + return true + } + // Check if there is a file as one of the parent paths. + return isParentDirObject(path.Dir(p)) + } + return isParentDirObject(parent) +} + +// isObject - returns `true` if the prefix is an object i.e if +// `xl.json` exists at the leaf, false otherwise. +func (xl xlObjects) isObject(bucket, prefix string) bool { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + _, err := disk.StatFile(bucket, path.Join(prefix, xlMetaJSONFile)) + if err != nil { + return false + } + break + } + return true +} + +// statPart - returns fileInfo structure for a successful stat on part file. +func (xl xlObjects) statPart(bucket, objectPart string) (fileInfo FileInfo, err error) { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + fileInfo, err = disk.StatFile(bucket, objectPart) + if err != nil { + return FileInfo{}, err + } + break + } + return fileInfo, nil +} diff --git a/xl-erasure-v1-common.go b/xl-v1-healing.go similarity index 52% rename from xl-erasure-v1-common.go rename to xl-v1-healing.go index 663c26878..4adb05112 100644 --- a/xl-erasure-v1-common.go +++ b/xl-v1-healing.go @@ -1,30 +1,14 @@ -/* - * Minio Cloud Storage, (C) 2016 Minio, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package main import ( - "errors" - slashpath "path" + "encoding/json" + "path" "sync" ) // Get the highest integer from a given integer slice. func highestInt(intSlice []int64) (highestInteger int64) { - highestInteger = int64(0) + highestInteger = int64(1) for _, integer := range intSlice { if highestInteger < integer { highestInteger = integer @@ -33,8 +17,8 @@ func highestInt(intSlice []int64) (highestInteger int64) { return highestInteger } -// Extracts file versions from partsMetadata slice and returns version slice. -func listFileVersions(partsMetadata []xlMetaV1, errs []error) (versions []int64) { +// Extracts objects versions from xlMetaV1 slice and returns version slice. +func listObjectVersions(partsMetadata []xlMetaV1, errs []error) (versions []int64) { versions = make([]int64, len(partsMetadata)) for index, metadata := range partsMetadata { if errs[index] == nil { @@ -46,16 +30,57 @@ func listFileVersions(partsMetadata []xlMetaV1, errs []error) (versions []int64) return versions } -// reduceError - convert collection of errors into a single +// Reads all `xl.json` metadata as a xlMetaV1 slice. +// Returns error slice indicating the failed metadata reads. +func (xl xlObjects) readAllXLMetadata(bucket, object string) ([]xlMetaV1, []error) { + errs := make([]error, len(xl.storageDisks)) + metadataArray := make([]xlMetaV1, len(xl.storageDisks)) + xlMetaPath := path.Join(object, xlMetaJSONFile) + var wg = &sync.WaitGroup{} + for index, disk := range xl.storageDisks { + if disk == nil { + errs[index] = errDiskNotFound + continue + } + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + buffer, err := readAll(disk, bucket, xlMetaPath) + if err != nil { + errs[index] = err + return + } + err = json.Unmarshal(buffer, &metadataArray[index]) + if err != nil { + // Unable to parse xl.json, set error. + errs[index] = err + return + } + // Relinquish buffer. + buffer = nil + errs[index] = nil + }(index, disk) + } + + // Wait for all the routines to finish. + wg.Wait() + + // Return all the metadata. + return metadataArray, errs +} + // error based on total errors and read quorum. -func (xl XL) reduceError(errs []error) error { +func (xl xlObjects) reduceError(errs []error) error { fileNotFoundCount := 0 + longNameCount := 0 diskNotFoundCount := 0 volumeNotFoundCount := 0 diskAccessDeniedCount := 0 for _, err := range errs { if err == errFileNotFound { fileNotFoundCount++ + } else if err == errFileNameTooLong { + longNameCount++ } else if err == errDiskNotFound { diskNotFoundCount++ } else if err == errVolumeAccessDenied { @@ -66,10 +91,12 @@ func (xl XL) reduceError(errs []error) error { } // If we have errors with 'file not found' greater than // readQuorum, return as errFileNotFound. - // else if we have errors with 'volume not found' greater than - // readQuorum, return as errVolumeNotFound. + // else if we have errors with 'volume not found' + // greater than readQuorum, return as errVolumeNotFound. if fileNotFoundCount > len(xl.storageDisks)-xl.readQuorum { return errFileNotFound + } else if longNameCount > len(xl.storageDisks)-xl.readQuorum { + return errFileNameTooLong } else if volumeNotFoundCount > len(xl.storageDisks)-xl.readQuorum { return errVolumeNotFound } @@ -78,8 +105,8 @@ func (xl XL) reduceError(errs []error) error { if diskNotFoundCount == len(xl.storageDisks) { return errDiskNotFound } else if diskNotFoundCount > len(xl.storageDisks)-xl.readQuorum { - // If we have errors with 'disk not found' greater than - // readQuorum, return as errFileNotFound. + // If we have errors with 'disk not found' + // greater than readQuorum, return as errFileNotFound. return errFileNotFound } // If we have errors with disk not found equal to the @@ -90,36 +117,21 @@ func (xl XL) reduceError(errs []error) error { return nil } -// Returns slice of online disks needed. -// - slice returing readable disks. -// - xlMetaV1 -// - bool value indicating if healing is needed. -// - error if any. -func (xl XL) listOnlineDisks(volume, path string) (onlineDisks []StorageAPI, mdata xlMetaV1, heal bool, err error) { - partsMetadata, errs := xl.getPartsMetadata(volume, path) - if err = xl.reduceError(errs); err != nil { - return nil, xlMetaV1{}, false, err - } - highestVersion := int64(0) - onlineDisks = make([]StorageAPI, len(xl.storageDisks)) - // List all the file versions from partsMetadata list. - versions := listFileVersions(partsMetadata, errs) - - // Get highest file version. - highestVersion = highestInt(versions) - - // Pick online disks with version set to highestVersion. - onlineDiskCount := 0 - for index, version := range versions { - if version == highestVersion { - mdata = partsMetadata[index] - onlineDisks[index] = xl.storageDisks[index] - onlineDiskCount++ - } else { - onlineDisks[index] = nil +// Similar to 'len(slice)' but returns the actualelements count +// skipping the unallocated elements. +func diskCount(disks []StorageAPI) int { + diskCount := 0 + for _, disk := range disks { + if disk == nil { + continue } + diskCount++ } + return diskCount +} +func (xl xlObjects) shouldHeal(onlineDisks []StorageAPI) (heal bool) { + onlineDiskCount := diskCount(onlineDisks) // If online disks count is lesser than configured disks, most // probably we need to heal the file, additionally verify if the // count is lesser than readQuorum, if not we throw an error. @@ -128,77 +140,45 @@ func (xl XL) listOnlineDisks(volume, path string) (onlineDisks []StorageAPI, mda // healed. unless we do not have readQuorum. heal = true // Verify if online disks count are lesser than readQuorum - // threshold, return an error if yes. + // threshold, return an error. if onlineDiskCount < xl.readQuorum { - return nil, xlMetaV1{}, false, errReadQuorum + errorIf(errXLReadQuorum, "Unable to establish read quorum, disks are offline.") + return false } } - return onlineDisks, mdata, heal, nil + return heal } -// Get file.json metadata as a map slice. -// Returns error slice indicating the failed metadata reads. -// Read lockNS() should be done by caller. -func (xl XL) getPartsMetadata(volume, path string) ([]xlMetaV1, []error) { - errs := make([]error, len(xl.storageDisks)) - metadataArray := make([]xlMetaV1, len(xl.storageDisks)) - xlMetaV1FilePath := slashpath.Join(path, xlMetaV1File) - var wg = &sync.WaitGroup{} - for index, disk := range xl.storageDisks { - wg.Add(1) - go func(index int, disk StorageAPI) { - defer wg.Done() - offset := int64(0) - metadataReader, err := disk.ReadFile(volume, xlMetaV1FilePath, offset) - if err != nil { - errs[index] = err - return - } - defer metadataReader.Close() - - metadata, err := xlMetaV1Decode(metadataReader) - if err != nil { - // Unable to parse file.json, set error. - errs[index] = err - return - } - metadataArray[index] = metadata - }(index, disk) +// Returns slice of online disks needed. +// - slice returing readable disks. +// - xlMetaV1 +// - bool value indicating if healing is needed. +// - error if any. +func (xl xlObjects) listOnlineDisks(partsMetadata []xlMetaV1, errs []error) (onlineDisks []StorageAPI, version int64, err error) { + onlineDisks = make([]StorageAPI, len(xl.storageDisks)) + if err = xl.reduceError(errs); err != nil { + if err == errFileNotFound { + // For file not found, treat as if disks are available + // return all the configured ones. + onlineDisks = xl.storageDisks + return onlineDisks, 1, nil + } + return nil, 0, err } - wg.Wait() - return metadataArray, errs -} - -// Writes/Updates `file.json` for given file. updateParts carries -// index of disks where `file.json` needs to be updated. -// -// Returns collection of errors, indexed in accordance with input -// updateParts order. -// Write lockNS() should be done by caller. -func (xl XL) updatePartsMetadata(volume, path string, metadata xlMetaV1, updateParts []bool) []error { - xlMetaV1FilePath := pathJoin(path, xlMetaV1File) - errs := make([]error, len(xl.storageDisks)) - - for index := range updateParts { - errs[index] = errors.New("Metadata not updated") - } - - for index, shouldUpdate := range updateParts { - if !shouldUpdate { - continue - } - writer, err := xl.storageDisks[index].CreateFile(volume, xlMetaV1FilePath) - errs[index] = err - if err != nil { - continue - } - err = metadata.Write(writer) - if err != nil { - errs[index] = err - safeCloseAndRemove(writer) - continue - } - writer.Close() - } - return errs + highestVersion := int64(0) + // List all the file versions from partsMetadata list. + versions := listObjectVersions(partsMetadata, errs) + + // Get highest object version. + highestVersion = highestInt(versions) + + // Pick online disks with version set to highestVersion. + for index, version := range versions { + if version == highestVersion { + onlineDisks[index] = xl.storageDisks[index] + } else { + onlineDisks[index] = nil + } + } + return onlineDisks, highestVersion, nil } diff --git a/xl-v1-list-objects.go b/xl-v1-list-objects.go new file mode 100644 index 000000000..2d5e8a71e --- /dev/null +++ b/xl-v1-list-objects.go @@ -0,0 +1,153 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import "strings" + +// listObjects - wrapper function implemented over file tree walk. +func (xl xlObjects) listObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { + // Default is recursive, if delimiter is set then list non recursive. + recursive := true + if delimiter == slashSeparator { + recursive = false + } + + walker := xl.lookupTreeWalk(listParams{bucket, recursive, marker, prefix}) + if walker == nil { + walker = xl.startTreeWalk(bucket, prefix, marker, recursive, xl.isObject) + } + var objInfos []ObjectInfo + var eof bool + var nextMarker string + for i := 0; i < maxKeys; { + walkResult, ok := <-walker.ch + if !ok { + // Closed channel. + eof = true + break + } + // For any walk error return right away. + if walkResult.err != nil { + // File not found is a valid case. + if walkResult.err == errFileNotFound { + return ListObjectsInfo{}, nil + } + return ListObjectsInfo{}, toObjectErr(walkResult.err, bucket, prefix) + } + entry := walkResult.entry + var objInfo ObjectInfo + if strings.HasSuffix(entry, slashSeparator) { + // Object name needs to be full path. + objInfo.Bucket = bucket + objInfo.Name = entry + objInfo.IsDir = true + } else { + // Set the Mode to a "regular" file. + var err error + objInfo, err = xl.getObjectInfo(bucket, entry) + if err != nil { + return ListObjectsInfo{}, toObjectErr(err, bucket, prefix) + } + } + + nextMarker = objInfo.Name + objInfos = append(objInfos, objInfo) + if walkResult.end { + eof = true + break + } + i++ + } + params := listParams{bucket, recursive, nextMarker, prefix} + if !eof { + xl.saveTreeWalk(params, walker) + } + + result := ListObjectsInfo{IsTruncated: !eof} + for _, objInfo := range objInfos { + result.NextMarker = objInfo.Name + if objInfo.IsDir { + result.Prefixes = append(result.Prefixes, objInfo.Name) + continue + } + result.Objects = append(result.Objects, ObjectInfo{ + Name: objInfo.Name, + ModTime: objInfo.ModTime, + Size: objInfo.Size, + IsDir: false, + }) + } + return result, nil +} + +// ListObjects - list all objects at prefix, delimited by '/'. +func (xl xlObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ListObjectsInfo{}, BucketNameInvalid{Bucket: bucket} + } + // Verify if bucket exists. + if !xl.isBucketExist(bucket) { + return ListObjectsInfo{}, BucketNotFound{Bucket: bucket} + } + if !IsValidObjectPrefix(prefix) { + return ListObjectsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} + } + // Verify if delimiter is anything other than '/', which we do not support. + if delimiter != "" && delimiter != slashSeparator { + return ListObjectsInfo{}, UnsupportedDelimiter{ + Delimiter: delimiter, + } + } + // Verify if marker has prefix. + if marker != "" { + if !strings.HasPrefix(marker, prefix) { + return ListObjectsInfo{}, InvalidMarkerPrefixCombination{ + Marker: marker, + Prefix: prefix, + } + } + } + + // With max keys of zero we have reached eof, return right here. + if maxKeys == 0 { + return ListObjectsInfo{}, nil + } + + // For delimiter and prefix as '/' we do not list anything at all + // since according to s3 spec we stop at the 'delimiter' along + // with the prefix. On a flat namespace with 'prefix' as '/' + // we don't have any entries, since all the keys are of form 'keyName/...' + if delimiter == slashSeparator && prefix == slashSeparator { + return ListObjectsInfo{}, nil + } + + // Over flowing count - reset to maxObjectList. + if maxKeys < 0 || maxKeys > maxObjectList { + maxKeys = maxObjectList + } + + // Initiate a list operation, if successful filter and return quickly. + listObjInfo, err := xl.listObjects(bucket, prefix, marker, delimiter, maxKeys) + if err == nil { + // We got the entries successfully return. + return listObjInfo, nil + } + + // Return error at the end. + return ListObjectsInfo{}, toObjectErr(err, bucket, prefix) +} diff --git a/xl-v1-metadata.go b/xl-v1-metadata.go new file mode 100644 index 000000000..066e4ef0e --- /dev/null +++ b/xl-v1-metadata.go @@ -0,0 +1,451 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "encoding/json" + "path" + "sort" + "sync" + "time" +) + +const ( + // Erasure related constants. + erasureAlgorithmKlauspost = "klauspost/reedsolomon/vandermonde" + erasureAlgorithmISAL = "isa-l/reedsolomon/cauchy" +) + +// objectPartInfo Info of each part kept in the multipart metadata +// file after CompleteMultipartUpload() is called. +type objectPartInfo struct { + Number int `json:"number"` + Name string `json:"name"` + ETag string `json:"etag"` + Size int64 `json:"size"` +} + +// byObjectPartNumber is a collection satisfying sort.Interface. +type byObjectPartNumber []objectPartInfo + +func (t byObjectPartNumber) Len() int { return len(t) } +func (t byObjectPartNumber) Swap(i, j int) { t[i], t[j] = t[j], t[i] } +func (t byObjectPartNumber) Less(i, j int) bool { return t[i].Number < t[j].Number } + +// checkSumInfo - carries checksums of individual scattered parts per disk. +type checkSumInfo struct { + Name string `json:"name"` + Algorithm string `json:"algorithm"` + Hash string `json:"hash"` +} + +// erasureInfo - carries erasure coding related information, block +// distribution and checksums. +type erasureInfo struct { + Algorithm string `json:"algorithm"` + DataBlocks int `json:"data"` + ParityBlocks int `json:"parity"` + BlockSize int64 `json:"blockSize"` + Index int `json:"index"` + Distribution []int `json:"distribution"` + Checksum []checkSumInfo `json:"checksum,omitempty"` +} + +// IsValid - tells if the erasure info is sane by validating the data +// blocks, parity blocks and distribution. +func (e erasureInfo) IsValid() bool { + return e.DataBlocks != 0 && e.ParityBlocks != 0 && len(e.Distribution) != 0 +} + +// pickValidErasureInfo - picks one valid erasure info content and returns, from a +// slice of erasure info content. If no value is found this function panics +// and dies. +func pickValidErasureInfo(eInfos []erasureInfo) erasureInfo { + for _, eInfo := range eInfos { + if eInfo.IsValid() { + return eInfo + } + } + panic("Unable to look for valid erasure info content") +} + +// statInfo - carries stat information of the object. +type statInfo struct { + Size int64 `json:"size"` // Size of the object `xl.json`. + ModTime time.Time `json:"modTime"` // ModTime of the object `xl.json`. + Version int64 `json:"version"` // Version of the object `xl.json`, useful to calculate quorum. +} + +// A xlMetaV1 represents `xl.json` metadata header. +type xlMetaV1 struct { + Version string `json:"version"` // Version of the current `xl.json`. + Format string `json:"format"` // Format of the current `xl.json`. + Stat statInfo `json:"stat"` // Stat of the current object `xl.json`. + // Erasure coded info for the current object `xl.json`. + Erasure erasureInfo `json:"erasure"` + // Minio release tag for current object `xl.json`. + Minio struct { + Release string `json:"release"` + } `json:"minio"` + // Metadata map for current object `xl.json`. + Meta map[string]string `json:"meta"` + // Captures all the individual object `xl.json`. + Parts []objectPartInfo `json:"parts,omitempty"` +} + +// newXLMetaV1 - initializes new xlMetaV1, adds version, allocates a +// fresh erasure info. +func newXLMetaV1(dataBlocks, parityBlocks int) (xlMeta xlMetaV1) { + xlMeta = xlMetaV1{} + xlMeta.Version = "1" + xlMeta.Format = "xl" + xlMeta.Minio.Release = minioReleaseTag + xlMeta.Erasure = erasureInfo{ + Algorithm: erasureAlgorithmKlauspost, + DataBlocks: dataBlocks, + ParityBlocks: parityBlocks, + BlockSize: blockSizeV1, + Distribution: randInts(dataBlocks + parityBlocks), + } + return xlMeta +} + +// IsValid - tells if the format is sane by validating the version +// string and format style. +func (m xlMetaV1) IsValid() bool { + return m.Version == "1" && m.Format == "xl" +} + +// ObjectPartIndex - returns the index of matching object part number. +func (m xlMetaV1) ObjectPartIndex(partNumber int) (index int) { + for i, part := range m.Parts { + if partNumber == part.Number { + index = i + return index + } + } + return -1 +} + +// AddObjectPart - add a new object part in order. +func (m *xlMetaV1) AddObjectPart(partNumber int, partName string, partETag string, partSize int64) { + partInfo := objectPartInfo{ + Number: partNumber, + Name: partName, + ETag: partETag, + Size: partSize, + } + + // Update part info if it already exists. + for i, part := range m.Parts { + if partNumber == part.Number { + m.Parts[i] = partInfo + return + } + } + + // Proceed to include new part info. + m.Parts = append(m.Parts, partInfo) + + // Parts in xlMeta should be in sorted order by part number. + sort.Sort(byObjectPartNumber(m.Parts)) +} + +// ObjectToPartOffset - translate offset of an object to offset of its individual part. +func (m xlMetaV1) ObjectToPartOffset(offset int64) (partIndex int, partOffset int64, err error) { + partOffset = offset + // Seek until object offset maps to a particular part offset. + for i, part := range m.Parts { + partIndex = i + // Last part can be of '0' bytes, treat it specially and + // return right here. + if part.Size == 0 { + return partIndex, partOffset, nil + } + // Offset is smaller than size we have reached the proper part offset. + if partOffset < part.Size { + return partIndex, partOffset, nil + } + // Continue to towards the next part. + partOffset -= part.Size + } + // Offset beyond the size of the object return InvalidRange. + return 0, 0, InvalidRange{} +} + +// pickValidXLMeta - picks one valid xlMeta content and returns from a +// slice of xlmeta content. If no value is found this function panics +// and dies. +func pickValidXLMeta(xlMetas []xlMetaV1) xlMetaV1 { + for _, xlMeta := range xlMetas { + if xlMeta.IsValid() { + return xlMeta + } + } + panic("Unable to look for valid XL metadata content") +} + +// readXLMetadata - returns the object metadata `xl.json` content from +// one of the disks picked at random. +func (xl xlObjects) readXLMetadata(bucket, object string) (xlMeta xlMetaV1, err error) { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + var buf []byte + buf, err = readAll(disk, bucket, path.Join(object, xlMetaJSONFile)) + if err != nil { + return xlMetaV1{}, err + } + err = json.Unmarshal(buf, &xlMeta) + if err != nil { + return xlMetaV1{}, err + } + break + } + return xlMeta, nil +} + +// renameXLMetadata - renames `xl.json` from source prefix to destination prefix. +func (xl xlObjects) renameXLMetadata(srcBucket, srcPrefix, dstBucket, dstPrefix string) error { + var wg = &sync.WaitGroup{} + var mErrs = make([]error, len(xl.storageDisks)) + + srcJSONFile := path.Join(srcPrefix, xlMetaJSONFile) + dstJSONFile := path.Join(dstPrefix, xlMetaJSONFile) + // Rename `xl.json` to all disks in parallel. + for index, disk := range xl.storageDisks { + if disk == nil { + mErrs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Rename `xl.json` in a routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + // Renames `xl.json` from source prefix to destination prefix. + rErr := disk.RenameFile(srcBucket, srcJSONFile, dstBucket, dstJSONFile) + if rErr != nil { + mErrs[index] = rErr + return + } + // Delete any dangling directories. + dErr := disk.DeleteFile(srcBucket, srcPrefix) + if dErr != nil { + mErrs[index] = dErr + return + } + mErrs[index] = nil + }(index, disk) + } + // Wait for all the routines. + wg.Wait() + + // Gather err count. + var errCount = 0 + for _, err := range mErrs { + if err == nil { + continue + } + errCount++ + } + // We can safely allow RenameFile errors up to len(xl.storageDisks) - xl.writeQuorum + // otherwise return failure. Cleanup successful renames. + if errCount > len(xl.storageDisks)-xl.writeQuorum { + // Check we have successful read quorum. + if errCount <= len(xl.storageDisks)-xl.readQuorum { + return nil // Return success. + } // else - failed to acquire read quorum. + + // Undo rename `xl.json` on disks where RenameFile succeeded. + for index, disk := range xl.storageDisks { + if disk == nil { + continue + } + // Undo rename object in parallel. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + if mErrs[index] != nil { + return + } + _ = disk.RenameFile(dstBucket, dstJSONFile, srcBucket, srcJSONFile) + }(index, disk) + } + wg.Wait() + return errXLWriteQuorum + } + return nil +} + +// deleteXLMetadata - deletes `xl.json` on a single disk. +func deleteXLMetdata(disk StorageAPI, bucket, prefix string) error { + jsonFile := path.Join(prefix, xlMetaJSONFile) + return disk.DeleteFile(bucket, jsonFile) +} + +// writeXLMetadata - writes `xl.json` to a single disk. +func writeXLMetadata(disk StorageAPI, bucket, prefix string, xlMeta xlMetaV1) error { + jsonFile := path.Join(prefix, xlMetaJSONFile) + + // Marshal json. + metadataBytes, err := json.Marshal(&xlMeta) + if err != nil { + return err + } + // Persist marshalled data. + n, err := disk.AppendFile(bucket, jsonFile, metadataBytes) + if err != nil { + return err + } + if n != int64(len(metadataBytes)) { + return errUnexpected + } + return nil +} + +// writeUniqueXLMetadata - writes unique `xl.json` content for each disk in order. +func (xl xlObjects) writeUniqueXLMetadata(bucket, prefix string, xlMetas []xlMetaV1) error { + var wg = &sync.WaitGroup{} + var mErrs = make([]error, len(xl.storageDisks)) + + // Start writing `xl.json` to all disks in parallel. + for index, disk := range xl.storageDisks { + if disk == nil { + mErrs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Write `xl.json` in a routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + + // Pick one xlMeta for a disk at index. + xlMetas[index].Erasure.Index = index + 1 + + // Write unique `xl.json` for a disk at index. + if err := writeXLMetadata(disk, bucket, prefix, xlMetas[index]); err != nil { + mErrs[index] = err + return + } + mErrs[index] = nil + }(index, disk) + } + + // Wait for all the routines. + wg.Wait() + + var errCount = 0 + // Return the first error. + for _, err := range mErrs { + if err == nil { + continue + } + errCount++ + } + // Count all the errors and validate if we have write quorum. + if errCount > len(xl.storageDisks)-xl.writeQuorum { + // Validate if we have read quorum, then return success. + if errCount > len(xl.storageDisks)-xl.readQuorum { + return nil + } + // Delete all the `xl.json` left over. + for index, disk := range xl.storageDisks { + if disk == nil { + continue + } + // Undo rename object in parallel. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + if mErrs[index] != nil { + return + } + _ = deleteXLMetdata(disk, bucket, prefix) + }(index, disk) + } + wg.Wait() + return errXLWriteQuorum + } + return nil +} + +// writeSameXLMetadata - write `xl.json` on all disks in order. +func (xl xlObjects) writeSameXLMetadata(bucket, prefix string, xlMeta xlMetaV1) error { + var wg = &sync.WaitGroup{} + var mErrs = make([]error, len(xl.storageDisks)) + + // Start writing `xl.json` to all disks in parallel. + for index, disk := range xl.storageDisks { + if disk == nil { + mErrs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Write `xl.json` in a routine. + go func(index int, disk StorageAPI, metadata xlMetaV1) { + defer wg.Done() + + // Save the disk order index. + metadata.Erasure.Index = index + 1 + + // Write xl metadata. + if err := writeXLMetadata(disk, bucket, prefix, metadata); err != nil { + mErrs[index] = err + return + } + mErrs[index] = nil + }(index, disk, xlMeta) + } + + // Wait for all the routines. + wg.Wait() + + var errCount = 0 + // Return the first error. + for _, err := range mErrs { + if err == nil { + continue + } + errCount++ + } + // Count all the errors and validate if we have write quorum. + if errCount > len(xl.storageDisks)-xl.writeQuorum { + // Validate if we have read quorum, then return success. + if errCount > len(xl.storageDisks)-xl.readQuorum { + return nil + } + // Delete all the `xl.json` left over. + for index, disk := range xl.storageDisks { + if disk == nil { + continue + } + // Undo rename object in parallel. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + if mErrs[index] != nil { + return + } + _ = deleteXLMetdata(disk, bucket, prefix) + }(index, disk) + } + wg.Wait() + return errXLWriteQuorum + } + return nil +} diff --git a/xl-v1-multipart-common.go b/xl-v1-multipart-common.go new file mode 100644 index 000000000..a2a0214df --- /dev/null +++ b/xl-v1-multipart-common.go @@ -0,0 +1,417 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "encoding/json" + "path" + "sort" + "strings" + "sync" + "time" +) + +// A uploadInfo represents the s3 compatible spec. +type uploadInfo struct { + UploadID string `json:"uploadId"` // UploadID for the active multipart upload. + Deleted bool `json:"deleted"` // Currently unused, for future use. + Initiated time.Time `json:"initiated"` // Indicates when the uploadID was initiated. +} + +// A uploadsV1 represents `uploads.json` metadata header. +type uploadsV1 struct { + Version string `json:"version"` // Version of the current `uploads.json` + Format string `json:"format"` // Format of the current `uploads.json` + Uploads []uploadInfo `json:"uploadIds"` // Captures all the upload ids for a given object. +} + +// byInitiatedTime is a collection satisfying sort.Interface. +type byInitiatedTime []uploadInfo + +func (t byInitiatedTime) Len() int { return len(t) } +func (t byInitiatedTime) Swap(i, j int) { t[i], t[j] = t[j], t[i] } +func (t byInitiatedTime) Less(i, j int) bool { + return t[i].Initiated.Before(t[j].Initiated) +} + +// AddUploadID - adds a new upload id in order of its initiated time. +func (u *uploadsV1) AddUploadID(uploadID string, initiated time.Time) { + u.Uploads = append(u.Uploads, uploadInfo{ + UploadID: uploadID, + Initiated: initiated, + }) + sort.Sort(byInitiatedTime(u.Uploads)) +} + +// Index - returns the index of matching the upload id. +func (u uploadsV1) Index(uploadID string) int { + for i, u := range u.Uploads { + if u.UploadID == uploadID { + return i + } + } + return -1 +} + +// readUploadsJSON - get all the saved uploads JSON. +func readUploadsJSON(bucket, object string, disk StorageAPI) (uploadIDs uploadsV1, err error) { + uploadJSONPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) + // Read all of 'uploads.json' + buffer, rErr := readAll(disk, minioMetaBucket, uploadJSONPath) + if rErr != nil { + return uploadsV1{}, rErr + } + rErr = json.Unmarshal(buffer, &uploadIDs) + if rErr != nil { + return uploadsV1{}, rErr + } + return uploadIDs, nil +} + +// updateUploadsJSON - update `uploads.json` with new uploadsJSON for all disks. +func updateUploadsJSON(bucket, object string, uploadsJSON uploadsV1, storageDisks ...StorageAPI) error { + uploadsPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) + uniqueID := getUUID() + tmpUploadsPath := path.Join(tmpMetaPrefix, uniqueID) + var errs = make([]error, len(storageDisks)) + var wg = &sync.WaitGroup{} + + // Update `uploads.json` for all the disks. + for index, disk := range storageDisks { + if disk == nil { + errs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Update `uploads.json` in routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + uploadsBytes, wErr := json.Marshal(uploadsJSON) + if wErr != nil { + errs[index] = wErr + return + } + n, wErr := disk.AppendFile(minioMetaBucket, tmpUploadsPath, uploadsBytes) + if wErr != nil { + errs[index] = wErr + return + } + if n != int64(len(uploadsBytes)) { + errs[index] = errUnexpected + return + } + if wErr = disk.RenameFile(minioMetaBucket, tmpUploadsPath, minioMetaBucket, uploadsPath); wErr != nil { + errs[index] = wErr + return + } + }(index, disk) + } + + // Wait for all the routines to finish updating `uploads.json` + wg.Wait() + + // For only single disk return first error. + if len(storageDisks) == 1 { + return errs[0] + } // else count all the errors for quorum validation. + var errCount = 0 + // Return for first error. + for _, err := range errs { + if err != nil { + errCount++ + } + } + // Count all the errors and validate if we have write quorum. + if errCount > len(storageDisks)-len(storageDisks)/2+3 { + // Validate if we have read quorum return success. + if errCount > len(storageDisks)-len(storageDisks)/2+1 { + return nil + } + // Rename `uploads.json` left over back to tmp location. + for index, disk := range storageDisks { + if disk == nil { + continue + } + // Undo rename `uploads.json` in parallel. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + if errs[index] != nil { + return + } + _ = disk.RenameFile(minioMetaBucket, uploadsPath, minioMetaBucket, tmpUploadsPath) + }(index, disk) + } + wg.Wait() + return errXLWriteQuorum + } + return nil +} + +// newUploadsV1 - initialize new uploads v1. +func newUploadsV1(format string) uploadsV1 { + uploadIDs := uploadsV1{} + uploadIDs.Version = "1" + uploadIDs.Format = format + return uploadIDs +} + +// writeUploadJSON - create `uploads.json` or update it with new uploadID. +func writeUploadJSON(bucket, object, uploadID string, initiated time.Time, storageDisks ...StorageAPI) (err error) { + uploadsPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) + uniqueID := getUUID() + tmpUploadsPath := path.Join(tmpMetaPrefix, uniqueID) + + var errs = make([]error, len(storageDisks)) + var wg = &sync.WaitGroup{} + + var uploadsJSON uploadsV1 + for _, disk := range storageDisks { + if disk == nil { + continue + } + uploadsJSON, err = readUploadsJSON(bucket, object, disk) + break + } + if err != nil { + // For any other errors. + if err != errFileNotFound { + return err + } + if len(storageDisks) == 1 { + // Set uploads format to `fs` for single disk. + uploadsJSON = newUploadsV1("fs") + } else { + // Set uploads format to `xl` otherwise. + uploadsJSON = newUploadsV1("xl") + } + } + // Add a new upload id. + uploadsJSON.AddUploadID(uploadID, initiated) + + // Update `uploads.json` on all disks. + for index, disk := range storageDisks { + if disk == nil { + errs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Update `uploads.json` in a routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + uploadsJSONBytes, wErr := json.Marshal(&uploadsJSON) + if wErr != nil { + errs[index] = wErr + return + } + // Write `uploads.json` to disk. + n, wErr := disk.AppendFile(minioMetaBucket, tmpUploadsPath, uploadsJSONBytes) + if wErr != nil { + errs[index] = wErr + return + } + if n != int64(len(uploadsJSONBytes)) { + errs[index] = errUnexpected + return + } + wErr = disk.RenameFile(minioMetaBucket, tmpUploadsPath, minioMetaBucket, uploadsPath) + if wErr != nil { + if dErr := disk.DeleteFile(minioMetaBucket, tmpUploadsPath); dErr != nil { + errs[index] = dErr + return + } + errs[index] = wErr + return + } + errs[index] = nil + }(index, disk) + } + + // Wait for all the writes to finish. + wg.Wait() + + // For only single disk return first error. + if len(storageDisks) == 1 { + return errs[0] + } // else count all the errors for quorum validation. + var errCount = 0 + // Return for first error. + for _, err := range errs { + if err != nil { + errCount++ + } + } + // Count all the errors and validate if we have write quorum. + if errCount > len(storageDisks)-len(storageDisks)/2+3 { + // Validate if we have read quorum return success. + if errCount > len(storageDisks)-len(storageDisks)/2+1 { + return nil + } + // Rename `uploads.json` left over back to tmp location. + for index, disk := range storageDisks { + if disk == nil { + continue + } + // Undo rename `uploads.json` in parallel. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + if errs[index] != nil { + return + } + _ = disk.RenameFile(minioMetaBucket, uploadsPath, minioMetaBucket, tmpUploadsPath) + }(index, disk) + } + wg.Wait() + return errXLWriteQuorum + } + return nil +} + +// Wrapper which removes all the uploaded parts. +func cleanupUploadedParts(bucket, object, uploadID string, storageDisks ...StorageAPI) error { + var errs = make([]error, len(storageDisks)) + var wg = &sync.WaitGroup{} + + // Construct uploadIDPath. + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + + // Cleanup uploadID for all disks. + for index, disk := range storageDisks { + if disk == nil { + errs[index] = errDiskNotFound + continue + } + wg.Add(1) + // Cleanup each uploadID in a routine. + go func(index int, disk StorageAPI) { + defer wg.Done() + err := cleanupDir(disk, minioMetaBucket, uploadIDPath) + if err != nil { + errs[index] = err + return + } + errs[index] = nil + }(index, disk) + } + + // Wait for all the cleanups to finish. + wg.Wait() + + // Return first error. + for _, err := range errs { + if err != nil { + return err + } + } + return nil +} + +// listMultipartUploadIDs - list all the upload ids from a marker up to 'count'. +func listMultipartUploadIDs(bucketName, objectName, uploadIDMarker string, count int, disk StorageAPI) ([]uploadMetadata, bool, error) { + var uploads []uploadMetadata + // Read `uploads.json`. + uploadsJSON, err := readUploadsJSON(bucketName, objectName, disk) + if err != nil { + return nil, false, err + } + index := 0 + if uploadIDMarker != "" { + for ; index < len(uploadsJSON.Uploads); index++ { + if uploadsJSON.Uploads[index].UploadID == uploadIDMarker { + // Skip the uploadID as it would already be listed in previous listing. + index++ + break + } + } + } + for index < len(uploadsJSON.Uploads) { + uploads = append(uploads, uploadMetadata{ + Object: objectName, + UploadID: uploadsJSON.Uploads[index].UploadID, + Initiated: uploadsJSON.Uploads[index].Initiated, + }) + count-- + index++ + if count == 0 { + break + } + } + end := (index == len(uploadsJSON.Uploads)) + return uploads, end, nil +} + +// Returns if the prefix is a multipart upload. +func (xl xlObjects) isMultipartUpload(bucket, prefix string) bool { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + _, err := disk.StatFile(bucket, pathJoin(prefix, uploadsJSONFile)) + if err != nil { + return false + } + break + } + return true +} + +// listUploadsInfo - list all uploads info. +func (xl xlObjects) listUploadsInfo(prefixPath string) (uploadsInfo []uploadInfo, err error) { + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + splitPrefixes := strings.SplitN(prefixPath, "/", 3) + uploadsJSON, err := readUploadsJSON(splitPrefixes[1], splitPrefixes[2], disk) + if err != nil { + if err == errFileNotFound { + return []uploadInfo{}, nil + } + return nil, err + } + uploadsInfo = uploadsJSON.Uploads + break + } + return uploadsInfo, nil +} + +// isUploadIDExists - verify if a given uploadID exists and is valid. +func (xl xlObjects) isUploadIDExists(bucket, object, uploadID string) bool { + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + return xl.isObject(minioMetaBucket, uploadIDPath) +} + +// Removes part given by partName belonging to a mulitpart upload from minioMetaBucket +func (xl xlObjects) removeObjectPart(bucket, object, uploadID, partName string) { + curpartPath := path.Join(mpartMetaPrefix, bucket, object, uploadID, partName) + wg := sync.WaitGroup{} + for i, disk := range xl.storageDisks { + if disk == nil { + continue + } + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + // Ignoring failure to remove parts that weren't present in CompleteMultipartUpload + // requests. xl.json is the authoritative source of truth on which parts constitute + // the object. The presence of parts that don't belong in the object doesn't affect correctness. + _ = disk.DeleteFile(minioMetaBucket, curpartPath) + }(i, disk) + } + wg.Wait() +} diff --git a/xl-v1-multipart.go b/xl-v1-multipart.go new file mode 100644 index 000000000..7becbe7ad --- /dev/null +++ b/xl-v1-multipart.go @@ -0,0 +1,776 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "path" + "path/filepath" + "strings" + "time" + + "github.com/minio/minio/pkg/mimedb" + "github.com/skyrings/skyring-common/tools/uuid" +) + +// listMultipartUploads - lists all multipart uploads. +func (xl xlObjects) listMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { + result := ListMultipartsInfo{ + IsTruncated: true, + MaxUploads: maxUploads, + KeyMarker: keyMarker, + Prefix: prefix, + Delimiter: delimiter, + } + + recursive := true + if delimiter == slashSeparator { + recursive = false + } + + // Not using path.Join() as it strips off the trailing '/'. + multipartPrefixPath := pathJoin(mpartMetaPrefix, bucket, prefix) + if prefix == "" { + // Should have a trailing "/" if prefix is "" + // For ex. multipartPrefixPath should be "multipart/bucket/" if prefix is "" + multipartPrefixPath += slashSeparator + } + multipartMarkerPath := "" + if keyMarker != "" { + multipartMarkerPath = pathJoin(mpartMetaPrefix, bucket, keyMarker) + } + var uploads []uploadMetadata + var err error + var eof bool + // List all upload ids for the keyMarker starting from + // uploadIDMarker first. + if uploadIDMarker != "" { + nsMutex.RLock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, keyMarker)) + for _, disk := range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + uploads, _, err = listMultipartUploadIDs(bucket, keyMarker, uploadIDMarker, maxUploads, disk) + break + } + nsMutex.RUnlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, keyMarker)) + if err != nil { + return ListMultipartsInfo{}, err + } + maxUploads = maxUploads - len(uploads) + } + // Validate if we need to list further depending on maxUploads. + if maxUploads > 0 { + walker := xl.lookupTreeWalk(listParams{minioMetaBucket, recursive, multipartMarkerPath, multipartPrefixPath}) + if walker == nil { + walker = xl.startTreeWalk(minioMetaBucket, multipartPrefixPath, multipartMarkerPath, recursive, xl.isMultipartUpload) + } + // Collect uploads until we have reached maxUploads count to 0. + for maxUploads > 0 { + walkResult, ok := <-walker.ch + if !ok { + // Closed channel. + eof = true + break + } + // For any walk error return right away. + if walkResult.err != nil { + // File not found or Disk not found is a valid case. + if walkResult.err == errFileNotFound || walkResult.err == errDiskNotFound { + continue + } + return ListMultipartsInfo{}, err + } + entry := strings.TrimPrefix(walkResult.entry, retainSlash(pathJoin(mpartMetaPrefix, bucket))) + // For an entry looking like a directory, store and + // continue the loop not need to fetch uploads. + if strings.HasSuffix(walkResult.entry, slashSeparator) { + uploads = append(uploads, uploadMetadata{ + Object: entry, + }) + maxUploads-- + if maxUploads == 0 { + if walkResult.end { + eof = true + break + } + } + continue + } + var newUploads []uploadMetadata + var end bool + uploadIDMarker = "" + // For the new object entry we get all its pending uploadIDs. + nsMutex.RLock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, entry)) + var disk StorageAPI + for _, disk = range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + break + } + newUploads, end, err = listMultipartUploadIDs(bucket, entry, uploadIDMarker, maxUploads, disk) + nsMutex.RUnlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, entry)) + if err != nil { + if err == errFileNotFound || walkResult.err == errDiskNotFound { + continue + } + return ListMultipartsInfo{}, err + } + uploads = append(uploads, newUploads...) + maxUploads -= len(newUploads) + if walkResult.end && end { + eof = true + break + } + } + } + // For all received uploads fill in the multiparts result. + for _, upload := range uploads { + var objectName string + var uploadID string + if strings.HasSuffix(upload.Object, slashSeparator) { + // All directory entries are common prefixes. + uploadID = "" // For common prefixes, upload ids are empty. + objectName = upload.Object + result.CommonPrefixes = append(result.CommonPrefixes, objectName) + } else { + uploadID = upload.UploadID + objectName = upload.Object + result.Uploads = append(result.Uploads, upload) + } + result.NextKeyMarker = objectName + result.NextUploadIDMarker = uploadID + } + result.IsTruncated = !eof + // Result is not truncated, reset the markers. + if !result.IsTruncated { + result.NextKeyMarker = "" + result.NextUploadIDMarker = "" + } + return result, nil +} + +// ListMultipartUploads - lists all the pending multipart uploads on a +// bucket. Additionally takes 'prefix, keyMarker, uploadIDmarker and a +// delimiter' which allows us to list uploads match a particular +// prefix or lexically starting from 'keyMarker' or delimiting the +// output to get a directory like listing. +// +// Implements S3 compatible ListMultipartUploads API. The resulting +// ListMultipartsInfo structure is unmarshalled directly into XML and +// replied back to the client. +func (xl xlObjects) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, error) { + result := ListMultipartsInfo{} + + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ListMultipartsInfo{}, BucketNameInvalid{Bucket: bucket} + } + if !xl.isBucketExist(bucket) { + return ListMultipartsInfo{}, BucketNotFound{Bucket: bucket} + } + if !IsValidObjectPrefix(prefix) { + return ListMultipartsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: prefix} + } + // Verify if delimiter is anything other than '/', which we do not support. + if delimiter != "" && delimiter != slashSeparator { + return ListMultipartsInfo{}, UnsupportedDelimiter{ + Delimiter: delimiter, + } + } + // Verify if marker has prefix. + if keyMarker != "" && !strings.HasPrefix(keyMarker, prefix) { + return ListMultipartsInfo{}, InvalidMarkerPrefixCombination{ + Marker: keyMarker, + Prefix: prefix, + } + } + if uploadIDMarker != "" { + if strings.HasSuffix(keyMarker, slashSeparator) { + return result, InvalidUploadIDKeyCombination{ + UploadIDMarker: uploadIDMarker, + KeyMarker: keyMarker, + } + } + id, err := uuid.Parse(uploadIDMarker) + if err != nil { + return result, err + } + if id.IsZero() { + return result, MalformedUploadID{ + UploadID: uploadIDMarker, + } + } + } + return xl.listMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) +} + +// newMultipartUpload - wrapper for initializing a new multipart +// request, returns back a unique upload id. +// +// Internally this function creates 'uploads.json' associated for the +// incoming object at '.minio/multipart/bucket/object/uploads.json' on +// all the disks. `uploads.json` carries metadata regarding on going +// multipart operation on the object. +func (xl xlObjects) newMultipartUpload(bucket string, object string, meta map[string]string) (uploadID string, err error) { + xlMeta := newXLMetaV1(xl.dataBlocks, xl.parityBlocks) + // If not set default to "application/octet-stream" + if meta["content-type"] == "" { + contentType := "application/octet-stream" + if objectExt := filepath.Ext(object); objectExt != "" { + content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))] + if ok { + contentType = content.ContentType + } + } + meta["content-type"] = contentType + } + xlMeta.Stat.ModTime = time.Now().UTC() + xlMeta.Stat.Version = 1 + xlMeta.Meta = meta + + // This lock needs to be held for any changes to the directory contents of ".minio/multipart/object/" + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + + uploadID = getUUID() + initiated := time.Now().UTC() + // Create 'uploads.json' + if err = writeUploadJSON(bucket, object, uploadID, initiated, xl.storageDisks...); err != nil { + return "", err + } + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + tempUploadIDPath := path.Join(tmpMetaPrefix, uploadID) + // Write updated `xl.json` to all disks. + if err = xl.writeSameXLMetadata(minioMetaBucket, tempUploadIDPath, xlMeta); err != nil { + return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) + } + rErr := xl.renameObject(minioMetaBucket, tempUploadIDPath, minioMetaBucket, uploadIDPath) + if rErr == nil { + // Return success. + return uploadID, nil + } + return "", toObjectErr(rErr, minioMetaBucket, uploadIDPath) +} + +// NewMultipartUpload - initialize a new multipart upload, returns a +// unique id. The unique id returned here is of UUID form, for each +// subsequent request each UUID is unique. +// +// Implements S3 compatible initiate multipart API. +func (xl xlObjects) NewMultipartUpload(bucket, object string, meta map[string]string) (string, error) { + // Verify if bucket name is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !xl.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + // Verify if object name is valid. + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{Bucket: bucket, Object: object} + } + // No metadata is set, allocate a new one. + if meta == nil { + meta = make(map[string]string) + } + return xl.newMultipartUpload(bucket, object, meta) +} + +// putObjectPart - reads incoming data until EOF for the part file on +// an ongoing multipart transaction. Internally incoming data is +// erasure coded and written across all disks. +func (xl xlObjects) putObjectPart(bucket string, object string, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) { + // Hold the lock and start the operation. + uploadIDPath := pathJoin(mpartMetaPrefix, bucket, object, uploadID) + nsMutex.Lock(minioMetaBucket, uploadIDPath) + defer nsMutex.Unlock(minioMetaBucket, uploadIDPath) + + if !xl.isUploadIDExists(bucket, object, uploadID) { + return "", InvalidUploadID{UploadID: uploadID} + } + + // Read metadata associated with the object from all disks. + partsMetadata, errs := xl.readAllXLMetadata(minioMetaBucket, uploadIDPath) + + // List all online disks. + onlineDisks, higherVersion, err := xl.listOnlineDisks(partsMetadata, errs) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Pick one from the first valid metadata. + xlMeta := pickValidXLMeta(partsMetadata) + + partSuffix := fmt.Sprintf("object%d", partID) + tmpPartPath := path.Join(tmpMetaPrefix, uploadID, partSuffix) + + // Initialize md5 writer. + md5Writer := md5.New() + + // Construct a tee reader for md5sum. + teeReader := io.TeeReader(data, md5Writer) + + // Collect all the previous erasure infos across the disk. + var eInfos []erasureInfo + for index := range onlineDisks { + eInfos = append(eInfos, partsMetadata[index].Erasure) + } + + // Erasure code data and write across all disks. + newEInfos, n, err := erasureCreateFile(onlineDisks, minioMetaBucket, tmpPartPath, partSuffix, teeReader, eInfos) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, tmpPartPath) + } + if size == -1 { + size = n + } + // Calculate new md5sum. + newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) + if md5Hex != "" { + if newMD5Hex != md5Hex { + // MD5 mismatch, delete the temporary object. + xl.deleteObject(minioMetaBucket, tmpPartPath) + // Returns md5 mismatch. + return "", BadDigest{md5Hex, newMD5Hex} + } + } + + // Validates if upload ID exists again. + if !xl.isUploadIDExists(bucket, object, uploadID) { + return "", InvalidUploadID{UploadID: uploadID} + } + + // Rename temporary part file to its final location. + partPath := path.Join(uploadIDPath, partSuffix) + err = xl.renameObject(minioMetaBucket, tmpPartPath, minioMetaBucket, partPath) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, partPath) + } + + // Once part is successfully committed, proceed with updating XL metadata. + xlMeta.Stat.Version = higherVersion + // Add the current part. + xlMeta.AddObjectPart(partID, partSuffix, newMD5Hex, size) + + // Update `xl.json` content for each disks. + for index := range partsMetadata { + partsMetadata[index].Parts = xlMeta.Parts + partsMetadata[index].Erasure = newEInfos[index] + } + + // Write all the checksum metadata. + tempUploadIDPath := path.Join(tmpMetaPrefix, uploadID) + + // Writes a unique `xl.json` each disk carrying new checksum + // related information. + if err = xl.writeUniqueXLMetadata(minioMetaBucket, tempUploadIDPath, partsMetadata); err != nil { + return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) + } + rErr := xl.renameXLMetadata(minioMetaBucket, tempUploadIDPath, minioMetaBucket, uploadIDPath) + if rErr != nil { + return "", toObjectErr(rErr, minioMetaBucket, uploadIDPath) + } + + // Return success. + return newMD5Hex, nil +} + +// PutObjectPart - reads incoming stream and internally erasure codes +// them. This call is similar to single put operation but it is part +// of the multipart transcation. +// +// Implements S3 compatible Upload Part API. +func (xl xlObjects) PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !xl.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{Bucket: bucket, Object: object} + } + return xl.putObjectPart(bucket, object, uploadID, partID, size, data, md5Hex) +} + +// listObjectParts - wrapper reading `xl.json` for a given object and +// uploadID. Lists all the parts captured inside `xl.json` content. +func (xl xlObjects) listObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { + result := ListPartsInfo{} + + uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) + + xlMeta, err := xl.readXLMetadata(minioMetaBucket, uploadIDPath) + if err != nil { + return ListPartsInfo{}, toObjectErr(err, minioMetaBucket, uploadIDPath) + } + + // Populate the result stub. + result.Bucket = bucket + result.Object = object + result.UploadID = uploadID + result.MaxParts = maxParts + + // For empty number of parts or maxParts as zero, return right here. + if len(xlMeta.Parts) == 0 || maxParts == 0 { + return result, nil + } + + // Limit output to maxPartsList. + if maxParts > maxPartsList { + maxParts = maxPartsList + } + + // Only parts with higher part numbers will be listed. + partIdx := xlMeta.ObjectPartIndex(partNumberMarker) + parts := xlMeta.Parts + if partIdx != -1 { + parts = xlMeta.Parts[partIdx+1:] + } + count := maxParts + for _, part := range parts { + partNamePath := path.Join(mpartMetaPrefix, bucket, object, uploadID, part.Name) + var fi FileInfo + fi, err = xl.statPart(minioMetaBucket, partNamePath) + if err != nil { + return ListPartsInfo{}, toObjectErr(err, minioMetaBucket, partNamePath) + } + result.Parts = append(result.Parts, partInfo{ + PartNumber: part.Number, + ETag: part.ETag, + LastModified: fi.ModTime, + Size: part.Size, + }) + count-- + if count == 0 { + break + } + } + // If listed entries are more than maxParts, we set IsTruncated as true. + if len(parts) > len(result.Parts) { + result.IsTruncated = true + // Make sure to fill next part number marker if IsTruncated is + // true for subsequent listing. + nextPartNumberMarker := result.Parts[len(result.Parts)-1].PartNumber + result.NextPartNumberMarker = nextPartNumberMarker + } + return result, nil +} + +// ListObjectParts - lists all previously uploaded parts for a given +// object and uploadID. Takes additional input of part-number-marker +// to indicate where the listing should begin from. +// +// Implements S3 compatible ListObjectParts API. The resulting +// ListPartsInfo structure is unmarshalled directly into XML and +// replied back to the client. +func (xl xlObjects) ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ListPartsInfo{}, BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !xl.isBucketExist(bucket) { + return ListPartsInfo{}, BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return ListPartsInfo{}, ObjectNameInvalid{Bucket: bucket, Object: object} + } + // Hold lock so that there is no competing abort-multipart-upload or complete-multipart-upload. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + + if !xl.isUploadIDExists(bucket, object, uploadID) { + return ListPartsInfo{}, InvalidUploadID{UploadID: uploadID} + } + result, err := xl.listObjectParts(bucket, object, uploadID, partNumberMarker, maxParts) + return result, err +} + +// CompleteMultipartUpload - completes an ongoing multipart +// transaction after receiving all the parts indicated by the client. +// Returns an md5sum calculated by concatenating all the individual +// md5sums of all the parts. +// +// Implements S3 compatible Complete multipart API. +func (xl xlObjects) CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (string, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify whether the bucket exists. + if !xl.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{ + Bucket: bucket, + Object: object, + } + } + // Hold lock so that + // 1) no one aborts this multipart upload + // 2) no one does a parallel complete-multipart-upload on this multipart upload + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + + if !xl.isUploadIDExists(bucket, object, uploadID) { + return "", InvalidUploadID{UploadID: uploadID} + } + // Calculate s3 compatible md5sum for complete multipart. + s3MD5, err := completeMultipartMD5(parts...) + if err != nil { + return "", err + } + + uploadIDPath := pathJoin(mpartMetaPrefix, bucket, object, uploadID) + + // Read metadata associated with the object from all disks. + partsMetadata, errs := xl.readAllXLMetadata(minioMetaBucket, uploadIDPath) + if err = xl.reduceError(errs); err != nil { + return "", toObjectErr(err, minioMetaBucket, uploadIDPath) + } + + // Calculate full object size. + var objectSize int64 + + // Pick one from the first valid metadata. + xlMeta := pickValidXLMeta(partsMetadata) + + // Save current xl meta for validation. + var currentXLMeta = xlMeta + + // Allocate parts similar to incoming slice. + xlMeta.Parts = make([]objectPartInfo, len(parts)) + + // Validate each part and then commit to disk. + for i, part := range parts { + partIdx := currentXLMeta.ObjectPartIndex(part.PartNumber) + if partIdx == -1 { + return "", InvalidPart{} + } + if currentXLMeta.Parts[partIdx].ETag != part.ETag { + return "", BadDigest{} + } + // All parts except the last part has to be atleast 5MB. + if (i < len(parts)-1) && !isMinAllowedPartSize(currentXLMeta.Parts[partIdx].Size) { + return "", PartTooSmall{} + } + + // Save for total object size. + objectSize += currentXLMeta.Parts[partIdx].Size + + // Add incoming parts. + xlMeta.Parts[i] = objectPartInfo{ + Number: part.PartNumber, + ETag: part.ETag, + Size: currentXLMeta.Parts[partIdx].Size, + Name: fmt.Sprintf("object%d", part.PartNumber), + } + } + + // Check if an object is present as one of the parent dir. + if xl.parentDirIsObject(bucket, path.Dir(object)) { + return "", toObjectErr(errFileAccessDenied, bucket, object) + } + + // Save the final object size and modtime. + xlMeta.Stat.Size = objectSize + xlMeta.Stat.ModTime = time.Now().UTC() + + // Save successfully calculated md5sum. + xlMeta.Meta["md5Sum"] = s3MD5 + uploadIDPath = path.Join(mpartMetaPrefix, bucket, object, uploadID) + tempUploadIDPath := path.Join(tmpMetaPrefix, uploadID) + + // Update all xl metadata, make sure to not modify fields like + // checksum which are different on each disks. + for index := range partsMetadata { + partsMetadata[index].Stat = xlMeta.Stat + partsMetadata[index].Meta = xlMeta.Meta + partsMetadata[index].Parts = xlMeta.Parts + } + + // Write unique `xl.json` for each disk. + if err = xl.writeUniqueXLMetadata(minioMetaBucket, tempUploadIDPath, partsMetadata); err != nil { + return "", toObjectErr(err, minioMetaBucket, tempUploadIDPath) + } + rErr := xl.renameXLMetadata(minioMetaBucket, tempUploadIDPath, minioMetaBucket, uploadIDPath) + if rErr != nil { + return "", toObjectErr(rErr, minioMetaBucket, uploadIDPath) + } + // Hold write lock on the destination before rename + nsMutex.Lock(bucket, object) + defer nsMutex.Unlock(bucket, object) + + // Rename if an object already exists to temporary location. + uniqueID := getUUID() + err = xl.renameObject(bucket, object, minioMetaBucket, path.Join(tmpMetaPrefix, uniqueID)) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Remove parts that weren't present in CompleteMultipartUpload request + for _, curpart := range currentXLMeta.Parts { + if xlMeta.ObjectPartIndex(curpart.Number) == -1 { + // Delete the missing part files. e.g, + // Request 1: NewMultipart + // Request 2: PutObjectPart 1 + // Request 3: PutObjectPart 2 + // Request 4: CompleteMultipartUpload --part 2 + // N.B. 1st part is not present. This part should be removed from the storage. + xl.removeObjectPart(bucket, object, uploadID, curpart.Name) + } + } + + // Rename the multipart object to final location. + if err = xl.renameObject(minioMetaBucket, uploadIDPath, bucket, object); err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Delete the previously successfully renamed object. + xl.deleteObject(minioMetaBucket, path.Join(tmpMetaPrefix, uniqueID)) + + // Hold the lock so that two parallel complete-multipart-uploads do not + // leave a stale uploads.json behind. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + + // Validate if there are other incomplete upload-id's present for + // the object, if yes do not attempt to delete 'uploads.json'. + var disk StorageAPI + for _, disk = range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + break + } + uploadsJSON, err := readUploadsJSON(bucket, object, disk) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, object) + } + // If we have successfully read `uploads.json`, then we proceed to + // purge or update `uploads.json`. + uploadIDIdx := uploadsJSON.Index(uploadID) + if uploadIDIdx != -1 { + uploadsJSON.Uploads = append(uploadsJSON.Uploads[:uploadIDIdx], uploadsJSON.Uploads[uploadIDIdx+1:]...) + } + if len(uploadsJSON.Uploads) > 0 { + if err = updateUploadsJSON(bucket, object, uploadsJSON, xl.storageDisks...); err != nil { + return "", toObjectErr(err, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + } + // Return success. + return s3MD5, nil + } // No more pending uploads for the object, proceed to delete + // object completely from '.minio/multipart'. + err = xl.deleteObject(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + } + + // Return md5sum. + return s3MD5, nil +} + +// abortMultipartUpload - wrapper for purging an ongoing multipart +// transaction, deletes uploadID entry from `uploads.json` and purges +// the directory at '.minio/multipart/bucket/object/uploadID' holding +// all the upload parts. +func (xl xlObjects) abortMultipartUpload(bucket, object, uploadID string) (err error) { + // Cleanup all uploaded parts. + if err = cleanupUploadedParts(bucket, object, uploadID, xl.storageDisks...); err != nil { + return toObjectErr(err, bucket, object) + } + + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object)) + // Validate if there are other incomplete upload-id's present for + // the object, if yes do not attempt to delete 'uploads.json'. + var disk StorageAPI + for _, disk = range xl.getLoadBalancedQuorumDisks() { + if disk == nil { + continue + } + break + } + uploadsJSON, err := readUploadsJSON(bucket, object, disk) + if err != nil { + return toObjectErr(err, bucket, object) + } + uploadIDIdx := uploadsJSON.Index(uploadID) + if uploadIDIdx != -1 { + uploadsJSON.Uploads = append(uploadsJSON.Uploads[:uploadIDIdx], uploadsJSON.Uploads[uploadIDIdx+1:]...) + } + if len(uploadsJSON.Uploads) > 0 { + // There are pending uploads for the same object, preserve + // them update 'uploads.json' in-place. + err = updateUploadsJSON(bucket, object, uploadsJSON, xl.storageDisks...) + if err != nil { + return toObjectErr(err, bucket, object) + } + return nil + } // No more pending uploads for the object, we purge the entire + // entry at '.minio/multipart/bucket/object'. + if err = xl.deleteObject(minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)); err != nil { + return toObjectErr(err, minioMetaBucket, path.Join(mpartMetaPrefix, bucket, object)) + } + + // Successfully purged. + return nil +} + +// AbortMultipartUpload - aborts an ongoing multipart operation +// signified by the input uploadID. This is an atomic operation +// doesn't require clients to initiate multiple such requests. +// +// All parts are purged from all disks and reference to the uploadID +// would be removed from the system, rollback is not possible on this +// operation. +// +// Implements S3 compatible Abort multipart API, slight difference is +// that this is an atomic idempotent operation. Subsequent calls have +// no affect and further requests to the same uploadID would not be honored. +func (xl xlObjects) AbortMultipartUpload(bucket, object, uploadID string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + if !xl.isBucketExist(bucket) { + return BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return ObjectNameInvalid{Bucket: bucket, Object: object} + } + + // Hold lock so that there is no competing complete-multipart-upload or put-object-part. + nsMutex.Lock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + defer nsMutex.Unlock(minioMetaBucket, pathJoin(mpartMetaPrefix, bucket, object, uploadID)) + + if !xl.isUploadIDExists(bucket, object, uploadID) { + return InvalidUploadID{UploadID: uploadID} + } + err := xl.abortMultipartUpload(bucket, object, uploadID) + return err +} diff --git a/xl-v1-object.go b/xl-v1-object.go new file mode 100644 index 000000000..da40ea52d --- /dev/null +++ b/xl-v1-object.go @@ -0,0 +1,455 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "io" + "path" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/minio/minio/pkg/mimedb" +) + +/// Object Operations + +// GetObject - reads an object erasured coded across multiple +// disks. Supports additional parameters like offset and length +// which is synonymous with HTTP Range requests. +// +// startOffset indicates the location at which the client requested +// object to be read at. length indicates the total length of the +// object requested by client. +func (xl xlObjects) GetObject(bucket, object string, startOffset int64, length int64, writer io.Writer) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + // Verify if object is valid. + if !IsValidObjectName(object) { + return ObjectNameInvalid{Bucket: bucket, Object: object} + } + + // Lock the object before reading. + nsMutex.RLock(bucket, object) + defer nsMutex.RUnlock(bucket, object) + + // Read metadata associated with the object from all disks. + partsMetadata, errs := xl.readAllXLMetadata(bucket, object) + if err := xl.reduceError(errs); err != nil { + return toObjectErr(err, bucket, object) + } + + // List all online disks. + onlineDisks, _, err := xl.listOnlineDisks(partsMetadata, errs) + if err != nil { + return toObjectErr(err, bucket, object) + } + + // Pick one from the first valid metadata. + xlMeta := partsMetadata[0] + if !xlMeta.IsValid() { + for _, partMetadata := range partsMetadata { + if partMetadata.IsValid() { + xlMeta = partMetadata + break + } + } + } + + // Get part index offset. + partIndex, partOffset, err := xlMeta.ObjectToPartOffset(startOffset) + if err != nil { + return toObjectErr(err, bucket, object) + } + + // Collect all the previous erasure infos across the disk. + var eInfos []erasureInfo + for index := range onlineDisks { + eInfos = append(eInfos, partsMetadata[index].Erasure) + } + + // Read from all parts. + for ; partIndex < len(xlMeta.Parts); partIndex++ { + // Save the current part name and size. + partName := xlMeta.Parts[partIndex].Name + partSize := xlMeta.Parts[partIndex].Size + + // Start reading the part name. + var buffer []byte + buffer, err = erasureReadFile(onlineDisks, bucket, pathJoin(object, partName), partName, partSize, eInfos) + if err != nil { + return err + } + + // Copy to client until length requested. + if length > int64(len(buffer)) { + var m int64 + m, err = io.Copy(writer, bytes.NewReader(buffer[partOffset:])) + if err != nil { + return err + } + length -= m + } else { + _, err = io.CopyN(writer, bytes.NewReader(buffer[partOffset:]), length) + if err != nil { + return err + } + return nil + } + + // Reset part offset to 0 to read rest of the part from the beginning. + partOffset = 0 + } // End of read all parts loop. + + // Return success. + return nil +} + +// GetObjectInfo - reads object metadata and replies back ObjectInfo. +func (xl xlObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return ObjectInfo{}, BucketNameInvalid{Bucket: bucket} + } + // Verify if object is valid. + if !IsValidObjectName(object) { + return ObjectInfo{}, ObjectNameInvalid{Bucket: bucket, Object: object} + } + nsMutex.RLock(bucket, object) + defer nsMutex.RUnlock(bucket, object) + info, err := xl.getObjectInfo(bucket, object) + if err != nil { + return ObjectInfo{}, toObjectErr(err, bucket, object) + } + return info, nil +} + +// getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo. +func (xl xlObjects) getObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) { + var xlMeta xlMetaV1 + xlMeta, err = xl.readXLMetadata(bucket, object) + if err != nil { + // Return error. + return ObjectInfo{}, err + } + objInfo = ObjectInfo{ + IsDir: false, + Bucket: bucket, + Name: object, + Size: xlMeta.Stat.Size, + ModTime: xlMeta.Stat.ModTime, + MD5Sum: xlMeta.Meta["md5Sum"], + ContentType: xlMeta.Meta["content-type"], + ContentEncoding: xlMeta.Meta["content-encoding"], + } + return objInfo, nil +} + +// renameObject - renames all source objects to destination object +// across all disks in parallel. Additionally if we have errors and do +// not have a readQuorum partially renamed files are renamed back to +// its proper location. +func (xl xlObjects) renameObject(srcBucket, srcObject, dstBucket, dstObject string) error { + // Initialize sync waitgroup. + var wg = &sync.WaitGroup{} + + // Initialize list of errors. + var errs = make([]error, len(xl.storageDisks)) + + // Rename file on all underlying storage disks. + for index, disk := range xl.storageDisks { + if disk == nil { + errs[index] = errDiskNotFound + continue + } + // Append "/" as srcObject and dstObject are either leaf-dirs or non-leaf-dris. + // If srcObject is an object instead of prefix we just rename the leaf-dir and + // not rename the part and metadata files separately. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + err := disk.RenameFile(srcBucket, retainSlash(srcObject), dstBucket, retainSlash(dstObject)) + if err != nil && err != errFileNotFound { + errs[index] = err + } + }(index, disk) + } + + // Wait for all renames to finish. + wg.Wait() + + // Gather err count. + var errCount = 0 + for _, err := range errs { + if err == nil { + continue + } + errCount++ + } + // We can safely allow RenameFile errors up to len(xl.storageDisks) - xl.writeQuorum + // otherwise return failure. Cleanup successful renames. + if errCount > len(xl.storageDisks)-xl.writeQuorum { + // Check we have successful read quorum. + if errCount <= len(xl.storageDisks)-xl.readQuorum { + return nil // Return success. + } // else - failed to acquire read quorum. + + // Undo rename object on disks where RenameFile succeeded. + for index, disk := range xl.storageDisks { + if disk == nil { + continue + } + // Undo rename object in parallel. + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + if errs[index] != nil { + return + } + _ = disk.RenameFile(dstBucket, retainSlash(dstObject), srcBucket, retainSlash(srcObject)) + }(index, disk) + } + wg.Wait() + return errXLWriteQuorum + } + return nil +} + +// PutObject - creates an object upon reading from the input stream +// until EOF, erasure codes the data across all disk and additionally +// writes `xl.json` which carries the necessary metadata for future +// object operations. +func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (string, error) { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return "", BucketNameInvalid{Bucket: bucket} + } + // Verify bucket exists. + if !xl.isBucketExist(bucket) { + return "", BucketNotFound{Bucket: bucket} + } + if !IsValidObjectName(object) { + return "", ObjectNameInvalid{ + Bucket: bucket, + Object: object, + } + } + // No metadata is set, allocate a new one. + if metadata == nil { + metadata = make(map[string]string) + } + nsMutex.Lock(bucket, object) + defer nsMutex.Unlock(bucket, object) + + uniqueID := getUUID() + tempErasureObj := path.Join(tmpMetaPrefix, uniqueID, "object1") + tempObj := path.Join(tmpMetaPrefix, uniqueID) + + // Initialize xl meta. + xlMeta := newXLMetaV1(xl.dataBlocks, xl.parityBlocks) + + // Read metadata associated with the object from all disks. + partsMetadata, errs := xl.readAllXLMetadata(bucket, object) + + // List all online disks. + onlineDisks, higherVersion, err := xl.listOnlineDisks(partsMetadata, errs) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Increment version only if we have online disks less than configured storage disks. + if diskCount(onlineDisks) < len(xl.storageDisks) { + higherVersion++ + } + + // Initialize md5 writer. + md5Writer := md5.New() + + // Tee reader combines incoming data stream and md5, data read + // from input stream is written to md5. + teeReader := io.TeeReader(data, md5Writer) + + // Collect all the previous erasure infos across the disk. + var eInfos []erasureInfo + for range onlineDisks { + eInfos = append(eInfos, xlMeta.Erasure) + } + + // Erasure code and write across all disks. + newEInfos, n, err := erasureCreateFile(onlineDisks, minioMetaBucket, tempErasureObj, "object1", teeReader, eInfos) + if err != nil { + return "", toObjectErr(err, minioMetaBucket, tempErasureObj) + } + if size == -1 { + size = n + } + // Save additional erasureMetadata. + modTime := time.Now().UTC() + + newMD5Hex := hex.EncodeToString(md5Writer.Sum(nil)) + // Update the md5sum if not set with the newly calculated one. + if len(metadata["md5Sum"]) == 0 { + metadata["md5Sum"] = newMD5Hex + } + + // If not set default to "application/octet-stream" + if metadata["content-type"] == "" { + contentType := "application/octet-stream" + if objectExt := filepath.Ext(object); objectExt != "" { + content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))] + if ok { + contentType = content.ContentType + } + } + metadata["content-type"] = contentType + } + + // md5Hex representation. + md5Hex := metadata["md5Sum"] + if md5Hex != "" { + if newMD5Hex != md5Hex { + // MD5 mismatch, delete the temporary object. + xl.deleteObject(minioMetaBucket, tempObj) + // Returns md5 mismatch. + return "", BadDigest{md5Hex, newMD5Hex} + } + } + + // Check if an object is present as one of the parent dir. + // -- FIXME. (needs a new kind of lock). + if xl.parentDirIsObject(bucket, path.Dir(object)) { + return "", toObjectErr(errFileAccessDenied, bucket, object) + } + + // Rename if an object already exists to temporary location. + newUniqueID := getUUID() + err = xl.renameObject(bucket, object, minioMetaBucket, path.Join(tmpMetaPrefix, newUniqueID)) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Fill all the necessary metadata. + xlMeta.Meta = metadata + xlMeta.Stat.Size = size + xlMeta.Stat.ModTime = modTime + xlMeta.Stat.Version = higherVersion + // Add the final part. + xlMeta.AddObjectPart(1, "object1", newMD5Hex, xlMeta.Stat.Size) + + // Update `xl.json` content on each disks. + for index := range partsMetadata { + partsMetadata[index] = xlMeta + partsMetadata[index].Erasure = newEInfos[index] + } + + // Write unique `xl.json` for each disk. + if err = xl.writeUniqueXLMetadata(minioMetaBucket, tempObj, partsMetadata); err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Rename the successfully written temporary object to final location. + err = xl.renameObject(minioMetaBucket, tempObj, bucket, object) + if err != nil { + return "", toObjectErr(err, bucket, object) + } + + // Delete the temporary object. + xl.deleteObject(minioMetaBucket, path.Join(tmpMetaPrefix, newUniqueID)) + + // Return md5sum, successfully wrote object. + return newMD5Hex, nil +} + +// deleteObject - wrapper for delete object, deletes an object from +// all the disks in parallel, including `xl.json` associated with the +// object. +func (xl xlObjects) deleteObject(bucket, object string) error { + // Initialize sync waitgroup. + var wg = &sync.WaitGroup{} + + // Initialize list of errors. + var dErrs = make([]error, len(xl.storageDisks)) + + for index, disk := range xl.storageDisks { + if disk == nil { + dErrs[index] = errDiskNotFound + continue + } + wg.Add(1) + go func(index int, disk StorageAPI) { + defer wg.Done() + err := cleanupDir(disk, bucket, object) + if err != nil { + dErrs[index] = err + return + } + dErrs[index] = nil + }(index, disk) + } + + // Wait for all routines to finish. + wg.Wait() + + var fileNotFoundCnt, deleteFileErr int + // Count for specific errors. + for _, err := range dErrs { + if err == nil { + continue + } + // If file not found, count them. + if err == errFileNotFound { + fileNotFoundCnt++ + continue + } + + // Update error counter separately. + deleteFileErr++ + } + // Return err if all disks report file not found. + if fileNotFoundCnt == len(xl.storageDisks) { + return errFileNotFound + } else if deleteFileErr > len(xl.storageDisks)-xl.writeQuorum { + // Return errXLWriteQuorum if errors were more than + // allowed write quorum. + return errXLWriteQuorum + } + + return nil +} + +// DeleteObject - deletes an object, this call doesn't necessary reply +// any error as it is not necessary for the handler to reply back a +// response to the client request. +func (xl xlObjects) DeleteObject(bucket, object string) error { + // Verify if bucket is valid. + if !IsValidBucketName(bucket) { + return BucketNameInvalid{Bucket: bucket} + } + if !IsValidObjectName(object) { + return ObjectNameInvalid{Bucket: bucket, Object: object} + } + nsMutex.Lock(bucket, object) + defer nsMutex.Unlock(bucket, object) + xl.deleteObject(bucket, object) + return nil +} diff --git a/xl-v1-utils.go b/xl-v1-utils.go new file mode 100644 index 000000000..a9722d56d --- /dev/null +++ b/xl-v1-utils.go @@ -0,0 +1,86 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "bytes" + "io" + "math/rand" + "path" + "time" +) + +// randInts - uses Knuth Fisher-Yates shuffle algorithm for generating uniform shuffling. +func randInts(count int) []int { + rand.Seed(time.Now().UTC().UnixNano()) // Seed with current time. + ints := make([]int, count) + for i := 0; i < count; i++ { + ints[i] = i + 1 + } + for i := 0; i < count; i++ { + // Choose index uniformly in [i, count-1] + r := i + rand.Intn(count-i) + ints[r], ints[i] = ints[i], ints[r] + } + return ints +} + +// readAll - returns contents from volume/path as byte array. +func readAll(disk StorageAPI, volume string, path string) ([]byte, error) { + var writer = new(bytes.Buffer) + startOffset := int64(0) + + // Allocate 10MiB buffer. + buf := make([]byte, blockSizeV1) + + // Read until io.EOF. + for { + n, err := disk.ReadFile(volume, path, startOffset, buf) + if err == io.EOF { + break + } + if err != nil && err != io.EOF { + return nil, err + } + writer.Write(buf[:n]) + startOffset += n + } + return writer.Bytes(), nil +} + +// readXLMeta reads `xl.json` returns contents as byte array. +func readXLMeta(disk StorageAPI, bucket string, object string) ([]byte, error) { + var writer = new(bytes.Buffer) + startOffset := int64(0) + + // Allocate 2MiB buffer, this is sufficient for the most of `xl.json`. + buf := make([]byte, 2*1024*1024) + + // Read until io.EOF. + for { + n, err := disk.ReadFile(bucket, path.Join(object, xlMetaJSONFile), startOffset, buf) + if err == io.EOF { + break + } + if err != nil && err != io.EOF { + return nil, err + } + writer.Write(buf[:n]) + startOffset += n + } + return writer.Bytes(), nil +} diff --git a/xl-v1.go b/xl-v1.go new file mode 100644 index 000000000..2211bbfd5 --- /dev/null +++ b/xl-v1.go @@ -0,0 +1,217 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "errors" + "fmt" + "sort" + "sync" + + "github.com/minio/minio/pkg/disk" +) + +// XL constants. +const ( + // Format config file carries backend format specific details. + formatConfigFile = "format.json" + // XL metadata file carries per object metadata. + xlMetaJSONFile = "xl.json" + // Uploads metadata file carries per multipart object metadata. + uploadsJSONFile = "uploads.json" +) + +// xlObjects - Implements XL object layer. +type xlObjects struct { + physicalDisks []string // Collection of regular disks. + storageDisks []StorageAPI // Collection of initialized backend disks. + dataBlocks int // dataBlocks count caculated for erasure. + parityBlocks int // parityBlocks count calculated for erasure. + readQuorum int // readQuorum minimum required disks to read data. + writeQuorum int // writeQuorum minimum required disks to write data. + + // List pool management. + listObjectMap map[listParams][]*treeWalker + listObjectMapMutex *sync.Mutex +} + +// errXLMaxDisks - returned for reached maximum of disks. +var errXLMaxDisks = errors.New("Number of disks are higher than supported maximum count '16'") + +// errXLMinDisks - returned for minimum number of disks. +var errXLMinDisks = errors.New("Number of disks are smaller than supported minimum count '8'") + +// errXLNumDisks - returned for odd number of disks. +var errXLNumDisks = errors.New("Number of disks should be multiples of '2'") + +// errXLReadQuorum - did not meet read quorum. +var errXLReadQuorum = errors.New("I/O error. did not meet read quorum.") + +// errXLWriteQuorum - did not meet write quorum. +var errXLWriteQuorum = errors.New("I/O error. did not meet write quorum.") + +// errXLDataCorrupt - err data corrupt. +var errXLDataCorrupt = errors.New("data likely corrupted, all blocks are zero in length") + +const ( + // Maximum erasure blocks. + maxErasureBlocks = 16 + // Minimum erasure blocks. + minErasureBlocks = 8 +) + +// Validate if input disks are sufficient for initializing XL. +func checkSufficientDisks(disks []string) error { + // Verify total number of disks. + totalDisks := len(disks) + if totalDisks > maxErasureBlocks { + return errXLMaxDisks + } + if totalDisks < minErasureBlocks { + return errXLMinDisks + } + + // isEven function to verify if a given number if even. + isEven := func(number int) bool { + return number%2 == 0 + } + + // Verify if we have even number of disks. + // only combination of 8, 12, 16 are supported. + if !isEven(totalDisks) { + return errXLNumDisks + } + + return nil +} + +// newXLObjects - initialize new xl object layer. +func newXLObjects(disks []string) (ObjectLayer, error) { + // Validate if input disks are sufficient. + if err := checkSufficientDisks(disks); err != nil { + return nil, err + } + + // Bootstrap disks. + storageDisks := make([]StorageAPI, len(disks)) + for index, disk := range disks { + var err error + // Intentionally ignore disk not found errors. XL will + // manage such errors internally. + storageDisks[index], err = newStorageAPI(disk) + if err != nil && err != errDiskNotFound { + return nil, err + } + } + + // Runs house keeping code, like creating minioMetaBucket, cleaning up tmp files etc. + xlHouseKeeping(storageDisks) + + // Attempt to load all `format.json` + formatConfigs, sErrs := loadAllFormats(storageDisks) + + // Generic format check validates all necessary cases. + if err := genericFormatCheck(formatConfigs, sErrs); err != nil { + return nil, err + } + + // Handles different cases properly. + switch reduceFormatErrs(sErrs, len(storageDisks)) { + case errUnformattedDisk: + // All drives online but fresh, initialize format. + if err := initFormatXL(storageDisks); err != nil { + return nil, fmt.Errorf("Unable to initialize format, %s", err) + } + case errSomeDiskUnformatted: + // All drives online but some report missing format.json. + if err := healFormatXL(storageDisks); err != nil { + // There was an unexpected unrecoverable error during healing. + return nil, fmt.Errorf("Unable to heal backend %s", err) + } + case errSomeDiskOffline: + // Some disks offline but some report missing format.json. + // FIXME. + } + + // Load saved XL format.json and validate. + newPosixDisks, err := loadFormatXL(storageDisks) + if err != nil { + // errCorruptedDisk - healing failed + return nil, fmt.Errorf("Unable to recognize backend format, %s", err) + } + + // Calculate data and parity blocks. + dataBlocks, parityBlocks := len(newPosixDisks)/2, len(newPosixDisks)/2 + + // Initialize xl objects. + xl := xlObjects{ + physicalDisks: disks, + storageDisks: newPosixDisks, + dataBlocks: dataBlocks, + parityBlocks: parityBlocks, + listObjectMap: make(map[listParams][]*treeWalker), + listObjectMapMutex: &sync.Mutex{}, + } + + // Figure out read and write quorum based on number of storage disks. + // Read quorum should be always N/2 + 1 (due to Vandermonde matrix + // erasure requirements) + xl.readQuorum = len(xl.storageDisks)/2 + 1 + + // Write quorum is assumed if we have total disks + 3 + // parity. (Need to discuss this again) + xl.writeQuorum = len(xl.storageDisks)/2 + 3 + if xl.writeQuorum > len(xl.storageDisks) { + xl.writeQuorum = len(xl.storageDisks) + } + + // Return successfully initialized object layer. + return xl, nil +} + +// byDiskTotal is a collection satisfying sort.Interface. +type byDiskTotal []disk.Info + +func (d byDiskTotal) Len() int { return len(d) } +func (d byDiskTotal) Swap(i, j int) { d[i], d[j] = d[j], d[i] } +func (d byDiskTotal) Less(i, j int) bool { + return d[i].Total < d[j].Total +} + +// StorageInfo - returns underlying storage statistics. +func (xl xlObjects) StorageInfo() StorageInfo { + var disksInfo []disk.Info + for _, diskPath := range xl.physicalDisks { + info, err := disk.GetInfo(diskPath) + if err != nil { + errorIf(err, "Unable to fetch disk info for "+diskPath) + continue + } + disksInfo = append(disksInfo, info) + } + + // Sort so that the first element is the smallest. + sort.Sort(byDiskTotal(disksInfo)) + + // Return calculated storage info, choose the lowest Total and + // Free as the total aggregated values. Total capacity is always + // the multiple of smallest disk among the disk list. + return StorageInfo{ + Total: disksInfo[0].Total * int64(len(xl.storageDisks)), + Free: disksInfo[0].Free * int64(len(xl.storageDisks)), + } +}