diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd8418e --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# IDE +.idea + +debug-server +exec +test +dev-docs + +# real config file +config.toml + +cmake-* +c/debug +c/release +c/libs/* \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3ff86e4 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,11 @@ +Copyright 2020 Yuan Tong + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a81026c --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# Chromatic + +Chromatic is a private image hosting site focused on optimizing web experience. + +# Requirement + +**Run this server on a decent machine!** + +Image encoding, especially using libaom for AVIF encoding, requires a lot of CPU and memory resources. +You should have at least 8GB memory for a 4096x4096 size image. + +This site uses MongoDB as database. + +## Deploy + +To deploy the site, follow the steps below. + +1. Get a domain. +2. Create a reCAPTCHA key for your site. You can get one free at [here](https://www.google.com/recaptcha/admin/create). +3. Copy `config.example.toml` to `config.toml`, and change the config items to your need. +4. Replace default website key (`6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI`) to yours in `main.*.js` and `main.*.js.map`. +5. (Optional) Execute `front/compress.sh` in `static` folder to get brotli and gzip compressed static files. +6. Everything ready. Run the executable to start the server. + +## Build + +To build this project from source, follow the instructions below. + +### Dependence libraries + +On Linux, You should have [libpng](http://www.libpng.org/pub/png/libpng.html), +libjpeg([mozjpeg](https://github.com/mozilla/mozjpeg), optimally), +[libwebp](https://chromium.googlesource.com/webm/libwebp) and +[libavif](https://github.com/AOMediaCodec/libavif) installed with headers and +static library available for compiler. Specially, mozjpeg's default install dir +`/opt/mozjpeg/` will also be considered. + +On Windows, you should have the same libraries, with headers and libraries +placed in `c/header` and `c/libs`. + +### Build C Part + +This project has a small wrapper written in C. You need `cmake` to build it. +This can be build on Linux and Windows(using MinGW). + +``` +cd c +mkdir release +cd release +cmake -DCMAKE_BUILD_TYPE=Release .. +make +``` + +### Build Go Part \ No newline at end of file diff --git a/api_gallery.go b/api_gallery.go new file mode 100644 index 0000000..1acf8be --- /dev/null +++ b/api_gallery.go @@ -0,0 +1,385 @@ +package main + +import ( + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "log" +) + +type ListImageQ struct { + UserID primitive.ObjectID `json:"id" binding:"required"` + Sort string `json:"sort" binding:"required"` + Offset uint `json:"offset"` + Limit uint `json:"limit"` +} + +type ListImageP struct { + Total uint64 `json:"total"` + Count uint64 `json:"count"` + Images []Image `json:"images"` +} + +func ListImage(r *ListImageQ) (*ListImageP, SErr) { + if r.Sort != "tag" && r.Sort != "upload" { + return nil, EBadRequest + } + + if r.Limit > uint(config.Site.MaxList) { + return nil, EBadRequest + } + + ci := C("image") + cu := C("user") + + if n := cu.FindOne(X(), bson.M{"_id": r.UserID}); n.Err() == mongo.ErrNoDocuments { + return nil, EUserNotExist + } else if n.Err() != nil { + log.Printf("[Warn] Failed checking user exist: %s", n.Err()) + return nil, EUnknown + } + + p := &ListImageP{} + + n, err := ci.CountDocuments(X(), bson.M{"user_id": r.UserID}) + + if err != nil { + log.Printf("[Warn] Failed counting images: %s", err) + return nil, EUnknown + } + + p.Total = uint64(n) + + if n-int64(r.Offset) <= 0 || r.Limit == 0 { + return p, EOk + } + + p.Images = make([]Image, 0, r.Limit) + + opts := options.Find() + opts.SetSkip(int64(r.Offset)) + opts.SetLimit(int64(r.Limit)) + if r.Sort == "tag" { + opts.SetSort(bson.D{{"tag", 1}, {"upload", 1}}) + } else { + opts.SetSort(bson.D{{"upload", 1}}) + } + opts.SetProjection(bson.M{ + "user_id": 1, + "user_name": 1, + "tag": 1, + "upload": 1, + "view": 1, + "origins": 1, + }) + + cur, err := ci.Find(X(), bson.M{"user_id": r.UserID}, opts) + + if err == mongo.ErrNoDocuments { + p.Count = 0 + return p, EOk + } else if err != nil { + log.Printf("[Warn] Failed reading images info: %s", err) + return nil, EUnknown + } + + if err = cur.All(X(), &p.Images); err != nil { + log.Printf("[Warn] Failed loading images info: %s", err) + return nil, EUnknown + } + + p.Count = uint64(len(p.Images)) + return p, EOk +} + +func ListImageTags(r primitive.ObjectID) ([]string, SErr) { + ci := C("image") + cu := C("user") + + if n := cu.FindOne(X(), bson.M{"_id": r}); n.Err() == mongo.ErrNoDocuments { + return nil, EUserNotExist + } else if n.Err() != nil { + log.Printf("[Warn] Failed checking user exist: %s", n.Err()) + return nil, EUnknown + } + + tags, err := ci.Distinct(X(), "tag", bson.M{"user_id": r}) + + if err != nil { + log.Printf("[Warn] Failed loading tags: %s", err) + return nil, EUnknown + } + + s := make([]string, 0, len(tags)) + + for _, t := range tags { + if st, ok := t.(string); ok { + s = append(s, st) + } + } + + return s, EOk +} + +type ListImageWithTagQ struct { + UserID primitive.ObjectID `json:"id" binding:"required"` + Tag string `json:"tag"` + Offset uint `json:"offset"` + Limit uint `json:"limit" binding:"required"` +} + +func ListImageWithTag(r *ListImageWithTagQ) (*ListImageP, SErr) { + if r.Limit > uint(config.Site.MaxList) { + return nil, EBadRequest + } + + ci := C("image") + cu := C("user") + + if n := cu.FindOne(X(), bson.M{"_id": r.UserID}); n.Err() == mongo.ErrNoDocuments { + return nil, EUserNotExist + } else if n.Err() != nil { + log.Printf("[Warn] Failed checking user exist: %s", n.Err()) + return nil, EUnknown + } + + p := &ListImageP{} + + n, err := ci.CountDocuments(X(), bson.M{"user_id": r.UserID, "tag": r.Tag}) + + if err != nil { + log.Printf("[Warn] Failed counting images: %s", err) + return nil, EUnknown + } + + p.Total = uint64(n) + + if n-int64(r.Offset) <= 0 { + return p, EOk + } + + p.Images = make([]Image, 0, r.Limit) + + opts := options.Find() + opts.SetSkip(int64(r.Offset)) + opts.SetLimit(int64(r.Limit)) + opts.SetSort(bson.D{{"upload", 1}}) + opts.SetProjection(bson.M{ + "user_id": 1, + "user_name": 1, + "tag": 1, + "upload": 1, + "view": 1, + "origins": 1, + }) + + cur, err := ci.Find(X(), bson.M{"user_id": r.UserID, "tag": r.Tag}, opts) + + if err == mongo.ErrNoDocuments { + p.Count = 0 + return p, EOk + } else if err != nil { + log.Printf("[Warn] Failed reading images info: %s", err) + return nil, EUnknown + } + + if err = cur.All(X(), &p.Images); err != nil { + log.Printf("[Warn] Failed loading images info: %s", err) + return nil, EUnknown + } + + p.Count = uint64(len(p.Images)) + return p, EOk +} + +type ListImageContainsTagQ struct { + UserID primitive.ObjectID `json:"id" binding:"required"` + Tag string `json:"tag"` + Offset uint `json:"offset"` + Limit uint `json:"limit" binding:"required"` +} + +func ListImageContainsTag(r *ListImageContainsTagQ) (*ListImageP, SErr) { + if r.Limit > uint(config.Site.MaxList) { + return nil, EBadRequest + } + + ci := C("image") + cu := C("user") + + if n := cu.FindOne(X(), bson.M{"_id": r.UserID}); n.Err() == mongo.ErrNoDocuments { + return nil, EUserNotExist + } else if n.Err() != nil { + log.Printf("[Warn] Failed checking user exist: %s", n.Err()) + return nil, EUnknown + } + + p := &ListImageP{} + + cCur, err := ci.Aggregate(X(), mongo.Pipeline{ + bson.D{{"$match", bson.M{ + "user_id": r.UserID, + }}}, + bson.D{{"$project", bson.M{ + "offset": bson.M{"$indexOfCP": bson.A{"$tag", r.Tag}}, + }}}, + bson.D{{"$match", bson.M{ + "offset": bson.M{"$ne": -1}, + }}}, + bson.D{{"$count", "count"}}, + }) + + if err != nil { + log.Printf("[Warn] Failed counting images: %s", err) + return nil, EUnknown + } + + cS := struct { + Count uint64 `bson:"count"` + }{} + _ = cCur.Next(X()) + err = cCur.Decode(&cS) + + if err != nil { + log.Printf("[Warn] Failed reading image count: %s\n", err) + return nil, EUnknown + } + + p.Total = cS.Count + + if int64(cS.Count)-int64(r.Offset) <= 0 { + return p, EOk + } + + p.Images = make([]Image, 0, r.Limit) + + cur, err := ci.Aggregate(X(), mongo.Pipeline{ + bson.D{{"$match", bson.M{ + "user_id": r.UserID, + }}}, + bson.D{{"$project", bson.M{ + "user_id": 1, + "user_name": 1, + "tag": 1, + "upload": 1, + "view": 1, + "origins": 1, + "offset": bson.M{"$indexOfCP": bson.A{"$tag", r.Tag}}, + }}}, + bson.D{{"$match", bson.M{ + "offset": bson.M{"$ne": -1}, + }}}, + bson.D{{"$sort", bson.D{ + {"tag", 1}, + {"offset", 1}, + {"upload", 1}, + }}}, + bson.D{{"$skip", r.Offset}}, + bson.D{{"$limit", r.Limit}}, + }) + + if err == mongo.ErrNoDocuments { + p.Count = 0 + return p, EOk + } else if err != nil { + log.Printf("[Warn] Failed reading images info: %s", err) + return nil, EUnknown + } + + if err = cur.All(X(), &p.Images); err != nil { + log.Printf("[Warn] Failed loading images info: %s", err) + return nil, EUnknown + } + + p.Count = uint64(len(p.Images)) + return p, EOk +} + +func RemoveImage(r []primitive.ObjectID, u *primitive.ObjectID) SErr { + ci := C("image") + + var filter bson.M + if u == nil { + filter = bson.M{"_id": bson.M{"$in": r}} + } else { + filter = bson.M{"_id": bson.M{"$in": r}, "user_id": *u} + } + + tags, err := ci.Distinct(X(), "storage", filter) + + if err != nil { + log.Printf("[Warn] Failed getting storage locations of images: %s\n", err) + return EUnknown + } + + if _, err := ci.DeleteMany(X(), filter); err != nil { + log.Printf("[Warn] Failed deleting images: %s\n", err) + return EUnknown + } + + for _, d := range tags { + if dir, ok := d.(string); ok { + removeFile(dir) + } + } + + return EOk +} + +type SetImageInfoQ struct { + Targets []primitive.ObjectID `json:"targets" binding:"required"` + Field string `json:"field" binding:"required"` + Data string `json:"data"` +} + +func SetImageInfo(r *SetImageInfoQ, u *primitive.ObjectID) SErr { + var filter bson.M + if u == nil { + filter = bson.M{"_id": bson.M{"$in": r.Targets}} + } else { + filter = bson.M{"_id": bson.M{"$in": r.Targets}, "user_id": *u} + } + + if r.Field == "tag" { + ci := C("image") + + if _, err := ci.UpdateMany(X(), filter, + bson.M{"$set": bson.M{"tag": r.Data}}); err != nil { + log.Printf("[Warn] Failed update images tag: %s", err) + return EUnknown + } else { + return EOk + } + } else if r.Field == "origins" { + ci := C("image") + + if _, err := ci.UpdateMany(X(), filter, + bson.M{"$set": bson.M{"origins": CleanOrigins(r.Data)}}); err != nil { + log.Printf("[Warn] Failed update images origins: %s", err) + return EUnknown + } else { + return EOk + } + } + + return EBadRequest +} + +func GetImage(r primitive.ObjectID) (*Image, SErr) { + ci := C("image") + + if n := ci.FindOne(X(), bson.M{"_id": r}); n.Err() == mongo.ErrNoDocuments { + return nil, EImageNotExist + } else if n.Err() != nil { + log.Printf("[Warn] Failed finding image: %s", n.Err()) + return nil, EUnknown + } else { + i := &Image{} + if err := n.Decode(i); err != nil { + log.Printf("[Warn] Failed reading image: %s", n.Err()) + return nil, EUnknown + } + return i, EOk + } +} diff --git a/api_image.go b/api_image.go new file mode 100644 index 0000000..3aa548d --- /dev/null +++ b/api_image.go @@ -0,0 +1,653 @@ +package main + +import ( + "ImageServer/native" + "fmt" + "github.com/OneOfOne/xxhash" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "io/ioutil" + "log" + "os" + "path" + "sync" + "time" +) + +var ( + _semaphore chan struct{} + _avifQueue chan primitive.ObjectID +) + +func InitEncoder() { + if config.Site.Thread > 3 { + _semaphore = make(chan struct{}, config.Site.Thread - 2) + for i := uint64(0); i < config.Site.Thread - 2; i++ { + _semaphore <- struct{}{} + } + } else { + _semaphore = make(chan struct{}, 1) + _semaphore <- struct{}{} + } + + _avifQueue = make(chan primitive.ObjectID, 100) + go func() { + for { + _ = genAvifImage(<-_avifQueue) + } + }() +} + +func InitCleanup() error { + ci := C("image") + _, err := ci.UpdateMany( + Xd(10 * time.Second), + bson.M{"files.hash": 0}, + bson.M{"$pull": bson.M{"files": bson.M{"hash": 0}}}) + return err +} + +func GenAvif(r primitive.ObjectID) { + _avifQueue <- r +} + +func acquire() { + <- _semaphore +} + +func release() { + _semaphore <- struct{}{} +} + +func fromInt(h uint64) (string, string, string) { + hash := fmt.Sprintf("%016x", h) + return hash, hash[0:2], hash[15:16] +} + +func fromString(hash string) (string, string) { + return hash[0:2], hash[15:16] +} + +func saveFile(b []byte, hash, name, ext string) error { + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash, fmt.Sprintf("%s.%s", name, ext)) + _ = os.MkdirAll(path.Join(config.Site.Storage, "image", high, low, hash), os.ModeDir) + return ioutil.WriteFile(p, b, os.ModePerm) +} + +func removeFile(hash string) { + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash) + _ = os.RemoveAll(p) +} + +func removeFileSingle(hash, name, ext string) { + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash, fmt.Sprintf("%s.%s", name, ext)) + _ = os.Remove(p) +} + +func readFile(hash, name, ext string) ([]byte, error) { + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash, fmt.Sprintf("%s.%s", name, ext)) + return ioutil.ReadFile(p) +} + +func renameOriginal(hash, name, ext string) error { + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash, "original.org") + np := path.Join(config.Site.Storage, "image", high, low, hash, fmt.Sprintf("%s.%s", name, ext)) + return os.Rename(p, np) +} + +func fileHash(b []byte) int64 { + h := xxhash.Checksum64(b) + + if h == 0 { + return 1 + } + + return int64(h) +} + +type UploadSimpleQ struct { + User primitive.ObjectID + Name string + Tag string + Origins []string + Data []byte + GuessedType string +} + +func getSpareHash(o uint64) (uint64, bool) { + ci := C("image") + + hashExist := func(h uint64) int { + if r := ci.FindOne(X(), bson.M{"location": fmt.Sprintf("%016x", h)}); r.Err() == nil { + return 1 + } else if r.Err() == mongo.ErrNoDocuments { + return 0 + } else { + log.Printf("[Warn] failed check image hash exist: %s\n", r.Err()) + return -1 + } + } + + offset := uint64(0) + direction := false + var result int + for result = hashExist(o); result == 1; { + // if the system has uint64(-1) images, then all available spaces are used. + // but it really unlikely to happen, so we just ignore it. + offset++ + direction = !direction + if direction { + o += offset + } else { + o -= offset + } + } + + if result == -1 { + return 0, false + } + + return o, true +} + +func UploadSimple(r *UploadSimpleQ) (primitive.ObjectID, SErr) { + f, t := Decode(r.Data, r.GuessedType) + + if f == nil { + return primitive.ObjectID{}, EBadImage + } + + var dJ, dW, dP []byte + + wg := sync.WaitGroup{} + wg.Add(3) + + go func() { + acquire() + dJ = Encode(f, "jpeg") + release() + wg.Done() + }() + + go func() { + acquire() + dW = Encode(f, "webp") + release() + wg.Done() + }() + + go func() { + defer wg.Done() + acquire() + defer release() + x, y, w, h := GetSquare(f.W(), f.H()) + fP := native.RescaleFrame(f, x, y, w, h, uint32(config.Site.PreviewSize), uint32(config.Site.PreviewSize)) + if fP == nil { + return + } + dP = Encode(fP, "jpeg") + }() + + wg.Wait() + + if dJ == nil || dW == nil || dP == nil { + return primitive.ObjectID{}, EEncodeFailed + } + + hNum := xxhash.Checksum64(r.Data) + + hNum, ok := getSpareHash(hNum) + + if !ok { + return primitive.ObjectID{}, EUnknown + } + + files := make([]ImageFormat, 0, 2) + + hStr := fmt.Sprintf("%016x", hNum) + + if err := saveFile(dP, hStr, "preview", "jpeg"); err != nil { + log.Printf("[Warn] failed saving file preview.jpeg: %s\n", err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + min := len(r.Data) + + if t == "avif" { + if err := saveFile(dJ, hStr, hStr, "jpeg"); err != nil { + log.Printf("[Warn] failed saving file %s.jpg: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: "jpeg", + Hash: fileHash(dJ), + }) + + if err := saveFile(dW, hStr, hStr, "webp"); err != nil { + log.Printf("[Warn] failed saving file %s.webp: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: "webp", + Hash: fileHash(dW), + }) + } else if t == "webp" { + if err := saveFile(dJ, hStr, hStr, "jpeg"); err != nil { + log.Printf("[Warn] failed saving file %s.jpg: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: "jpeg", + Hash: fileHash(dJ), + }) + + if len(dW) < len(r.Data) { + if err := saveFile(dW, hStr, hStr, "webp"); err != nil { + log.Printf("[Warn] failed saving file %s.webp: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: "webp", + Hash: fileHash(dW), + }) + } else { + if err := saveFile(r.Data, hStr, hStr, "webp"); err != nil { + log.Printf("[Warn] failed saving file %s.webp: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: "webp", + Hash: fileHash(dW), + }) + } + } else { + useOriginal := false + + if len(dJ) < min { + if err := saveFile(dJ, hStr, hStr, "jpeg"); err != nil { + log.Printf("[Warn] failed saving file %s.jpg: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + min = len(dJ) + files = append(files, ImageFormat{ + Format: "jpeg", + Hash: fileHash(dJ), + }) + } else { + useOriginal = true + } + + if len(dW) < min { + if err := saveFile(dW, hStr, hStr, "webp"); err != nil { + log.Printf("[Warn] failed saving file %s.webp: %s\n", hStr, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + min = len(dW) + files = append(files, ImageFormat{ + Format: "webp", + Hash: fileHash(dW), + }) + } else if !useOriginal { + useOriginal = true + } + + if useOriginal { + if err := saveFile(r.Data, hStr, hStr, t); err != nil { + log.Printf("[Warn] failed saving file %s.%s: %s\n", hStr, t, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: t, + Hash: fileHash(r.Data), + }) + } + } + + if len(dW) < min { + min = len(dW) + } + + if len(dJ) < min { + min = len(dJ) + } + + if err := saveFile(r.Data, hStr, "original", "org"); err != nil { + log.Printf("[Warn] failed saving file original.org: %s\n", err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + id := primitive.NewObjectID() + + ci := C("image") + if _, err := ci.InsertOne(X(), Image{ + ID: id, + UserID: r.User, + UserName: r.Name, + Storage: hStr, + Tag: r.Tag, + Upload: primitive.NewDateTimeFromTime(time.Now()), + View: 0, + Origins: r.Origins, + Original: true, + Size: min, + Files: files, + }); err != nil { + log.Printf("[Warn] failed inserting image: %s\n", err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + return id, EOk +} + +type ImageFile struct { + Type string + Data []byte +} + +type UploadAdvancedQ struct { + User primitive.ObjectID + Name string + Tag string + Origins []string + Files []ImageFile +} + +var AllowType = map[string]struct{} { + "jpeg": {}, + "png": {}, + "webp": {}, + "avif": {}, +} + +func UploadAdvanced(r *UploadAdvancedQ) (primitive.ObjectID, SErr) { + if len(r.Files) == 0 { + return primitive.ObjectID{}, EBadRequest + } + + hNum := xxhash.Checksum64(r.Files[0].Data) + + f, _ := Decode(r.Files[0].Data, r.Files[0].Type) + if f == nil { + return primitive.ObjectID{}, EBadImage + } + + acquire() + x, y, w, h := GetSquare(f.W(), f.H()) + fP := native.RescaleFrame(f, x, y, w, h, uint32(config.Site.PreviewSize), uint32(config.Site.PreviewSize)) + if fP == nil { + release() + log.Println("[Warn] failed resizing preview.") + return primitive.ObjectID{}, EEncodeFailed + } + dP := Encode(fP, "jpeg") + if dP == nil { + release() + log.Println("[Warn] failed encoding preview.") + return primitive.ObjectID{}, EEncodeFailed + } + release() + + hNum, ok := getSpareHash(hNum) + + if !ok { + return primitive.ObjectID{}, EUnknown + } + + hStr := fmt.Sprintf("%016x", hNum) + + if err := saveFile(dP, hStr, "preview", "jpeg"); err != nil { + log.Printf("[Warn] failed saving file preview.jpeg: %s\n", err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files := make([]ImageFormat, 0, len(r.Files)) + + for _, f := range r.Files { + if _, exist := AllowType[f.Type]; !exist { + continue + } + + if err := saveFile(f.Data, hStr, hStr, f.Type); err != nil { + log.Printf("[Warn] failed saving file %s.%s: %s\n", hStr, f.Type, err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + files = append(files, ImageFormat{ + Format: f.Type, + Hash: fileHash(f.Data), + }) + } + + if len(files) == 0 { + return primitive.ObjectID{}, EBadRequest + } + + id := primitive.NewObjectID() + + ci := C("image") + if _, err := ci.InsertOne(X(), Image{ + ID: id, + UserID: r.User, + UserName: r.Name, + Storage: hStr, + Tag: r.Tag, + Upload: primitive.NewDateTimeFromTime(time.Now()), + View: 0, + Origins: r.Origins, + Original: false, + Size: 0, + Files: files, + }); err != nil { + log.Printf("[Warn] failed inserting image: %s\n", err) + removeFile(hStr) + return primitive.ObjectID{}, EUnknown + } + + return id, EOk +} + +type UpdateImageQ struct { + ID primitive.ObjectID + Files []ImageFile +} + +func UpdateImage(r *UpdateImageQ) SErr { + if len(r.Files) == 0 { + return EBadRequest + } + + ci := C("image") + + im := &Image{} + + if u := ci.FindOne(X(), bson.M{"_id": r.ID}); u.Err() == nil { + if err := u.Decode(im); err != nil { + log.Printf("[Warn] failed loading image with id %s: %s\n", r.ID, err) + return EUnknown + } + } else if u.Err() == mongo.ErrNoDocuments { + return EImageNotExist + } else { + log.Printf("[Warn] failed finding image with id %s: %s\n", r.ID, u.Err()) + return EUnknown + } + + hStr := im.Storage + files := make([]ImageFormat, 0, len(r.Files) + len(im.Files)) + + for _, f := range r.Files { + if _, exist := AllowType[f.Type]; !exist { + continue + } + + if err := saveFile(f.Data, hStr, hStr, f.Type); err != nil { + log.Printf("[Warn] failed saving file %s.%s: %s\n", hStr, f.Type, err) + removeFileSingle(hStr, hStr, f.Type) + continue + } + + files = append(files, ImageFormat{ + Format: f.Type, + Hash: fileHash(f.Data), + }) + } + + if len(files) == 0 { + return EBadRequest + } + + fMap := make(map[string]struct{}) + + for _, f := range files { + fMap[f.Format] = struct{}{} + } + + for _, f := range im.Files { + if _, exist := fMap[f.Format]; !exist { + files = append(files, f) + } + } + + if im.Original { + removeFileSingle(im.Storage, "original", "org") + } + + if _, err := ci.UpdateOne(X(), + bson.M{"_id": im.ID}, + bson.M{"$set": bson.M{"files": files, "original": false}}); err != nil { + log.Printf("[Warn] failed inserting image: %s\n", err) + return EUnknown + } + + return EOk +} + +func genAvifImage(r primitive.ObjectID) SErr { + ci := C("image") + + im := &Image{} + + if u := ci.FindOne(X(), bson.M{"_id": r}); u.Err() == nil { + if err := u.Decode(im); err != nil { + log.Printf("[Warn] failed loading image with id %s: %s\n", r, err) + return EUnknown + } + } else if u.Err() == mongo.ErrNoDocuments { + return EImageNotExist + } else { + log.Printf("[Warn] failed finding image with id %s: %s\n", r, u.Err()) + return EUnknown + } + + var err error + var d []byte + + if im.Original { + d, err = readFile(im.Storage, "original", "org") + } else { + return EMissingOriginal + } + + if err != nil { + log.Printf("[Warn] failed opening orginal image of %s: %s\n", im.Storage, err) + return EUnknown + } + + f, t := Decode(d, "") + + if f == nil { + return EBadImage + } + + dA := Encode(f, "avif") + + if dA == nil { + return EEncodeFailed + } + + if len(dA) >= im.Size { + if t != "avif" { + return EOk + } else if err := renameOriginal(im.Storage, im.Storage, "avif"); err != nil { + return EUnknown + } + } else { + if err := saveFile(dA, im.Storage, im.Storage, "avif"); err != nil { + log.Printf("[Warn] failed saving file %s.avif: %s\n", im.Storage, err) + removeFileSingle(im.Storage, im.Storage, "avif") + return EUnknown + } + } + + for i := range im.Files { + if im.Files[i].Format == "avif" { + im.Files[i].Hash = fileHash(dA) + break + } + } + + if _, err := ci.UpdateOne(X(), + bson.M{"_id": im.ID}, + bson.M{"$set": bson.M{"files": im.Files, "original": false}}); err != nil { + log.Printf("[Warn] failed inserting image: %s\n", err) + return EUnknown + } + + if im.Original { + removeFileSingle(im.Storage, "original", "org") + } + + return EOk +} + +func PostVisitImage(r primitive.ObjectID, emitAvif bool) SErr { + ci := C("image") + + if _, err := ci.UpdateOne(X(), bson.M{"_id": r}, bson.M{"$inc": bson.M{"view": 1}}); err != nil { + log.Printf("[Warn] failed update view count of image %s: %s\n",r , err) + return EUnknown + } + + if emitAvif { + _, err := ci.UpdateOne(X(), bson.M{"_id": r}, bson.M{"$push": bson.M{"files": ImageFormat{ + Format: "avif", + Hash: 0, + }}}) + + if err != nil { + log.Printf("[Warn] failed adding avif placeholder of %s: %s\n", r, err) + return EUnknown + } + + GenAvif(r) + } + + return EOk +} \ No newline at end of file diff --git a/api_user.go b/api_user.go new file mode 100644 index 0000000..a656a04 --- /dev/null +++ b/api_user.go @@ -0,0 +1,680 @@ +package main + +import ( + "ImageServer/native" + "crypto/subtle" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "io/ioutil" + "log" + "os" + "path" + "time" +) + +var AdminID primitive.ObjectID + +func InitUser() { + m := C("user").FindOne(X(), bson.M{"name": "admin"}) + + if m.Err() == nil { + if config.Site.Debug { + log.Println("[Info] Admin account exist, skip bootstrap.") + } + + u := &User{} + err := m.Decode(u) + + if err != nil { + log.Fatalf("Failed loading admin info: %s", err) + } + + AdminID = u.ID + return + } else if m.Err() != mongo.ErrNoDocuments { + log.Fatalf("Failed reading `user` collection: %s", m.Err()) + } + + AdminID = primitive.NewObjectID() + + _, err := C("user").InsertOne(X(), &User{ + ID: AdminID, + Name: "admin", + Password: PasswordHash(config.Security.AdminPassword), + Privileged: true, + }) + + if err != nil { + log.Fatalf("Failed creating system admin account: %s", err) + } +} + +func UserExist(name string) (bool, SErr) { + cu := C("user") + + n := cu.FindOne(X(), bson.M{"name": name}) + + if n.Err() == mongo.ErrNoDocuments { + return false, EOk + } else if n.Err() != nil { + log.Printf("[Warn] Failed checking user named `%s` exist: %s\n", name, n.Err()) + return false, EUnknown + } + + return true, EOk +} + +type RegisterQ struct { + Name string `json:"name" binding:"required"` + Password string `json:"password" binding:"required"` + InviteCode string `json:"invite_code" binding:"required"` + Recaptcha string `json:"recaptcha" binding:"required"` +} + +func Register(r *RegisterQ) SErr { + cu := C("user") + + if e := RecaptchaVerify(r.Recaptcha); e != EOk { + return e + } + + if !PasswordVerify(r.Password) { + if config.Site.Debug { + log.Printf("[Info] Some tries to register with name `%s` but weak password.\n", r.Name) + } + return EWeakPassword + } + + e, serr := UserExist(r.Name) + + if serr != EOk { + return serr + } + + if e { + return EUserExist + } + + code := &InviteCode{} + + ci := C("invite") + + m := ci.FindOne(X(), bson.M{"code": r.InviteCode}) + + if m.Err() == mongo.ErrNoDocuments { + return EInvalidInviteCode + } else if m.Err() != nil { + log.Printf("[Warn] Failed checking invite code `%s` is valid: %s\n", r.InviteCode, m.Err()) + return EUnknown + } + + if err := m.Decode(code); err != nil { + log.Printf("[Warn] Failed loading invite code info: %s\n", err) + return EUnknown + } + + if code.Times > 0 { + _, err := ci.UpdateOne(X(), + bson.M{"code": r.InviteCode}, + bson.M{"$inc": bson.M{"times": -1}}) + + if err != nil { + log.Printf("[Warn] Failed updating count of invite code `%s`: %s\n", r.InviteCode, err) + return EUnknown + } + } else { + if config.Site.Debug { + log.Printf("[Info] Some tries to register with expired invite code `%s`.\n", r.InviteCode) + } + return EInvalidInviteCode + } + + if config.Site.Debug { + log.Printf("[Info] `%s` registered with invite code `%s.\n", r.Name, r.InviteCode) + } + + _, err := cu.InsertOne(X(), &User{ + ID: primitive.NewObjectID(), + Name: r.Name, + Password: PasswordHash(r.Password), + Privileged: false, + Frozen: false, + VersionP: 0, + VersionC: 0, + }) + + if err != nil { + log.Printf("[Warn] Failed adding user `%s`: %s\n", r.Name, err) + return EUnknown + } + + return EOk +} + +type LoginQ struct { + Name string `json:"name" binding:"required"` + Password string `json:"password" binding:"required"` + Recaptcha string `json:"recaptcha" binding:"required"` +} + +func Login(r *LoginQ) (*User, SErr) { + if e := RecaptchaVerify(r.Recaptcha); e != EOk { + return nil, e + } + + u := &User{} + + cu := C("user") + + m := cu.FindOne(X(), bson.M{"name": r.Name}) + + if m.Err() == mongo.ErrNoDocuments { + if config.Site.Debug { + log.Printf("[Info] Some tries login to nonexist account `%s`.\n", r.Name) + } + return nil, EBadCredential + } else if m.Err() != nil { + log.Printf("[Warn] Failed checking if user `%s` exist: %s\n", r.Name, m.Err()) + return nil, EUnknown + } + + if err := m.Decode(u); err != nil { + log.Printf("[Warn] Failed loading user: %s\n", err) + return nil, EUnknown + } + + if subtle.ConstantTimeCompare(PasswordHash(r.Password), u.Password) == 0 { + if config.Site.Debug { + log.Printf("[Info] Some tries login to account `%s` with wrong credential.\n", r.Name) + } + return nil, EBadCredential + } + + return u, EOk +} + +func GetUser(r string) (*User, SErr) { + id, err := primitive.ObjectIDFromHex(r) + var filter bson.M + if err == nil { + filter = bson.M{"_id": id} + } else { + filter = bson.M{"name": r} + } + + u := &User{} + + cu := C("user") + + m := cu.FindOne(X(), filter) + + if m.Err() == mongo.ErrNoDocuments { + return nil, EUserNotExist + } else if m.Err() != nil { + log.Printf("[Warn] Failed get user `%s`: %s\n", r, m.Err()) + return nil, EUnknown + } + + if err := m.Decode(u); err != nil { + log.Printf("[Warn] Failed loading user: %s\n", err) + return nil, EUnknown + } + + return u, EOk +} + +type ChangePasswordQ struct { + OldPassword string `json:"old_password" binding:"required"` + NewPassword string `json:"new_password" binding:"required"` +} + +func ChangePassword(r *ChangePasswordQ, u *User) SErr { + if subtle.ConstantTimeCompare(PasswordHash(r.OldPassword), u.Password) == 0 { + if config.Site.Debug { + log.Printf("[Info] User `%s` tries to change password with wrong credential.\n", u.Name) + } + return EBadCredential + } + + if !PasswordVerify(r.NewPassword) { + if config.Site.Debug { + log.Printf("[Info] User `%s` tries to change to a weak password.\n", u.Name) + } + return EWeakPassword + } + + cu := C("user") + + _, err := cu.UpdateOne(X(), + bson.M{"_id": u.ID}, + bson.M{ + "$inc": bson.M{"version_p": 1}, + "$set": bson.M{"password": PasswordHash(r.NewPassword)}, + }) + + if err != nil { + log.Printf("[Warn] Failed updating password of `%s`: %s\n", u.Name, err) + return EUnknown + } + + return EOk +} + +type SetAvatarQ struct { + ID primitive.ObjectID + Data []byte +} + +func saveAvatar(b []byte, id primitive.ObjectID) error { + h := id.Hex() + high := h[6:8] + low := h[10:12] + p := path.Join(config.Site.Storage, "avatar", high, low, h+".jpeg") + _ = os.MkdirAll(path.Join(config.Site.Storage, "avatar", high, low), os.ModeDir) + return ioutil.WriteFile(p, b, os.ModePerm) +} + +func removeAvatar(id primitive.ObjectID) { + h := id.Hex() + high := h[6:8] + low := h[10:12] + p := path.Join(config.Site.Storage, "avatar", high, low, h+".jpeg") + _ = os.Remove(p) +} + +func SetAvatar(r *SetAvatarQ) SErr { + f, _ := Decode(r.Data, "") + if f == nil { + return EBadImage + } + + var d []byte + + if f.W() == f.H() && f.W() <= 256 { + d = Encode(f, "jpeg") + } else { + x, y, w, h := GetSquare(f.W(), f.H()) + fR := native.RescaleFrame(f, x, y, w, h, uint32(config.Site.AvatarSize), uint32(config.Site.AvatarSize)) + if fR == nil { + log.Println("[Warn] Failed resizing avatar.") + return EEncodeFailed + } + + d = Encode(fR, "jpeg") + } + + if err := saveAvatar(d, r.ID); err != nil { + log.Printf("[Warn] Failed saving avatar: %s.", err) + return EUnknown + } + + return EOk +} + +type ListUserQ struct { + Offset uint `json:"offset"` + Limit uint `json:"limit" binding:"required"` + Keyword string `json:"keyword"` +} + +type ListUserP struct { + Total uint `json:"total"` + Count uint `json:"count"` + Users []User `json:"users"` +} + +func ListUser(r *ListUserQ) (*ListUserP, SErr) { + cu := C("user") + + cCur, err := cu.Aggregate(X(), mongo.Pipeline{ + bson.D{{"$match", bson.M{ + "_id": bson.M{"$ne": AdminID}, + }}}, + bson.D{{"$project", bson.M{ + "offset": bson.M{"$indexOfCP": bson.A{"$name", r.Keyword}}, + }}}, + bson.D{{"$match", bson.M{ + "offset": bson.M{"$ne": -1}, + }}}, + bson.D{{"$count", "count"}}, + }) + + if err != nil { + log.Printf("[Warn] Failed counting users with keyword `%s`: %s\n", r.Keyword, err) + return nil, EUnknown + } + + cS := struct { + Count uint `bson:"count"` + }{} + _ = cCur.Next(X()) + err = cCur.Decode(&cS) + + if err != nil { + log.Printf("[Warn] Failed reading user count: %s\n", err) + return nil, EUnknown + } + + cur, err := cu.Aggregate(X(), mongo.Pipeline{ + bson.D{{"$match", bson.M{ + "_id": bson.M{"$ne": AdminID}, + }}}, + bson.D{{"$project", bson.M{ + "name": 1, + "avatar": 1, + "privileged": 1, + "frozen": 1, + "offset": bson.M{"$indexOfCP": bson.A{"$name", r.Keyword}}, + }}}, + bson.D{{"$match", bson.M{ + "offset": bson.M{"$ne": -1}, + }}}, + bson.D{{"$sort", bson.D{ + {"offset", 1}, + {"name", 1}, + }}}, + bson.D{{"$skip", r.Offset}}, + bson.D{{"$limit", r.Limit}}, + }) + + if err != nil { + log.Printf("[Warn] Failed searching users with keyword `%s`: %s\n", r.Keyword, err) + return nil, EUnknown + } + + if cur.Err() == mongo.ErrNoDocuments { + return &ListUserP{ + Total: cS.Count, + Count: 0, + Users: nil, + }, EOk + } + + result := &ListUserP{ + Total: cS.Count, + Users: make([]User, 0, r.Limit), + } + + err = cur.All(X(), &result.Users) + + if err != nil { + log.Printf("[Warn] Failed loading users: %s\n", err) + return nil, EUnknown + } + + result.Count = uint(len(result.Users)) + + return result, EOk +} + +type AddUserQ struct { + Name string `json:"name" binding:"required"` + Password string `json:"password" binding:"required"` + Privileged bool `json:"privileged"` +} + +func AddUser(r *AddUserQ) SErr { + cu := C("user") + + e, serr := UserExist(r.Name) + + if serr != EOk { + return serr + } + + if e { + log.Printf("[Info] Admin tries to create with exist account name `%s`.\n", r.Name) + return EUserExist + } + + _, err := cu.InsertOne(X(), &User{ + ID: primitive.NewObjectID(), + Name: r.Name, + Password: PasswordHash(r.Password), + Privileged: r.Privileged, + Frozen: false, + VersionP: 0, + VersionC: 0, + }) + + if err != nil { + log.Printf("[Warn] Failed adding user `%s`: %s\n", r.Name, err) + return EUnknown + } + + return EOk +} + +type RemoveUserQ struct { + Users []primitive.ObjectID `json:"users" binding:"required"` + Cascade bool `json:"cascade"` +} + +func RemoveUser(r *RemoveUserQ) SErr { + for _, id := range r.Users { + if id == AdminID { + return EBadRequest + } + } + + cu := C("user") + ci := C("image") + + if r.Cascade { + _, err := ci.DeleteMany(Xd(5 * time.Second), bson.M{ + "user_id": bson.M{"$in": r.Users}, + }) + + if err != nil { + log.Printf("[Warn] Failed deleting images: %s\n", err) + return EUnknown + } + } else { + _, err := ci.UpdateMany(Xd(5 * time.Second), + bson.M{"user_id": bson.M{"$in": r.Users}}, + bson.M{"$set": bson.M{"user_id": AdminID, "user_name": "admin"}}, + ) + + if err != nil { + log.Printf("[Warn] Failed changing owner of images: %s\n", err) + return EUnknown + } + } + + _, err := cu.DeleteMany(X(), bson.M{ + "_id": bson.M{"$in": r.Users}, + }) + + if err != nil { + log.Printf("[Warn] Failed deleting users: %s\n", err) + return EUnknown + } + + return EOk +} + +type SetPasswordQ struct { + UserID primitive.ObjectID `json:"user_id" binding:"required"` + Password string `json:"password" binding:"required"` +} + +func SetPassword(r *SetPasswordQ) SErr { + cu := C("user") + + n, err := cu.UpdateOne(X(), + bson.M{"_id": r.UserID}, + bson.M{ + "$inc": bson.M{"version_p": 1}, + "$set": bson.M{"password": PasswordHash(r.Password)}, + }) + + if err != nil { + log.Printf("[Warn] Failed setting password: %s\n", err) + return EUnknown + } + + if n.MatchedCount == 0 { + if config.Site.Debug { + log.Println("[Info] Admin tries to set password of unknown user.") + } + + return EUserNotExist + } + return EOk +} + +type SetUserPermissionQ struct { + UserID primitive.ObjectID `json:"user_id" binding:"required"` + Privileged bool `json:"privileged"` + Frozen bool `json:"frozen"` +} + +func SetUserPermission(r *SetUserPermissionQ) SErr { + cu := C("user") + + n, err := cu.UpdateOne(X(), + bson.M{"_id": r.UserID}, + bson.M{ + "$inc": bson.M{"version_c": 1}, + "$set": bson.M{ + "privileged": r.Privileged, + "frozen": r.Frozen, + }, + }) + + if err != nil { + log.Printf("[Warn] Failed setting permission: %s\n", err) + return EUnknown + } + + if n.MatchedCount == 0 { + if config.Site.Debug { + log.Println("[Info] Admin tries to set permission of unknown user.") + } + + return EUserNotExist + } + + return EOk +} + +type ListInviteQ struct { + Offset uint `json:"offset"` + Limit uint `json:"limit" binding:"required"` +} + +type ListInviteP struct { + Total uint `json:"total"` + Count uint `json:"count"` + Codes []InviteCode `json:"codes"` +} + +func ListInvite(r *ListInviteQ) (*ListInviteP, SErr) { + ci := C("invite") + + n, err := ci.EstimatedDocumentCount(X()) + + if err != nil { + log.Printf("[Warn] Failed counting invite code: %s\n", err) + return nil, EUnknown + } + + p := &ListInviteP{} + p.Total = uint(n) + + if n - int64(r.Offset) <= 0 { + return p, EOk + } + + p.Codes = make([]InviteCode, 0, r.Limit) + + opts := options.Find() + opts.SetSkip(int64(r.Offset)) + opts.SetLimit(int64(r.Limit)) + + cur, err := ci.Find(X(), bson.M{}, opts) + + if err == mongo.ErrNoDocuments { + p.Count = 0 + return p, EOk + } else if err != nil { + log.Printf("[Warn] Failed reading invite code: %s", err) + return nil, EUnknown + } + + if err = cur.All(X(), &p.Codes); err != nil { + log.Printf("[Warn] Failed loading invite code: %s", err) + return nil, EUnknown + } + + p.Count = uint(len(p.Codes)) + return p, EOk +} + +func AddInvite(r *InviteCode) SErr { + if r.Times < 1 { + return EBadRequest + } + + ci := C("invite") + + n := ci.FindOne(X(), bson.M{"code": r.Code}) + + if n.Err() == nil { + if config.Site.Debug { + log.Printf("[Info] Admin tries to add existed invite code `%s`.\n", r.Code) + } + + return EInviteCodeExist + } + + if n.Err() != mongo.ErrNoDocuments { + log.Printf("[Warn] Failed checking invite code `%s` exist: %s\n", r.Code, n.Err()) + return EUnknown + } + + _, err := ci.InsertOne(X(), r) + + if err != nil { + log.Printf("[Warn] Failed adding invite code `%s`: %s\n", r.Code, n.Err()) + return EUnknown + } + + return EOk +} + +func RemoveInvite(code string) SErr { + ci := C("invite") + + n, err := ci.DeleteOne(X(), bson.M{"code": code}) + + if err != nil { + log.Printf("[Warn] Failed removing invite code `%s`: %s\n", code, err) + return EUnknown + } + + if n.DeletedCount == 0 { + return EInviteCodeNotExist + } + + return EOk +} + +func SetInviteTimes(r *InviteCode) SErr { + ci := C("invite") + + n, err := ci.UpdateOne(X(), bson.M{"code": r.Code}, bson.M{"$set": bson.M{"times": r.Times}}) + + if err != nil { + log.Printf("[Warn] Failed updating invite code available count `%s`: %s\n", r.Code, err) + return EUnknown + } + + if n.MatchedCount == 0 { + return EInviteCodeNotExist + } + + return EOk +} \ No newline at end of file diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt new file mode 100644 index 0000000..b04e082 --- /dev/null +++ b/c/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.10) +project(cImg C) + +set(CMAKE_C_STANDARD 99) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") + +add_library(cImg STATIC cimg.h config.c image.c im_png.c im_webp.c q_webp.c im_avif.c im_jpeg.c image_rescale.c mem.c) \ No newline at end of file diff --git a/c/cimg.h b/c/cimg.h new file mode 100644 index 0000000..f319a76 --- /dev/null +++ b/c/cimg.h @@ -0,0 +1,117 @@ +#ifndef C_LIBRARY_H +#define C_LIBRARY_H + +#include +#include +#include +#include + +// defines +typedef int8_t i8; +typedef uint8_t u8; + +typedef int16_t i16; +typedef uint16_t u16; + +typedef int32_t i32; +typedef uint32_t u32; + +typedef int64_t i64; +typedef uint64_t u64; + +#if defined(__GNUC__) && __GNUC__ >= 4 +# define pub __attribute__ ((visibility ("default"))) +# define really_inline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define pub __declspec(dllexport) +# define really_inline inline __forceinline +#else +# define pub +# define really_inline inline +#endif /* __GNUC__ >= 4 */ + +// settings +pub void SetMaxSize(u32 size); + +pub u32 GetMaxSize(); + +pub void SetMaxThread(u32 thread); + +pub u32 GetMaxThread(); + +pub u32 Quality(u32 width, u32 height, u8 quality); + +// types +typedef enum ImageFormat { + FORMAT_UNDEFINED = 0, + FORMAT_RGB = 1, + FORMAT_BGR, + + FORMAT_RGBA = (1u << 31u) + 1, + FORMAT_BGRA, + FORMAT_ARGB, + FORMAT_ABGR +} ImageFormat; + +really_inline u8 FormatChannelCount(ImageFormat format) { + return format ? ((format >> 31u) ? 4 : 3) : 0; +} + +typedef struct Frame { + u32 width; + u32 height; + u8 depth; + ImageFormat format; + u8 quality; + + u8* pixel; // real type depends on depth + u32 stride; +} Frame; + +// frame utils +pub bool AllocateFrame(Frame *img); + +pub bool ReleaseFrame(Frame *img); + +pub bool ZeroFrame(Frame *img, u32 x, u32 y, u32 w, u32 h); + +pub bool CloneFrame(Frame *src, Frame *dst, u32 x, u32 y, u32 w, u32 h); + +pub bool BlendFrame(Frame *src, Frame *dst, u32 x, u32 y, u32 w, u32 h); + +pub bool BlendImageAlpha(Frame *src, Frame *dst, u32 background_rgb); + +pub bool RescaleImage(Frame *src, Frame* dst, u32 x, u32 y, u32 w, u32 h); + +// png utils +pub bool IsPNG(const u8 *data, size_t size, u32* width, u32* height); + +pub bool DecPNG(const u8* data, size_t size, Frame* img); + +//jpeg utils +pub bool IsJPEG(const u8 *data, size_t size, u32* width, u32* height); + +pub bool DecJPEG(const u8 *data, size_t size, Frame *img); + +pub bool EncJPEG(u8** data, size_t* size, Frame *img); + +//webp utils +pub bool IsWEBP(const u8 *data, size_t size, u32* width, u32* height); + +pub bool DecWEBP(const u8* data, size_t size, Frame* img); + +pub bool EncWEBP(u8** data, size_t* size, Frame *img); + +pub int QualityWEBP(const u8* data, size_t size); + +//avif utils +pub bool IsAVIF(const u8 *data, size_t size, u32* width, u32* height); + +pub bool DecAVIF(const u8 *data, size_t size, Frame *img); + +pub bool EncAVIF(u8** data, size_t* size, Frame *img); + +//mem utils + +pub bool TrimMemory(); +#endif //C_LIBRARY_H diff --git a/c/config.c b/c/config.c new file mode 100644 index 0000000..a3e515e --- /dev/null +++ b/c/config.c @@ -0,0 +1,38 @@ +// +// Created by TYTY on 2020-08-07 007. +// + +#include "cimg.h" + +static u32 MAX_SIZE = 0; +static u32 MAX_THREAD = 0; + +void SetMaxSize(u32 size) { + MAX_SIZE = size; +} + +u32 GetMaxSize() { + return MAX_SIZE; +} + +void SetMaxThread(u32 thread) { + MAX_THREAD = thread; +} + +u32 GetMaxThread() { + return MAX_THREAD; +} + +u32 Quality(u32 width, u32 height, u8 quality) { + u32 size = width > height ? width : height; + if (size > MAX_SIZE) { + return -1; + } + if (2 * size > MAX_SIZE) { + return quality > 80 ? 80 : quality; + } else if (4 * size > MAX_SIZE) { + return quality > 90 ? 90 : quality; + } else { + return quality; + } +} \ No newline at end of file diff --git a/c/header/avif.h b/c/header/avif.h new file mode 100644 index 0000000..9a6830c --- /dev/null +++ b/c/header/avif.h @@ -0,0 +1,706 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#ifndef AVIF_AVIF_H +#define AVIF_AVIF_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +// Constants + +#define AVIF_VERSION_MAJOR 0 +#define AVIF_VERSION_MINOR 8 +#define AVIF_VERSION_PATCH 0 +#define AVIF_VERSION (AVIF_VERSION_MAJOR * 10000) + (AVIF_VERSION_MINOR * 100) + AVIF_VERSION_PATCH + +typedef int avifBool; +#define AVIF_TRUE 1 +#define AVIF_FALSE 0 + +#define AVIF_QUANTIZER_LOSSLESS 0 +#define AVIF_QUANTIZER_BEST_QUALITY 0 +#define AVIF_QUANTIZER_WORST_QUALITY 63 + +#define AVIF_PLANE_COUNT_YUV 3 + +#define AVIF_SPEED_DEFAULT -1 +#define AVIF_SPEED_SLOWEST 0 +#define AVIF_SPEED_FASTEST 10 + +enum avifPlanesFlags +{ + AVIF_PLANES_YUV = (1 << 0), + AVIF_PLANES_A = (1 << 1), + + AVIF_PLANES_ALL = 0xff +}; + +enum avifChannelIndex +{ + // rgbPlanes + AVIF_CHAN_R = 0, + AVIF_CHAN_G = 1, + AVIF_CHAN_B = 2, + + // yuvPlanes + AVIF_CHAN_Y = 0, + AVIF_CHAN_U = 1, + AVIF_CHAN_V = 2 +}; + +// --------------------------------------------------------------------------- +// Version + +const char * avifVersion(void); +void avifCodecVersions(char outBuffer[256]); + +// --------------------------------------------------------------------------- +// Memory management + +void * avifAlloc(size_t size); +void avifFree(void * p); + +// --------------------------------------------------------------------------- +// avifResult + +typedef enum avifResult +{ + AVIF_RESULT_OK = 0, + AVIF_RESULT_UNKNOWN_ERROR, + AVIF_RESULT_INVALID_FTYP, + AVIF_RESULT_NO_CONTENT, + AVIF_RESULT_NO_YUV_FORMAT_SELECTED, + AVIF_RESULT_REFORMAT_FAILED, + AVIF_RESULT_UNSUPPORTED_DEPTH, + AVIF_RESULT_ENCODE_COLOR_FAILED, + AVIF_RESULT_ENCODE_ALPHA_FAILED, + AVIF_RESULT_BMFF_PARSE_FAILED, + AVIF_RESULT_NO_AV1_ITEMS_FOUND, + AVIF_RESULT_DECODE_COLOR_FAILED, + AVIF_RESULT_DECODE_ALPHA_FAILED, + AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH, + AVIF_RESULT_ISPE_SIZE_MISMATCH, + AVIF_RESULT_NO_CODEC_AVAILABLE, + AVIF_RESULT_NO_IMAGES_REMAINING, + AVIF_RESULT_INVALID_EXIF_PAYLOAD, + AVIF_RESULT_INVALID_IMAGE_GRID +} avifResult; + +const char * avifResultToString(avifResult result); + +// --------------------------------------------------------------------------- +// avifROData/avifRWData: Generic raw memory storage + +typedef struct avifROData +{ + const uint8_t * data; + size_t size; +} avifROData; + +// Note: Use avifRWDataFree() if any avif*() function populates one of these. + +typedef struct avifRWData +{ + uint8_t * data; + size_t size; +} avifRWData; + +// clang-format off +// Initialize avifROData/avifRWData on the stack with this +#define AVIF_DATA_EMPTY { NULL, 0 } +// clang-format on + +void avifRWDataRealloc(avifRWData * raw, size_t newSize); +void avifRWDataSet(avifRWData * raw, const uint8_t * data, size_t len); +void avifRWDataFree(avifRWData * raw); + +// --------------------------------------------------------------------------- +// avifPixelFormat + +typedef enum avifPixelFormat +{ + // No pixels are present + AVIF_PIXEL_FORMAT_NONE = 0, + + AVIF_PIXEL_FORMAT_YUV444, + AVIF_PIXEL_FORMAT_YUV422, + AVIF_PIXEL_FORMAT_YUV420, + AVIF_PIXEL_FORMAT_YUV400 +} avifPixelFormat; +const char * avifPixelFormatToString(avifPixelFormat format); + +typedef struct avifPixelFormatInfo +{ + avifBool monochrome; + int chromaShiftX; + int chromaShiftY; +} avifPixelFormatInfo; + +void avifGetPixelFormatInfo(avifPixelFormat format, avifPixelFormatInfo * info); + +// --------------------------------------------------------------------------- +// avifChromaSamplePosition + +typedef enum avifChromaSamplePosition +{ + AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN = 0, + AVIF_CHROMA_SAMPLE_POSITION_VERTICAL = 1, + AVIF_CHROMA_SAMPLE_POSITION_COLOCATED = 2 +} avifChromaSamplePosition; + +// --------------------------------------------------------------------------- +// avifRange + +typedef enum avifRange +{ + AVIF_RANGE_LIMITED = 0, + AVIF_RANGE_FULL = 1 +} avifRange; + +// --------------------------------------------------------------------------- +// CICP enums - https://www.itu.int/rec/T-REC-H.273-201612-I/en + +typedef enum avifColorPrimaries +{ + // This is actually reserved, but libavif uses it as a sentinel value. + AVIF_COLOR_PRIMARIES_UNKNOWN = 0, + + AVIF_COLOR_PRIMARIES_BT709 = 1, + AVIF_COLOR_PRIMARIES_IEC61966_2_4 = 1, + AVIF_COLOR_PRIMARIES_UNSPECIFIED = 2, + AVIF_COLOR_PRIMARIES_BT470M = 4, + AVIF_COLOR_PRIMARIES_BT470BG = 5, + AVIF_COLOR_PRIMARIES_BT601 = 6, + AVIF_COLOR_PRIMARIES_SMPTE240 = 7, + AVIF_COLOR_PRIMARIES_GENERIC_FILM = 8, + AVIF_COLOR_PRIMARIES_BT2020 = 9, + AVIF_COLOR_PRIMARIES_XYZ = 10, + AVIF_COLOR_PRIMARIES_SMPTE431 = 11, + AVIF_COLOR_PRIMARIES_SMPTE432 = 12, // DCI P3 + AVIF_COLOR_PRIMARIES_EBU3213 = 22 +} avifColorPrimaries; + +// outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY +void avifColorPrimariesGetValues(avifColorPrimaries acp, float outPrimaries[8]); +avifColorPrimaries avifColorPrimariesFind(const float inPrimaries[8], const char ** outName); + +typedef enum avifTransferCharacteristics +{ + // This is actually reserved, but libavif uses it as a sentinel value. + AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN = 0, + + AVIF_TRANSFER_CHARACTERISTICS_BT709 = 1, + AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED = 2, + AVIF_TRANSFER_CHARACTERISTICS_BT470M = 4, // 2.2 gamma + AVIF_TRANSFER_CHARACTERISTICS_BT470BG = 5, // 2.8 gamma + AVIF_TRANSFER_CHARACTERISTICS_BT601 = 6, + AVIF_TRANSFER_CHARACTERISTICS_SMPTE240 = 7, + AVIF_TRANSFER_CHARACTERISTICS_LINEAR = 8, + AVIF_TRANSFER_CHARACTERISTICS_LOG100 = 9, + AVIF_TRANSFER_CHARACTERISTICS_LOG100_SQRT10 = 10, + AVIF_TRANSFER_CHARACTERISTICS_IEC61966 = 11, + AVIF_TRANSFER_CHARACTERISTICS_BT1361 = 12, + AVIF_TRANSFER_CHARACTERISTICS_SRGB = 13, + AVIF_TRANSFER_CHARACTERISTICS_BT2020_10BIT = 14, + AVIF_TRANSFER_CHARACTERISTICS_BT2020_12BIT = 15, + AVIF_TRANSFER_CHARACTERISTICS_SMPTE2084 = 16, // PQ + AVIF_TRANSFER_CHARACTERISTICS_SMPTE428 = 17, + AVIF_TRANSFER_CHARACTERISTICS_HLG = 18 +} avifTransferCharacteristics; + +typedef enum avifMatrixCoefficients +{ + AVIF_MATRIX_COEFFICIENTS_IDENTITY = 0, + AVIF_MATRIX_COEFFICIENTS_BT709 = 1, + AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED = 2, + AVIF_MATRIX_COEFFICIENTS_FCC = 4, + AVIF_MATRIX_COEFFICIENTS_BT470BG = 5, + AVIF_MATRIX_COEFFICIENTS_BT601 = 6, + AVIF_MATRIX_COEFFICIENTS_SMPTE240 = 7, + AVIF_MATRIX_COEFFICIENTS_YCGCO = 8, + AVIF_MATRIX_COEFFICIENTS_BT2020_NCL = 9, + AVIF_MATRIX_COEFFICIENTS_BT2020_CL = 10, + AVIF_MATRIX_COEFFICIENTS_SMPTE2085 = 11, + AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL = 12, + AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL = 13, + AVIF_MATRIX_COEFFICIENTS_ICTCP = 14 +} avifMatrixCoefficients; + +// --------------------------------------------------------------------------- +// Optional transformation structs + +typedef enum avifTransformationFlags +{ + AVIF_TRANSFORM_NONE = 0, + + AVIF_TRANSFORM_PASP = (1 << 0), + AVIF_TRANSFORM_CLAP = (1 << 1), + AVIF_TRANSFORM_IROT = (1 << 2), + AVIF_TRANSFORM_IMIR = (1 << 3) +} avifTransformationFlags; + +typedef struct avifPixelAspectRatioBox +{ + // 'pasp' from ISO/IEC 14496-12:2015 12.1.4.3 + + // define the relative width and height of a pixel + uint32_t hSpacing; + uint32_t vSpacing; +} avifPixelAspectRatioBox; + +typedef struct avifCleanApertureBox +{ + // 'clap' from ISO/IEC 14496-12:2015 12.1.4.3 + + // a fractional number which defines the exact clean aperture width, in counted pixels, of the video image + uint32_t widthN; + uint32_t widthD; + + // a fractional number which defines the exact clean aperture height, in counted pixels, of the video image + uint32_t heightN; + uint32_t heightD; + + // a fractional number which defines the horizontal offset of clean aperture centre minus (width‐1)/2. Typically 0. + uint32_t horizOffN; + uint32_t horizOffD; + + // a fractional number which defines the vertical offset of clean aperture centre minus (height‐1)/2. Typically 0. + uint32_t vertOffN; + uint32_t vertOffD; +} avifCleanApertureBox; + +typedef struct avifImageRotation +{ + // 'irot' from ISO/IEC 23008-12:2017 6.5.10 + + // angle * 90 specifies the angle (in anti-clockwise direction) in units of degrees. + uint8_t angle; // legal values: [0-3] +} avifImageRotation; + +typedef struct avifImageMirror +{ + // 'imir' from ISO/IEC 23008-12:2017 6.5.12 + + // axis specifies a vertical (axis = 0) or horizontal (axis = 1) axis for the mirroring operation. + uint8_t axis; // legal values: [0, 1] +} avifImageMirror; + +// --------------------------------------------------------------------------- +// avifImage + +typedef struct avifImage +{ + // Image information + uint32_t width; + uint32_t height; + uint32_t depth; // all planes must share this depth; if depth>8, all planes are uint16_t internally + + avifPixelFormat yuvFormat; + avifRange yuvRange; + avifChromaSamplePosition yuvChromaSamplePosition; + uint8_t * yuvPlanes[AVIF_PLANE_COUNT_YUV]; + uint32_t yuvRowBytes[AVIF_PLANE_COUNT_YUV]; + avifBool imageOwnsYUVPlanes; + + avifRange alphaRange; + uint8_t * alphaPlane; + uint32_t alphaRowBytes; + avifBool imageOwnsAlphaPlane; + + // ICC Profile + avifRWData icc; + + // CICP information: + // These are stored in the AV1 payload and used to signal YUV conversion. Additionally, if an + // ICC profile is not specified, these will be stored in the AVIF container's `colr` box with + // a type of `nclx`. If your system supports ICC profiles, be sure to check for the existence + // of one (avifImage.icc) before relying on the values listed here! + avifColorPrimaries colorPrimaries; + avifTransferCharacteristics transferCharacteristics; + avifMatrixCoefficients matrixCoefficients; + + // Transformations - These metadata values are encoded/decoded when transformFlags are set + // appropriately, but do not impact/adjust the actual pixel buffers used (images won't be + // pre-cropped or mirrored upon decode). Basic explanations from the standards are offered in + // comments above, but for detailed explanations, please refer to the HEIF standard (ISO/IEC + // 23008-12:2017) and the BMFF standard (ISO/IEC 14496-12:2015). + // + // To encode any of these boxes, set the values in the associated box, then enable the flag in + // transformFlags. On decode, only honor the values in boxes with the associated transform flag set. + uint32_t transformFlags; + avifPixelAspectRatioBox pasp; + avifCleanApertureBox clap; + avifImageRotation irot; + avifImageMirror imir; + + // Metadata - set with avifImageSetMetadata*() before write, check .size>0 for existence after read + avifRWData exif; + avifRWData xmp; +} avifImage; + +avifImage * avifImageCreate(int width, int height, int depth, avifPixelFormat yuvFormat); +avifImage * avifImageCreateEmpty(void); // helper for making an image to decode into +void avifImageCopy(avifImage * dstImage, const avifImage * srcImage, uint32_t planes); // deep copy +void avifImageDestroy(avifImage * image); + +void avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize); + +// Warning: If the Exif payload is set and invalid, avifEncoderWrite() may return AVIF_RESULT_INVALID_EXIF_PAYLOAD +void avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize); +void avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize); + +void avifImageAllocatePlanes(avifImage * image, uint32_t planes); // Ignores any pre-existing planes +void avifImageFreePlanes(avifImage * image, uint32_t planes); // Ignores already-freed planes +void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, uint32_t planes); + +// --------------------------------------------------------------------------- +// Optional YUV<->RGB support + +// To convert to/from RGB, create an avifRGBImage on the stack, call avifRGBImageSetDefaults() on +// it, and then tweak the values inside of it accordingly. At a minimum, you should populate +// ->pixels and ->rowBytes with an appropriately sized pixel buffer, which should be at least +// (->rowBytes * ->height) bytes, where ->rowBytes is at least (->width * avifRGBImagePixelSize()). +// If you don't want to supply your own pixel buffer, you can use the +// avifRGBImageAllocatePixels()/avifRGBImageFreePixels() convenience functions. + +// avifImageRGBToYUV() and avifImageYUVToRGB() will perform depth rescaling and limited<->full range +// conversion, if necessary. Pixels in an avifRGBImage buffer are always full range, and conversion +// routines will fail if the width and height don't match the associated avifImage. + +typedef enum avifRGBFormat +{ + AVIF_RGB_FORMAT_RGB = 0, + AVIF_RGB_FORMAT_RGBA, + AVIF_RGB_FORMAT_ARGB, + AVIF_RGB_FORMAT_BGR, + AVIF_RGB_FORMAT_BGRA, + AVIF_RGB_FORMAT_ABGR +} avifRGBFormat; +uint32_t avifRGBFormatChannelCount(avifRGBFormat format); +avifBool avifRGBFormatHasAlpha(avifRGBFormat format); + +typedef enum avifChromaUpsampling +{ + AVIF_CHROMA_UPSAMPLING_BILINEAR = 0, // Slower and prettier (default) + AVIF_CHROMA_UPSAMPLING_NEAREST = 1 // Faster and uglier +} avifChromaUpsampling; + +typedef struct avifRGBImage +{ + uint32_t width; // must match associated avifImage + uint32_t height; // must match associated avifImage + uint32_t depth; // legal depths [8, 10, 12, 16]. if depth>8, pixels must be uint16_t internally + avifRGBFormat format; // all channels are always full range + avifChromaUpsampling chromaUpsampling; // How to upsample non-4:4:4 UV (ignored for 444) when converting to RGB. + // Unused when converting to YUV. avifRGBImageSetDefaults() prefers quality over speed. + + uint8_t * pixels; + uint32_t rowBytes; +} avifRGBImage; + +void avifRGBImageSetDefaults(avifRGBImage * rgb, const avifImage * image); +uint32_t avifRGBImagePixelSize(const avifRGBImage * rgb); + +// Convenience functions. If you supply your own pixels/rowBytes, you do not need to use these. +void avifRGBImageAllocatePixels(avifRGBImage * rgb); +void avifRGBImageFreePixels(avifRGBImage * rgb); + +// The main conversion functions +avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb); +avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb); + +// --------------------------------------------------------------------------- +// YUV Utils + +int avifFullToLimitedY(int depth, int v); +int avifFullToLimitedUV(int depth, int v); +int avifLimitedToFullY(int depth, int v); +int avifLimitedToFullUV(int depth, int v); + +typedef enum avifReformatMode +{ + AVIF_REFORMAT_MODE_YUV_COEFFICIENTS = 0, // Normal YUV conversion using coefficients + AVIF_REFORMAT_MODE_IDENTITY // Pack GBR directly into YUV planes (AVIF_MATRIX_COEFFICIENTS_IDENTITY) +} avifReformatMode; + +typedef struct avifReformatState +{ + // YUV coefficients + float kr; + float kg; + float kb; + + uint32_t yuvChannelBytes; + uint32_t rgbChannelBytes; + uint32_t rgbChannelCount; + uint32_t rgbPixelBytes; + uint32_t rgbOffsetBytesR; + uint32_t rgbOffsetBytesG; + uint32_t rgbOffsetBytesB; + uint32_t rgbOffsetBytesA; + + avifPixelFormatInfo formatInfo; + + // LUTs for going from YUV limited/full unorm -> full range RGB FP32 + float unormFloatTableY[1 << 12]; + float unormFloatTableUV[1 << 12]; + + avifReformatMode mode; +} avifReformatState; +avifBool avifPrepareReformatState(const avifImage * image, const avifRGBImage * rgb, avifReformatState * state); + +// --------------------------------------------------------------------------- +// Codec selection + +typedef enum avifCodecChoice +{ + AVIF_CODEC_CHOICE_AUTO = 0, + AVIF_CODEC_CHOICE_AOM, + AVIF_CODEC_CHOICE_DAV1D, // Decode only + AVIF_CODEC_CHOICE_LIBGAV1, // Decode only + AVIF_CODEC_CHOICE_RAV1E // Encode only +} avifCodecChoice; + +typedef enum avifCodecFlags +{ + AVIF_CODEC_FLAG_CAN_DECODE = (1 << 0), + AVIF_CODEC_FLAG_CAN_ENCODE = (1 << 1) +} avifCodecFlags; + +// If this returns NULL, the codec choice/flag combination is unavailable +const char * avifCodecName(avifCodecChoice choice, uint32_t requiredFlags); +avifCodecChoice avifCodecChoiceFromName(const char * name); + +typedef struct avifCodecConfigurationBox +{ + // [skipped; is constant] unsigned int (1)marker = 1; + // [skipped; is constant] unsigned int (7)version = 1; + + uint8_t seqProfile; // unsigned int (3) seq_profile; + uint8_t seqLevelIdx0; // unsigned int (5) seq_level_idx_0; + uint8_t seqTier0; // unsigned int (1) seq_tier_0; + uint8_t highBitdepth; // unsigned int (1) high_bitdepth; + uint8_t twelveBit; // unsigned int (1) twelve_bit; + uint8_t monochrome; // unsigned int (1) monochrome; + uint8_t chromaSubsamplingX; // unsigned int (1) chroma_subsampling_x; + uint8_t chromaSubsamplingY; // unsigned int (1) chroma_subsampling_y; + uint8_t chromaSamplePosition; // unsigned int (2) chroma_sample_position; + + // unsigned int (3)reserved = 0; + // unsigned int (1)initial_presentation_delay_present; + // if (initial_presentation_delay_present) { + // unsigned int (4)initial_presentation_delay_minus_one; + // } else { + // unsigned int (4)reserved = 0; + // } +} avifCodecConfigurationBox; + +// --------------------------------------------------------------------------- +// avifDecoder + +// Useful stats related to a read/write +typedef struct avifIOStats +{ + size_t colorOBUSize; + size_t alphaOBUSize; +} avifIOStats; + +struct avifDecoderData; + +typedef enum avifDecoderSource +{ + // If a moov box is present in the .avif(s), use the tracks in it, otherwise decode the primary item. + AVIF_DECODER_SOURCE_AUTO = 0, + + // Use the primary item and the aux (alpha) item in the avif(s). + // This is where single-image avifs store their image. + AVIF_DECODER_SOURCE_PRIMARY_ITEM, + + // Use the chunks inside primary/aux tracks in the moov block. + // This is where avifs image sequences store their images. + AVIF_DECODER_SOURCE_TRACKS, + + // Decode the thumbnail item. Currently unimplemented. + // AVIF_DECODER_SOURCE_THUMBNAIL_ITEM +} avifDecoderSource; + +// Information about the timing of a single image in an image sequence +typedef struct avifImageTiming +{ + uint64_t timescale; // timescale of the media (Hz) + double pts; // presentation timestamp in seconds (ptsInTimescales / timescale) + uint64_t ptsInTimescales; // presentation timestamp in "timescales" + double duration; // in seconds (durationInTimescales / timescale) + uint64_t durationInTimescales; // duration in "timescales" +} avifImageTiming; + +typedef struct avifDecoder +{ + // Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c) + avifCodecChoice codecChoice; + + // avifs can have multiple sets of images in them. This specifies which to decode. + // Set this via avifDecoderSetSource(). + avifDecoderSource requestedSource; + + // All decoded image data; owned by the decoder. All information in this image is incrementally + // added and updated as avifDecoder*() functions are called. After a successful call to + // avifDecoderParse(), all values in decoder->image (other than the planes/rowBytes themselves) + // will be pre-populated with all information found in the outer AVIF container, prior to any + // AV1 decoding. If the contents of the inner AV1 payload disagree with the outer container, + // these values may change after calls to avifDecoderRead(),avifDecoderNextImage(), or + // avifDecoderNthImage(). + // + // The YUV and A contents of this image are likely owned by the decoder, so be sure to copy any + // data inside of this image before advancing to the next image or reusing the decoder. It is + // legal to call avifImageYUVToRGB() on this in between calls to avifDecoderNextImage(), but use + // avifImageCopy() if you want to make a complete, permanent copy of this image's YUV content or + // metadata. + avifImage * image; + + // Counts and timing for the current image in an image sequence. Uninteresting for single image files. + int imageIndex; // 0-based + int imageCount; // Always 1 for non-sequences + avifImageTiming imageTiming; // + uint64_t timescale; // timescale of the media (Hz) + double duration; // in seconds (durationInTimescales / timescale) + uint64_t durationInTimescales; // duration in "timescales" + + // This is true when avifDecoderParse() detects an alpha plane. Use this to find out if alpha is + // present after a successful call to avifDecoderParse(), but prior to any call to + // avifDecoderNextImage() or avifDecoderNthImage(), as decoder->image->alphaPlane won't exist yet. + avifBool alphaPresent; + + // stats from the most recent read, possibly 0s if reading an image sequence + avifIOStats ioStats; + + // Internals used by the decoder + struct avifDecoderData * data; +} avifDecoder; + +avifDecoder * avifDecoderCreate(void); +void avifDecoderDestroy(avifDecoder * decoder); + +// Simple interface to decode a single image, independent of the decoder afterwards (decoder may be deestroyed). +avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image, const avifROData * input); + +// Multi-function alternative to avifDecoderRead() for image sequences and gaining direct access +// to the decoder's YUV buffers (for performance's sake). Data passed into avifDecoderParse() is NOT +// copied, so it must continue to exist until the decoder is destroyed. +// +// Usage / function call order is: +// * avifDecoderCreate() +// * avifDecoderSetSource() - optional, the default (AVIF_DECODER_SOURCE_AUTO) is usually sufficient +// * avifDecoderParse() +// * avifDecoderNextImage() - in a loop, using decoder->image after each successful call +// * avifDecoderDestroy() +// +// You can use avifDecoderReset() any time after a successful call to avifDecoderParse() +// to reset the internal decoder back to before the first frame. Calling either +// avifDecoderSetSource() or avifDecoderParse() will automatically Reset the decoder. +// +// avifDecoderSetSource() allows you not only to choose whether to parse tracks or +// items in a file containing both, but switch between sources without having to +// Parse again. Normally AVIF_DECODER_SOURCE_AUTO is enough for the common path. +avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source); +avifResult avifDecoderParse(avifDecoder * decoder, const avifROData * input); +avifResult avifDecoderNextImage(avifDecoder * decoder); +avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex); +avifResult avifDecoderReset(avifDecoder * decoder); + +// Keyframe information +// frameIndex - 0-based, matching avifDecoder->imageIndex, bound by avifDecoder->imageCount +// "nearest" keyframe means the keyframe prior to this frame index (returns frameIndex if it is a keyframe) +avifBool avifDecoderIsKeyframe(const avifDecoder * decoder, uint32_t frameIndex); +uint32_t avifDecoderNearestKeyframe(const avifDecoder * decoder, uint32_t frameIndex); + +// Timing helper - This does not change the current image or invoke the codec (safe to call repeatedly) +// This function may be used after a successful call to avifDecoderParse(). +avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming); + +// --------------------------------------------------------------------------- +// avifEncoder + +struct avifEncoderData; + +// Notes: +// * If avifEncoderWrite() returns AVIF_RESULT_OK, output must be freed with avifRWDataFree() +// * If (maxThreads < 2), multithreading is disabled +// * Quality range: [AVIF_QUANTIZER_BEST_QUALITY - AVIF_QUANTIZER_WORST_QUALITY] +// * To enable tiling, set tileRowsLog2 > 0 and/or tileColsLog2 > 0. +// Tiling values range [0-6], where the value indicates a request for 2^n tiles in that dimension. +// * Speed range: [AVIF_SPEED_SLOWEST - AVIF_SPEED_FASTEST]. Slower should make for a better quality +// image in less bytes. AVIF_SPEED_DEFAULT means "Leave the AV1 codec to its default speed settings"./ +// If avifEncoder uses rav1e, the speed value is directly passed through (0-10). If libaom is used, +// a combination of settings are tweaked to simulate this speed range. +typedef struct avifEncoder +{ + // Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c) + avifCodecChoice codecChoice; + + // settings (see Notes above) + int maxThreads; + int minQuantizer; + int maxQuantizer; + int minQuantizerAlpha; + int maxQuantizerAlpha; + int tileRowsLog2; + int tileColsLog2; + int speed; + int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default). + uint64_t timescale; // timescale of the media (Hz) + + // stats from the most recent write + avifIOStats ioStats; + + // Internals used by the encoder + struct avifEncoderData * data; +} avifEncoder; + +avifEncoder * avifEncoderCreate(void); +avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avifRWData * output); +void avifEncoderDestroy(avifEncoder * encoder); + +enum avifAddImageFlags +{ + AVIF_ADD_IMAGE_FLAG_NONE = 0, + + // Force this frame to be a keyframe (sync frame). + AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME = (1 << 0), + + // Use this flag when encoding a single image. Signals "still_picture" to AV1 encoders, which + // tweaks various compression rules. This is enabled automatically when using the + // avifEncoderWrite() single-image encode path. + AVIF_ADD_IMAGE_FLAG_SINGLE = (1 << 1) +}; + +// Multi-function alternative to avifEncoderWrite() for image sequences. +// +// Usage / function call order is: +// * avifEncoderCreate() +// * Set encoder->timescale (Hz) correctly +// * avifEncoderAddImage() ... [repeatedly; at least once] +// * avifEncoderFinish() +// * avifEncoderDestroy() +// +avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, uint32_t addImageFlags); +avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output); + +// Helpers +avifBool avifImageUsesU16(const avifImage * image); + +// Returns AVIF_TRUE if input begins with a valid FileTypeBox (ftyp) that supports +// either the brand 'avif' or 'avis' (or both), without performing any allocations. +avifBool avifPeekCompatibleFileType(const avifROData * input); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ifndef AVIF_AVIF_H diff --git a/c/header/jpeg/jconfig.h b/c/header/jpeg/jconfig.h new file mode 100644 index 0000000..f3b356d --- /dev/null +++ b/c/header/jpeg/jconfig.h @@ -0,0 +1,34 @@ +#define JPEG_LIB_VERSION 62 +#define LIBJPEG_TURBO_VERSION 4.0.0 +#define LIBJPEG_TURBO_VERSION_NUMBER 4000000 + +/* #undef C_ARITH_CODING_SUPPORTED */ +/* #undef D_ARITH_CODING_SUPPORTED */ +#define MEM_SRCDST_SUPPORTED +#define WITH_SIMD + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_SYS_TYPES_H +#undef NEED_BSD_STRINGS + +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +#undef INCOMPLETE_TYPES_BROKEN +#undef RIGHT_SHIFT_IS_UNSIGNED +#undef __CHAR_UNSIGNED__ + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + +/* Define "INT32" as int, not long, per Windows custom */ +#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */ +typedef short INT16; +typedef signed int INT32; +#endif +#define XMD_H /* prevent jmorecfg.h from redefining it */ diff --git a/c/header/jpeg/jerror.h b/c/header/jpeg/jerror.h new file mode 100644 index 0000000..df5502c --- /dev/null +++ b/c/header/jpeg/jerror.h @@ -0,0 +1,320 @@ +/* + * jerror.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2014, 2017, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +#if JPEG_LIB_VERSION < 70 +JMESSAGE(JERR_ARITH_NOTIMPL, "Sorry, arithmetic coding is not implemented") +#endif +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +#endif +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +#endif +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +#endif +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT_SHORT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +#endif +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") +#if JPEG_LIB_VERSION < 70 +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +#if defined(C_ARITH_CODING_SUPPORTED) || defined(D_ARITH_CODING_SUPPORTED) +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +#endif +#endif +JMESSAGE(JERR_BAD_PARAM, "Bogus parameter") +JMESSAGE(JERR_BAD_PARAM_VALUE, "Bogus parameter value") + +JMESSAGE(JERR_UNSUPPORTED_SUSPEND, "I/O suspension not supported in scan optimization") +JMESSAGE(JWRN_BOGUS_ICC, "Corrupt JPEG data: bad ICC marker") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/c/header/jpeg/jmorecfg.h b/c/header/jpeg/jmorecfg.h new file mode 100644 index 0000000..d0b9300 --- /dev/null +++ b/c/header/jpeg/jmorecfg.h @@ -0,0 +1,421 @@ +/* + * jmorecfg.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2009, 2011, 2014-2015, 2018, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of Rec. ITU-T T.81 | ISO/IEC 10918-1, set this to 255. + * However, darn few applications need more than 4 channels (maybe 5 for CMYK + + * alpha mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int)(value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef __CHAR_UNSIGNED__ +#define GETJSAMPLE(value) ((int)(value)) +#else +#define GETJSAMPLE(value) ((int)(value) & 0xFF) +#endif /* __CHAR_UNSIGNED__ */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int)(value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef __CHAR_UNSIGNED__ +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* __CHAR_UNSIGNED__ */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef __CHAR_UNSIGNED__ +typedef char UINT8; +#else /* not __CHAR_UNSIGNED__ */ +typedef short UINT8; +#endif /* __CHAR_UNSIGNED__ */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. + * + * NOTE: The INT32 typedef dates back to libjpeg v5 (1994.) Integers were + * sometimes 16-bit back then (MS-DOS), which is why INT32 is typedef'd to + * long. It also wasn't common (or at least as common) in 1994 for INT32 to be + * defined by platform headers. Since then, however, INT32 is defined in + * several other common places: + * + * Xmd.h (X11 header) typedefs INT32 to int on 64-bit platforms and long on + * 32-bit platforms (i.e always a 32-bit signed type.) + * + * basetsd.h (Win32 header) typedefs INT32 to int (always a 32-bit signed type + * on modern platforms.) + * + * qglobal.h (Qt header) typedefs INT32 to int (always a 32-bit signed type on + * modern platforms.) + * + * This is a recipe for conflict, since "long" and "int" aren't always + * compatible types. Since the definition of INT32 has technically been part + * of the libjpeg API for more than 20 years, we can't remove it, but we do not + * use it internally any longer. We instead define a separate type (JLONG) + * for internal use, which ensures that internal behavior will always be the + * same regardless of any external headers that may be included. + */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. (Note that changing this datatype will + * potentially require modifying the SIMD code. The x86-64 SIMD extensions, + * in particular, assume a 32-bit JDIMENSION.) + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* Originally, this macro was used as a way of defining function prototypes + * for both modern compilers as well as older compilers that did not support + * prototype parameters. libjpeg-turbo has never supported these older, + * non-ANSI compilers, but the macro is still included because there is some + * software out there that uses it. + */ + +#define JMETHOD(type, methodname, arglist) type (*methodname) arglist + + +/* libjpeg-turbo no longer supports platforms that have far symbols (MS-DOS), + * but again, some software relies on this macro. + */ + +#undef FAR +#define FAR + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * The RGB_RED, RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE macros are a vestigial + * feature of libjpeg. The idea was that, if an application developer needed + * to compress from/decompress to a BGR/BGRX/RGBX/XBGR/XRGB buffer, they could + * change these macros, rebuild libjpeg, and link their application statically + * with it. In reality, few people ever did this, because there were some + * severe restrictions involved (cjpeg and djpeg no longer worked properly, + * compressing/decompressing RGB JPEGs no longer worked properly, and the color + * quantizer wouldn't work with pixel sizes other than 3.) Furthermore, since + * all of the O/S-supplied versions of libjpeg were built with the default + * values of RGB_RED, RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE, many applications + * have come to regard these values as immutable. + * + * The libjpeg-turbo colorspace extensions provide a much cleaner way of + * compressing from/decompressing to buffers with arbitrary component orders + * and pixel sizes. Thus, we do not support changing the values of RGB_RED, + * RGB_GREEN, RGB_BLUE, or RGB_PIXELSIZE. In addition to the restrictions + * listed above, changing these values will also break the SIMD extensions and + * the regression tests. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + +#define JPEG_NUMCS 17 + +#define EXT_RGB_RED 0 +#define EXT_RGB_GREEN 1 +#define EXT_RGB_BLUE 2 +#define EXT_RGB_PIXELSIZE 3 + +#define EXT_RGBX_RED 0 +#define EXT_RGBX_GREEN 1 +#define EXT_RGBX_BLUE 2 +#define EXT_RGBX_PIXELSIZE 4 + +#define EXT_BGR_RED 2 +#define EXT_BGR_GREEN 1 +#define EXT_BGR_BLUE 0 +#define EXT_BGR_PIXELSIZE 3 + +#define EXT_BGRX_RED 2 +#define EXT_BGRX_GREEN 1 +#define EXT_BGRX_BLUE 0 +#define EXT_BGRX_PIXELSIZE 4 + +#define EXT_XBGR_RED 3 +#define EXT_XBGR_GREEN 2 +#define EXT_XBGR_BLUE 1 +#define EXT_XBGR_PIXELSIZE 4 + +#define EXT_XRGB_RED 1 +#define EXT_XRGB_GREEN 2 +#define EXT_XRGB_BLUE 3 +#define EXT_XRGB_PIXELSIZE 4 + +static const int rgb_red[JPEG_NUMCS] = { + -1, -1, RGB_RED, -1, -1, -1, EXT_RGB_RED, EXT_RGBX_RED, + EXT_BGR_RED, EXT_BGRX_RED, EXT_XBGR_RED, EXT_XRGB_RED, + EXT_RGBX_RED, EXT_BGRX_RED, EXT_XBGR_RED, EXT_XRGB_RED, + -1 +}; + +static const int rgb_green[JPEG_NUMCS] = { + -1, -1, RGB_GREEN, -1, -1, -1, EXT_RGB_GREEN, EXT_RGBX_GREEN, + EXT_BGR_GREEN, EXT_BGRX_GREEN, EXT_XBGR_GREEN, EXT_XRGB_GREEN, + EXT_RGBX_GREEN, EXT_BGRX_GREEN, EXT_XBGR_GREEN, EXT_XRGB_GREEN, + -1 +}; + +static const int rgb_blue[JPEG_NUMCS] = { + -1, -1, RGB_BLUE, -1, -1, -1, EXT_RGB_BLUE, EXT_RGBX_BLUE, + EXT_BGR_BLUE, EXT_BGRX_BLUE, EXT_XBGR_BLUE, EXT_XRGB_BLUE, + EXT_RGBX_BLUE, EXT_BGRX_BLUE, EXT_XBGR_BLUE, EXT_XRGB_BLUE, + -1 +}; + +static const int rgb_pixelsize[JPEG_NUMCS] = { + -1, -1, RGB_PIXELSIZE, -1, -1, -1, EXT_RGB_PIXELSIZE, EXT_RGBX_PIXELSIZE, + EXT_BGR_PIXELSIZE, EXT_BGRX_PIXELSIZE, EXT_XBGR_PIXELSIZE, EXT_XRGB_PIXELSIZE, + EXT_RGBX_PIXELSIZE, EXT_BGRX_PIXELSIZE, EXT_XBGR_PIXELSIZE, EXT_XRGB_PIXELSIZE, + -1 +}; + +/* Definitions for speed-related optimizations. */ + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#ifndef WITH_SIMD +#define MULTIPLIER int /* type for fastest integer multiply */ +#else +#define MULTIPLIER short /* prefer 16-bit with SIMD for parellelism */ +#endif +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + */ + +#ifndef FAST_FLOAT +#define FAST_FLOAT float +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/c/header/jpeg/jpeglib.h b/c/header/jpeg/jpeglib.h new file mode 100644 index 0000000..f80dece --- /dev/null +++ b/c/header/jpeg/jpeglib.h @@ -0,0 +1,1211 @@ +/* + * jpeglib.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2009-2011, 2013-2014, 2016-2017, D. R. Commander. + * Copyright (C) 2015, Google, Inc. + * mozjpeg Modifications: + * Copyright (C) 2014, Mozilla Corporation. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + */ + +typedef JSAMPLE *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different IDCT scalings. + */ +#if JPEG_LIB_VERSION >= 70 + int DCT_h_scaled_size; + int DCT_v_scaled_size; +#else + int DCT_scaled_size; +#endif + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_[h_]scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_[h_]scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL *quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void *dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct *jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET *data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +#define JCS_EXTENSIONS 1 +#define JCS_ALPHA_EXTENSIONS 1 + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue as specified by the RGB_RED, + RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE macros */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK, /* Y/Cb/Cr/K */ + JCS_EXT_RGB, /* red/green/blue */ + JCS_EXT_RGBX, /* red/green/blue/x */ + JCS_EXT_BGR, /* blue/green/red */ + JCS_EXT_BGRX, /* blue/green/red/x */ + JCS_EXT_XBGR, /* x/blue/green/red */ + JCS_EXT_XRGB, /* x/red/green/blue */ + /* When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR, + or JCS_EXT_XRGB during decompression, the X byte is undefined, and in + order to ensure the best performance, libjpeg-turbo can set that byte to + whatever value it wishes. Use the following colorspace constants to + ensure that the X byte is set to 0xFF, so that it can be interpreted as an + opaque alpha channel. */ + JCS_EXT_RGBA, /* red/green/blue/alpha */ + JCS_EXT_BGRA, /* blue/green/red/alpha */ + JCS_EXT_ABGR, /* alpha/blue/green/red */ + JCS_EXT_ARGB, /* alpha/red/green/blue */ + JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* These 32-bit GUIDs and the corresponding jpeg_*_get_*_param()/ + * jpeg_*_set_*_param() functions allow for extending the libjpeg API without + * breaking backward ABI compatibility. The actual parameters are stored in + * the opaque jpeg_comp_master and jpeg_decomp_master structs. + */ + +/* Boolean extension parameters */ + +typedef enum { + JBOOLEAN_OPTIMIZE_SCANS = 0x680C061E, /* TRUE=optimize progressive coding scans */ + JBOOLEAN_TRELLIS_QUANT = 0xC5122033, /* TRUE=use trellis quantization */ + JBOOLEAN_TRELLIS_QUANT_DC = 0x339D4C0C, /* TRUE=use trellis quant for DC coefficient */ + JBOOLEAN_TRELLIS_EOB_OPT = 0xD7F73780, /* TRUE=optimize for sequences of EOB */ + JBOOLEAN_USE_LAMBDA_WEIGHT_TBL = 0x339DB65F, /* TRUE=use lambda weighting table */ + JBOOLEAN_USE_SCANS_IN_TRELLIS = 0xFD841435, /* TRUE=use scans in trellis optimization */ + JBOOLEAN_TRELLIS_Q_OPT = 0xE12AE269, /* TRUE=optimize quant table in trellis loop */ + JBOOLEAN_OVERSHOOT_DERINGING = 0x3F4BBBF9 /* TRUE=preprocess input to reduce ringing of edges on white background */ +} J_BOOLEAN_PARAM; + +/* Floating point parameters */ + +typedef enum { + JFLOAT_LAMBDA_LOG_SCALE1 = 0x5B61A599, + JFLOAT_LAMBDA_LOG_SCALE2 = 0xB9BBAE03, + JFLOAT_TRELLIS_DELTA_DC_WEIGHT = 0x13775453 +} J_FLOAT_PARAM; + +/* Integer parameters */ + +typedef enum { + JINT_COMPRESS_PROFILE = 0xE9918625, /* compression profile */ + JINT_TRELLIS_FREQ_SPLIT = 0x6FAFF127, /* splitting point for frequency in trellis quantization */ + JINT_TRELLIS_NUM_LOOPS = 0xB63EBF39, /* number of trellis loops */ + JINT_BASE_QUANT_TBL_IDX = 0x44492AB1, /* base quantization table index */ + JINT_DC_SCAN_OPT_MODE = 0x0BE7AD3C /* DC scan optimization mode */ +} J_INT_PARAM; + + +/* Values for the JINT_COMPRESS_PROFILE parameter (32-bit GUIDs) */ + +enum { + JCP_MAX_COMPRESSION = 0x5D083AAD, /* best compression ratio (progressive, all mozjpeg extensions) */ + JCP_FASTEST = 0x2AEA5CB4 /* libjpeg[-turbo] defaults (baseline, no mozjpeg extensions) */ +}; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr *err; /* Error handler module */\ + struct jpeg_memory_mgr *mem; /* Memory manager module */\ + struct jpeg_progress_mgr *progress; /* Progress monitor, or NULL if none */\ + void *client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct *j_common_ptr; +typedef struct jpeg_compress_struct *j_compress_ptr; +typedef struct jpeg_decompress_struct *j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr *dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + +#if JPEG_LIB_VERSION >= 70 + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ +#endif + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info *comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; +#if JPEG_LIB_VERSION >= 70 + int q_scale_factor[NUM_QUANT_TBLS]; +#endif + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info *scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ +#if JPEG_LIB_VERSION >= 70 + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ +#endif + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + +#if JPEG_LIB_VERSION >= 70 + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ +#endif + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + +#if JPEG_LIB_VERSION >= 80 + int block_size; /* the basic DCT block size: 1..16 */ + const int *natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ +#endif + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master *master; + struct jpeg_c_main_controller *main; + struct jpeg_c_prep_controller *prep; + struct jpeg_c_coef_controller *coef; + struct jpeg_marker_writer *marker; + struct jpeg_color_converter *cconvert; + struct jpeg_downsampler *downsample; + struct jpeg_forward_dct *fdct; + struct jpeg_entropy_encoder *entropy; + jpeg_scan_info *script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + +typedef void (*jpeg_idct_method) (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); +typedef void (*jpeg_idct_method_selector) (j_decompress_ptr cinfo, jpeg_component_info *compptr, jpeg_idct_method * set_idct_method, int * set_idct_category); + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr *src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info *comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + +#if JPEG_LIB_VERSION >= 80 + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ +#endif + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + +#if JPEG_LIB_VERSION >= 70 + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ +#else + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ +#endif + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_[v_]scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE *sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + +#if JPEG_LIB_VERSION >= 80 + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int *natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ +#endif + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master *master; + struct jpeg_d_main_controller *main; + struct jpeg_d_coef_controller *coef; + struct jpeg_d_post_controller *post; + struct jpeg_input_controller *inputctl; + struct jpeg_marker_reader *marker; + struct jpeg_entropy_decoder *entropy; + struct jpeg_inverse_dct *idct; + struct jpeg_upsampler *upsample; + struct jpeg_color_deconverter *cconvert; + struct jpeg_color_quantizer *cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + void (*error_exit) (j_common_ptr cinfo); + /* Conditionally emit a trace or warning message */ + void (*emit_message) (j_common_ptr cinfo, int msg_level); + /* Routine that actually outputs a trace or error message */ + void (*output_message) (j_common_ptr cinfo); + /* Format a message string for the most recent JPEG error or message */ + void (*format_message) (j_common_ptr cinfo, char *buffer); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + void (*reset_error_mgr) (j_common_ptr cinfo); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const *jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const *addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + void (*progress_monitor) (j_common_ptr cinfo); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET *next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + void (*init_destination) (j_compress_ptr cinfo); + boolean (*empty_output_buffer) (j_compress_ptr cinfo); + void (*term_destination) (j_compress_ptr cinfo); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET *next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + void (*init_source) (j_decompress_ptr cinfo); + boolean (*fill_input_buffer) (j_decompress_ptr cinfo); + void (*skip_input_data) (j_decompress_ptr cinfo, long num_bytes); + boolean (*resync_to_restart) (j_decompress_ptr cinfo, int desired); + void (*term_source) (j_decompress_ptr cinfo); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control *jvirt_sarray_ptr; +typedef struct jvirt_barray_control *jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + void *(*alloc_small) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); + void *(*alloc_large) (j_common_ptr cinfo, int pool_id, + size_t sizeofobject); + JSAMPARRAY (*alloc_sarray) (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows); + JBLOCKARRAY (*alloc_barray) (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows); + jvirt_sarray_ptr (*request_virt_sarray) (j_common_ptr cinfo, int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess); + jvirt_barray_ptr (*request_virt_barray) (j_common_ptr cinfo, int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess); + void (*realize_virt_arrays) (j_common_ptr cinfo); + JSAMPARRAY (*access_virt_sarray) (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable); + JBLOCKARRAY (*access_virt_barray) (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable); + void (*free_pool) (j_common_ptr cinfo, int pool_id); + void (*self_destruct) (j_common_ptr cinfo); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef boolean (*jpeg_marker_parser_method) (j_decompress_ptr cinfo); + + +/* Originally, this macro was used as a way of defining function prototypes + * for both modern compilers as well as older compilers that did not support + * prototype parameters. libjpeg-turbo has never supported these older, + * non-ANSI compilers, but the macro is still included because there is some + * software out there that uses it. + */ + +#define JPP(arglist) arglist + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error (struct jpeg_error_mgr *err); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress (j_compress_ptr cinfo, int version, + size_t structsize); +EXTERN(void) jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, + size_t structsize); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress (j_compress_ptr cinfo); +EXTERN(void) jpeg_destroy_decompress (j_decompress_ptr cinfo); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +// No. we don't need to read file. +//EXTERN(void) jpeg_stdio_dest (j_compress_ptr cinfo, FILE *outfile); +//EXTERN(void) jpeg_stdio_src (j_decompress_ptr cinfo, FILE *infile); + +#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest (j_compress_ptr cinfo, unsigned char **outbuffer, + unsigned long *outsize); +EXTERN(void) jpeg_mem_src (j_decompress_ptr cinfo, + const unsigned char *inbuffer, unsigned long insize); +#endif + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults (j_compress_ptr cinfo); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace (j_compress_ptr cinfo, + J_COLOR_SPACE colorspace); +EXTERN(void) jpeg_default_colorspace (j_compress_ptr cinfo); +EXTERN(void) jpeg_set_quality (j_compress_ptr cinfo, int quality, + boolean force_baseline); +EXTERN(void) jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + boolean force_baseline); +#if JPEG_LIB_VERSION >= 70 +EXTERN(void) jpeg_default_qtables (j_compress_ptr cinfo, + boolean force_baseline); +#endif +EXTERN(void) jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline); +EXTERN(int) jpeg_quality_scaling (int quality); +EXTERN(float) jpeg_float_quality_scaling (float quality); +EXTERN(void) jpeg_simple_progression (j_compress_ptr cinfo); +EXTERN(void) jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table (j_common_ptr cinfo); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table (j_common_ptr cinfo); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress (j_compress_ptr cinfo, + boolean write_all_tables); +EXTERN(JDIMENSION) jpeg_write_scanlines (j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines); +EXTERN(void) jpeg_finish_compress (j_compress_ptr cinfo); + +#if JPEG_LIB_VERSION >= 70 +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions (j_compress_ptr cinfo); +#endif + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION num_lines); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header (j_compress_ptr cinfo, int marker, + unsigned int datalen); +EXTERN(void) jpeg_write_m_byte (j_compress_ptr cinfo, int val); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables (j_compress_ptr cinfo); + +/* Write ICC profile. See libjpeg.txt for usage information. */ +EXTERN(void) jpeg_write_icc_profile(j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len); + + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header (j_decompress_ptr cinfo, boolean require_image); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress (j_decompress_ptr cinfo); +EXTERN(JDIMENSION) jpeg_read_scanlines (j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg_skip_scanlines (j_decompress_ptr cinfo, + JDIMENSION num_lines); +EXTERN(void) jpeg_crop_scanline (j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width); +EXTERN(boolean) jpeg_finish_decompress (j_decompress_ptr cinfo); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans (const j_decompress_ptr cinfo); +EXTERN(boolean) jpeg_start_output (j_decompress_ptr cinfo, int scan_number); +EXTERN(boolean) jpeg_finish_output (j_decompress_ptr cinfo); +EXTERN(boolean) jpeg_input_complete (const j_decompress_ptr cinfo); +EXTERN(void) jpeg_new_colormap (j_decompress_ptr cinfo); +EXTERN(int) jpeg_consume_input (j_decompress_ptr cinfo); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +#if JPEG_LIB_VERSION >= 80 +EXTERN(void) jpeg_core_output_dimensions (j_decompress_ptr cinfo); +#endif +EXTERN(void) jpeg_calc_output_dimensions (j_decompress_ptr cinfo); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers (j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor (j_decompress_ptr cinfo, + int marker_code, + jpeg_marker_parser_method routine); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients (j_decompress_ptr cinfo); +EXTERN(void) jpeg_write_coefficients (j_compress_ptr cinfo, + jvirt_barray_ptr *coef_arrays); +EXTERN(void) jpeg_copy_critical_parameters (const j_decompress_ptr srcinfo, + j_compress_ptr dstinfo); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress (j_compress_ptr cinfo); +EXTERN(void) jpeg_abort_decompress (j_decompress_ptr cinfo); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort (j_common_ptr cinfo); +EXTERN(void) jpeg_destroy (j_common_ptr cinfo); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired); + +/* Accessor functions for extension parameters */ +#define JPEG_C_PARAM_SUPPORTED 1 +EXTERN(boolean) jpeg_c_bool_param_supported (const j_compress_ptr cinfo, + J_BOOLEAN_PARAM param); +EXTERN(void) jpeg_c_set_bool_param (j_compress_ptr cinfo, + J_BOOLEAN_PARAM param, boolean value); +EXTERN(boolean) jpeg_c_get_bool_param (const j_compress_ptr cinfo, + J_BOOLEAN_PARAM param); + +EXTERN(boolean) jpeg_c_float_param_supported (const j_compress_ptr cinfo, + J_FLOAT_PARAM param); +EXTERN(void) jpeg_c_set_float_param (j_compress_ptr cinfo, J_FLOAT_PARAM param, + float value); +EXTERN(float) jpeg_c_get_float_param (const j_compress_ptr cinfo, + J_FLOAT_PARAM param); + +EXTERN(boolean) jpeg_c_int_param_supported (const j_compress_ptr cinfo, + J_INT_PARAM param); +EXTERN(void) jpeg_c_set_int_param (j_compress_ptr cinfo, J_INT_PARAM param, + int value); +EXTERN(int) jpeg_c_get_int_param (const j_compress_ptr cinfo, J_INT_PARAM param); +/* Read ICC profile. See libjpeg.txt for usage information. */ +EXTERN(boolean) jpeg_read_icc_profile(j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len); + +/* + * Permit users to replace the IDCT method dynamically. + * The selector callback is called after the default idct implementation was choosen, + * and is able to override it. + */ +EXTERN(void) jpeg_set_idct_method_selector (j_decompress_ptr cinfo, jpeg_idct_method_selector selector); + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/c/header/png/png.h b/c/header/png/png.h new file mode 100644 index 0000000..b12174b --- /dev/null +++ b/c/header/png/png.h @@ -0,0 +1,3342 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.37 - April 14, 2019 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.37, April 2019: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2019 The PNG Reference Library Authors. + * * Copyright (c) 2018-2019 Cosmin Truta. + * * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. + * * Copyright (c) 1996-1997 Andreas Dilger. + * * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * The software is supplied "as is", without warranty of any kind, + * express or implied, including, without limitation, the warranties + * of merchantability, fitness for a particular purpose, title, and + * non-infringement. In no event shall the Copyright owners, or + * anyone distributing the software, be liable for any damages or + * other liability, whether in contract, tort or otherwise, arising + * from, out of, or in connection with the software, or the use or + * other dealings in the software, even if advised of the possibility + * of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute + * this software, or portions hereof, for any purpose, without fee, + * subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you + * use this software in a product, an acknowledgment in the product + * documentation would be appreciated, but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.37 16 10637 16.so.16.37[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.37" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 37 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10637 /* 1.6.37 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#define PNG_APNG_SUPPORTED +#define PNG_READ_APNG_SUPPORTED +#define PNG_WRITE_APNG_SUPPORTED + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +#ifdef PNG_APNG_SUPPORTED +/* dispose_op flags from inside fcTL */ +#define PNG_DISPOSE_OP_NONE 0x00U +#define PNG_DISPOSE_OP_BACKGROUND 0x01U +#define PNG_DISPOSE_OP_PREVIOUS 0x02U + +/* blend_op flags from inside fcTL */ +#define PNG_BLEND_OP_SOURCE 0x00U +#define PNG_BLEND_OP_OVER 0x01U +#endif /* PNG_APNG_SUPPORTED */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_37; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ +#ifdef PNG_APNG_SUPPORTED +#define PNG_INFO_acTL 0x20000U +#define PNG_INFO_fcTL 0x40000U +#endif + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); +#ifdef PNG_APNG_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_frame_ptr, (png_structp, + png_uint_32)); +#endif + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ +#ifdef PNG_APNG_SUPPORTED +PNG_EXPORT(250, png_uint_32, png_get_acTL, (png_structp png_ptr, + png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays)); + +PNG_EXPORT(251, png_uint_32, png_set_acTL, (png_structp png_ptr, + png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays)); + +PNG_EXPORT(252, png_uint_32, png_get_num_frames, (png_structp png_ptr, + png_infop info_ptr)); + +PNG_EXPORT(253, png_uint_32, png_get_num_plays, (png_structp png_ptr, + png_infop info_ptr)); + +PNG_EXPORT(254, png_uint_32, png_get_next_frame_fcTL, + (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, + png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset, + png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op, + png_byte *blend_op)); + +PNG_EXPORT(255, png_uint_32, png_set_next_frame_fcTL, + (png_structp png_ptr, png_infop info_ptr, png_uint_32 width, + png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, + png_byte blend_op)); + +PNG_EXPORT(256, png_uint_32, png_get_next_frame_width, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(257, png_uint_32, png_get_next_frame_height, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(258, png_uint_32, png_get_next_frame_x_offset, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(259, png_uint_32, png_get_next_frame_y_offset, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(260, png_uint_16, png_get_next_frame_delay_num, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(261, png_uint_16, png_get_next_frame_delay_den, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(262, png_byte, png_get_next_frame_dispose_op, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(263, png_byte, png_get_next_frame_blend_op, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(264, png_byte, png_get_first_frame_is_hidden, + (png_structp png_ptr, png_infop info_ptr)); +PNG_EXPORT(265, png_uint_32, png_set_first_frame_is_hidden, + (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden)); + +#ifdef PNG_READ_APNG_SUPPORTED +PNG_EXPORT(266, void, png_read_frame_head, (png_structp png_ptr, + png_infop info_ptr)); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXPORT(267, void, png_set_progressive_frame_fn, (png_structp png_ptr, + png_progressive_frame_ptr frame_info_fn, + png_progressive_frame_ptr frame_end_fn)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ +#endif /* PNG_READ_APNG_SUPPORTED */ + +#ifdef PNG_WRITE_APNG_SUPPORTED +PNG_EXPORT(268, void, png_write_frame_head, (png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers, + png_uint_32 width, png_uint_32 height, + png_uint_32 x_offset, png_uint_32 y_offset, + png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, + png_byte blend_op)); + +PNG_EXPORT(269, void, png_write_frame_tail, (png_structp png_ptr, + png_infop info_ptr)); +#endif /* PNG_WRITE_APNG_SUPPORTED */ +#endif /* PNG_APNG_SUPPORTED */ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL +#ifdef PNG_APNG_SUPPORTED + PNG_EXPORT_LAST_ORDINAL(269); +#else + PNG_EXPORT_LAST_ORDINAL(249); +#endif /* PNG_APNG_SUPPORTED */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/c/header/png/pngconf.h b/c/header/png/pngconf.h new file mode 100644 index 0000000..927a769 --- /dev/null +++ b/c/header/png/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.37 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/c/header/png/pnglibconf.h b/c/header/png/pnglibconf.h new file mode 100644 index 0000000..7f6a817 --- /dev/null +++ b/c/header/png/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.37 */ + +/* Copyright (c) 2018-2019 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/c/header/png/pngprefix.h b/c/header/png/pngprefix.h new file mode 100644 index 0000000..0737855 --- /dev/null +++ b/c/header/png/pngprefix.h @@ -0,0 +1 @@ +/* No libpng symbol prefix configured. */ \ No newline at end of file diff --git a/c/header/webp/config.h b/c/header/webp/config.h new file mode 100644 index 0000000..7f43bfa --- /dev/null +++ b/c/header/webp/config.h @@ -0,0 +1,150 @@ +/* src/webp/config.h. Generated from config.h.in by configure. */ +/* src/webp/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Set to 1 if __builtin_bswap16 is available */ +#define HAVE_BUILTIN_BSWAP16 1 + +/* Set to 1 if __builtin_bswap32 is available */ +#define HAVE_BUILTIN_BSWAP32 1 + +/* Set to 1 if __builtin_bswap64 is available */ +#define HAVE_BUILTIN_BSWAP64 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CPU_FEATURES_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GLUT_GLUT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GL_GLUT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENGL_GLUT_H */ + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SHLWAPI_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINCODEC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libwebp" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "https://bugs.chromium.org/p/webp" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libwebp" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libwebp 1.1.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libwebp" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "http://developers.google.com/speed/webp" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.0" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ + +/* Version number of package */ +#define VERSION "1.1.0" + +/* Set to 1 if GIF library is installed */ +/* #undef WEBP_HAVE_GIF */ + +/* Set to 1 if OpenGL is supported */ +/* #undef WEBP_HAVE_GL */ + +/* Set to 1 if JPEG library is installed */ +/* #undef WEBP_HAVE_JPEG */ + +/* Set to 1 if NEON is supported */ +/* #undef WEBP_HAVE_NEON */ + +/* Set to 1 if runtime detection of NEON is enabled */ +/* #undef WEBP_HAVE_NEON_RTCD */ + +/* Set to 1 if PNG library is installed */ +#define WEBP_HAVE_PNG 1 + +/* Set to 1 if SDL library is installed */ +/* #undef WEBP_HAVE_SDL */ + +/* Set to 1 if SSE2 is supported */ +/* #undef WEBP_HAVE_SSE2 */ + +/* Set to 1 if SSE4.1 is supported */ +/* #undef WEBP_HAVE_SSE41 */ + +/* Set to 1 if TIFF library is installed */ +#define WEBP_HAVE_TIFF 1 + +/* Enable near lossless encoding */ +#define WEBP_NEAR_LOSSLESS 1 + +/* Undefine this to disable thread support. */ +#define WEBP_USE_THREAD 1 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif diff --git a/c/header/webp/decode.h b/c/header/webp/decode.h new file mode 100644 index 0000000..80dd0ef --- /dev/null +++ b/c/header/webp/decode.h @@ -0,0 +1,503 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Main decoding functions for WebP images. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_WEBP_DECODE_H_ +#define WEBP_WEBP_DECODE_H_ + +#include "./types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_DECODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b) + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum VP8StatusCode VP8StatusCode; +// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE; +typedef struct WebPRGBABuffer WebPRGBABuffer; +typedef struct WebPYUVABuffer WebPYUVABuffer; +typedef struct WebPDecBuffer WebPDecBuffer; +typedef struct WebPIDecoder WebPIDecoder; +typedef struct WebPBitstreamFeatures WebPBitstreamFeatures; +typedef struct WebPDecoderOptions WebPDecoderOptions; +typedef struct WebPDecoderConfig WebPDecoderConfig; + +// Return the decoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetDecoderVersion(void); + +// Retrieve basic header information: width, height. +// This function will also validate the header, returning true on success, +// false otherwise. '*width' and '*height' are only valid on successful return. +// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant. +// Note: The following chunk sequences (before the raw VP8/VP8L data) are +// considered valid by this function: +// RIFF + VP8(L) +// RIFF + VP8X + (optional chunks) + VP8(L) +// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. +// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. +WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Decodes WebP images pointed to by 'data' and returns RGBA samples, along +// with the dimensions in *width and *height. The ordering of samples in +// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent). +// The returned pointer should be deleted calling WebPFree(). +// Returns NULL in case of error. +WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data. +WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data. +WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data. +// If the bitstream contains transparency, it is ignored. +WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data. +WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, + int* width, int* height); + + +// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer +// returned is the Y samples buffer. Upon return, *u and *v will point to +// the U and V chroma data. These U and V buffers need NOT be passed to +// WebPFree(), unlike the returned Y luma one. The dimension of the U and V +// planes are both (*width + 1) / 2 and (*height + 1)/ 2. +// Upon return, the Y buffer has a stride returned as '*stride', while U and V +// have a common stride returned as '*uv_stride'. +// Return NULL in case of error. +// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr +WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, + int* width, int* height, + uint8_t** u, uint8_t** v, + int* stride, int* uv_stride); + +// These five functions are variants of the above ones, that decode the image +// directly into a pre-allocated buffer 'output_buffer'. The maximum storage +// available in this buffer is indicated by 'output_buffer_size'. If this +// storage is not sufficient (or an error occurred), NULL is returned. +// Otherwise, output_buffer is returned, for convenience. +// The parameter 'output_stride' specifies the distance (in bytes) +// between scanlines. Hence, output_buffer_size is expected to be at least +// output_stride x picture-height. +WEBP_EXTERN uint8_t* WebPDecodeRGBAInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); +WEBP_EXTERN uint8_t* WebPDecodeARGBInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); +WEBP_EXTERN uint8_t* WebPDecodeBGRAInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); + +// RGB and BGR variants. Here too the transparency information, if present, +// will be dropped and ignored. +WEBP_EXTERN uint8_t* WebPDecodeRGBInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); +WEBP_EXTERN uint8_t* WebPDecodeBGRInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); + +// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly +// into pre-allocated luma/chroma plane buffers. This function requires the +// strides to be passed: one for the luma plane and one for each of the +// chroma ones. The size of each plane buffer is passed as 'luma_size', +// 'u_size' and 'v_size' respectively. +// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred +// during decoding (or because some buffers were found to be too small). +WEBP_EXTERN uint8_t* WebPDecodeYUVInto( + const uint8_t* data, size_t data_size, + uint8_t* luma, size_t luma_size, int luma_stride, + uint8_t* u, size_t u_size, int u_stride, + uint8_t* v, size_t v_size, int v_stride); + +//------------------------------------------------------------------------------ +// Output colorspaces and buffer + +// Colorspaces +// Note: the naming describes the byte-ordering of packed samples in memory. +// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,... +// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels. +// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order: +// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ... +// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ... +// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for +// these two modes: +// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ... +// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ... + +typedef enum WEBP_CSP_MODE { + MODE_RGB = 0, MODE_RGBA = 1, + MODE_BGR = 2, MODE_BGRA = 3, + MODE_ARGB = 4, MODE_RGBA_4444 = 5, + MODE_RGB_565 = 6, + // RGB-premultiplied transparent modes (alpha value is preserved) + MODE_rgbA = 7, + MODE_bgrA = 8, + MODE_Argb = 9, + MODE_rgbA_4444 = 10, + // YUV modes must come after RGB ones. + MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0 + MODE_LAST = 13 +} WEBP_CSP_MODE; + +// Some useful macros: +static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) { + return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb || + mode == MODE_rgbA_4444); +} + +static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) { + return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB || + mode == MODE_RGBA_4444 || mode == MODE_YUVA || + WebPIsPremultipliedMode(mode)); +} + +static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) { + return (mode < MODE_YUV); +} + +//------------------------------------------------------------------------------ +// WebPDecBuffer: Generic structure for describing the output sample buffer. + +struct WebPRGBABuffer { // view as RGBA + uint8_t* rgba; // pointer to RGBA samples + int stride; // stride in bytes from one scanline to the next. + size_t size; // total size of the *rgba buffer. +}; + +struct WebPYUVABuffer { // view as YUVA + uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples + int y_stride; // luma stride + int u_stride, v_stride; // chroma strides + int a_stride; // alpha stride + size_t y_size; // luma plane size + size_t u_size, v_size; // chroma planes size + size_t a_size; // alpha-plane size +}; + +// Output buffer +struct WebPDecBuffer { + WEBP_CSP_MODE colorspace; // Colorspace. + int width, height; // Dimensions. + int is_external_memory; // If non-zero, 'internal_memory' pointer is not + // used. If value is '2' or more, the external + // memory is considered 'slow' and multiple + // read/write will be avoided. + union { + WebPRGBABuffer RGBA; + WebPYUVABuffer YUVA; + } u; // Nameless union of buffer parameters. + uint32_t pad[4]; // padding for later use + + uint8_t* private_memory; // Internally allocated memory (only when + // is_external_memory is 0). Should not be used + // externally, but accessed via the buffer union. +}; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int); + +// Initialize the structure as empty. Must be called before any other use. +// Returns false in case of version mismatch +static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) { + return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION); +} + +// Free any memory associated with the buffer. Must always be called last. +// Note: doesn't free the 'buffer' structure itself. +WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer); + +//------------------------------------------------------------------------------ +// Enumeration of the status codes + +typedef enum VP8StatusCode { + VP8_STATUS_OK = 0, + VP8_STATUS_OUT_OF_MEMORY, + VP8_STATUS_INVALID_PARAM, + VP8_STATUS_BITSTREAM_ERROR, + VP8_STATUS_UNSUPPORTED_FEATURE, + VP8_STATUS_SUSPENDED, + VP8_STATUS_USER_ABORT, + VP8_STATUS_NOT_ENOUGH_DATA +} VP8StatusCode; + +//------------------------------------------------------------------------------ +// Incremental decoding +// +// This API allows streamlined decoding of partial data. +// Picture can be incrementally decoded as data become available thanks to the +// WebPIDecoder object. This object can be left in a SUSPENDED state if the +// picture is only partially decoded, pending additional input. +// Code example: +// +// WebPInitDecBuffer(&output_buffer); +// output_buffer.colorspace = mode; +// ... +// WebPIDecoder* idec = WebPINewDecoder(&output_buffer); +// while (additional_data_is_available) { +// // ... (get additional data in some new_data[] buffer) +// status = WebPIAppend(idec, new_data, new_data_size); +// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) { +// break; // an error occurred. +// } +// +// // The above call decodes the current available buffer. +// // Part of the image can now be refreshed by calling +// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc. +// } +// WebPIDelete(idec); + +// Creates a new incremental decoder with the supplied buffer parameter. +// This output_buffer can be passed NULL, in which case a default output buffer +// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer' +// is kept, which means that the lifespan of 'output_buffer' must be larger than +// that of the returned WebPIDecoder object. +// The supplied 'output_buffer' content MUST NOT be changed between calls to +// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is +// not set to 0. In such a case, it is allowed to modify the pointers, size and +// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain +// within valid bounds. +// All other fields of WebPDecBuffer MUST remain constant between calls. +// Returns NULL if the allocation failed. +WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer); + +// This function allocates and initializes an incremental-decoder object, which +// will output the RGB/A samples specified by 'csp' into a preallocated +// buffer 'output_buffer'. The size of this buffer is at least +// 'output_buffer_size' and the stride (distance in bytes between two scanlines) +// is specified by 'output_stride'. +// Additionally, output_buffer can be passed NULL in which case the output +// buffer will be allocated automatically when the decoding starts. The +// colorspace 'csp' is taken into account for allocating this buffer. All other +// parameters are ignored. +// Returns NULL if the allocation failed, or if some parameters are invalid. +WEBP_EXTERN WebPIDecoder* WebPINewRGB( + WEBP_CSP_MODE csp, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); + +// This function allocates and initializes an incremental-decoder object, which +// will output the raw luma/chroma samples into a preallocated planes if +// supplied. The luma plane is specified by its pointer 'luma', its size +// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane +// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v +// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer +// can be pass NULL in case one is not interested in the transparency plane. +// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied. +// In this case, the output buffer will be automatically allocated (using +// MODE_YUVA) when decoding starts. All parameters are then ignored. +// Returns NULL if the allocation failed or if a parameter is invalid. +WEBP_EXTERN WebPIDecoder* WebPINewYUVA( + uint8_t* luma, size_t luma_size, int luma_stride, + uint8_t* u, size_t u_size, int u_stride, + uint8_t* v, size_t v_size, int v_stride, + uint8_t* a, size_t a_size, int a_stride); + +// Deprecated version of the above, without the alpha plane. +// Kept for backward compatibility. +WEBP_EXTERN WebPIDecoder* WebPINewYUV( + uint8_t* luma, size_t luma_size, int luma_stride, + uint8_t* u, size_t u_size, int u_stride, + uint8_t* v, size_t v_size, int v_stride); + +// Deletes the WebPIDecoder object and associated memory. Must always be called +// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded. +WEBP_EXTERN void WebPIDelete(WebPIDecoder* idec); + +// Copies and decodes the next available data. Returns VP8_STATUS_OK when +// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more +// data is expected. Returns error in other cases. +WEBP_EXTERN VP8StatusCode WebPIAppend( + WebPIDecoder* idec, const uint8_t* data, size_t data_size); + +// A variant of the above function to be used when data buffer contains +// partial data from the beginning. In this case data buffer is not copied +// to the internal memory. +// Note that the value of the 'data' pointer can change between calls to +// WebPIUpdate, for instance when the data buffer is resized to fit larger data. +WEBP_EXTERN VP8StatusCode WebPIUpdate( + WebPIDecoder* idec, const uint8_t* data, size_t data_size); + +// Returns the RGB/A image decoded so far. Returns NULL if output params +// are not initialized yet. The RGB/A output type corresponds to the colorspace +// specified during call to WebPINewDecoder() or WebPINewRGB(). +// *last_y is the index of last decoded row in raster scan order. Some pointers +// (*last_y, *width etc.) can be NULL if corresponding information is not +// needed. The values in these pointers are only valid on successful (non-NULL) +// return. +WEBP_EXTERN uint8_t* WebPIDecGetRGB( + const WebPIDecoder* idec, int* last_y, + int* width, int* height, int* stride); + +// Same as above function to get a YUVA image. Returns pointer to the luma +// plane or NULL in case of error. If there is no alpha information +// the alpha pointer '*a' will be returned NULL. +WEBP_EXTERN uint8_t* WebPIDecGetYUVA( + const WebPIDecoder* idec, int* last_y, + uint8_t** u, uint8_t** v, uint8_t** a, + int* width, int* height, int* stride, int* uv_stride, int* a_stride); + +// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the +// alpha information (if present). Kept for backward compatibility. +static WEBP_INLINE uint8_t* WebPIDecGetYUV( + const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v, + int* width, int* height, int* stride, int* uv_stride) { + return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height, + stride, uv_stride, NULL); +} + +// Generic call to retrieve information about the displayable area. +// If non NULL, the left/right/width/height pointers are filled with the visible +// rectangular area so far. +// Returns NULL in case the incremental decoder object is in an invalid state. +// Otherwise returns the pointer to the internal representation. This structure +// is read-only, tied to WebPIDecoder's lifespan and should not be modified. +WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea( + const WebPIDecoder* idec, int* left, int* top, int* width, int* height); + +//------------------------------------------------------------------------------ +// Advanced decoding parametrization +// +// Code sample for using the advanced decoding API +/* + // A) Init a configuration object + WebPDecoderConfig config; + CHECK(WebPInitDecoderConfig(&config)); + + // B) optional: retrieve the bitstream's features. + CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK); + + // C) Adjust 'config', if needed + config.no_fancy_upsampling = 1; + config.output.colorspace = MODE_BGRA; + // etc. + + // Note that you can also make config.output point to an externally + // supplied memory buffer, provided it's big enough to store the decoded + // picture. Otherwise, config.output will just be used to allocate memory + // and store the decoded picture. + + // D) Decode! + CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK); + + // E) Decoded image is now in config.output (and config.output.u.RGBA) + + // F) Reclaim memory allocated in config's object. It's safe to call + // this function even if the memory is external and wasn't allocated + // by WebPDecode(). + WebPFreeDecBuffer(&config.output); +*/ + +// Features gathered from the bitstream +struct WebPBitstreamFeatures { + int width; // Width in pixels, as read from the bitstream. + int height; // Height in pixels, as read from the bitstream. + int has_alpha; // True if the bitstream contains an alpha channel. + int has_animation; // True if the bitstream is an animation. + int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless + + uint32_t pad[5]; // padding for later use +}; + +// Internal, version-checked, entry point +WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal( + const uint8_t*, size_t, WebPBitstreamFeatures*, int); + +// Retrieve features from the bitstream. The *features structure is filled +// with information gathered from the bitstream. +// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns +// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the +// features from headers. Returns error in other cases. +// Note: The following chunk sequences (before the raw VP8/VP8L data) are +// considered valid by this function: +// RIFF + VP8(L) +// RIFF + VP8X + (optional chunks) + VP8(L) +// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. +// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. +static WEBP_INLINE VP8StatusCode WebPGetFeatures( + const uint8_t* data, size_t data_size, + WebPBitstreamFeatures* features) { + return WebPGetFeaturesInternal(data, data_size, features, + WEBP_DECODER_ABI_VERSION); +} + +// Decoding options +struct WebPDecoderOptions { + int bypass_filtering; // if true, skip the in-loop filtering + int no_fancy_upsampling; // if true, use faster pointwise upsampler + int use_cropping; // if true, cropping is applied _first_ + int crop_left, crop_top; // top-left position for cropping. + // Will be snapped to even values. + int crop_width, crop_height; // dimension of the cropping area + int use_scaling; // if true, scaling is applied _afterward_ + int scaled_width, scaled_height; // final resolution + int use_threads; // if true, use multi-threaded decoding + int dithering_strength; // dithering strength (0=Off, 100=full) + int flip; // flip output vertically + int alpha_dithering_strength; // alpha dithering strength in [0..100] + + uint32_t pad[5]; // padding for later use +}; + +// Main object storing the configuration for advanced decoding. +struct WebPDecoderConfig { + WebPBitstreamFeatures input; // Immutable bitstream features (optional) + WebPDecBuffer output; // Output buffer (can point to external mem) + WebPDecoderOptions options; // Decoding options +}; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int); + +// Initialize the configuration as empty. This function must always be +// called first, unless WebPGetFeatures() is to be called. +// Returns false in case of mismatched version. +static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) { + return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION); +} + +// Instantiate a new incremental decoder object with the requested +// configuration. The bitstream can be passed using 'data' and 'data_size' +// parameter, in which case the features will be parsed and stored into +// config->input. Otherwise, 'data' can be NULL and no parsing will occur. +// Note that 'config' can be NULL too, in which case a default configuration +// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object +// as some references to its fields will be used. No internal copy of 'config' +// is made. +// The return WebPIDecoder object must always be deleted calling WebPIDelete(). +// Returns NULL in case of error (and config->status will then reflect +// the error condition, if available). +WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, + WebPDecoderConfig* config); + +// Non-incremental version. This version decodes the full data at once, taking +// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK +// if the decoding was successful). Note that 'config' cannot be NULL. +WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, + WebPDecoderConfig* config); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_DECODE_H_ diff --git a/c/header/webp/demux.h b/c/header/webp/demux.h new file mode 100644 index 0000000..846eeb1 --- /dev/null +++ b/c/header/webp/demux.h @@ -0,0 +1,363 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Demux API. +// Enables extraction of image and extended format data from WebP files. + +// Code Example: Demuxing WebP data to extract all the frames, ICC profile +// and EXIF/XMP metadata. +/* + WebPDemuxer* demux = WebPDemux(&webp_data); + + uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); + uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); + // ... (Get information about the features present in the WebP file). + uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); + + // ... (Iterate over all frames). + WebPIterator iter; + if (WebPDemuxGetFrame(demux, 1, &iter)) { + do { + // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(), + // ... and get other frame properties like width, height, offsets etc. + // ... see 'struct WebPIterator' below for more info). + } while (WebPDemuxNextFrame(&iter)); + WebPDemuxReleaseIterator(&iter); + } + + // ... (Extract metadata). + WebPChunkIterator chunk_iter; + if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter); + // ... (Consume the ICC profile in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter); + // ... (Consume the EXIF metadata in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter); + // ... (Consume the XMP metadata in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + WebPDemuxDelete(demux); +*/ + +#ifndef WEBP_WEBP_DEMUX_H_ +#define WEBP_WEBP_DEMUX_H_ + +#include "./decode.h" // for WEBP_CSP_MODE +#include "./mux_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_DEMUX_ABI_VERSION 0x0107 // MAJOR(8b) + MINOR(8b) + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPDemuxState WebPDemuxState; +// typedef enum WebPFormatFeature WebPFormatFeature; +typedef struct WebPDemuxer WebPDemuxer; +typedef struct WebPIterator WebPIterator; +typedef struct WebPChunkIterator WebPChunkIterator; +typedef struct WebPAnimInfo WebPAnimInfo; +typedef struct WebPAnimDecoderOptions WebPAnimDecoderOptions; + +//------------------------------------------------------------------------------ + +// Returns the version number of the demux library, packed in hexadecimal using +// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetDemuxVersion(void); + +//------------------------------------------------------------------------------ +// Life of a Demux object + +typedef enum WebPDemuxState { + WEBP_DEMUX_PARSE_ERROR = -1, // An error occurred while parsing. + WEBP_DEMUX_PARSING_HEADER = 0, // Not enough data to parse full header. + WEBP_DEMUX_PARSED_HEADER = 1, // Header parsing complete, + // data may be available. + WEBP_DEMUX_DONE = 2 // Entire file has been parsed. +} WebPDemuxState; + +// Internal, version-checked, entry point +WEBP_EXTERN WebPDemuxer* WebPDemuxInternal( + const WebPData*, int, WebPDemuxState*, int); + +// Parses the full WebP file given by 'data'. For single images the WebP file +// header alone or the file header and the chunk header may be absent. +// Returns a WebPDemuxer object on successful parse, NULL otherwise. +static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) { + return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION); +} + +// Parses the possibly incomplete WebP file given by 'data'. +// If 'state' is non-NULL it will be set to indicate the status of the demuxer. +// Returns NULL in case of error or if there isn't enough data to start parsing; +// and a WebPDemuxer object on successful parse. +// Note that WebPDemuxer keeps internal pointers to 'data' memory segment. +// If this data is volatile, the demuxer object should be deleted (by calling +// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data. +// This is usually an inexpensive operation. +static WEBP_INLINE WebPDemuxer* WebPDemuxPartial( + const WebPData* data, WebPDemuxState* state) { + return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION); +} + +// Frees memory associated with 'dmux'. +WEBP_EXTERN void WebPDemuxDelete(WebPDemuxer* dmux); + +//------------------------------------------------------------------------------ +// Data/information extraction. + +typedef enum WebPFormatFeature { + WEBP_FF_FORMAT_FLAGS, // bit-wise combination of WebPFeatureFlags + // corresponding to the 'VP8X' chunk (if present). + WEBP_FF_CANVAS_WIDTH, + WEBP_FF_CANVAS_HEIGHT, + WEBP_FF_LOOP_COUNT, // only relevant for animated file + WEBP_FF_BACKGROUND_COLOR, // idem. + WEBP_FF_FRAME_COUNT // Number of frames present in the demux object. + // In case of a partial demux, this is the number + // of frames seen so far, with the last frame + // possibly being partial. +} WebPFormatFeature; + +// Get the 'feature' value from the 'dmux'. +// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial() +// returned a state > WEBP_DEMUX_PARSING_HEADER. +// If 'feature' is WEBP_FF_FORMAT_FLAGS, the returned value is a bit-wise +// combination of WebPFeatureFlags values. +// If 'feature' is WEBP_FF_LOOP_COUNT, WEBP_FF_BACKGROUND_COLOR, the returned +// value is only meaningful if the bitstream is animated. +WEBP_EXTERN uint32_t WebPDemuxGetI( + const WebPDemuxer* dmux, WebPFormatFeature feature); + +//------------------------------------------------------------------------------ +// Frame iteration. + +struct WebPIterator { + int frame_num; + int num_frames; // equivalent to WEBP_FF_FRAME_COUNT. + int x_offset, y_offset; // offset relative to the canvas. + int width, height; // dimensions of this frame. + int duration; // display duration in milliseconds. + WebPMuxAnimDispose dispose_method; // dispose method for the frame. + int complete; // true if 'fragment' contains a full frame. partial images + // may still be decoded with the WebP incremental decoder. + WebPData fragment; // The frame given by 'frame_num'. Note for historical + // reasons this is called a fragment. + int has_alpha; // True if the frame contains transparency. + WebPMuxAnimBlend blend_method; // Blend operation for the frame. + + uint32_t pad[2]; // padding for later use. + void* private_; // for internal use only. +}; + +// Retrieves frame 'frame_number' from 'dmux'. +// 'iter->fragment' points to the frame on return from this function. +// Setting 'frame_number' equal to 0 will return the last frame of the image. +// Returns false if 'dmux' is NULL or frame 'frame_number' is not present. +// Call WebPDemuxReleaseIterator() when use of the iterator is complete. +// NOTE: 'dmux' must persist for the lifetime of 'iter'. +WEBP_EXTERN int WebPDemuxGetFrame( + const WebPDemuxer* dmux, int frame_number, WebPIterator* iter); + +// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or +// previous ('iter->frame_num' - 1) frame. These functions do not loop. +// Returns true on success, false otherwise. +WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter); +WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter); + +// Releases any memory associated with 'iter'. +// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same +// iter. Also, must be called before destroying the associated WebPDemuxer with +// WebPDemuxDelete(). +WEBP_EXTERN void WebPDemuxReleaseIterator(WebPIterator* iter); + +//------------------------------------------------------------------------------ +// Chunk iteration. + +struct WebPChunkIterator { + // The current and total number of chunks with the fourcc given to + // WebPDemuxGetChunk(). + int chunk_num; + int num_chunks; + WebPData chunk; // The payload of the chunk. + + uint32_t pad[6]; // padding for later use + void* private_; +}; + +// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from +// 'dmux'. +// 'fourcc' is a character array containing the fourcc of the chunk to return, +// e.g., "ICCP", "XMP ", "EXIF", etc. +// Setting 'chunk_number' equal to 0 will return the last chunk in a set. +// Returns true if the chunk is found, false otherwise. Image related chunk +// payloads are accessed through WebPDemuxGetFrame() and related functions. +// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete. +// NOTE: 'dmux' must persist for the lifetime of the iterator. +WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux, + const char fourcc[4], int chunk_number, + WebPChunkIterator* iter); + +// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous +// ('iter->chunk_num' - 1) chunk. These functions do not loop. +// Returns true on success, false otherwise. +WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter); +WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter); + +// Releases any memory associated with 'iter'. +// Must be called before destroying the associated WebPDemuxer with +// WebPDemuxDelete(). +WEBP_EXTERN void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter); + +//------------------------------------------------------------------------------ +// WebPAnimDecoder API +// +// This API allows decoding (possibly) animated WebP images. +// +// Code Example: +/* + WebPAnimDecoderOptions dec_options; + WebPAnimDecoderOptionsInit(&dec_options); + // Tune 'dec_options' as needed. + WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options); + WebPAnimInfo anim_info; + WebPAnimDecoderGetInfo(dec, &anim_info); + for (uint32_t i = 0; i < anim_info.loop_count; ++i) { + while (WebPAnimDecoderHasMoreFrames(dec)) { + uint8_t* buf; + int timestamp; + WebPAnimDecoderGetNext(dec, &buf, ×tamp); + // ... (Render 'buf' based on 'timestamp'). + // ... (Do NOT free 'buf', as it is owned by 'dec'). + } + WebPAnimDecoderReset(dec); + } + const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec); + // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data). + WebPAnimDecoderDelete(dec); +*/ + +typedef struct WebPAnimDecoder WebPAnimDecoder; // Main opaque object. + +// Global options. +struct WebPAnimDecoderOptions { + // Output colorspace. Only the following modes are supported: + // MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA. + WEBP_CSP_MODE color_mode; + int use_threads; // If true, use multi-threaded decoding. + uint32_t padding[7]; // Padding for later use. +}; + +// Internal, version-checked, entry point. +WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal( + WebPAnimDecoderOptions*, int); + +// Should always be called, to initialize a fresh WebPAnimDecoderOptions +// structure before modification. Returns false in case of version mismatch. +// WebPAnimDecoderOptionsInit() must have succeeded before using the +// 'dec_options' object. +static WEBP_INLINE int WebPAnimDecoderOptionsInit( + WebPAnimDecoderOptions* dec_options) { + return WebPAnimDecoderOptionsInitInternal(dec_options, + WEBP_DEMUX_ABI_VERSION); +} + +// Internal, version-checked, entry point. +WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal( + const WebPData*, const WebPAnimDecoderOptions*, int); + +// Creates and initializes a WebPAnimDecoder object. +// Parameters: +// webp_data - (in) WebP bitstream. This should remain unchanged during the +// lifetime of the output WebPAnimDecoder object. +// dec_options - (in) decoding options. Can be passed NULL to choose +// reasonable defaults (in particular, color mode MODE_RGBA +// will be picked). +// Returns: +// A pointer to the newly created WebPAnimDecoder object, or NULL in case of +// parsing error, invalid option or memory error. +static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew( + const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) { + return WebPAnimDecoderNewInternal(webp_data, dec_options, + WEBP_DEMUX_ABI_VERSION); +} + +// Global information about the animation.. +struct WebPAnimInfo { + uint32_t canvas_width; + uint32_t canvas_height; + uint32_t loop_count; + uint32_t bgcolor; + uint32_t frame_count; + uint32_t pad[4]; // padding for later use +}; + +// Get global information about the animation. +// Parameters: +// dec - (in) decoder instance to get information from. +// info - (out) global information fetched from the animation. +// Returns: +// True on success. +WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, + WebPAnimInfo* info); + +// Fetch the next frame from 'dec' based on options supplied to +// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size +// 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The +// returned buffer 'buf' is valid only until the next call to +// WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete(). +// Parameters: +// dec - (in/out) decoder instance from which the next frame is to be fetched. +// buf - (out) decoded frame. +// timestamp - (out) timestamp of the frame in milliseconds. +// Returns: +// False if any of the arguments are NULL, or if there is a parsing or +// decoding error, or if there are no more frames. Otherwise, returns true. +WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, + uint8_t** buf, int* timestamp); + +// Check if there are more frames left to decode. +// Parameters: +// dec - (in) decoder instance to be checked. +// Returns: +// True if 'dec' is not NULL and some frames are yet to be decoded. +// Otherwise, returns false. +WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec); + +// Resets the WebPAnimDecoder object, so that next call to +// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be +// helpful when all frames need to be decoded multiple times (e.g. +// info.loop_count times) without destroying and recreating the 'dec' object. +// Parameters: +// dec - (in/out) decoder instance to be reset +WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec); + +// Grab the internal demuxer object. +// Getting the demuxer object can be useful if one wants to use operations only +// available through demuxer; e.g. to get XMP/EXIF/ICC metadata. The returned +// demuxer object is owned by 'dec' and is valid only until the next call to +// WebPAnimDecoderDelete(). +// +// Parameters: +// dec - (in) decoder instance from which the demuxer object is to be fetched. +WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer( + const WebPAnimDecoder* dec); + +// Deletes the WebPAnimDecoder object. +// Parameters: +// dec - (in/out) decoder instance to be deleted +WEBP_EXTERN void WebPAnimDecoderDelete(WebPAnimDecoder* dec); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_DEMUX_H_ diff --git a/c/header/webp/encode.h b/c/header/webp/encode.h new file mode 100644 index 0000000..655166e --- /dev/null +++ b/c/header/webp/encode.h @@ -0,0 +1,546 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// WebP encoder: main interface +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_WEBP_ENCODE_H_ +#define WEBP_WEBP_ENCODE_H_ + +#include "./types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_ENCODER_ABI_VERSION 0x020f // MAJOR(8b) + MINOR(8b) + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPImageHint WebPImageHint; +// typedef enum WebPEncCSP WebPEncCSP; +// typedef enum WebPPreset WebPPreset; +// typedef enum WebPEncodingError WebPEncodingError; +typedef struct WebPConfig WebPConfig; +typedef struct WebPPicture WebPPicture; // main structure for I/O +typedef struct WebPAuxStats WebPAuxStats; +typedef struct WebPMemoryWriter WebPMemoryWriter; + +// Return the encoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetEncoderVersion(void); + +//------------------------------------------------------------------------------ +// One-stop-shop call! No questions asked: + +// Returns the size of the compressed data (pointed to by *output), or 0 if +// an error occurred. The compressed data must be released by the caller +// using the call 'WebPFree(*output)'. +// These functions compress using the lossy format, and the quality_factor +// can go from 0 (smaller output, lower quality) to 100 (best quality, +// larger output). +WEBP_EXTERN size_t WebPEncodeRGB(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, uint8_t** output); +WEBP_EXTERN size_t WebPEncodeBGR(const uint8_t* bgr, + int width, int height, int stride, + float quality_factor, uint8_t** output); +WEBP_EXTERN size_t WebPEncodeRGBA(const uint8_t* rgba, + int width, int height, int stride, + float quality_factor, uint8_t** output); +WEBP_EXTERN size_t WebPEncodeBGRA(const uint8_t* bgra, + int width, int height, int stride, + float quality_factor, uint8_t** output); + +// These functions are the equivalent of the above, but compressing in a +// lossless manner. Files are usually larger than lossy format, but will +// not suffer any compression loss. +// Note these functions, like the lossy versions, use the library's default +// settings. For lossless this means 'exact' is disabled. RGB values in +// transparent areas will be modified to improve compression. To avoid this, +// use WebPEncode() and set WebPConfig::exact to 1. +WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb, + int width, int height, int stride, + uint8_t** output); +WEBP_EXTERN size_t WebPEncodeLosslessBGR(const uint8_t* bgr, + int width, int height, int stride, + uint8_t** output); +WEBP_EXTERN size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, + int width, int height, int stride, + uint8_t** output); +WEBP_EXTERN size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, + int width, int height, int stride, + uint8_t** output); + +//------------------------------------------------------------------------------ +// Coding parameters + +// Image characteristics hint for the underlying encoder. +typedef enum WebPImageHint { + WEBP_HINT_DEFAULT = 0, // default preset. + WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot + WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting + WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc). + WEBP_HINT_LAST +} WebPImageHint; + +// Compression parameters. +struct WebPConfig { + int lossless; // Lossless encoding (0=lossy(default), 1=lossless). + float quality; // between 0 and 100. For lossy, 0 gives the smallest + // size and 100 the largest. For lossless, this + // parameter is the amount of effort put into the + // compression: 0 is the fastest but gives larger + // files compared to the slowest, but best, 100. + int method; // quality/speed trade-off (0=fast, 6=slower-better) + + WebPImageHint image_hint; // Hint for image type (lossless only for now). + + int target_size; // if non-zero, set the desired target size in bytes. + // Takes precedence over the 'compression' parameter. + float target_PSNR; // if non-zero, specifies the minimal distortion to + // try to achieve. Takes precedence over target_size. + int segments; // maximum number of segments to use, in [1..4] + int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum. + int filter_strength; // range: [0 = off .. 100 = strongest] + int filter_sharpness; // range: [0 = off .. 7 = least sharp] + int filter_type; // filtering type: 0 = simple, 1 = strong (only used + // if filter_strength > 0 or autofilter > 0) + int autofilter; // Auto adjust filter's strength [0 = off, 1 = on] + int alpha_compression; // Algorithm for encoding the alpha plane (0 = none, + // 1 = compressed with WebP lossless). Default is 1. + int alpha_filtering; // Predictive filtering method for alpha plane. + // 0: none, 1: fast, 2: best. Default if 1. + int alpha_quality; // Between 0 (smallest size) and 100 (lossless). + // Default is 100. + int pass; // number of entropy-analysis passes (in [1..10]). + + int show_compressed; // if true, export the compressed picture back. + // In-loop filtering is not applied. + int preprocessing; // preprocessing filter: + // 0=none, 1=segment-smooth, 2=pseudo-random dithering + int partitions; // log2(number of token partitions) in [0..3]. Default + // is set to 0 for easier progressive decoding. + int partition_limit; // quality degradation allowed to fit the 512k limit + // on prediction modes coding (0: no degradation, + // 100: maximum possible degradation). + int emulate_jpeg_size; // If true, compression parameters will be remapped + // to better match the expected output size from + // JPEG compression. Generally, the output size will + // be similar but the degradation will be lower. + int thread_level; // If non-zero, try and use multi-threaded encoding. + int low_memory; // If set, reduce memory usage (but increase CPU use). + + int near_lossless; // Near lossless encoding [0 = max loss .. 100 = off + // (default)]. + int exact; // if non-zero, preserve the exact RGB values under + // transparent area. Otherwise, discard this invisible + // RGB information for better compression. The default + // value is 0. + + int use_delta_palette; // reserved for future lossless feature + int use_sharp_yuv; // if needed, use sharp (and slow) RGB->YUV conversion + + uint32_t pad[2]; // padding for later use +}; + +// Enumerate some predefined settings for WebPConfig, depending on the type +// of source picture. These presets are used when calling WebPConfigPreset(). +typedef enum WebPPreset { + WEBP_PRESET_DEFAULT = 0, // default preset. + WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot + WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting + WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details + WEBP_PRESET_ICON, // small-sized colorful images + WEBP_PRESET_TEXT // text-like +} WebPPreset; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int); + +// Should always be called, to initialize a fresh WebPConfig structure before +// modification. Returns false in case of version mismatch. WebPConfigInit() +// must have succeeded before using the 'config' object. +// Note that the default values are lossless=0 and quality=75. +static WEBP_INLINE int WebPConfigInit(WebPConfig* config) { + return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f, + WEBP_ENCODER_ABI_VERSION); +} + +// This function will initialize the configuration according to a predefined +// set of parameters (referred to by 'preset') and a given quality factor. +// This function can be called as a replacement to WebPConfigInit(). Will +// return false in case of error. +static WEBP_INLINE int WebPConfigPreset(WebPConfig* config, + WebPPreset preset, float quality) { + return WebPConfigInitInternal(config, preset, quality, + WEBP_ENCODER_ABI_VERSION); +} + +// Activate the lossless compression mode with the desired efficiency level +// between 0 (fastest, lowest compression) and 9 (slower, best compression). +// A good default level is '6', providing a fair tradeoff between compression +// speed and final compressed size. +// This function will overwrite several fields from config: 'method', 'quality' +// and 'lossless'. Returns false in case of parameter error. +WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level); + +// Returns true if 'config' is non-NULL and all configuration parameters are +// within their valid ranges. +WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config); + +//------------------------------------------------------------------------------ +// Input / Output +// Structure for storing auxiliary statistics. + +struct WebPAuxStats { + int coded_size; // final size + + float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha + int block_count[3]; // number of intra4/intra16/skipped macroblocks + int header_bytes[2]; // approximate number of bytes spent for header + // and mode-partition #0 + int residual_bytes[3][4]; // approximate number of bytes spent for + // DC/AC/uv coefficients for each (0..3) segments. + int segment_size[4]; // number of macroblocks in each segments + int segment_quant[4]; // quantizer values for each segments + int segment_level[4]; // filtering strength for each segments [0..63] + + int alpha_data_size; // size of the transparency data + int layer_data_size; // size of the enhancement layer data + + // lossless encoder statistics + uint32_t lossless_features; // bit0:predictor bit1:cross-color transform + // bit2:subtract-green bit3:color indexing + int histogram_bits; // number of precision bits of histogram + int transform_bits; // precision bits for transform + int cache_bits; // number of bits for color cache lookup + int palette_size; // number of color in palette, if used + int lossless_size; // final lossless size + int lossless_hdr_size; // lossless header (transform, huffman etc) size + int lossless_data_size; // lossless image data size + + uint32_t pad[2]; // padding for later use +}; + +// Signature for output function. Should return true if writing was successful. +// data/data_size is the segment of data to write, and 'picture' is for +// reference (and so one can make use of picture->custom_ptr). +typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size, + const WebPPicture* picture); + +// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using +// the following WebPMemoryWriter object (to be set as a custom_ptr). +struct WebPMemoryWriter { + uint8_t* mem; // final buffer (of size 'max_size', larger than 'size'). + size_t size; // final size + size_t max_size; // total capacity + uint32_t pad[1]; // padding for later use +}; + +// The following must be called first before any use. +WEBP_EXTERN void WebPMemoryWriterInit(WebPMemoryWriter* writer); + +// The following must be called to deallocate writer->mem memory. The 'writer' +// object itself is not deallocated. +WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer); +// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon +// completion, writer.mem and writer.size will hold the coded data. +// writer.mem must be freed by calling WebPMemoryWriterClear. +WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size, + const WebPPicture* picture); + +// Progress hook, called from time to time to report progress. It can return +// false to request an abort of the encoding process, or true otherwise if +// everything is OK. +typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture); + +// Color spaces. +typedef enum WebPEncCSP { + // chroma sampling + WEBP_YUV420 = 0, // 4:2:0 + WEBP_YUV420A = 4, // alpha channel variant + WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors + WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present +} WebPEncCSP; + +// Encoding error conditions. +typedef enum WebPEncodingError { + VP8_ENC_OK = 0, + VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects + VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits + VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL + VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid + VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height + VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is bigger than 512k + VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is bigger than 16M + VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes + VP8_ENC_ERROR_FILE_TOO_BIG, // file is bigger than 4G + VP8_ENC_ERROR_USER_ABORT, // abort request by user + VP8_ENC_ERROR_LAST // list terminator. always last. +} WebPEncodingError; + +// maximum width/height allowed (inclusive), in pixels +#define WEBP_MAX_DIMENSION 16383 + +// Main exchange structure (input samples, output bytes, statistics) +struct WebPPicture { + // INPUT + ////////////// + // Main flag for encoder selecting between ARGB or YUV input. + // It is recommended to use ARGB input (*argb, argb_stride) for lossless + // compression, and YUV input (*y, *u, *v, etc.) for lossy compression + // since these are the respective native colorspace for these formats. + int use_argb; + + // YUV input (mostly used for input to lossy compression) + WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr). + int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION) + uint8_t* y, *u, *v; // pointers to luma/chroma planes. + int y_stride, uv_stride; // luma/chroma strides. + uint8_t* a; // pointer to the alpha plane + int a_stride; // stride of the alpha plane + uint32_t pad1[2]; // padding for later use + + // ARGB input (mostly used for input to lossless compression) + uint32_t* argb; // Pointer to argb (32 bit) plane. + int argb_stride; // This is stride in pixels units, not bytes. + uint32_t pad2[3]; // padding for later use + + // OUTPUT + /////////////// + // Byte-emission hook, to store compressed bytes as they are ready. + WebPWriterFunction writer; // can be NULL + void* custom_ptr; // can be used by the writer. + + // map for extra information (only for lossy compression mode) + int extra_info_type; // 1: intra type, 2: segment, 3: quant + // 4: intra-16 prediction mode, + // 5: chroma prediction mode, + // 6: bit cost, 7: distortion + uint8_t* extra_info; // if not NULL, points to an array of size + // ((width + 15) / 16) * ((height + 15) / 16) that + // will be filled with a macroblock map, depending + // on extra_info_type. + + // STATS AND REPORTS + /////////////////////////// + // Pointer to side statistics (updated only if not NULL) + WebPAuxStats* stats; + + // Error code for the latest error encountered during encoding + WebPEncodingError error_code; + + // If not NULL, report progress during encoding. + WebPProgressHook progress_hook; + + void* user_data; // this field is free to be set to any value and + // used during callbacks (like progress-report e.g.). + + uint32_t pad3[3]; // padding for later use + + // Unused for now + uint8_t* pad4, *pad5; + uint32_t pad6[8]; // padding for later use + + // PRIVATE FIELDS + //////////////////// + void* memory_; // row chunk of memory for yuva planes + void* memory_argb_; // and for argb too. + void* pad7[2]; // padding for later use +}; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int); + +// Should always be called, to initialize the structure. Returns false in case +// of version mismatch. WebPPictureInit() must have succeeded before using the +// 'picture' object. +// Note that, by default, use_argb is false and colorspace is WEBP_YUV420. +static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) { + return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION); +} + +//------------------------------------------------------------------------------ +// WebPPicture utils + +// Convenience allocation / deallocation based on picture->width/height: +// Allocate y/u/v buffers as per colorspace/width/height specification. +// Note! This function will free the previous buffer if needed. +// Returns false in case of memory error. +WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture); + +// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*(). +// Note that this function does _not_ free the memory used by the 'picture' +// object itself. +// Besides memory (which is reclaimed) all other fields of 'picture' are +// preserved. +WEBP_EXTERN void WebPPictureFree(WebPPicture* picture); + +// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst +// will fully own the copied pixels (this is not a view). The 'dst' picture need +// not be initialized as its content is overwritten. +// Returns false in case of memory allocation error. +WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst); + +// Compute the single distortion for packed planes of samples. +// 'src' will be compared to 'ref', and the raw distortion stored into +// '*distortion'. The refined metric (log(MSE), log(1 - ssim),...' will be +// stored in '*result'. +// 'x_step' is the horizontal stride (in bytes) between samples. +// 'src/ref_stride' is the byte distance between rows. +// Returns false in case of error (bad parameter, memory allocation error, ...). +WEBP_EXTERN int WebPPlaneDistortion(const uint8_t* src, size_t src_stride, + const uint8_t* ref, size_t ref_stride, + int width, int height, + size_t x_step, + int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM + float* distortion, float* result); + +// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results +// are in dB, stored in result[] in the B/G/R/A/All order. The distortion is +// always performed using ARGB samples. Hence if the input is YUV(A), the +// picture will be internally converted to ARGB (just for the measurement). +// Warning: this function is rather CPU-intensive. +WEBP_EXTERN int WebPPictureDistortion( + const WebPPicture* src, const WebPPicture* ref, + int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM + float result[5]); + +// self-crops a picture to the rectangle defined by top/left/width/height. +// Returns false in case of memory allocation error, or if the rectangle is +// outside of the source picture. +// The rectangle for the view is defined by the top-left corner pixel +// coordinates (left, top) as well as its width and height. This rectangle +// must be fully be comprised inside the 'src' source picture. If the source +// picture uses the YUV420 colorspace, the top and left coordinates will be +// snapped to even values. +WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture, + int left, int top, int width, int height); + +// Extracts a view from 'src' picture into 'dst'. The rectangle for the view +// is defined by the top-left corner pixel coordinates (left, top) as well +// as its width and height. This rectangle must be fully be comprised inside +// the 'src' source picture. If the source picture uses the YUV420 colorspace, +// the top and left coordinates will be snapped to even values. +// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed +// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so, +// the original dimension will be lost). Picture 'dst' need not be initialized +// with WebPPictureInit() if it is different from 'src', since its content will +// be overwritten. +// Returns false in case of memory allocation error or invalid parameters. +WEBP_EXTERN int WebPPictureView(const WebPPicture* src, + int left, int top, int width, int height, + WebPPicture* dst); + +// Returns true if the 'picture' is actually a view and therefore does +// not own the memory for pixels. +WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture); + +// Rescale a picture to new dimension width x height. +// If either 'width' or 'height' (but not both) is 0 the corresponding +// dimension will be calculated preserving the aspect ratio. +// No gamma correction is applied. +// Returns false in case of error (invalid parameter or insufficient memory). +WEBP_EXTERN int WebPPictureRescale(WebPPicture* pic, int width, int height); + +// Colorspace conversion function to import RGB samples. +// Previous buffer will be free'd, if any. +// *rgb buffer should have a size of at least height * rgb_stride. +// Returns false in case of memory error. +WEBP_EXTERN int WebPPictureImportRGB( + WebPPicture* picture, const uint8_t* rgb, int rgb_stride); +// Same, but for RGBA buffer. +WEBP_EXTERN int WebPPictureImportRGBA( + WebPPicture* picture, const uint8_t* rgba, int rgba_stride); +// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format +// input buffer ignoring the alpha channel. Avoids needing to copy the data +// to a temporary 24-bit RGB buffer to import the RGB only. +WEBP_EXTERN int WebPPictureImportRGBX( + WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride); + +// Variants of the above, but taking BGR(A|X) input. +WEBP_EXTERN int WebPPictureImportBGR( + WebPPicture* picture, const uint8_t* bgr, int bgr_stride); +WEBP_EXTERN int WebPPictureImportBGRA( + WebPPicture* picture, const uint8_t* bgra, int bgra_stride); +WEBP_EXTERN int WebPPictureImportBGRX( + WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride); + +// Converts picture->argb data to the YUV420A format. The 'colorspace' +// parameter is deprecated and should be equal to WEBP_YUV420. +// Upon return, picture->use_argb is set to false. The presence of real +// non-opaque transparent values is detected, and 'colorspace' will be +// adjusted accordingly. Note that this method is lossy. +// Returns false in case of error. +WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture, + WebPEncCSP /*colorspace = WEBP_YUV420*/); + +// Same as WebPPictureARGBToYUVA(), but the conversion is done using +// pseudo-random dithering with a strength 'dithering' between +// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful +// for photographic picture. +WEBP_EXTERN int WebPPictureARGBToYUVADithered( + WebPPicture* picture, WebPEncCSP colorspace, float dithering); + +// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion. +// Downsampling is handled with extra care in case of color clipping. This +// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better +// and sharper YUV representation. +// Returns false in case of error. +WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture); +// kept for backward compatibility: +WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture); + +// Converts picture->yuv to picture->argb and sets picture->use_argb to true. +// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to +// ARGB incurs a small loss too. +// Note that the use of this colorspace is discouraged if one has access to the +// raw ARGB samples, since using YUV420 is comparatively lossy. +// Returns false in case of error. +WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture); + +// Helper function: given a width x height plane of RGBA or YUV(A) samples +// clean-up or smoothen the YUV or RGB samples under fully transparent area, +// to help compressibility (no guarantee, though). +WEBP_EXTERN void WebPCleanupTransparentArea(WebPPicture* picture); + +// Scan the picture 'picture' for the presence of non fully opaque alpha values. +// Returns true in such case. Otherwise returns false (indicating that the +// alpha plane can be ignored altogether e.g.). +WEBP_EXTERN int WebPPictureHasTransparency(const WebPPicture* picture); + +// Remove the transparency information (if present) by blending the color with +// the background color 'background_rgb' (specified as 24bit RGB triplet). +// After this call, all alpha values are reset to 0xff. +WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb); + +//------------------------------------------------------------------------------ +// Main call + +// Main encoding call, after config and picture have been initialized. +// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION), +// and the 'config' object must be a valid one. +// Returns false in case of error, true otherwise. +// In case of error, picture->error_code is updated accordingly. +// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending +// on the value of 'picture->use_argb'. It is highly recommended to use +// the former for lossy encoding, and the latter for lossless encoding +// (when config.lossless is true). Automatic conversion from one format to +// another is provided but they both incur some loss. +WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_ENCODE_H_ diff --git a/c/header/webp/format_constants.h b/c/header/webp/format_constants.h new file mode 100644 index 0000000..eca6981 --- /dev/null +++ b/c/header/webp/format_constants.h @@ -0,0 +1,87 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Internal header for constants related to WebP file format. +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_ +#define WEBP_WEBP_FORMAT_CONSTANTS_H_ + +// Create fourcc of the chunk from the chunk tag characters. +#define MKFOURCC(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (uint32_t)(d) << 24) + +// VP8 related constants. +#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data. +#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition +#define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition +#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data. + +// VP8L related constants. +#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size. +#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte. +#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store + // width and height. +#define VP8L_VERSION_BITS 3 // 3 bits reserved for version. +#define VP8L_VERSION 0 // version 0 +#define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header. + +#define MAX_PALETTE_SIZE 256 +#define MAX_CACHE_BITS 11 +#define HUFFMAN_CODES_PER_META_CODE 5 +#define ARGB_BLACK 0xff000000 + +#define DEFAULT_CODE_LENGTH 8 +#define MAX_ALLOWED_CODE_LENGTH 15 + +#define NUM_LITERAL_CODES 256 +#define NUM_LENGTH_CODES 24 +#define NUM_DISTANCE_CODES 40 +#define CODE_LENGTH_CODES 19 + +#define MIN_HUFFMAN_BITS 2 // min number of Huffman bits +#define MAX_HUFFMAN_BITS 9 // max number of Huffman bits + +#define TRANSFORM_PRESENT 1 // The bit to be written when next data + // to be read is a transform. +#define NUM_TRANSFORMS 4 // Maximum number of allowed transform + // in a bitstream. +typedef enum { + PREDICTOR_TRANSFORM = 0, + CROSS_COLOR_TRANSFORM = 1, + SUBTRACT_GREEN = 2, + COLOR_INDEXING_TRANSFORM = 3 +} VP8LImageTransformType; + +// Alpha related constants. +#define ALPHA_HEADER_LEN 1 +#define ALPHA_NO_COMPRESSION 0 +#define ALPHA_LOSSLESS_COMPRESSION 1 +#define ALPHA_PREPROCESSED_LEVELS 1 + +// Mux related constants. +#define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L"). +#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size. +#define CHUNK_HEADER_SIZE 8 // Size of a chunk header. +#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP"). +#define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk. +#define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk. +#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. + +#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. +#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height. +#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count +#define MAX_DURATION (1 << 24) // maximum duration +#define MAX_POSITION_OFFSET (1 << 24) // maximum frame x/y offset + +// Maximum chunk payload is such that adding the header and padding won't +// overflow a uint32_t. +#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1) + +#endif // WEBP_WEBP_FORMAT_CONSTANTS_H_ diff --git a/c/header/webp/mux.h b/c/header/webp/mux.h new file mode 100644 index 0000000..7d27489 --- /dev/null +++ b/c/header/webp/mux.h @@ -0,0 +1,530 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// RIFF container manipulation and encoding for WebP images. +// +// Authors: Urvang (urvang@google.com) +// Vikas (vikasa@google.com) + +#ifndef WEBP_WEBP_MUX_H_ +#define WEBP_WEBP_MUX_H_ + +#include "./mux_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_MUX_ABI_VERSION 0x0108 // MAJOR(8b) + MINOR(8b) + +//------------------------------------------------------------------------------ +// Mux API +// +// This API allows manipulation of WebP container images containing features +// like color profile, metadata, animation. +// +// Code Example#1: Create a WebPMux object with image data, color profile and +// XMP metadata. +/* + int copy_data = 0; + WebPMux* mux = WebPMuxNew(); + // ... (Prepare image data). + WebPMuxSetImage(mux, &image, copy_data); + // ... (Prepare ICCP color profile data). + WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data); + // ... (Prepare XMP metadata). + WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data); + // Get data from mux in WebP RIFF format. + WebPMuxAssemble(mux, &output_data); + WebPMuxDelete(mux); + // ... (Consume output_data; e.g. write output_data.bytes to file). + WebPDataClear(&output_data); +*/ + +// Code Example#2: Get image and color profile data from a WebP file. +/* + int copy_data = 0; + // ... (Read data from file). + WebPMux* mux = WebPMuxCreate(&data, copy_data); + WebPMuxGetFrame(mux, 1, &image); + // ... (Consume image; e.g. call WebPDecode() to decode the data). + WebPMuxGetChunk(mux, "ICCP", &icc_profile); + // ... (Consume icc_data). + WebPMuxDelete(mux); + WebPFree(data); +*/ + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPMuxError WebPMuxError; +// typedef enum WebPChunkId WebPChunkId; +typedef struct WebPMux WebPMux; // main opaque object. +typedef struct WebPMuxFrameInfo WebPMuxFrameInfo; +typedef struct WebPMuxAnimParams WebPMuxAnimParams; +typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions; + +// Error codes +typedef enum WebPMuxError { + WEBP_MUX_OK = 1, + WEBP_MUX_NOT_FOUND = 0, + WEBP_MUX_INVALID_ARGUMENT = -1, + WEBP_MUX_BAD_DATA = -2, + WEBP_MUX_MEMORY_ERROR = -3, + WEBP_MUX_NOT_ENOUGH_DATA = -4 +} WebPMuxError; + +// IDs for different types of chunks. +typedef enum WebPChunkId { + WEBP_CHUNK_VP8X, // VP8X + WEBP_CHUNK_ICCP, // ICCP + WEBP_CHUNK_ANIM, // ANIM + WEBP_CHUNK_ANMF, // ANMF + WEBP_CHUNK_DEPRECATED, // (deprecated from FRGM) + WEBP_CHUNK_ALPHA, // ALPH + WEBP_CHUNK_IMAGE, // VP8/VP8L + WEBP_CHUNK_EXIF, // EXIF + WEBP_CHUNK_XMP, // XMP + WEBP_CHUNK_UNKNOWN, // Other chunks. + WEBP_CHUNK_NIL +} WebPChunkId; + +//------------------------------------------------------------------------------ + +// Returns the version number of the mux library, packed in hexadecimal using +// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetMuxVersion(void); + +//------------------------------------------------------------------------------ +// Life of a Mux object + +// Internal, version-checked, entry point +WEBP_EXTERN WebPMux* WebPNewInternal(int); + +// Creates an empty mux object. +// Returns: +// A pointer to the newly created empty mux object. +// Or NULL in case of memory error. +static WEBP_INLINE WebPMux* WebPMuxNew(void) { + return WebPNewInternal(WEBP_MUX_ABI_VERSION); +} + +// Deletes the mux object. +// Parameters: +// mux - (in/out) object to be deleted +WEBP_EXTERN void WebPMuxDelete(WebPMux* mux); + +//------------------------------------------------------------------------------ +// Mux creation. + +// Internal, version-checked, entry point +WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int); + +// Creates a mux object from raw data given in WebP RIFF format. +// Parameters: +// bitstream - (in) the bitstream data in WebP RIFF format +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// object and value 0 indicates data will NOT be copied. +// Returns: +// A pointer to the mux object created from given data - on success. +// NULL - In case of invalid data or memory error. +static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, + int copy_data) { + return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION); +} + +//------------------------------------------------------------------------------ +// Non-image chunks. + +// Note: Only non-image related chunks should be managed through chunk APIs. +// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH"). +// To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(), +// WebPMuxGetFrame() and WebPMuxDeleteFrame(). + +// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object. +// Any existing chunk(s) with the same id will be removed. +// Parameters: +// mux - (in/out) object to which the chunk is to be added +// fourcc - (in) a character array containing the fourcc of the given chunk; +// e.g., "ICCP", "XMP ", "EXIF" etc. +// chunk_data - (in) the chunk data to be added +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// object and value 0 indicates data will NOT be copied. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL +// or if fourcc corresponds to an image chunk. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetChunk( + WebPMux* mux, const char fourcc[4], const WebPData* chunk_data, + int copy_data); + +// Gets a reference to the data of the chunk with id 'fourcc' in the mux object. +// The caller should NOT free the returned data. +// Parameters: +// mux - (in) object from which the chunk data is to be fetched +// fourcc - (in) a character array containing the fourcc of the chunk; +// e.g., "ICCP", "XMP ", "EXIF" etc. +// chunk_data - (out) returned chunk data +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL +// or if fourcc corresponds to an image chunk. +// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetChunk( + const WebPMux* mux, const char fourcc[4], WebPData* chunk_data); + +// Deletes the chunk with the given 'fourcc' from the mux object. +// Parameters: +// mux - (in/out) object from which the chunk is to be deleted +// fourcc - (in) a character array containing the fourcc of the chunk; +// e.g., "ICCP", "XMP ", "EXIF" etc. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL +// or if fourcc corresponds to an image chunk. +// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxDeleteChunk( + WebPMux* mux, const char fourcc[4]); + +//------------------------------------------------------------------------------ +// Images. + +// Encapsulates data about a single frame. +struct WebPMuxFrameInfo { + WebPData bitstream; // image data: can be a raw VP8/VP8L bitstream + // or a single-image WebP file. + int x_offset; // x-offset of the frame. + int y_offset; // y-offset of the frame. + int duration; // duration of the frame (in milliseconds). + + WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF + // or WEBP_CHUNK_IMAGE + WebPMuxAnimDispose dispose_method; // Disposal method for the frame. + WebPMuxAnimBlend blend_method; // Blend operation for the frame. + uint32_t pad[1]; // padding for later use +}; + +// Sets the (non-animated) image in the mux object. +// Note: Any existing images (including frames) will be removed. +// Parameters: +// mux - (in/out) object in which the image is to be set +// bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image +// WebP file (non-animated) +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// object and value 0 indicates data will NOT be copied. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetImage( + WebPMux* mux, const WebPData* bitstream, int copy_data); + +// Adds a frame at the end of the mux object. +// Notes: (1) frame.id should be WEBP_CHUNK_ANMF +// (2) For setting a non-animated image, use WebPMuxSetImage() instead. +// (3) Type of frame being pushed must be same as the frames in mux. +// (4) As WebP only supports even offsets, any odd offset will be snapped +// to an even location using: offset &= ~1 +// Parameters: +// mux - (in/out) object to which the frame is to be added +// frame - (in) frame data. +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// object and value 0 indicates data will NOT be copied. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL +// or if content of 'frame' is invalid. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxPushFrame( + WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data); + +// Gets the nth frame from the mux object. +// The content of 'frame->bitstream' is allocated using WebPMalloc(), and NOT +// owned by the 'mux' object. It MUST be deallocated by the caller by calling +// WebPDataClear(). +// nth=0 has a special meaning - last position. +// Parameters: +// mux - (in) object from which the info is to be fetched +// nth - (in) index of the frame in the mux object +// frame - (out) data of the returned frame +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL. +// WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object. +// WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetFrame( + const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame); + +// Deletes a frame from the mux object. +// nth=0 has a special meaning - last position. +// Parameters: +// mux - (in/out) object from which a frame is to be deleted +// nth - (in) The position from which the frame is to be deleted +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL. +// WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object +// before deletion. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth); + +//------------------------------------------------------------------------------ +// Animation. + +// Animation parameters. +struct WebPMuxAnimParams { + uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as: + // Bits 00 to 07: Alpha. + // Bits 08 to 15: Red. + // Bits 16 to 23: Green. + // Bits 24 to 31: Blue. + int loop_count; // Number of times to repeat the animation [0 = infinite]. +}; + +// Sets the animation parameters in the mux object. Any existing ANIM chunks +// will be removed. +// Parameters: +// mux - (in/out) object in which ANIM chunk is to be set/added +// params - (in) animation parameters. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetAnimationParams( + WebPMux* mux, const WebPMuxAnimParams* params); + +// Gets the animation parameters from the mux object. +// Parameters: +// mux - (in) object from which the animation parameters to be fetched +// params - (out) animation parameters extracted from the ANIM chunk +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL. +// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetAnimationParams( + const WebPMux* mux, WebPMuxAnimParams* params); + +//------------------------------------------------------------------------------ +// Misc Utilities. + +// Sets the canvas size for the mux object. The width and height can be +// specified explicitly or left as zero (0, 0). +// * When width and height are specified explicitly, then this frame bound is +// enforced during subsequent calls to WebPMuxAssemble() and an error is +// reported if any animated frame does not completely fit within the canvas. +// * When unspecified (0, 0), the constructed canvas will get the frame bounds +// from the bounding-box over all frames after calling WebPMuxAssemble(). +// Parameters: +// mux - (in) object to which the canvas size is to be set +// width - (in) canvas width +// height - (in) canvas height +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or +// width or height are invalid or out of bounds +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux, + int width, int height); + +// Gets the canvas size from the mux object. +// Note: This method assumes that the VP8X chunk, if present, is up-to-date. +// That is, the mux object hasn't been modified since the last call to +// WebPMuxAssemble() or WebPMuxCreate(). +// Parameters: +// mux - (in) object from which the canvas size is to be fetched +// width - (out) canvas width +// height - (out) canvas height +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, width or height is NULL. +// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, + int* width, int* height); + +// Gets the feature flags from the mux object. +// Note: This method assumes that the VP8X chunk, if present, is up-to-date. +// That is, the mux object hasn't been modified since the last call to +// WebPMuxAssemble() or WebPMuxCreate(). +// Parameters: +// mux - (in) object from which the features are to be fetched +// flags - (out) the flags specifying which features are present in the +// mux object. This will be an OR of various flag values. +// Enum 'WebPFeatureFlags' can be used to test individual flag values. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL. +// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, + uint32_t* flags); + +// Gets number of chunks with the given 'id' in the mux object. +// Parameters: +// mux - (in) object from which the info is to be fetched +// id - (in) chunk id specifying the type of chunk +// num_elements - (out) number of chunks with the given chunk id +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, or num_elements is NULL. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxNumChunks(const WebPMux* mux, + WebPChunkId id, int* num_elements); + +// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'. +// This function also validates the mux object. +// Note: The content of 'assembled_data' will be ignored and overwritten. +// Also, the content of 'assembled_data' is allocated using WebPMalloc(), and +// NOT owned by the 'mux' object. It MUST be deallocated by the caller by +// calling WebPDataClear(). It's always safe to call WebPDataClear() upon +// return, even in case of error. +// Parameters: +// mux - (in/out) object whose chunks are to be assembled +// assembled_data - (out) assembled WebP data +// Returns: +// WEBP_MUX_BAD_DATA - if mux object is invalid. +// WEBP_MUX_INVALID_ARGUMENT - if mux or assembled_data is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxAssemble(WebPMux* mux, + WebPData* assembled_data); + +//------------------------------------------------------------------------------ +// WebPAnimEncoder API +// +// This API allows encoding (possibly) animated WebP images. +// +// Code Example: +/* + WebPAnimEncoderOptions enc_options; + WebPAnimEncoderOptionsInit(&enc_options); + // Tune 'enc_options' as needed. + WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options); + while() { + WebPConfig config; + WebPConfigInit(&config); + // Tune 'config' as needed. + WebPAnimEncoderAdd(enc, frame, timestamp_ms, &config); + } + WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL); + WebPAnimEncoderAssemble(enc, webp_data); + WebPAnimEncoderDelete(enc); + // Write the 'webp_data' to a file, or re-mux it further. +*/ + +typedef struct WebPAnimEncoder WebPAnimEncoder; // Main opaque object. + +// Forward declarations. Defined in encode.h. +struct WebPPicture; +struct WebPConfig; + +// Global options. +struct WebPAnimEncoderOptions { + WebPMuxAnimParams anim_params; // Animation parameters. + int minimize_size; // If true, minimize the output size (slow). Implicitly + // disables key-frame insertion. + int kmin; + int kmax; // Minimum and maximum distance between consecutive key + // frames in the output. The library may insert some key + // frames as needed to satisfy this criteria. + // Note that these conditions should hold: kmax > kmin + // and kmin >= kmax / 2 + 1. Also, if kmax <= 0, then + // key-frame insertion is disabled; and if kmax == 1, + // then all frames will be key-frames (kmin value does + // not matter for these special cases). + int allow_mixed; // If true, use mixed compression mode; may choose + // either lossy and lossless for each frame. + int verbose; // If true, print info and warning messages to stderr. + + uint32_t padding[4]; // Padding for later use. +}; + +// Internal, version-checked, entry point. +WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal( + WebPAnimEncoderOptions*, int); + +// Should always be called, to initialize a fresh WebPAnimEncoderOptions +// structure before modification. Returns false in case of version mismatch. +// WebPAnimEncoderOptionsInit() must have succeeded before using the +// 'enc_options' object. +static WEBP_INLINE int WebPAnimEncoderOptionsInit( + WebPAnimEncoderOptions* enc_options) { + return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION); +} + +// Internal, version-checked, entry point. +WEBP_EXTERN WebPAnimEncoder* WebPAnimEncoderNewInternal( + int, int, const WebPAnimEncoderOptions*, int); + +// Creates and initializes a WebPAnimEncoder object. +// Parameters: +// width/height - (in) canvas width and height of the animation. +// enc_options - (in) encoding options; can be passed NULL to pick +// reasonable defaults. +// Returns: +// A pointer to the newly created WebPAnimEncoder object. +// Or NULL in case of memory error. +static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew( + int width, int height, const WebPAnimEncoderOptions* enc_options) { + return WebPAnimEncoderNewInternal(width, height, enc_options, + WEBP_MUX_ABI_VERSION); +} + +// Optimize the given frame for WebP, encode it and add it to the +// WebPAnimEncoder object. +// The last call to 'WebPAnimEncoderAdd' should be with frame = NULL, which +// indicates that no more frames are to be added. This call is also used to +// determine the duration of the last frame. +// Parameters: +// enc - (in/out) object to which the frame is to be added. +// frame - (in/out) frame data in ARGB or YUV(A) format. If it is in YUV(A) +// format, it will be converted to ARGB, which incurs a small loss. +// timestamp_ms - (in) timestamp of this frame in milliseconds. +// Duration of a frame would be calculated as +// "timestamp of next frame - timestamp of this frame". +// Hence, timestamps should be in non-decreasing order. +// config - (in) encoding options; can be passed NULL to pick +// reasonable defaults. +// Returns: +// On error, returns false and frame->error_code is set appropriately. +// Otherwise, returns true. +WEBP_EXTERN int WebPAnimEncoderAdd( + WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms, + const struct WebPConfig* config); + +// Assemble all frames added so far into a WebP bitstream. +// This call should be preceded by a call to 'WebPAnimEncoderAdd' with +// frame = NULL; if not, the duration of the last frame will be internally +// estimated. +// Parameters: +// enc - (in/out) object from which the frames are to be assembled. +// webp_data - (out) generated WebP bitstream. +// Returns: +// True on success. +WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, + WebPData* webp_data); + +// Get error string corresponding to the most recent call using 'enc'. The +// returned string is owned by 'enc' and is valid only until the next call to +// WebPAnimEncoderAdd() or WebPAnimEncoderAssemble() or WebPAnimEncoderDelete(). +// Parameters: +// enc - (in/out) object from which the error string is to be fetched. +// Returns: +// NULL if 'enc' is NULL. Otherwise, returns the error string if the last call +// to 'enc' had an error, or an empty string if the last call was a success. +WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc); + +// Deletes the WebPAnimEncoder object. +// Parameters: +// enc - (in/out) object to be deleted +WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_MUX_H_ diff --git a/c/header/webp/mux_types.h b/c/header/webp/mux_types.h new file mode 100644 index 0000000..2fe8195 --- /dev/null +++ b/c/header/webp/mux_types.h @@ -0,0 +1,98 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Data-types common to the mux and demux libraries. +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_WEBP_MUX_TYPES_H_ +#define WEBP_WEBP_MUX_TYPES_H_ + +#include // memset() +#include "./types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPFeatureFlags WebPFeatureFlags; +// typedef enum WebPMuxAnimDispose WebPMuxAnimDispose; +// typedef enum WebPMuxAnimBlend WebPMuxAnimBlend; +typedef struct WebPData WebPData; + +// VP8X Feature Flags. +typedef enum WebPFeatureFlags { + ANIMATION_FLAG = 0x00000002, + XMP_FLAG = 0x00000004, + EXIF_FLAG = 0x00000008, + ALPHA_FLAG = 0x00000010, + ICCP_FLAG = 0x00000020, + + ALL_VALID_FLAGS = 0x0000003e +} WebPFeatureFlags; + +// Dispose method (animation only). Indicates how the area used by the current +// frame is to be treated before rendering the next frame on the canvas. +typedef enum WebPMuxAnimDispose { + WEBP_MUX_DISPOSE_NONE, // Do not dispose. + WEBP_MUX_DISPOSE_BACKGROUND // Dispose to background color. +} WebPMuxAnimDispose; + +// Blend operation (animation only). Indicates how transparent pixels of the +// current frame are blended with those of the previous canvas. +typedef enum WebPMuxAnimBlend { + WEBP_MUX_BLEND, // Blend. + WEBP_MUX_NO_BLEND // Do not blend. +} WebPMuxAnimBlend; + +// Data type used to describe 'raw' data, e.g., chunk data +// (ICC profile, metadata) and WebP compressed image data. +// 'bytes' memory must be allocated using WebPMalloc() and such. +struct WebPData { + const uint8_t* bytes; + size_t size; +}; + +// Initializes the contents of the 'webp_data' object with default values. +static WEBP_INLINE void WebPDataInit(WebPData* webp_data) { + if (webp_data != NULL) { + memset(webp_data, 0, sizeof(*webp_data)); + } +} + +// Clears the contents of the 'webp_data' object by calling WebPFree(). +// Does not deallocate the object itself. +static WEBP_INLINE void WebPDataClear(WebPData* webp_data) { + if (webp_data != NULL) { + WebPFree((void*)webp_data->bytes); + WebPDataInit(webp_data); + } +} + +// Allocates necessary storage for 'dst' and copies the contents of 'src'. +// Returns true on success. +static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) { + if (src == NULL || dst == NULL) return 0; + WebPDataInit(dst); + if (src->bytes != NULL && src->size != 0) { + dst->bytes = (uint8_t*)WebPMalloc(src->size); + if (dst->bytes == NULL) return 0; + memcpy((void*)dst->bytes, src->bytes, src->size); + dst->size = src->size; + } + return 1; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_MUX_TYPES_H_ diff --git a/c/header/webp/rescaler_utils.h b/c/header/webp/rescaler_utils.h new file mode 100644 index 0000000..e37e5bd --- /dev/null +++ b/c/header/webp/rescaler_utils.h @@ -0,0 +1,101 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Rescaling functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_RESCALER_UTILS_H_ +#define WEBP_UTILS_RESCALER_UTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "types.h" + +#define WEBP_RESCALER_RFIX 32 // fixed-point precision for multiplies +#define WEBP_RESCALER_ONE (1ull << WEBP_RESCALER_RFIX) +#define WEBP_RESCALER_FRAC(x, y) \ + ((uint32_t)(((uint64_t)(x) << WEBP_RESCALER_RFIX) / (y))) + +// Structure used for on-the-fly rescaling +typedef uint32_t rescaler_t; // type for side-buffer +typedef struct WebPRescaler WebPRescaler; +struct WebPRescaler { + int x_expand; // true if we're expanding in the x direction + int y_expand; // true if we're expanding in the y direction + int num_channels; // bytes to jump between pixels + uint32_t fx_scale; // fixed-point scaling factors + uint32_t fy_scale; // '' + uint32_t fxy_scale; // '' + int y_accum; // vertical accumulator + int y_add, y_sub; // vertical increments + int x_add, x_sub; // horizontal increments + int src_width, src_height; // source dimensions + int dst_width, dst_height; // destination dimensions + int src_y, dst_y; // row counters for input and output + uint8_t* dst; + int dst_stride; + rescaler_t* irow, *frow; // work buffer +}; + +// Initialize a rescaler given scratch area 'work' and dimensions of src & dst. +void WebPRescalerInit(WebPRescaler* const rescaler, + int src_width, int src_height, + uint8_t* const dst, + int dst_width, int dst_height, int dst_stride, + int num_channels, + rescaler_t* const work); + +// If either 'scaled_width' or 'scaled_height' (but not both) is 0 the value +// will be calculated preserving the aspect ratio, otherwise the values are +// left unmodified. Returns true on success, false if either value is 0 after +// performing the scaling calculation. +int WebPRescalerGetScaledDimensions(int src_width, int src_height, + int* const scaled_width, + int* const scaled_height); + +// Returns the number of input lines needed next to produce one output line, +// considering that the maximum available input lines are 'max_num_lines'. +int WebPRescaleNeededLines(const WebPRescaler* const rescaler, + int max_num_lines); + +// Import multiple rows over all channels, until at least one row is ready to +// be exported. Returns the actual number of lines that were imported. +int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows, + const uint8_t* src, int src_stride); + +// Export as many rows as possible. Return the numbers of rows written. +int WebPRescalerExport(WebPRescaler* const rescaler); + +// Return true if input is finished +static WEBP_INLINE +int WebPRescalerInputDone(const WebPRescaler* const rescaler) { + return (rescaler->src_y >= rescaler->src_height); +} +// Return true if output is finished +static WEBP_INLINE +int WebPRescalerOutputDone(const WebPRescaler* const rescaler) { + return (rescaler->dst_y >= rescaler->dst_height); +} + +// Return true if there are pending output rows ready. +static WEBP_INLINE +int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) { + return !WebPRescalerOutputDone(rescaler) && (rescaler->y_accum <= 0); +} + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_UTILS_RESCALER_UTILS_H_ diff --git a/c/header/webp/types.h b/c/header/webp/types.h new file mode 100644 index 0000000..47f7f2b --- /dev/null +++ b/c/header/webp/types.h @@ -0,0 +1,68 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Common types + memory wrappers +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_WEBP_TYPES_H_ +#define WEBP_WEBP_TYPES_H_ + +#include // for size_t + +#ifndef _MSC_VER +#include +#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +#define WEBP_INLINE inline +#else +#define WEBP_INLINE +#endif +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +typedef long long int int64_t; +#define WEBP_INLINE __forceinline +#endif /* _MSC_VER */ + +#ifndef WEBP_EXTERN +// This explicitly marks library functions and allows for changing the +// signature for e.g., Windows DLL builds. +# if defined(__GNUC__) && __GNUC__ >= 4 +# define WEBP_EXTERN extern __attribute__ ((visibility ("default"))) +# else +# define WEBP_EXTERN extern +# endif /* __GNUC__ >= 4 */ +#endif /* WEBP_EXTERN */ + +// Macro to check ABI compatibility (same major revision number) +#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8)) + +#ifdef __cplusplus +extern "C" { +#endif + +// Allocates 'size' bytes of memory. Returns NULL upon error. Memory +// must be deallocated by calling WebPFree(). This function is made available +// by the core 'libwebp' library. +WEBP_EXTERN void* WebPMalloc(size_t size); + +// Releases memory returned by the WebPDecode*() functions (from decode.h). +WEBP_EXTERN void WebPFree(void* ptr); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_TYPES_H_ diff --git a/c/im_avif.c b/c/im_avif.c new file mode 100644 index 0000000..9eaee18 --- /dev/null +++ b/c/im_avif.c @@ -0,0 +1,222 @@ +// +// Created by TYTY on 2020-08-11 011. +// + +#include "cimg.h" + +#ifdef WIN32 +#include "header/avif.h" +#else +#include +#endif + +// Estimated by human eye. + +i32 AVIFQualityTransform(u8 quality) { + if (quality > 100) { + return 6; + } else if (quality > 90) { + return -2.2 * quality + 226; + } else if (quality > 80) { + return -0.8 * quality + 100; + } else if (quality > 20) { + return -0.2 * quality + 52; + } else if (quality > 10) { + return -0.4 * quality + 56; + } else { + return -0.8 * quality + 60; + } +} + +bool IsAVIF(const u8 *data, size_t size, u32* width, u32* height) { + avifDecoder *decoder = avifDecoderCreate(); + avifROData source = {data, size}; + bool readResult = false; + + if (avifDecoderParse(decoder, &source) != AVIF_RESULT_OK) { + goto cleanup; + } + + *width = decoder->image->width; + *height = decoder->image->height; + readResult = true; + + cleanup: + avifDecoderDestroy(decoder); + + return readResult; +} + +bool DecAVIF(const u8 *data, size_t size, Frame *img) { + if (data == NULL || img == NULL || size < 16) { + return false; + } + + avifROData source = {data, size}; + bool readResult = false; + + avifImage* image = avifImageCreateEmpty(); + avifDecoder* decoder = avifDecoderCreate(); + avifResult result; + result = avifDecoderRead(decoder, image, &source); + + if (result != AVIF_RESULT_OK) { + goto cleanup; + } + + if (image->width > GetMaxSize() || image->height > GetMaxSize()) { + goto cleanup; + } + + avifRGBImage rgb; + avifRGBImageSetDefaults(&rgb, image); + + if (!image->alphaPlane) { + rgb.format = AVIF_RGB_FORMAT_RGB; + img->format = FORMAT_RGB; + } else { + img->format = FORMAT_RGBA; + } + + if (image->depth > 8) { + rgb.depth = 16; + } + + img->width = rgb.width; + img->height = rgb.height; + img->depth = rgb.depth; + + // currently no way to do estimate. provide a conservative value + img->quality = 95; + + AllocateFrame(img); + + rgb.pixels = img->pixel; + rgb.rowBytes = img->stride; + + result = avifImageYUVToRGB(image, &rgb); + + if (result == AVIF_RESULT_OK) { + readResult = true; + } + + cleanup: + avifImageDestroy(image); + avifDecoderDestroy(decoder); + + if (!readResult) { + ReleaseFrame(img); + } + + return readResult; +} + +bool EncAVIF(u8** data, size_t* size, Frame *img) { + if (img == NULL || img->format == FORMAT_UNDEFINED || img->pixel == NULL) { + return false; + } + + bool encodeResult = false; + + // drawings usually have sharp border so we use yuv444 to prevent color bleeding + avifPixelFormat format = AVIF_PIXEL_FORMAT_YUV444; + + // Keep 10bit at max, until HDR content is widely supported + // and `correctly` handled. + // Currently browsers' HDR handling lack consideration. + // Chrome simply overexpose high brightness area, + // Firefox assume everything non-HDR. + // So, No HDR content currently. + + // Currently we are forced to use limited range + // because Firefox assume everything in limited range. + // However we are also forced to use 8bit + // because Chrome is buggy on high color depth big image. + + // Now we can only use the most simple limited range 8 bit content. + // Sorry for possible color banding :( +// u32 depth = img->depth > 8 ? 10 : 8; + u32 depth = 8; + + avifImage* image = avifImageCreate(img->width, img->height, depth, format); + + // firefox can't handle full ranged image (or, any content. An old bug.) correctly. + image->yuvRange = AVIF_RANGE_LIMITED; + + //defaults + image->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; + image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; + image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709; + + avifRGBImage rgb; + avifRGBImageSetDefaults(&rgb, image); + rgb.depth = img->depth; + + avifEncoder* encoder = avifEncoderCreate(); + avifRWData output = AVIF_DATA_EMPTY; + + switch (img->format) { + case FORMAT_RGB: + rgb.format = AVIF_RGB_FORMAT_RGB; + break; + case FORMAT_BGR: + rgb.format = AVIF_RGB_FORMAT_BGR; + break; + case FORMAT_RGBA: + rgb.format = AVIF_RGB_FORMAT_RGBA; + break; + case FORMAT_BGRA: + rgb.format = AVIF_RGB_FORMAT_BGRA; + break; + case FORMAT_ARGB: + rgb.format = AVIF_RGB_FORMAT_ARGB; + break; + case FORMAT_ABGR: + rgb.format = AVIF_RGB_FORMAT_ABGR; + break; + default: + goto cleanup; + } + + rgb.pixels = img->pixel; + rgb.rowBytes = img->stride; + + avifImageRGBToYUV(image, &rgb); + + // rav1e can't utilize full CPU unless image is really huge, aom can + // rav1e use less memory, aom use more + // rav1e is 18% slower than aom under single thread + // so sorry rav1e + encoder->codecChoice = AVIF_CODEC_CHOICE_AOM; + + // however due to huge memory usage of aom, we can't perform multiple encode + // simultaneously. We let one task take all CPU. + encoder->maxThreads = GetMaxThread(); + + i32 quality = AVIFQualityTransform(Quality(img->width, img->height, img->quality)); + + encoder->minQuantizer = quality; + encoder->maxQuantizer = (quality + 8) > 64 ? 64 : (quality + 8); + + // use highest quality for alpha, to reduce quality loss + encoder->minQuantizerAlpha = 0; + encoder->maxQuantizerAlpha = 0; + + // relatively good choice between speed and size + encoder->speed = 5; + + if (avifEncoderWrite(encoder, image, &output) == AVIF_RESULT_OK) { + *data = output.data; + *size = output.size; + encodeResult = true; + } + + cleanup: + avifImageDestroy(image); + avifEncoderDestroy(encoder); + if (!encodeResult && output.data) { + avifRWDataFree(&output); + } + + return encodeResult; +} diff --git a/c/im_jpeg.c b/c/im_jpeg.c new file mode 100644 index 0000000..500f0d7 --- /dev/null +++ b/c/im_jpeg.c @@ -0,0 +1,243 @@ +// +// Created by TYTY on 2020-08-12 012. +// + +#include "cimg.h" + +#include +#include +#include + +#ifdef WIN32 +#include "header/jpeg/jpeglib.h" +#else +#include +#endif + + +// stuffs copied from libavif +struct my_error_mgr +{ + struct jpeg_error_mgr p; + jmp_buf setjmp_buffer; +}; +typedef struct my_error_mgr * my_error_ptr; +static void my_error_exit(j_common_ptr cinfo) +{ + my_error_ptr myerr = (my_error_ptr)cinfo->err; +// (*cinfo->err->output_message)(cinfo); + longjmp(myerr->setjmp_buffer, 1); +} + +// from https://cloudinary.com/blog/progressive_jpegs_and_green_martians +const jpeg_scan_info scans[5] = {{ + .comps_in_scan = 3, + .component_index = {0, 1, 2}, + .Ss = 0, .Se = 0, .Ah = 0, .Al = 0 +}, { + .comps_in_scan = 1, + .component_index = {0}, + .Ss = 1, .Se = 9, .Ah = 0, .Al = 0 +}, { + .comps_in_scan = 1, + .component_index = {2}, + .Ss = 1, .Se = 63, .Ah = 0, .Al = 0 +}, { + .comps_in_scan = 1, + .component_index = {1}, + .Ss = 1, .Se = 63, .Ah = 0, .Al = 0 +}, { + .comps_in_scan = 1, + .component_index = {0}, + .Ss = 10, .Se = 63, .Ah = 0, .Al = 0 +}}; + +i32 JPEGQualityTransform(u8 quality) { + if (quality > 100) { + return 100; + } else if (quality > 15) { + return 0.70588235294117647059 * quality + 29.411764705882352941; + } else if (quality > 1) { + return 2.1428571428571428571 * quality + 7.8571428571428571429; + } else { + return 10; + } +} + +bool IsJPEG(const u8 *data, size_t size, u32* width, u32* height) { + bool readResult = false; + + struct my_error_mgr jerr; + struct jpeg_decompress_struct cinfo; + cinfo.err = jpeg_std_error(&jerr.p); + jerr.p.error_exit = my_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + goto cleanup; + } + + jpeg_create_decompress(&cinfo); + + jpeg_mem_src(&cinfo, data, size); + if (jpeg_read_header(&cinfo, FALSE) != JPEG_HEADER_OK) { + goto cleanup; + } + + jpeg_calc_output_dimensions(&cinfo); + + *width = cinfo.output_width; + *height = cinfo.output_height; + + readResult = true; + + cleanup: + jpeg_destroy_decompress(&cinfo); + return readResult; +} + +bool DecJPEG(const u8 *data, size_t size, Frame *img) { + if (data == NULL || img == NULL || size < 16) { + return false; + } + + bool readResult = false; + + struct my_error_mgr jerr; + struct jpeg_decompress_struct cinfo; + cinfo.err = jpeg_std_error(&jerr.p); + jerr.p.error_exit = my_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + goto cleanup; + } + + jpeg_create_decompress(&cinfo); + + jpeg_mem_src(&cinfo, data, size); + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_EXT_RGB; + jpeg_start_decompress(&cinfo); + + u32 row_stride = cinfo.output_width * cinfo.output_components; + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); + + img->width = cinfo.output_width; + img->height = cinfo.output_height; + img->format = FORMAT_RGB; + img->depth = 8; + // proper image quality need to be read using other way + img->quality = 90; + + if (img->width > GetMaxSize() || img->height > GetMaxSize()) { + goto cleanup; + } + + AllocateFrame(img); + + int row = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + u8* pixelRow = img->pixel + row * img->stride; + memcpy(pixelRow, buffer[0], img->stride); + ++row; + } + + jpeg_finish_decompress(&cinfo); + readResult = true; + +cleanup: + jpeg_destroy_decompress(&cinfo); + + if (!readResult) { + ReleaseFrame(img); + } + + return readResult; +} + +bool EncJPEG(u8** data, size_t* size, Frame *img) { + if (img == NULL || img->format == FORMAT_UNDEFINED || img->pixel == NULL) { + return false; + } + + Frame* src = img; + + if (src->depth == 16) { + Frame* tmp = (Frame *)malloc(sizeof(Frame)); + tmp->width = src->width; + tmp->height = src->height; + tmp->format = src->format; + tmp->depth = 8; + tmp->pixel = NULL; + tmp->stride = 0; + + AllocateFrame(tmp); + + CloneFrame(src, tmp, 0, 0, src->width, src->height); + src = tmp; + } + + if (FormatChannelCount(src->format) == 4) { + + Frame* tmp = (Frame *)malloc(sizeof(Frame)); + tmp->width = src->width; + tmp->height = src->height; + tmp->format = FORMAT_RGB; + tmp->depth = 8; + tmp->pixel = NULL; + tmp->stride = 0; + + AllocateFrame(tmp); + + BlendImageAlpha(src, tmp, 0xffffffffu); + + if (src != img) { + ReleaseFrame(src); + free(src); + } + + src = tmp; + } + + src->quality = img->quality; + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + jpeg_mem_dest(&cinfo, data, (unsigned long *) size); + + cinfo.image_width = src->width; + cinfo.image_height = src->height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + i32 quality = JPEGQualityTransform(Quality(src->width, src->height, src->quality)); + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_scan_info *scan_ptr = (jpeg_scan_info *) + (cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_IMAGE, + 5 * sizeof(jpeg_scan_info)); + memcpy(scan_ptr, scans, 5 * sizeof(jpeg_scan_info)); + cinfo.scan_info = scan_ptr; + cinfo.num_scans = 5; + jpeg_c_set_bool_param(&cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE); + + jpeg_start_compress(&cinfo, TRUE); + + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = src->pixel + cinfo.next_scanline * src->stride; + (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + if (src != img) { + ReleaseFrame(src); + free(src); + } + + return true; +} + diff --git a/c/im_png.c b/c/im_png.c new file mode 100644 index 0000000..342257c --- /dev/null +++ b/c/im_png.c @@ -0,0 +1,175 @@ +// +// Created by TYTY on 2020-08-07 007. +// + +#include +#include "cimg.h" + +#ifdef WIN32 +#include "header/png/png.h" +#else +#include +#endif + + +typedef struct { + const uint8_t* data; + size_t data_size; + png_size_t offset; +} PNGReadContext; + +static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) { + PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr); + if (ctx->data_size - ctx->offset < length) { + png_error(png_ptr, "ReadFunc: invalid read length (overflow)!"); + } + memcpy(data, ctx->data + ctx->offset, length); + ctx->offset += length; +} + +bool IsPNG(const u8 *data, size_t size, u32* width, u32* height) { + if (png_sig_cmp(data, 0, size < 8 ? size : 8)) { + return false; + } + + bool readResult = false; + volatile png_structp png = NULL; + volatile png_infop info = NULL; + PNGReadContext context = { data, size, 0 }; + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + goto cleanup; + } + + if (setjmp(png_jmpbuf(png))) { + goto cleanup; + } + + info = png_create_info_struct(png); + if (info == NULL) goto cleanup; + + png_set_read_fn(png, &context, ReadFunc); + png_read_info(png, info); + if (!png_get_IHDR(png, info, + width, height, + NULL, NULL, NULL,NULL, NULL)) goto cleanup; + + readResult = true; + + cleanup: + if (png) { + png_destroy_read_struct((png_structpp)&png, (png_infopp)&info, NULL); + } + + return readResult; +} + +bool DecPNG(const u8* data, size_t size, Frame* img) { + if (data == NULL || img == NULL || size < 8) { + return false; + } + + volatile bool readResult = false; + volatile png_structp png = NULL; + volatile png_infop info = NULL; + volatile png_infop end_info = NULL; + PNGReadContext context = { data, size, 0 }; + i32 color_type, bit_depth, interlaced; + i32 has_alpha; + i32 num_passes; + u32 width, height, y; + + if (png_sig_cmp(data, 0, 8)) { + goto cleanup; + } + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + goto cleanup; + } + + if (setjmp(png_jmpbuf(png))) { + goto cleanup; + } + + info = png_create_info_struct(png); + if (info == NULL) goto cleanup; + end_info = png_create_info_struct(png); + if (end_info == NULL) goto cleanup; + + png_set_read_fn(png, &context, ReadFunc); + png_read_info(png, info); + if (!png_get_IHDR(png, info, + &width, &height, &bit_depth, &color_type, &interlaced, + NULL, NULL)) goto cleanup; + + if (width > GetMaxSize() || height > GetMaxSize()) { + goto cleanup; + } + + img->width = width; + img->height = height; + img->depth = bit_depth; + img->quality = 100; + + if (bit_depth == 16) { + png_set_swap(png); + } + + png_set_packing(png); + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png); + } + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + if (bit_depth < 8) { + png_set_expand_gray_1_2_4_to_8(png); + } + png_set_gray_to_rgb(png); + } + if (png_get_valid(png, info, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png); + has_alpha = 1; + } else { + has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA); + } + + img->format = has_alpha ? FORMAT_RGBA : FORMAT_RGB; + AllocateFrame(img); + + // Apply gamma correction if needed. + { + double image_gamma = 1 / 2.2, screen_gamma = 2.2; + int srgb_intent; + if (png_get_sRGB(png, info, &srgb_intent) || + png_get_gAMA(png, info, &image_gamma)) { + png_set_gamma(png, screen_gamma, image_gamma); + } + } + + num_passes = png_set_interlace_handling(png); + png_read_update_info(png, info); + + + for (u32 p = 0; p < num_passes; ++p) { + png_bytep row = img->pixel; + for (y = 0; y < height; ++y) { + png_read_rows(png, &row, NULL, 1); + row += img->stride; + } + } + + readResult = true; + + cleanup: + if (png) { + png_destroy_read_struct((png_structpp)&png, (png_infopp)&info, (png_infopp)&end_info); + } + + if (!readResult) { + ReleaseFrame(img); + } + + return readResult; +} diff --git a/c/im_webp.c b/c/im_webp.c new file mode 100644 index 0000000..0be85aa --- /dev/null +++ b/c/im_webp.c @@ -0,0 +1,184 @@ +// +// Created by TYTY on 2020-08-08 008. +// + +#include "cimg.h" + +#ifdef WIN32 +#include "header/webp/encode.h" +#include "header/webp/decode.h" +#include "header/webp/mux.h" +#include "header/webp/demux.h" +#else +#include +#include +#include +#include +#endif + +bool IsWEBP(const u8 *data, size_t size, u32* width, u32* height) { + // how can an image dimension overflow i32 + #pragma GCC diagnostic ignored "-Wpointer-sign" + return WebPGetInfo(data, size, width, height); +} + +bool DecWEBP(const u8 *data, size_t size, Frame *img) { + if (data == NULL || img == NULL || size < 16) { + return false; + } + + bool readResult = false; + VP8StatusCode status; + WebPDecoderConfig config; + WebPDecBuffer* const output_buffer = &config.output; + WebPBitstreamFeatures* const bitstream = &config.input; + + if (!WebPInitDecoderConfig(&config)) { + goto cleanup; + } + + status = WebPGetFeatures(data, size, bitstream); + if (status != VP8_STATUS_OK) { + goto cleanup; + } + + img->width = bitstream->width; + img->height = bitstream->height; + img->depth = 8; + img->format = bitstream->has_alpha ? FORMAT_RGBA : FORMAT_RGB; + + if (img->width > GetMaxSize() || img->height > GetMaxSize()) { + goto cleanup; + } + + WebPData d = {data, size}; + WebPDemuxer* demux = WebPDemux(&d); + WebPIterator iter; + WebPDemuxGetFrame(demux, 1, &iter); + + if (bitstream->format != 1) { + img->quality = 100; + } else { + img->quality = QualityWEBP(iter.fragment.bytes, iter.fragment.size); + } + + AllocateFrame(img); + + output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; + output_buffer->width = img->width; + output_buffer->height = img->height; + output_buffer->u.RGBA.rgba = img->pixel; + output_buffer->u.RGBA.stride = img->stride; + output_buffer->u.RGBA.size = img->stride * img->height; + + output_buffer->is_external_memory = 1; + status = WebPDecode(iter.fragment.bytes, iter.fragment.size, &config); + + readResult = (status == VP8_STATUS_OK); + + cleanup: + if (!readResult) { + ReleaseFrame(img); + } + + return readResult; +} + +bool EncWEBP(u8** data, size_t* size, Frame *img) { + if (img == NULL || img->format == FORMAT_UNDEFINED || img->pixel == NULL) { + return false; + } + + WebPMemoryWriter memory_writer; + WebPConfig config; + WebPPicture picture; + + bool readResult = false; + + WebPMemoryWriterInit(&memory_writer); + + if (!WebPPictureInit(&picture) || + !WebPConfigInit(&config)) { + goto cleanup; + } + + // lossy compress + config.lossless = 0; + + // conditional decide quality + config.quality = Quality(img->width, img->height, img->quality); + + // mainly for drawing. + config.sns_strength = 25; + + // slower for better quality + config.method = 6; + + // allow auto filter adjust + config.autofilter = 1; + + // allow multi thread encode + config.thread_level = 1; + + // enhance image quality + config.use_sharp_yuv = 1; + + + picture.width = img->width; + picture.height = img->height; + + Frame* src = img; + + if (src->depth == 16) { + Frame* tmp = (Frame *)malloc(sizeof(Frame)); + tmp->width = src->width; + tmp->height = src->height; + tmp->format = src->format; + tmp->depth = 8; + tmp->pixel = NULL; + tmp->stride = 0; + + AllocateFrame(tmp); + + CloneFrame(src, tmp, 0, 0, src->width, src->height); + src = tmp; + } + + switch (img->format) { + case FORMAT_RGB: + WebPPictureImportRGB(&picture, src->pixel, src->stride); + break; + case FORMAT_BGR: + WebPPictureImportBGR(&picture, src->pixel, src->stride); + break; + case FORMAT_RGBA: + WebPPictureImportRGBA(&picture, src->pixel, src->stride); + break; + case FORMAT_BGRA: + WebPPictureImportBGRA(&picture, src->pixel, src->stride); + break; + default: + // not implemented + goto cleanup; + } + + picture.writer = WebPMemoryWrite; + picture.custom_ptr = (void*)&memory_writer; + + readResult = WebPEncode(&config, &picture) != 0; + + WebPPictureFree(&picture); + + // data shall be freed by caller + *data = memory_writer.mem; + *size = memory_writer.size; + + cleanup: + if (src != img) { + ReleaseFrame(src); + free(src); + } + + return readResult; +} + diff --git a/c/image.c b/c/image.c new file mode 100644 index 0000000..387f7e0 --- /dev/null +++ b/c/image.c @@ -0,0 +1,392 @@ +// +// Created by TYTY on 2020-08-06 006. +// + +#include "cimg.h" +#include +#include + +bool AllocateFrame(Frame *img) { + // already allocated + if (img->pixel != NULL) { + return false; + } + + // invalid dimension + if (img->width == 0 || img->height == 0) { + return false; + } + + // invalid depth + if (img->depth < 8 || img->depth > 16) { + return false; + } + + if (img->format == FORMAT_UNDEFINED) { + return false; + } + + const u32 pixelSize = img->depth > 8 ? 2 : 1; + + if (img->stride != 0) { + // explicit set stride + if (img->stride < img->width * pixelSize) { + return false; + } + } else { + img->stride = img->width * pixelSize * FormatChannelCount(img->format); + } + + u8* mem = (u8*)malloc((size_t)img->stride * img->height * FormatChannelCount(img->format)); + + // allocation failed + if (mem == NULL) { + return false; + } + + img->pixel = mem; + return true; +} + +bool ZeroFrame(Frame *img, u32 x, u32 y, u32 w, u32 h) { + if (img->pixel == NULL) { + return false; + } + + const u32 step = FormatChannelCount(img->format) * (img->depth >> 3u); + + for (u32 i = 0; i < h; i++) { + memset(img->pixel + img->stride * (y + i) + step * x, 0, step * w); + } + + return true; +} + +static inline u32 MakeARGB32(u32 r, u32 g, u32 b, u32 a) { + return ((a << 24u) | (r << 16u) | (g << 8u) | b); +} + +static inline u32 MakeABGR32(u32 r, u32 g, u32 b, u32 a) { + return ((a << 24u) | (b << 16u) | (g << 8u) | r); +} + +static inline u32 MakeRGBA32(u32 r, u32 g, u32 b, u32 a) { + return ((r << 24u) | (g << 16u) | (b << 8u) | a); +} + +static inline u32 MakeBGRA32(u32 r, u32 g, u32 b, u32 a) { + return ((b << 24u) | (g << 16u) | (r << 8u) | a); +} + + +bool FillFrame(Frame *img, u32 x, u32 y, u32 w, u32 h, u8 r, u8 g, u8 b, u8 a) { + if (img->pixel == NULL) { + return false; + } + + // no 16bit now + if (img->depth != 8) { + return false; + } + + if (img->format == FORMAT_UNDEFINED) { + return false; + } + + if (img->format >> 31u) { + u32 fill; + switch (img->format) { + case FORMAT_RGBA: + fill = MakeRGBA32(r, g, b, a); + break; + case FORMAT_BGRA: + fill = MakeBGRA32(r, g, b, a); + break; + case FORMAT_ARGB: + fill = MakeARGB32(r, g, b, a); + break; + case FORMAT_ABGR: + fill = MakeABGR32(r, g, b, a); + break; + default: + return false; + } + for (u32 i = y; i < y + h; i++) { + for (u32 j = x; j < x + w; j++) { + *(u32 *)(img->pixel + i * img->stride + j * 4) = fill; + } + } + } else { + switch (img->format) { + case FORMAT_RGB: + for (u32 i = y; i < y + h; i++) { + for (u32 j = x; j < x + w; j++) { + u8* pos = img->pixel + i * img->stride + j * 3; + *(pos + 0) = r; + *(pos + 1) = g; + *(pos + 2) = b; + } + } + break; + case FORMAT_BGR: + for (u32 i = y; i < y + h; i++) { + for (u32 j = x; j < x + w; j++) { + u8* pos = img->pixel + i * img->stride + j * 3; + *(pos + 0) = b; + *(pos + 1) = g; + *(pos + 2) = r; + } + } + break; + default: + return false; + } + } + + return true; +} + +bool ReleaseFrame(Frame *img) { + if (img->pixel == NULL) { + return false; + } + + free(img->pixel); + img->pixel = NULL; + return true; +} + +bool CloneFrame(Frame *src, Frame *dst, u32 x, u32 y, u32 w, u32 h) { + if (src->format != dst->format) { + return false; + } + + const u32 step = FormatChannelCount(src->format); + + if (src->depth == 8 && dst->depth == 8) { + for (u32 l = 0; l < h; l++) { + u8* sp = src->pixel + l * src->stride; + u8* dp = dst->pixel + (l + y) * dst->stride + x * step; + + memcpy(dp, sp, w * step); + } + return true; + } else if (src->depth == 16 && dst->depth == 16) { + for (u32 l = 0; l < h; l++) { + u16* sp = (u16 *)src->pixel + l * src->stride; + u16* dp = (u16 *)dst->pixel + (l + y) * dst->stride + x * step; + + memcpy(dp, sp, w * step * 2); + } + return true; + } else if (src->depth == 16 && dst->depth == 8) { + for (u32 l = 0; l < h; l++) { + u16* sp = (u16 *)(src->pixel + l * src->stride); + u8* dp = dst->pixel + (l + y) * dst->stride + x * step; + + for (u32 c = 0; c < w * step; c++) { + *(dp + c) = (*(sp + c)) >> 8u; + } + } + return true; + } else if (src->depth == 8 && dst->depth == 16) { + for (u32 l = 0; l < h; l++) { + u8* sp = src->pixel + l * src->stride; + u16* dp = (u16 *)(dst->pixel + (l + y) * dst->stride + x * step); + + for (u32 c = 0; c < w * step; c++) { + *(dp + c) = (*(sp + c)) << 8u; + } + } + return true; + } else { + return false; + } +} + +bool BlendFrame(Frame *src, Frame *dst, u32 x, u32 y, u32 w, u32 h) { + if (src->format != dst->format) { + return false; + } + + if (FormatChannelCount(src->format) == 3) { + return CloneFrame(src, dst, x, y, w, h); + } + + u32 i, j; + u32 u, v, al; + + if (src->depth == 8 && dst->depth == 8) { + for (u32 l = 0; l < h; l++) { + u8* sp = src->pixel + l * src->stride; + u8* dp = dst->pixel + (l + y) * dst->stride + x * 4; + + for (i = 0; i < w; i++, sp += 4, dp += 4) { + if (sp[3] == 255) { + memcpy(dp, sp, 4); + } else if (sp[3] != 0) { + if (dp[3] != 0) { + u = sp[3]*255; + v = (255-sp[3])*dp[3]; + al = u + v; + dp[0] = (sp[0]*u + dp[0]*v)/al; + dp[1] = (sp[1]*u + dp[1]*v)/al; + dp[2] = (sp[2]*u + dp[2]*v)/al; + dp[3] = al/255; + } else { + memcpy(dp, sp, 4); + } + } + } + } + return true; + } else if (src->depth == 16 && dst->depth == 16) { + for (u32 l = 0; l < h; l++) { + u16* sp = (u16 *)src->pixel + l * src->stride; + u16* dp = (u16 *)dst->pixel + (l + y) * dst->stride + x * 4; + + for (i = 0; i < w; i++, sp += 4, dp += 4) { + if (sp[3] == 65535) { + memcpy(dp, sp, 8); + } else if (sp[3] != 0) { + if (dp[3] != 0) { + u = sp[3]*65535; + v = (65535-sp[3])*dp[3]; + al = u + v; + dp[0] = (sp[0]*u + dp[0]*v)/al; + dp[1] = (sp[1]*u + dp[1]*v)/al; + dp[2] = (sp[2]*u + dp[2]*v)/al; + dp[3] = al/65535; + } else { + memcpy(dp, sp, 8); + } + } + } + } + return true; + } else if (src->depth == 16 && dst->depth == 8) { + for (u32 l = 0; l < h; l++) { + u16* sp = (u16 *)src->pixel + l * src->stride; + u8* dp = dst->pixel + (l + y) * dst->stride + x * 4; + + for (i = 0; i < w; i++, sp += 4, dp += 4) { + if (sp[3] == 65535) { + dp[0] = sp[0] >> 8u; + dp[1] = sp[1] >> 8u; + dp[2] = sp[2] >> 8u; + dp[3] = sp[3] >> 8u; + } else if (sp[3] != 0) { + if (dp[3] != 0) { + u = sp[3]*65535; + v = (65535-sp[3])*(dp[3]<<8u); + al = u + v; + dp[0] = ((sp[0]*u + (dp[0]<<8u)*v)/al) >> 8u; + dp[1] = ((sp[1]*u + (dp[1]<<8u)*v)/al) >> 8u; + dp[2] = ((sp[2]*u + (dp[2]<<8u)*v)/al) >> 8u; + dp[3] = (al / 65535) >> 8u; + } else { + dp[0] = sp[0] >> 8u; + dp[1] = sp[1] >> 8u; + dp[2] = sp[2] >> 8u; + dp[3] = sp[3] >> 8u; + } + } + } + } + return true; + } else if (src->depth == 8 && dst->depth == 16) { + for (u32 l = 0; l < h; l++) { + u8* sp = src->pixel + l * src->stride; + u16* dp = (u16 *)dst->pixel + (l + y) * dst->stride + x * 4; + + for (i = 0; i < w; i++, sp += 4, dp += 4) { + if (sp[3] == 255) { + dp[0] = sp[0] << 8u; + dp[1] = sp[1] << 8u; + dp[2] = sp[2] << 8u; + dp[3] = sp[3] << 8u; + } else if (sp[3] != 0) { + if (dp[3] != 0) { + u = (sp[3]<<8u)*65535; + v = (65535-(sp[3]<<8u))*dp[3]; + al = u + v; + dp[0] = ((sp[0]<<8u)*u + dp[0]*v)/al; + dp[1] = ((sp[1]<<8u)*u + dp[1]*v)/al; + dp[2] = ((sp[2]<<8u)*u + dp[2]*v)/al; + dp[3] = al/65535; + } else { + dp[0] = sp[0] << 8u; + dp[1] = sp[1] << 8u; + dp[2] = sp[2] << 8u; + dp[3] = sp[3] << 8u; + } + } + } + } + return true; + } else { + return false; + } +} + +#define BLEND(V0, V1, ALPHA) \ + ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 256) >> 16u) + +bool BlendImageAlpha(Frame *src, Frame *dst, u32 background_rgb) { + const u32 red = (background_rgb >> 16u) & 0xffu; + const u32 green = (background_rgb >> 8u) & 0xffu; + const u32 blue = (background_rgb >> 0u) & 0xffu; + + // we are blending + if (FormatChannelCount(src->format) != 4 || FormatChannelCount(dst->format) != 3){ + return false; + } + + // can't deal different size + if (src->width != dst->width || src->height != dst->height) { + return false; + } + + // can't deal 16bit now + if (src->depth == 16 || dst->depth == 16) { + return false; + } + + for (u32 l = 0; l < src->height; l++) { + u8* sp = src->pixel + l * src->stride; + u8* dp = dst->pixel + l * dst->stride; + + for (u32 i = 0; i < src->width; i++, sp += 4, dp += 3) { + if (sp[3] == 255) { + dp[0] = sp[0]; + dp[1] = sp[1]; + dp[2] = sp[2]; + } else if (sp[3] == 0) { + dp[0] = red; + dp[1] = green; + dp[2] = blue; + } else { + dp[0] = BLEND(red, sp[0], sp[3]); + dp[1] = BLEND(green, sp[1], sp[3]); + dp[2] = BLEND(blue, sp[2], sp[3]); + } + } + } + + return true; +} + +//void ReleaseImage(Image *img) { +// if (img->i) { +// if (!img->animation) { +// ReleaseFrame(img->i); +// } else { +// for (int j = 0; j < img->frames; ++j) { +// if (img->i + j != NULL) { +// ReleaseFrame(img->i + j); +// } +// } +// } +// free(img->i); +// } +//} \ No newline at end of file diff --git a/c/image_rescale.c b/c/image_rescale.c new file mode 100644 index 0000000..a6498cd --- /dev/null +++ b/c/image_rescale.c @@ -0,0 +1,71 @@ +// +// Created by TYTY on 2020-08-13 013. +// +// use rescaler from libwebp + +#include "header/webp/rescaler_utils.h" +#include "cimg.h" + +bool RescaleImage(Frame *src, Frame* dst, u32 x, u32 y, u32 w, u32 h) { + WebPRescaler rescaler; + u32 l = 0; + rescaler_t *work = (rescaler_t *)malloc(2ULL * dst->width * 3 * sizeof(*work)); + + if (dst->depth != 8 || FormatChannelCount(dst->format) != 3 || dst->pixel == NULL) { + return false; + } + + Frame* source = src; + if (source->depth == 16) { + Frame* tmp = (Frame *)malloc(sizeof(Frame)); + tmp->width = source->width; + tmp->height = source->height; + tmp->format = source->format; + tmp->depth = 8; + tmp->pixel = NULL; + tmp->stride = 0; + + AllocateFrame(tmp); + + CloneFrame(src, tmp, 0, 0, src->width, src->height); + source = tmp; + } + + if (FormatChannelCount(source->format) == 4) { + Frame* tmp = (Frame *)malloc(sizeof(Frame)); + tmp->width = source->width; + tmp->height = source->height; + tmp->format = FORMAT_RGB; + tmp->depth = 8; + tmp->pixel = NULL; + tmp->stride = 0; + + AllocateFrame(tmp); + + BlendImageAlpha(source, tmp, 0xffffffffu); + + if (source != src) { + ReleaseFrame(source); + free(source); + } + + source = tmp; + } + + WebPRescalerInit(&rescaler, w, h, + dst->pixel, dst->width, dst->height, dst->stride, + 3, work); + while (l < h) { + l += WebPRescalerImport(&rescaler, h - l, + source->pixel + (l + y) * source->stride + 3 * x, source->stride); + WebPRescalerExport(&rescaler); + } + + free(work); + if (source != src) { + ReleaseFrame(source); + free(source); + } + + return true; +} \ No newline at end of file diff --git a/c/mem.c b/c/mem.c new file mode 100644 index 0000000..80afae8 --- /dev/null +++ b/c/mem.c @@ -0,0 +1,14 @@ +// +// Created by TYTY on 2020-09-13 013. +// + +#include "cimg.h" +#include + +bool TrimMemory() { + #ifdef __GLIBC__ + return malloc_trim(0); + #else + return true; + #endif +} \ No newline at end of file diff --git a/c/q_webp.c b/c/q_webp.c new file mode 100644 index 0000000..6a6204e --- /dev/null +++ b/c/q_webp.c @@ -0,0 +1,129 @@ +// Following code is derived from webp_quality tool in libwebp + +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// VP8EstimateQuality(): rough encoding quality estimate +// +// Author: Skal (pascal.massimino@gmail.com) + + +//------------------------------------------------------------------------------ + +#include +#ifdef WIN32 +#include "header/webp/decode.h" +#else +#include +#endif + +#include "cimg.h" + +#define INVALID_BIT_POS (1ull << 63) + +// In most cases, we don't need to use a full arithmetic decoder, since +// all the header's bits are written using a uniform probability of 128. +// We can just parse the header as if it was bits (works in 99.999% cases). +static WEBP_INLINE uint32_t GetBit(const uint8_t* const data, size_t nb, + uint64_t max_size, uint64_t* const bit_pos) { + uint32_t val = 0; + if (*bit_pos + nb <= 8 * max_size) { + while (nb-- > 0) { + const uint64_t p = (*bit_pos)++; + const int bit = !!(data[p >> 3] & (128 >> ((p & 7)))); + val = (val << 1) | bit; + } + } else { + *bit_pos = INVALID_BIT_POS; + } + return val; +} + +#define GET_BIT(n) GetBit(data, (n), size, &bit_pos) +#define CONDITIONAL_SKIP(n) (GET_BIT(1) ? GET_BIT((n)) : 0) + +int QualityWEBP(const uint8_t* const data, size_t size) { + size_t pos = 0; + uint64_t bit_pos; + uint64_t sig = 0x00; + int ok = 0; + int Q = -1; + + if (data == NULL) return -1; + + while (pos < size) { + sig = (sig >> 8) | ((uint64_t)data[pos++] << 40); + if ((sig >> 24) == 0x2a019dull) { + ok = 1; + break; + } + } + if (!ok) return -1; + if (pos + 4 > size) return -1; + + // Skip main Header + // width = (data[pos + 0] | (data[pos + 1] << 8)) & 0x3fff; + // height = (data[pos + 2] | (data[pos + 3] << 8)) & 0x3fff; + pos += 4; + bit_pos = pos * 8; + + GET_BIT(2); // colorspace + clamp type + + // Segment header + if (GET_BIT(1)) { // use_segment_ + int s; + const int update_map = GET_BIT(1); + if (GET_BIT(1)) { // update data + const int absolute_delta = GET_BIT(1); + int q[4] = { 0, 0, 0, 0 }; + for (s = 0; s < 4; ++s) { + if (GET_BIT(1)) { + q[s] = GET_BIT(7); + if (GET_BIT(1)) q[s] = -q[s]; // sign + } + } + if (absolute_delta) Q = q[0]; // just use the first segment's quantizer + for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength + } + if (update_map) { + for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8); + } + } + // Filter header + GET_BIT(1 + 6 + 3); // simple + level + sharpness + if (GET_BIT(1)) { // use_lf_delta + if (GET_BIT(1)) { // update lf_delta? + int n; + for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6); + } + } + // num partitions + GET_BIT(2); + + // ParseQuant + { + const int base_q = GET_BIT(7); + /* dqy1_dc = */ CONDITIONAL_SKIP(5); + /* dqy2_dc = */ CONDITIONAL_SKIP(5); + /* dqy2_ac = */ CONDITIONAL_SKIP(5); + /* dquv_dc = */ CONDITIONAL_SKIP(5); + /* dquv_ac = */ CONDITIONAL_SKIP(5); + + if (Q < 0) Q = base_q; + } + if (bit_pos == INVALID_BIT_POS) return -1; + + // base mapping + Q = (127 - Q) * 100 / 127; + // correction for power-law behavior in low range + if (Q < 80) { + Q = (int)(pow(Q / 80., 1. / 0.38) * 80); + } + return Q; +} \ No newline at end of file diff --git a/c/utils.c b/c/utils.c new file mode 100644 index 0000000..16585ca --- /dev/null +++ b/c/utils.c @@ -0,0 +1,39 @@ +// +// Created by TYTY on 2020-08-08 008. +// + +#include "utils.h" + +inline void helper_recurrent(u32* cur, u32* pre, u32 mul) { + u32 tmp = mul * *cur + *pre; + *pre = *cur; + *cur = tmp; +} + +// rationalize number into fraction using continue fraction +void Rationalize(double number, u32* num, u32* den, u32 max) { + u32 num_cur = (u32)number; + u32 den_cur = 1; + + *num = 1; + *den = 0; + + number = number - num_cur; + + while (*den < max && den_cur > *den) { + number = 1 / number; + if (number > (1ull << 32u)) { + // number overflow. current estimate is extremely good. + *den = den_cur; + *num = num_cur; + break; + } + + u32 a = (u32) number; + + helper_recurrent(&num_cur, num, a); + helper_recurrent(&den_cur, den, a); + + number = number - a; + } +} \ No newline at end of file diff --git a/codec.go b/codec.go new file mode 100644 index 0000000..b7c1d14 --- /dev/null +++ b/codec.go @@ -0,0 +1,63 @@ +package main + +import "ImageServer/native" + +var decoders = map[string]func([]byte) *native.Frame{ + "png": native.DecPNG, + "webp": native.DecWEBP, + "avif": native.DecAVIF, + "jpeg": native.DecJPEG, +} + +var detectors = map[string]func([]byte) (bool, uint32, uint32){ + "png": native.IsPNG, + "webp": native.IsWEBP, + "avif": native.IsAVIF, + "jpeg": native.IsJPEG, +} + +var encoders = map[string]func(*native.Frame) []byte{ + "jpeg": native.EncJPEG, + "webp": native.EncWEBP, + "avif": native.EncAVIF, +} + +func Decode(d []byte, t string) (*native.Frame, string) { + if detector, exist := detectors[t]; exist { + ok, width, height := detector(d) + if !ok || width > uint32(config.Site.MaxSize) || height > uint32(config.Site.MaxSize) { + return nil, "" + } + + if f := decoders[t](d); f != nil { + return f, t + } + } + + for codec, detector := range detectors { + if codec == t { + continue + } + + ok, width, height := detector(d) + if !ok { + continue + } else if width > uint32(config.Site.MaxSize) || height > uint32(config.Site.MaxSize) { + return nil, "" + } + + if f := decoders[codec](d); f != nil { + return f, codec + } + } + + return nil, "" +} + +func Encode(f *native.Frame, t string) []byte { + if encoder, exist := encoders[t]; exist { + return encoder(f) + } else { + return nil + } +} diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..a868a7f --- /dev/null +++ b/config.example.toml @@ -0,0 +1,47 @@ +[site] +# the address to listen to +listen = ":8080" +# the host users accessed from. +host = "localhost" +# image storage path +storage = "./storage" +# static assets path +static = "./static" +# allowed max size (in bytes) of request body +body_size = 52428800 +# max number of images info allowed to get per request +max_list = 100 +# size of user avatar +avatar_size = 256 +# size of image preview +preview_size = 256 +# max image size +max_size = 8192 +# generate avif when view count reaches this number +avif_threshold = 100 +# run in debug mode +debug = false +# compression thread count (0 for auto) +thread = 0 + +[database] +# uri to connect to mongodb +mongo_uri = "mongodb://localhost:27017" +# name of database to be used +db_name = "image" + +[security] +# a random string to ensure password safety +salt = "salt" +# a random string to ensure seesion safety +hmac = "hmac" +# default password of admin account +admin_password = "adminadmin" +# recaptcha key. Note that the key below is the official test key +recaptcha = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" + +[https] +# tls cert file +cert = "" +# tls cert key file +key = "" \ No newline at end of file diff --git a/config.go b/config.go new file mode 100644 index 0000000..4e0630a --- /dev/null +++ b/config.go @@ -0,0 +1,67 @@ +package main + +type ConfigSite struct { + Listen string `toml:"listen"` + Host string `toml:"host"` + Storage string `toml:"storage"` + Static string `toml:"static"` + + BodySize uint64 `toml:"body_size"` + MaxList uint64 `toml:"max_list"` + AvatarSize uint64 `toml:"avatar_size"` + PreviewSize uint64 `toml:"preview_size"` + MaxSize uint64 `toml:"max_size"` + AvifThreshold uint64 `toml:"avif_threshold"` + + Debug bool `toml:"debug"` + Thread uint64 `toml:"thread"` +} + +type ConfigDatabase struct { + MongoUri string `toml:"mongo_uri"` + DBName string `toml:"db_name"` +} + +type ConfigSecurity struct { + Salt string `toml:"salt"` + HMAC string `toml:"hmac"` + AdminPassword string `toml:"admin_password"` + RecaptchaKey string `toml:"recaptcha_key"` +} + +type ConfigHTTPS struct { + Cert string `toml:"cert"` + Key string `toml:"key"` +} + +type Config struct { + Site ConfigSite `toml:"site"` + DB ConfigDatabase `toml:"database"` + Security ConfigSecurity `toml:"security"` + HTTPS ConfigHTTPS `toml:"https"` +} + +func (c *Config) SetDefault() { + c.Site.Listen = ":8080" + c.Site.Host = "localhost" + c.Site.Storage = "./storage" + c.Site.Static = "./static" + + c.Site.BodySize = 52428800 + c.Site.MaxList = 100 + c.Site.AvatarSize = 256 + c.Site.PreviewSize = 256 + c.Site.MaxSize = 8192 + c.Site.AvifThreshold = 100 + + c.DB.MongoUri = "mongodb://localhost:27017" + c.DB.DBName = "image" + + c.Security.Salt = "salt" + c.Security.HMAC = "hmac" + c.Security.AdminPassword = "adminadmin" + c.Security.RecaptchaKey = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" + + c.HTTPS.Cert = "" + c.HTTPS.Key = "" +} \ No newline at end of file diff --git a/const.go b/const.go new file mode 100644 index 0000000..370ef5f --- /dev/null +++ b/const.go @@ -0,0 +1,25 @@ +package main + +//var salt = []byte("q08R72qeOc6Qe4Rs") +//var hmac = []byte("r86T4edj6Fd2Ob9e") + +//const RecaptchaKey = "6LdSrOYUAAAAAEjTaw8TpyzHDEGURwr6oZRz0ISb" + +// test +//const RecaptchaKey = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" +// +//const MongoURI = "mongodb://localhost:27017" +//const DBName = "image" +// +//const DefaultAdminPassword = "adminadmin" +// +//const StoragePath = "./storage" +// +//const BodySize = 50 << 20 +//const MaxListLimit = 100 +//const AvatarSize = 256 +//const PreviewSize = 256 +//const GenAvifThreshold = 100 +//const MaxImageSize = 8192 +// +//const SiteHost = "localhost:4201" \ No newline at end of file diff --git a/db.go b/db.go new file mode 100644 index 0000000..98ce278 --- /dev/null +++ b/db.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "sync" + "time" +) + +var client *mongo.Client + +func InitDB() error { + c, err := mongo.NewClient(options.Client().ApplyURI(config.DB.MongoUri)) + if err != nil { + return err + } + ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) + err = c.Connect(ctx) + if err != nil { + return err + } + client = c + return nil +} + +var onceDB sync.Once +var db *mongo.Database + +func DB() *mongo.Database { + onceDB.Do(func() { + db = client.Database(config.DB.DBName) + }) + return db +} + +var cols = new(sync.Map) + +func C(collection string) *mongo.Collection { + if c, exist := cols.Load(collection); exist { + return c.(*mongo.Collection) + } else { + c := DB().Collection(collection) + cols.Store(collection, c) + return c + } +} \ No newline at end of file diff --git a/error.go b/error.go new file mode 100644 index 0000000..5bab79b --- /dev/null +++ b/error.go @@ -0,0 +1,54 @@ +package main + +import ( + "errors" + "fmt" + "strconv" +) + +type SErr int + +const ( + EOk = SErr(iota) + EWeakPassword + EUserExist + EInvalidInviteCode + EBadCredential + EUserNotExist + EInviteCodeExist + EInviteCodeNotExist + EBadRequest + EImageNotExist + EBadImage + EEncodeFailed + EMissingOriginal + EMissingAuthorization + ECredentialExpired + EPermissionDenied + EBadRecaptcha + + EUnknown = SErr(9999) +) + +func (err SErr) Error() string { + return fmt.Sprintf("operation error: %d", int(err)) +} + +func (err SErr) MarshalJSON() ([]byte, error) { + return json.Marshal(fmt.Sprintf("%04d", int(err))) +} + +func (err *SErr) UnmarshalJSON(b []byte) error { + if len(b) != 4 { + return errors.New("bad Error code: invalid length") + } + + i, e := strconv.Atoi(string(b)) + + if e != nil { + return fmt.Errorf("bad Error code: %s", e) + } + + *err = SErr(i) + return nil +} diff --git a/front/.browserslistrc b/front/.browserslistrc new file mode 100644 index 0000000..0ccadaf --- /dev/null +++ b/front/.browserslistrc @@ -0,0 +1,18 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR +not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/front/.editorconfig b/front/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/front/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/front/.gitignore b/front/.gitignore new file mode 100644 index 0000000..38faa88 --- /dev/null +++ b/front/.gitignore @@ -0,0 +1,52 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/dist-old +/tmp +/out-tsc +# Only exists if Bazel was run +/bazel-out + +# dependencies +/node_modules + +# profiling files +chrome-profiler-events*.json +speed-measure-plugin*.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +/src/app/components/test/test.component.html +/src/app/components/test/test.component.spec.ts +/src/app/components/test/test.component.styl +/src/app/components/test/test.component.ts diff --git a/front/README.md b/front/README.md new file mode 100644 index 0000000..18a957f --- /dev/null +++ b/front/README.md @@ -0,0 +1,11 @@ +# Frontend project for Chromatic image hosting server + +## Build + +To build this project, follow the steps below. + +1. Make sure you have npm installed. +2. Replace the value `recaptchaKey` in `src/environments/environments.prod.json` with your own **Website Key**. +3. Use `npm install` to get all dependencies installed. +4. Use `ng build --prod` to get a build ready for production. +5. (Optional) Execute `compress.sh` in `dist/front` to get brotli and gzip compressed static files. diff --git a/front/angular.json b/front/angular.json new file mode 100644 index 0000000..a4dd96c --- /dev/null +++ b/front/angular.json @@ -0,0 +1,137 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "front": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "styl" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/front", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "aot": true, + "assets": [ + "src/favicon.ico", + "src/assets", + "src/manifest.json", + "src/browserconfig.xml" + ], + "styles": [ + "src/custom-theme.scss", + "src/styles.styl" + ], + "scripts": [], + "allowedCommonJsDependencies": [ + "is-ip" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": true, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "10kb", + "maximumError": "15kb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "front:build" + }, + "configurations": { + "production": { + "browserTarget": "front:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "front:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.styl" + ], + "scripts": [] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "tsconfig.app.json", + "tsconfig.spec.json", + "e2e/tsconfig.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + }, + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "front:serve" + }, + "configurations": { + "production": { + "devServerTarget": "front:serve:production" + } + } + } + } + } + }, + "defaultProject": "front", + "cli": { + "analytics": false + } +} diff --git a/front/compress.sh b/front/compress.sh new file mode 100644 index 0000000..c1e41ca --- /dev/null +++ b/front/compress.sh @@ -0,0 +1,32 @@ +# used packages and licenses already included. +rm 3rdpartylicenses.txt +# minify html. should be fine with this aggressive settings as we are not going to edit it +html-minifier --collapse-whitespace \ + --remove-attribute-quotes \ + --remove-redundant-attributes \ + --remove-script-type-attributes \ + --remove-style-link-type-attributes \ + --remove-empty-attributes \ + -o index.html index.html +# minify manifest json +minify-json manifest.json + +mkdir tmp +cp ./*.css tmp +cp ./*.js tmp +cp ./*.json tmp +cp ./*.html tmp +cp ./*.xml tmp +cp ./*.map tmp + +( + cd tmp || exit + for file in *; do brotli -o "$file".br "$file"; done + mv ./*.br ../ + cp ../*.ico ./ + for file in *; do zopfli --i100 "$file"; done + mv ./*.gz ../ + cd .. +) + +rm -r tmp diff --git a/front/e2e/protractor.conf.js b/front/e2e/protractor.conf.js new file mode 100644 index 0000000..f238c0b --- /dev/null +++ b/front/e2e/protractor.conf.js @@ -0,0 +1,36 @@ +// @ts-check +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); + +/** + * @type { import("protractor").Config } + */ +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + browserName: 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ + spec: { + displayStacktrace: StacktraceOption.PRETTY + } + })); + } +}; \ No newline at end of file diff --git a/front/e2e/src/app.e2e-spec.ts b/front/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000..96654ea --- /dev/null +++ b/front/e2e/src/app.e2e-spec.ts @@ -0,0 +1,23 @@ +import { AppPage } from './app.po'; +import { browser, logging } from 'protractor'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getTitleText()).toEqual('front app is running!'); + }); + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + expect(logs).not.toContain(jasmine.objectContaining({ + level: logging.Level.SEVERE, + } as logging.Entry)); + }); +}); diff --git a/front/e2e/src/app.po.ts b/front/e2e/src/app.po.ts new file mode 100644 index 0000000..b68475e --- /dev/null +++ b/front/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo(): Promise { + return browser.get(browser.baseUrl) as Promise; + } + + getTitleText(): Promise { + return element(by.css('app-root .content span')).getText() as Promise; + } +} diff --git a/front/e2e/tsconfig.json b/front/e2e/tsconfig.json new file mode 100644 index 0000000..426058e --- /dev/null +++ b/front/e2e/tsconfig.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "module": "commonjs", + "target": "es2018", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} diff --git a/front/icon.png b/front/icon.png new file mode 100644 index 0000000..4921b08 Binary files /dev/null and b/front/icon.png differ diff --git a/front/karma.conf.js b/front/karma.conf.js new file mode 100644 index 0000000..54377f3 --- /dev/null +++ b/front/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, './coverage/front'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/front/package-lock.json b/front/package-lock.json new file mode 100644 index 0000000..07e2dd3 --- /dev/null +++ b/front/package-lock.json @@ -0,0 +1,14159 @@ +{ + "name": "front", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.1100.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.0.tgz", + "integrity": "sha512-JFPEpEgxJGk5eaJsEilQNI5rOAKCawMdGFAq1uBlYeXSt3iMfFfn//ayvIsE7L2y5b4MC0rzafWSNyDSP3+WuA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.0", + "rxjs": "6.6.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "0.1100.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.0.tgz", + "integrity": "sha512-jCgtnqfBLO00LNImqtjeW07ijYXdpzhsOM4jzlhafh/NesjWJXgg1NI1K7QJvmVL79TeqbBsMj8IOLGTMUCDJw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1100.0", + "@angular-devkit/build-optimizer": "0.1100.0", + "@angular-devkit/build-webpack": "0.1100.0", + "@angular-devkit/core": "11.0.0", + "@babel/core": "7.12.3", + "@babel/generator": "7.12.1", + "@babel/plugin-transform-runtime": "7.12.1", + "@babel/preset-env": "7.12.1", + "@babel/runtime": "7.12.1", + "@babel/template": "7.10.4", + "@jsdevtools/coverage-istanbul-loader": "3.0.5", + "@ngtools/webpack": "11.0.0", + "ansi-colors": "4.1.1", + "autoprefixer": "9.8.6", + "babel-loader": "8.1.0", + "browserslist": "^4.9.1", + "cacache": "15.0.5", + "caniuse-lite": "^1.0.30001032", + "circular-dependency-plugin": "5.2.0", + "copy-webpack-plugin": "6.2.1", + "core-js": "3.6.5", + "css-loader": "5.0.0", + "cssnano": "4.1.10", + "file-loader": "6.1.1", + "find-cache-dir": "3.3.1", + "glob": "7.1.6", + "inquirer": "7.3.3", + "jest-worker": "26.5.0", + "karma-source-map-support": "1.4.0", + "less": "3.12.2", + "less-loader": "7.0.2", + "license-webpack-plugin": "2.3.1", + "loader-utils": "2.0.0", + "mini-css-extract-plugin": "1.2.1", + "minimatch": "3.0.4", + "open": "7.3.0", + "ora": "5.1.0", + "parse5-html-rewriting-stream": "6.0.1", + "pnp-webpack-plugin": "1.6.4", + "postcss": "7.0.32", + "postcss-import": "12.0.1", + "postcss-loader": "4.0.4", + "raw-loader": "4.0.2", + "regenerator-runtime": "0.13.7", + "resolve-url-loader": "3.1.2", + "rimraf": "3.0.2", + "rollup": "2.32.1", + "rxjs": "6.6.3", + "sass": "1.27.0", + "sass-loader": "10.0.5", + "semver": "7.3.2", + "source-map": "0.7.3", + "source-map-loader": "1.1.2", + "source-map-support": "0.5.19", + "speed-measure-webpack-plugin": "1.3.3", + "style-loader": "2.0.0", + "stylus": "0.54.8", + "stylus-loader": "4.1.1", + "terser": "5.3.7", + "terser-webpack-plugin": "4.2.3", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "webpack": "4.44.2", + "webpack-dev-middleware": "3.7.2", + "webpack-dev-server": "3.11.0", + "webpack-merge": "5.2.0", + "webpack-sources": "2.0.1", + "webpack-subresource-integrity": "1.5.1", + "worker-plugin": "5.0.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.1100.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.0.tgz", + "integrity": "sha512-RitDB5JCNDUN2CoNqf/FwLCwdWruApjxb7nUVb9C/uQgGEnrBojyxS/Rv/jCioom86s0sfY9wo79jdxd6AercQ==", + "dev": true, + "requires": { + "loader-utils": "2.0.0", + "source-map": "0.7.3", + "tslib": "2.0.3", + "typescript": "4.0.5", + "webpack-sources": "2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1100.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.0.tgz", + "integrity": "sha512-9diP/A6NtQxSxjbBMj9h9MHrAj4VqCvuFraR928eFaxEoRKcIwSTHhOiolRm+GL5V0VB+O53FRYDk3gC7BGjmQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1100.0", + "@angular-devkit/core": "11.0.0", + "rxjs": "6.6.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.0.tgz", + "integrity": "sha512-fXZtSs3J4S12hboi3om1FA+QS0e8nuQMyzl2nkmtuhcELUFMmSrEl36dtCni5e7Svs46BUAZ5w8EazIkgGQDJg==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/schematics": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.0.tgz", + "integrity": "sha512-oCz9E0thA5WdGDuv6biu3X5kw5/vNE4ZZOKT2sHBQMpAuuDYrDpfTYQJjXQtjfXWvmlr8L8aqDD9N4HXsE4Esw==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.0", + "ora": "5.1.0", + "rxjs": "6.6.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/animations": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.0.0.tgz", + "integrity": "sha512-RGaAnZOI73bPnNWrJq/p8sc+hpUBhScq139M6r4qQjQPsPahazL6v6hHAgRhZNemqw164d1oE4K/22O/i0E3Tw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/cdk": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.0.0.tgz", + "integrity": "sha512-kfgEE/LDYMZB4NICChzWn/nTEeoZ3wxrYsDDcy2Qj+57zUmJcxBLL1h+tawYCy3a1g7ypDLYX7yrbPEBE7/EXQ==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + } + } + }, + "@angular/cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.0.tgz", + "integrity": "sha512-U9sh9r1CSqS78QjuosM3JDXUUTf8eVP1+kSchWEsxjJ0kfdvj7PvtKD1kmRH7HA5lD2q7QfGEvfHpfxMVzKxRg==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1100.0", + "@angular-devkit/core": "11.0.0", + "@angular-devkit/schematics": "11.0.0", + "@schematics/angular": "11.0.0", + "@schematics/update": "0.1100.0", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.2.0", + "ini": "1.3.5", + "inquirer": "7.3.3", + "npm-package-arg": "8.1.0", + "npm-pick-manifest": "6.1.0", + "open": "7.3.0", + "pacote": "9.5.12", + "resolve": "1.18.1", + "rimraf": "3.0.2", + "semver": "7.3.2", + "symbol-observable": "2.0.3", + "universal-analytics": "0.4.23", + "uuid": "8.3.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "dev": true, + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true + } + } + }, + "@angular/common": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-11.0.0.tgz", + "integrity": "sha512-chlbtxR7jpPs3Rc1ymdp3UfUzqEr57OFIxVMG6hROODclPQQk/7oOHdQB4hpUObaF9y4ZTLeKHKWiR/twi21Pg==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/compiler": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.0.0.tgz", + "integrity": "sha512-I7wVhdqvhtBTQTtW61z0lwPb1LiQQ0NOwjsbfN5sAc7/uwxw7em+Kyb/XJgBwgaTKtAL8bZEzdoQGLdsSKQF2g==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/compiler-cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-11.0.0.tgz", + "integrity": "sha512-zrd/cU9syZ8XuQ3ItfIGaKDn1ZBCWyiqdLVRH9VDmyNqQFiCc/VWQ9Th9z8qpLptgdpzE9+lKFgeZJTDtbcveQ==", + "dev": true, + "requires": { + "@babel/core": "^7.8.6", + "@babel/types": "^7.8.6", + "canonical-path": "1.0.0", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.7.2", + "fs-extra": "4.0.2", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "semver": "^6.3.0", + "source-map": "^0.6.1", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.0.0", + "yargs": "15.3.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", + "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.0" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@angular/core": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.0.tgz", + "integrity": "sha512-FNewyMwYy+kGdw1xWfrtaPD2cSQs3kDVFbl8mNMSzp933W5yMsHDvjXb0+nPFqEb8ywEIdm3MsBMK0y3iBWZQw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/forms": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.0.tgz", + "integrity": "sha512-hP6GF1ZkxKQp7Y+EVbEe9PPDQPrUQNdfVxphCWQYwu3tm8+tn1r91KVXkp2MA3M4Fh6Xo2HQEU2d+VXv4w0iNQ==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/material": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-11.0.0.tgz", + "integrity": "sha512-pTwYmBrRXbEzF5J/oayZF0ApA0tLN+CUl/2MaYFNLzvE/Kn6hIdDb7TonWAEBgeSHIzqzyTV8IUQuXwGaPds9A==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/platform-browser": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.0.tgz", + "integrity": "sha512-p8sF6JfaBI+YyLpp5OSg6UcCqjtLKRR+Otq1P/tro5SuxrsrBNRVU8j0tl/crkScsMwAvgmJ1joRyUKdI2mUGQ==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.0.tgz", + "integrity": "sha512-NAmKGhHK+tl7dr/Hcqxvr/813Opec3Mv0IRwIgmKdlpZd7qAwT/mw4RnO4YPSEoDOM6hqGt7GdlWrSDX802duQ==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@angular/router": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-11.0.0.tgz", + "integrity": "sha512-10ZeobfK3HqVeWS6zjdKU16ccxFtdCHkxT11bnFg3Jwq9vKt+LI5KitAkCI5rYTY3DRfVzasRkqBzZfZMkbftw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/compat-data": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.5.tgz", + "integrity": "sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg==", + "dev": true + }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", + "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.12.5", + "@babel/helper-validator-option": "^7.12.1", + "browserslist": "^4.14.5", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", + "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", + "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", + "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz", + "integrity": "sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", + "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", + "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", + "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.12.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", + "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-identifier": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", + "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", + "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", + "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", + "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz", + "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "resolve": "^1.8.1", + "semver": "^5.5.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz", + "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz", + "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", + "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.12.1", + "@babel/helper-compilation-targets": "^7.12.1", + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.1", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.1", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.12.1", + "core-js-compat": "^3.6.2", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", + "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jsdevtools/coverage-istanbul-loader": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", + "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", + "dev": true, + "requires": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.3", + "loader-utils": "^2.0.0", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.7.0" + } + }, + "@ngtools/webpack": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.0.tgz", + "integrity": "sha512-thWOXiMfyVUUWDDRUUAIvb5HASovX1C0GcxRBFE8fXJMCwOPIwqZiAyJJlUUnie8BEP9yC/x6uLCud56ai4Uaw==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.0", + "enhanced-resolve": "5.3.1", + "webpack-sources": "2.0.1" + } + }, + "@ngx-pwa/local-storage": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@ngx-pwa/local-storage/-/local-storage-10.1.0.tgz", + "integrity": "sha512-dEEgPaOoWth3sJJIHSMa1uvljIdZIpd2f4lf6/vD9zbMNuBSvn96/8vallUCDuCZ2Hd/XuNkHJKVA84/lA4VMg==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "@schematics/angular": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.0.tgz", + "integrity": "sha512-/4fkfryoCKQv7nnZgbQ/2aLg8418/SdrCi4ASN0xpfcj34oe2FqsKypeoJG+3bQVF8CLfseorvPNR2YINb4RQA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.0", + "@angular-devkit/schematics": "11.0.0", + "jsonc-parser": "2.3.1" + } + }, + "@schematics/update": { + "version": "0.1100.0", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.0.tgz", + "integrity": "sha512-61zhqIvKHiMR3nezM5FlUoWe2Lw2uKzmuSwcxA2d6SqjDXYyXrOSKmaPcbi7Emgh3VWsQadNpXuc5A2tbKCQhg==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.0", + "@angular-devkit/schematics": "11.0.0", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "npm-package-arg": "^8.0.0", + "pacote": "9.5.12", + "semver": "7.3.2", + "semver-intersect": "1.4.0" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/grecaptcha": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/@types/grecaptcha/-/grecaptcha-2.0.36.tgz", + "integrity": "sha512-SfzcAJPls94/Lz1FznWe6Vm9NLkakhGmk3EO7g2QdkDDR2OU4FH63YILnDpGIU6l7gPnsf5WQ81Kr05JOYAlZw==", + "optional": true + }, + "@types/jasmine": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.13.tgz", + "integrity": "sha512-bVSrTEWdCNH2RHN+E0QlEr4pGPMRA6puKOmL/X13ZeZmUS0q12ZR1rkB9PVvJSX0zi/OXrMDNvUai+PC380+rQ==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", + "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "12.12.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.54.tgz", + "integrity": "sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", + "dev": true + }, + "@types/sortablejs": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.10.6.tgz", + "integrity": "sha512-QRz8Z+uw2Y4Gwrtxw8hD782zzuxxugdcq8X/FkPsXUa1kfslhGzy13+4HugO9FXNo+jlWVcE6DYmmegniIQ30A==", + "dev": true + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz", + "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "adjust-sourcemap-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", + "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "app-root-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", + "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", + "dev": true + }, + "axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.14.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz", + "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.591", + "escalade": "^3.1.1", + "node-releases": "^1.1.66" + } + }, + "browserstack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.0.tgz", + "integrity": "sha512-HJDJ0TSlmkwnt9RZ+v5gFpa1XZTBYTj0ywvLwJ3241J7vMw2jAsGNVhKHtmCOyg+VxeLZyaibO9UL71AsUeDIw==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001157", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001157.tgz", + "integrity": "sha512-gOerH9Wz2IRZ2ZPdMfBvyOi3cjaz4O4dgNwPGzx8EhqAs4+2IL/O+fJsbt+znSigujoZG8bVcIAUM/I/E5K3MA==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-dependency-plugin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", + "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz", + "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "codelyzer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.0.tgz", + "integrity": "sha512-edJIQCIcxD9DhVSyBEdJ38AbLikm515Wl91t5RDGNT88uA6uQdTm4phTWfn9JhzAI8kXNUcfYyAE90lJElpGtA==", + "dev": true, + "requires": { + "@angular/compiler": "9.0.0", + "@angular/core": "9.0.0", + "app-root-path": "^3.0.0", + "aria-query": "^3.0.0", + "axobject-query": "2.0.2", + "css-selector-tokenizer": "^0.7.1", + "cssauron": "^1.4.0", + "damerau-levenshtein": "^1.0.4", + "rxjs": "^6.5.3", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.2", + "tslib": "^1.10.0", + "zone.js": "~0.10.3" + }, + "dependencies": { + "@angular/compiler": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz", + "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==", + "dev": true + }, + "@angular/core": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz", + "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=", + "dev": true, + "requires": { + "arity-n": "^1.0.4" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz", + "integrity": "sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q==", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "fast-glob": "^3.2.4", + "find-cache-dir": "^3.3.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true + }, + "core-js-compat": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz", + "integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==", + "dev": true, + "requires": { + "browserslist": "^4.14.6", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.0.0.tgz", + "integrity": "sha512-9g35eXRBgjvswyJWoqq/seWp+BOxvUl8IinVNTsUBFFxtwfEYvlmEn6ciyn0liXGbGh5HyJjPGCuobDSfqMIVg==", + "dev": true, + "requires": { + "camelcase": "^6.1.0", + "cssesc": "^3.0.0", + "icss-utils": "^5.0.0", + "loader-utils": "^2.0.0", + "postcss": "^8.1.1", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "postcss": { + "version": "8.1.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.1.7.tgz", + "integrity": "sha512-llCQW1Pz4MOPwbZLmOddGM9eIJ8Bh7SZ2Oj5sxZva77uVaotYDsYTch1WBTNu7fUY0fpWp0fdt7uW40D4sRiiQ==", + "dev": true, + "requires": { + "colorette": "^1.2.1", + "line-column": "^1.0.2", + "nanoid": "^3.1.16", + "source-map": "^0.6.1" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "requires": { + "css": "^2.0.0" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.1.0.tgz", + "integrity": "sha512-h+6w/W1WqXaJA4tb1dk7r5tVbOm97MsKxzwnvOR04UQ6GILroryjMWu3pmCCtL2mLaEStQ0fZgeGiy99mo7iyg==", + "dev": true, + "requires": { + "css-tree": "^1.0.0" + }, + "dependencies": { + "css-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.1.tgz", + "integrity": "sha512-WroX+2MvsYcRGP8QA0p+rxzOniT/zpAoQ/DTKDSJzh5T3IQKUkFHeIIfgIapm2uaP178GWY3Mime1qbk8GO/tA==", + "dev": true, + "requires": { + "mdn-data": "2.0.12", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.12.tgz", + "integrity": "sha512-ULbAlgzVb8IqZ0Hsxm6hHSlQl3Jckst2YEQS7fODu9ilNWy2LvcoSY7TRFIktABP2mdppBioc66va90T+NUs8Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", + "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", + "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.593", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.593.tgz", + "integrity": "sha512-GvO7G1ZxvffnMvPCr4A7+iQPVuvpyqMrx2VWSERAjG+pHK6tmO9XqYdBfMIq9corRyi4bNImSDEiDvIoDb8HrA==", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", + "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "0.3.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "^7.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "ws": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", + "dev": true + } + } + }, + "engine.io-client": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", + "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", + "dev": true, + "requires": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", + "dev": true + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", + "dev": true + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "enhanced-resolve": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz", + "integrity": "sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.0.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fastq": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", + "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz", + "integrity": "sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", + "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=", + "optional": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.0.0.tgz", + "integrity": "sha512-aF2Cf/CkEZrI/vsu5WI/I+akFgdbwQHVE9YRZxATrhH4PVIe6a3BIjwjEcW+z+jP/hNh+YvM3lAAn1wJQ6opSg==", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "requires": { + "ip-regex": "^4.0.0" + }, + "dependencies": { + "ip-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.1.0.tgz", + "integrity": "sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA==" + } + } + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz", + "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "dependencies": { + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", + "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", + "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "dev": true, + "requires": { + "colors": "1.4.0" + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "jest-worker": { + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", + "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "karma": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-5.1.1.tgz", + "integrity": "sha512-xAlOr5PMqUbiKXSv5PCniHWV3aiwj6wIZ0gUVcwpTCPVQm/qH2WAMFWxtnpM6KJqhkRWrIpovR4Rb0rn8GtJzQ==", + "dev": true, + "requires": { + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.0.0", + "colors": "^1.4.0", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "flatted": "^2.0.2", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.6", + "lodash": "^4.17.15", + "log4js": "^6.2.1", + "mime": "^2.4.5", + "minimatch": "^3.0.4", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^2.3.0", + "source-map": "^0.6.1", + "tmp": "0.2.1", + "ua-parser-js": "0.7.21", + "yargs": "^15.3.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "karma-chrome-launcher": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + } + }, + "karma-jasmine": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.3.1.tgz", + "integrity": "sha512-Nxh7eX9mOQMyK0VSsMxdod+bcqrR/ikrmEiWj5M6fwuQ7oI+YEF1FckaDsWfs6TIpULm9f0fTKMjF7XcrvWyqQ==", + "dev": true, + "requires": { + "jasmine-core": "^3.5.0" + } + }, + "karma-jasmine-html-reporter": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", + "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", + "dev": true + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", + "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "dev": true + }, + "less": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz", + "integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==", + "dev": true, + "requires": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "less-loader": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-7.0.2.tgz", + "integrity": "sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "license-webpack-plugin": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz", + "integrity": "sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "line-column": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz", + "integrity": "sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI=", + "dev": true, + "requires": { + "isarray": "^1.0.0", + "isobject": "^2.0.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log4js": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", + "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", + "dev": true, + "requires": { + "date-format": "^3.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.1", + "rfdc": "^1.1.4", + "streamroller": "^2.2.4" + } + }, + "loglevel": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", + "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "make-fetch-happen": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + }, + "dependencies": { + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz", + "integrity": "sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.16.tgz", + "integrity": "sha512-+AK8MN0WHji40lj8AEuwLOvLSbWYApQpre/aFJZD71r43wVRLrOYS4FmJOPQYon1TqB462RzrrxlfA74XRES8w==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "native-request": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.8.tgz", + "integrity": "sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag==", + "dev": true, + "optional": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "ng-recaptcha": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ng-recaptcha/-/ng-recaptcha-5.0.0.tgz", + "integrity": "sha512-tUVMmHiCDgWSjqXddjRde9jTnyODwki/akixvFiYK8SaQhgW1aor1WKS8dnNtc+Spx7XFiurEXRVfgyd70G6Mw==", + "requires": { + "@types/grecaptcha": "^2.0.33", + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "ngx-image-cropper": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-3.2.1.tgz", + "integrity": "sha512-5EmPVtb1ywdmPMJ2kXgfv7UgqxFaw6AtaFHGpuP0IpCSx5/u08Pt0ClB0LaI6TXcMIefhXK17W85MvkppuvBag==", + "requires": { + "hammerjs": "^2.0.8", + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "ngx-sortablejs": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/ngx-sortablejs/-/ngx-sortablejs-3.1.4.tgz", + "integrity": "sha512-jrEC4loWf01JxBcPiOHiyHbXwNrs4acIus1c9NVv7hiK574dywoqOnL5/OVQFnluqItWC7llqCUXfDr3Kmyqfg==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + } + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-fetch-npm": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz", + "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.66", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz", + "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-package-arg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.0.tgz", + "integrity": "sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig==", + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz", + "integrity": "sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw==", + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.0.0", + "semver": "^7.0.0" + } + }, + "npm-registry-fetch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz", + "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "dev": true, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", + "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + } + } + }, + "ora": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", + "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.4.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pacote": { + "version": "9.5.12", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz", + "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-normalize-package-bin": "^1.0.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^3.0.0", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "dev": true, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "dev": true, + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.0.4.tgz", + "integrity": "sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", + "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + } + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "protractor": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", + "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.1.7", + "yargs": "^15.3.1" + }, + "dependencies": { + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + } + } + }, + "raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "resolve-url-loader": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz", + "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "3.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.21", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "postcss": { + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "dev": true, + "requires": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", + "dev": true + } + } + }, + "rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", + "dev": true + }, + "rfdc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rollup": { + "version": "2.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.32.1.tgz", + "integrity": "sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz", + "integrity": "sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==", + "dev": true, + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.5.tgz", + "integrity": "sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "requires": { + "semver": "^5.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", + "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "dev": true, + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.4.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.3.0", + "socket.io-parser": "~3.4.0" + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", + "dev": true + }, + "socket.io-client": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", + "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "engine.io-client": "~3.4.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "socket.io-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", + "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", + "dev": true, + "requires": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + } + } + }, + "socket.io-parser": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", + "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "socks": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", + "dev": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "dev": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sortablejs": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz", + "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==" + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.1.2.tgz", + "integrity": "sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.6.1", + "whatwg-mimetype": "^2.3.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "speed-measure-webpack-plugin": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz", + "integrity": "sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "streamroller": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", + "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", + "dev": true, + "requires": { + "date-format": "^2.1.0", + "debug": "^4.1.1", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "stylus": { + "version": "0.54.8", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", + "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", + "dev": true, + "requires": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.6", + "mkdirp": "~1.0.4", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.3.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "stylus-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.1.1.tgz", + "integrity": "sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA==", + "dev": true, + "requires": { + "fast-glob": "^3.2.4", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "symbol-observable": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz", + "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==", + "dev": true + }, + "tapable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.1.1.tgz", + "integrity": "sha512-Wib1S8m2wdpLbmQz0RBEVosIyvb/ykfKXf3ZIDqvWoMg/zTNm6G/tDSuUM61J1kNCDXWJrLHGSFeMhAG+gAGpQ==", + "dev": true + }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "terser": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.7.tgz", + "integrity": "sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + } + }, + "terser-webpack-plugin": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", + "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.5.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.3.4", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-node": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + } + }, + "ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", + "dev": true + }, + "tslib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", + "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" + }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universal-analytics": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz", + "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "request": "^2.88.2", + "uuid": "^3.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "optional": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "web-animations-js": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", + "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==" + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webpack": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.3.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.2.0.tgz", + "integrity": "sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.0.1.tgz", + "integrity": "sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz", + "integrity": "sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q==", + "dev": true, + "requires": { + "webpack-sources": "^1.3.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "dev": true, + "requires": { + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "worker-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-5.0.0.tgz", + "integrity": "sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "zone.js": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", + "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==" + } + } +} diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..c14d96c --- /dev/null +++ b/front/package.json @@ -0,0 +1,56 @@ +{ + "name": "front", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "~11.0.0", + "@angular/cdk": "^11.0.0", + "@angular/common": "~11.0.0", + "@angular/compiler": "~11.0.0", + "@angular/core": "~11.0.0", + "@angular/forms": "~11.0.0", + "@angular/material": "^11.0.0", + "@angular/platform-browser": "~11.0.0", + "@angular/platform-browser-dynamic": "~11.0.0", + "@angular/router": "~11.0.0", + "@ngx-pwa/local-storage": "^10.1.0", + "is-ip": "^3.1.0", + "ng-recaptcha": "^5.0.0", + "ngx-image-cropper": "^3.2.1", + "ngx-sortablejs": "^3.1.4", + "rxjs": "~6.6.3", + "sortablejs": "^1.10.2", + "tslib": "^2.0.0", + "web-animations-js": "^2.3.2", + "zone.js": "~0.10.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.1100.0", + "@angular/cli": "~11.0.0", + "@angular/compiler-cli": "~11.0.0", + "@types/jasmine": "~3.5.0", + "@types/jasminewd2": "~2.0.3", + "@types/node": "^12.11.1", + "@types/sortablejs": "^1.10.6", + "codelyzer": "^6.0.0", + "jasmine-core": "~3.5.0", + "jasmine-spec-reporter": "~5.0.0", + "karma": "~5.1.1", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~3.3.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", + "ts-node": "~8.3.0", + "tslint": "~6.1.0", + "typescript": "~4.0.5" + } +} diff --git a/front/src/action-panel.styl b/front/src/action-panel.styl new file mode 100644 index 0000000..1e03f90 --- /dev/null +++ b/front/src/action-panel.styl @@ -0,0 +1,45 @@ +.action-panel-sentinel + position fixed + width 100% + height 100px + left 0 + bottom 0 + +.action-panel-container-6 + z-index 2 + height 60px + position fixed + width 100% + display flex + justify-content center + left 0 + + .action-panel + height 40px + background white + padding 10px + opacity 0 + + @media (min-width 600px) + bottom 20px + + .action-panel + width 290px + border-radius 5px + display flex + + .action-button-container:not(:last-child) + margin-right 10px + + @media (max-width 599px) + bottom 0 + + .action-panel + width 100% + display grid + grid-template-columns repeat(6, 1fr) + + .action-button-container + display flex + justify-content center + align-items center diff --git a/front/src/app/app-routing.module.ts b/front/src/app/app-routing.module.ts new file mode 100644 index 0000000..b7cbc07 --- /dev/null +++ b/front/src/app/app-routing.module.ts @@ -0,0 +1,33 @@ +import {NgModule} from '@angular/core'; +import {Routes, RouterModule} from '@angular/router'; +import {MainComponent} from './components/main/main.component'; +import {GalleryComponent} from './components/gallery/gallery.component'; +import {LoginComponent} from './components/login/login.component'; +import {RegisterComponent} from './components/register/register.component'; +import {NonLoginGuard} from './guards/non-login.guard'; +import {LoginGuard} from './guards/login.guard'; +import {UploadComponent} from './components/upload/upload.component'; +import {AdminGuard} from './guards/admin.guard'; +import {AdminComponent} from './components/admin/admin.component'; +import {NotFoundComponent} from './components/not-found/not-found.component'; +import {AboutComponent} from './components/about/about.component'; + +const routes: Routes = [ + {path: 'show', component: UploadComponent, outlet: 'upload', data: {animation: 'upload'}}, + {path: 'login', component: LoginComponent, canActivate: [NonLoginGuard]}, + {path: 'register', component: RegisterComponent, canActivate: [NonLoginGuard]}, + {path: 'gallery/me', component: GalleryComponent, canActivate: [LoginGuard]}, + {path: 'admin', component: AdminComponent, canActivate: [AdminGuard]}, + {path: 'gallery/:id', component: GalleryComponent}, + {path: 'about', component: AboutComponent}, + {path: '', component: MainComponent, pathMatch: 'full', canActivate: [NonLoginGuard]}, + {path: '**', component: NotFoundComponent} +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { +} + diff --git a/front/src/app/app.component.html b/front/src/app/app.component.html new file mode 100644 index 0000000..4988068 --- /dev/null +++ b/front/src/app/app.component.html @@ -0,0 +1,7 @@ + + +
+ +
+ + diff --git a/front/src/app/app.component.spec.ts b/front/src/app/app.component.spec.ts new file mode 100644 index 0000000..3da163d --- /dev/null +++ b/front/src/app/app.component.spec.ts @@ -0,0 +1,35 @@ +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + RouterTestingModule + ], + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'front'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('front'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement; + expect(compiled.querySelector('.content span').textContent).toContain('front app is running!'); + }); +}); diff --git a/front/src/app/app.component.styl b/front/src/app/app.component.styl new file mode 100644 index 0000000..7fca44a --- /dev/null +++ b/front/src/app/app.component.styl @@ -0,0 +1,19 @@ +.app-header + position fixed + top 0 + left 0 + right 0 + z-index 10 + +.upload-wrapper + position fixed + right 0 + width 50vw + max-width 750px + z-index 10 + + @media (max-width 799px) + width 66.7vw + + @media (max-width 599px) + width 100vw diff --git a/front/src/app/app.component.ts b/front/src/app/app.component.ts new file mode 100644 index 0000000..51bad13 --- /dev/null +++ b/front/src/app/app.component.ts @@ -0,0 +1,36 @@ +import {Component} from '@angular/core'; +import {RouterOutlet} from '@angular/router'; +import {animate, query, sequence, state, style, transition, trigger} from '@angular/animations'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.styl'], + animations: [ + trigger('routeAnimation', [ + state('*', style({bottom: '-450px'})), + state('upload', style({bottom: '0'})), + transition('* => upload', [ + animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)', style({bottom: '0'})), + ]), + transition('upload => *', [ + sequence([ + animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({bottom: '-450px'})), + query(':leave', animate('300ms 300ms')), + ]), + ]), + ]) + ] +}) +export class AppComponent { + title = 'Luminous'; + + getAnimationData(outlet: RouterOutlet): string { + return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation; + } + + constructor() { + + } +} diff --git a/front/src/app/app.module.ts b/front/src/app/app.module.ts new file mode 100644 index 0000000..e619bed --- /dev/null +++ b/front/src/app/app.module.ts @@ -0,0 +1,171 @@ +import {BrowserModule} from '@angular/platform-browser'; +import {NgModule} from '@angular/core'; + +import {AppRoutingModule} from './app-routing.module'; +import {AppComponent} from './app.component'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {MainComponent} from './components/main/main.component'; +import {MatButtonModule} from '@angular/material/button'; +import {LanguagePickerComponent} from './components/language-picker/language-picker.component'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatIconModule} from '@angular/material/icon'; +import {HeaderComponent} from './components/header/header.component'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {GalleryComponent} from './components/gallery/gallery.component'; +import {TestComponent} from './components/test/test.component'; +import {GalleryImageComponent} from './components/gallery-image/gallery-image.component'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import {MatInputModule} from '@angular/material/input'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {MatButtonToggleModule} from '@angular/material/button-toggle'; +import {LoginComponent} from './components/login/login.component'; +import {RECAPTCHA_BASE_URL, RECAPTCHA_SETTINGS, RecaptchaFormsModule, RecaptchaModule} from 'ng-recaptcha'; +import {environment} from '../environments/environment'; +import {MatDividerModule} from '@angular/material/divider'; +import {HttpClientModule} from '@angular/common/http'; +import {MatSnackBarModule} from '@angular/material/snack-bar'; +import {httpInterceptorProviders} from './interceptors'; +import {RegisterComponent} from './components/register/register.component'; +import {InfoComponent} from './components/info/info.component'; +import {MatAutocompleteModule} from '@angular/material/autocomplete'; +import {AwareStickyComponent} from './components/aware-sticky/aware-sticky.component'; +import {registerLocaleData} from '@angular/common'; +import localeZH_CN from '@angular/common/locales/zh-Hans'; +import localeZH_TW from '@angular/common/locales/zh-Hant'; +import localeZH_HK from '@angular/common/locales/zh-Hant-HK'; +import localeJA from '@angular/common/locales/ja'; +import {LongPressDirective} from './directives/long-press.directive'; +import {GalleryOverlayComponent} from './components/gallery-overlay/gallery-overlay.component'; +import {MatDialogModule} from '@angular/material/dialog'; +import {UploadComponent} from './components/upload/upload.component'; +import {DragDropModule} from '@angular/cdk/drag-drop'; +import {AsDataUrlPipe} from './pipes/as-data-url.pipe'; +import {FileSizePipe} from './pipes/file-size.pipe'; +import {SortablejsModule} from 'ngx-sortablejs'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatChipsModule} from '@angular/material/chips'; +import {PluralComponent} from './components/plural/plural.component'; +import {IntersectionDirective} from './directives/intersection.directive'; +import {FILE_READER_CONCURRENT} from './services/file-reader.service'; +import {EditImagesComponent} from './dialogs/edit-images/edit-images.component'; +import {MatTabsModule} from '@angular/material/tabs'; +import {ConfirmationComponent} from './dialogs/confirmation/confirmation.component'; +import {ImageInfoComponent} from './dialogs/image-info/image-info.component'; +import {GenCodeSingleComponent} from './dialogs/gen-code-single/gen-code-single.component'; +import {MatRadioModule} from '@angular/material/radio'; +import {ClipboardModule} from '@angular/cdk/clipboard'; +import {GenCodeMultiComponent} from './dialogs/gen-code-multi/gen-code-multi.component'; +import {UploadAdvancedComponent} from './dialogs/upload-advanced/upload-advanced.component'; +import {MatProgressBarModule} from '@angular/material/progress-bar'; +import {ChangePasswordComponent} from './dialogs/change-password/change-password.component'; +import {MatRippleModule} from '@angular/material/core'; +import {ChangeAvatarComponent} from './dialogs/change-avatar/change-avatar.component'; +import {ImageCropperModule} from 'ngx-image-cropper'; +import {AdminComponent, LOCALE_PAGINATOR_INTL_PROVIDER} from './components/admin/admin.component'; +import {MatTableModule} from '@angular/material/table'; +import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {SinglePromptComponent} from './dialogs/single-prompt/single-prompt.component'; +import {ChoiceComponent} from './dialogs/choice/choice.component'; +import {AddUserComponent} from './dialogs/add-user/add-user.component'; +import {AddCodeComponent} from './dialogs/add-code/add-code.component'; +import {NotFoundComponent} from './components/not-found/not-found.component'; +import {FormatPreferenceComponent} from './dialogs/format-preference/format-preference.component'; +import {AboutComponent} from './components/about/about.component'; + +registerLocaleData(localeZH_CN, 'zh-CN'); +registerLocaleData(localeZH_TW, 'zh-TW'); +registerLocaleData(localeZH_HK, 'zh-HK'); +registerLocaleData(localeJA, 'ja'); + +@NgModule({ + declarations: [ + AppComponent, + MainComponent, + LanguagePickerComponent, + HeaderComponent, + GalleryComponent, + TestComponent, + GalleryImageComponent, + LoginComponent, + RegisterComponent, + InfoComponent, + AwareStickyComponent, + LongPressDirective, + GalleryOverlayComponent, + UploadComponent, + AsDataUrlPipe, + FileSizePipe, + PluralComponent, + IntersectionDirective, + EditImagesComponent, + ConfirmationComponent, + ImageInfoComponent, + GenCodeSingleComponent, + GenCodeMultiComponent, + UploadAdvancedComponent, + ChangePasswordComponent, + ChangeAvatarComponent, + AdminComponent, + SinglePromptComponent, + ChoiceComponent, + AddUserComponent, + AddCodeComponent, + NotFoundComponent, + FormatPreferenceComponent, + AboutComponent + ], + imports: [ + BrowserModule, + AppRoutingModule, + BrowserAnimationsModule, + HttpClientModule, + MatButtonModule, + MatMenuModule, + MatIconModule, + MatTooltipModule, + MatProgressSpinnerModule, + MatInputModule, + ReactiveFormsModule, + MatButtonToggleModule, + RecaptchaModule, + RecaptchaFormsModule, + MatDividerModule, + MatSnackBarModule, + FormsModule, + MatAutocompleteModule, + MatDialogModule, + DragDropModule, + SortablejsModule, + MatSlideToggleModule, + MatChipsModule, + MatTabsModule, + MatRadioModule, + ClipboardModule, + MatProgressBarModule, + MatRippleModule, + ImageCropperModule, + MatTableModule, + MatCheckboxModule, + MatPaginatorModule + ], + providers: [ + httpInterceptorProviders, + { + provide: RECAPTCHA_BASE_URL, + useValue: 'https://recaptcha.net/recaptcha/api.js', + }, + { + provide: RECAPTCHA_SETTINGS, + useValue: {siteKey: environment.recaptchaKey}, + }, + { + provide: FILE_READER_CONCURRENT, + useValue: 4, + }, + LOCALE_PAGINATOR_INTL_PROVIDER + ], + bootstrap: [AppComponent] +}) +export class AppModule { +} diff --git a/front/src/app/components/about/about.component.html b/front/src/app/components/about/about.component.html new file mode 100644 index 0000000..7c7eb90 --- /dev/null +++ b/front/src/app/components/about/about.component.html @@ -0,0 +1,34 @@ +
+
+

+ {{'about_title' | i18nSelect:locale.dict}} +

+

+ {{'about_content' | i18nSelect:locale.dict}} +

+

+ {{'about_privacy' | i18nSelect:locale.dict}} +

+

+ {{'about_privacy_content' | i18nSelect:locale.dict}} +

+

+ {{'about_based_on' | i18nSelect:locale.dict}} +

+

+ {{'about_based_content' | i18nSelect:locale.dict}} +

+
+
+
+

{{project.name}}

+

+ {{project.license === 'other' ? ('about_license_other' | i18nSelect:locale.dict) : project.license}} +

+
+ + code + +
+
+
diff --git a/front/src/app/components/about/about.component.spec.ts b/front/src/app/components/about/about.component.spec.ts new file mode 100644 index 0000000..b4ddaaf --- /dev/null +++ b/front/src/app/components/about/about.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {AboutComponent} from './about.component'; + +describe('AboutComponent', () => { + let component: AboutComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AboutComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AboutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/about/about.component.styl b/front/src/app/components/about/about.component.styl new file mode 100644 index 0000000..c500e48 --- /dev/null +++ b/front/src/app/components/about/about.component.styl @@ -0,0 +1,40 @@ +.page-container + height 100vh + width 90vw + overflow-x hidden + overflow-y auto + padding 0 5vw + + .projects + display grid + grid-template-columns repeat(3, 1fr) + grid-gap 12px + gap 12px + margin-bottom 80px + + @media (max-width 1000px) + grid-template-columns 1fr 1fr + + @media (max-width 600px) + grid-template-columns 1fr + + .project + padding 10px + display flex + justify-content space-between + align-items center + + .project-info-wrapper + flex 1 1 auto + + .project-name + margin-bottom 5px + + .project-license + margin-bottom 0 + + .project-url + flex 0 0 auto + margin-right 8px + cursor alias + diff --git a/front/src/app/components/about/about.component.ts b/front/src/app/components/about/about.component.ts new file mode 100644 index 0000000..f37d1c4 --- /dev/null +++ b/front/src/app/components/about/about.component.ts @@ -0,0 +1,40 @@ +import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {ScrollService} from '../../services/scroll.service'; +import {TitleService} from '../../services/title.service'; +import {Subject} from 'rxjs'; +import {takeUntil} from 'rxjs/operators'; + +const depsLiteral = '[{"name": "angular", "license": "MIT", "link": "https://github.com/angular/angular"}, {"name": "angular/components", "license": "MIT", "link": "https://github.com/angular/components"}, {"name": "@ngx-pwa/local-storage", "license": "MIT", "link": "https://github.com/cyrilletuzi/angular-async-local-storage"}, {"name": "css-loader", "license": "MIT", "link": "https://github.com/webpack-contrib/css-loader"}, {"name": "ip-regex", "license": "MIT", "link": "https://github.com/sindresorhus/ip-regex"}, {"name": "is-ip", "license": "MIT", "link": "https://github.com/sindresorhus/is-ip"}, {"name": "ng-recaptcha", "license": "MIT", "link": "https://github.com/DethAriel/ng-recaptcha"}, {"name": "ngx-image-cropper", "license": "MIT", "link": "https://github.com/Mawi137/ngx-image-cropper"}, {"name": "ngx-sortablejs", "license": "MIT", "link": "https://github.com/sortablejs/ngx-sortablejs"}, {"name": "rxjs", "license": "Apache-2.0", "link": "https://github.com/ReactiveX/rxjs"}, {"name": "sortablejs", "license": "MIT", "link": "https://github.com/SortableJS/Sortable"}, {"name": "zone.js", "license": "MIT", "link": "https://github.com/angular/angular/tree/master/packages/zone.js"}, {"name": "toml", "license": "MIT", "link": "https://github.com/BurntSushi/toml"}, {"name": "xxhash", "license": "Apache-2.0", "link": "https://github.com/OneOfOne/xxhash"}, {"name": "jwt-go", "license": "MIT", "link": "https://github.com/dgrijalva/jwt-go"}, {"name": "gin", "license": "MIT", "link": "https://github.com/gin-gonic/gin"}, {"name": "gzipped", "license": "BSD-3-Clause", "link": "https://github.com/lpar/gzipped"}, {"name": "jsoniter", "license": "MIT", "link": "https://github.com/json-iterator/go"}, {"name": "jpegquality", "license": "MIT", "link": "https://github.com/liut/jpegquality"}, {"name": "mongo-driver", "license": "Apache-2.0", "link": "https://go.mongodb.org/mongo-driver"}, {"name": "mozjpeg", "license": "other", "link": "https://github.com/mozilla/mozjpeg"}, {"name": "libpng", "license": "other", "link": "https://github.com/glennrp/libpng"}, {"name": "libwebp", "license": "BSD-3-Clause", "link": "https://chromium.googlesource.com/webm/libwebp"}, {"name": "libavif", "license": "other", "link": "https://github.com/AOMediaCodec/libavif"}, {"name": "libaom", "license": "BSD-2-Clause", "link": "https://aomedia.googlesource.com/aom"}, {"name": "dav1d", "license": "BSD-2-Clause", "link": "https://code.videolan.org/videolan/dav1d"}]'; + +@Component({ + selector: 'app-about', + templateUrl: './about.component.html', + styleUrls: ['./about.component.styl'] +}) +export class AboutComponent implements OnInit, OnDestroy { + @ViewChild('container', {static: true}) container: ElementRef; + + readonly deps = JSON.parse(depsLiteral) as { name: string, license: string, link: string }[]; + readonly destroy$: Subject; + + constructor(public locale: LocaleService, + private scroll: ScrollService, + private title: TitleService) { + this.destroy$ = new Subject(); + } + + ngOnInit(): void { + this.locale.dict$.pipe(takeUntil(this.destroy$)).subscribe(dict => { + this.title.firstPart = dict.title_about; + }); + this.title.firstPart = this.locale.dict.title_about; + this.scroll.WatchOn(this.container.nativeElement); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.scroll.UnwatchOn(this.container.nativeElement); + } + +} diff --git a/front/src/app/components/admin/admin.component.html b/front/src/app/components/admin/admin.component.html new file mode 100644 index 0000000..efa9f56 --- /dev/null +++ b/front/src/app/components/admin/admin.component.html @@ -0,0 +1,215 @@ +
+
+ + + + +
+
+ + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + {{'admin_user_username' | i18nSelect:locale.dict}} + +
+
+ avatar +
+ person +
+
+ + {{element.name}} + +
+
+ {{'admin_user_privileged' | i18nSelect:locale.dict}} + + + + {{'admin_user_frozen' | i18nSelect:locale.dict}} + + + + {{'admin_operation' | i18nSelect:locale.dict}} + + + collections + + + + +
+ +
+
+ + +
+ + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + {{'admin_code_code' | i18nSelect:locale.dict}} + +
+ + {{element.code}} + +
+
+ {{'admin_code_times' | i18nSelect:locale.dict}} + + + {{element.times}} + + + {{'admin_operation' | i18nSelect:locale.dict}} + + + +
+ +
+
+
+
+
diff --git a/front/src/app/components/admin/admin.component.spec.ts b/front/src/app/components/admin/admin.component.spec.ts new file mode 100644 index 0000000..cbf96c3 --- /dev/null +++ b/front/src/app/components/admin/admin.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {AdminComponent} from './admin.component'; + +describe('AdminComponent', () => { + let component: AdminComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AdminComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdminComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/admin/admin.component.styl b/front/src/app/components/admin/admin.component.styl new file mode 100644 index 0000000..db9ec09 --- /dev/null +++ b/front/src/app/components/admin/admin.component.styl @@ -0,0 +1,119 @@ +.page-container + height 100vh + width 90vw + overflow-x hidden + overflow-y auto + padding 0 5vw + +.tab + .table-top + width 100% + padding-top 20px + display flex + align-items center + height 80px + + &.code-table-top + justify-content flex-end + + .tag-form-field + flex 1 1 auto + + button + flex 0 0 auto + margin-left 10px + + &:last-child + margin-right 10px + + .table-container + overflow-x auto + + .user-table + min-width 400px + + .code-table + min-width 350px + + .mat-cell, .mat-header-cell + padding 16px + + &:last-child + padding-right 0 + + .mat-header-cell + word-break keep-all + + .mat-column-user, + .mat-column-code + width 100% + max-width 45vw + box-sizing border-box + overflow-x hidden + + .user-wrapper + width 100% + height 100% + display flex + justify-content flex-start + align-items center + + .user-avatar + height 30px + width 30px + + .avatar-image + height 30px + width 30px + border-radius 15px + background #ddd + + &.errored + display none + + .pseudo-avatar-image + color #555 + justify-content center + align-items center + user-select none + display none + + &.errored + display flex + + .user-name + margin-left 10px + max-width calc(45vw - 72px) + overflow hidden + white-space nowrap + text-overflow ellipsis + + .code-content-wrapper + max-width calc(45vw - 32px) + overflow hidden + white-space nowrap + text-overflow ellipsis + + .code-content + font-family var(--font-mono) + //user-select all + + &.user-tab .mat-column-operation + min-width 160px + + &.code-tab .mat-column-operation + min-width 80px + + &.mat-header-cell + text-align center + + .mat-column-select, .mat-column-privileged, .mat-column-frozen + text-align center + + .paginator + position sticky + left 0 + +.tab-group + margin-bottom 40px + diff --git a/front/src/app/components/admin/admin.component.ts b/front/src/app/components/admin/admin.component.ts new file mode 100644 index 0000000..1579a09 --- /dev/null +++ b/front/src/app/components/admin/admin.component.ts @@ -0,0 +1,525 @@ +import {AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {ScrollService} from '../../services/scroll.service'; +import {LocaleService} from '../../services/locale.service'; +import {MatPaginator, MatPaginatorIntl, PageEvent} from '@angular/material/paginator'; +import {CollectionViewer, DataSource, ListRange, SelectionModel} from '@angular/cdk/collections'; +import {InviteCode, User} from '../../types'; +import {combineLatest, merge, Observable, Subject, Subscriber, Subscription} from 'rxjs'; +import {ApiService} from '../../services/api.service'; +import {map, takeUntil} from 'rxjs/operators'; +import {MessageService} from '../../services/message.service'; +import {ChangeAvatarComponent} from '../../dialogs/change-avatar/change-avatar.component'; +import {MatDialog} from '@angular/material/dialog'; +import {ConfirmationComponent} from '../../dialogs/confirmation/confirmation.component'; +import {stringError} from '../../utils'; +import {SinglePromptComponent} from '../../dialogs/single-prompt/single-prompt.component'; +import {ChoiceComponent} from '../../dialogs/choice/choice.component'; +import {AddUserComponent} from '../../dialogs/add-user/add-user.component'; +import {AddCodeComponent} from '../../dialogs/add-code/add-code.component'; +import {TitleService} from '../../services/title.service'; + +@Component({ + selector: 'app-admin', + templateUrl: './admin.component.html', + styleUrls: ['./admin.component.styl'] +}) +export class AdminComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('container', {static: true}) container: ElementRef; + @ViewChild('userPaginator') userPaginator: MatPaginator; + @ViewChild('codePaginator') codePaginator: MatPaginator; + + readonly destroy$: Subject; + + userFilter = ''; + userDataSource: UserDataSource; + userSelection = new SelectionModel(true, []); + userUpdating = false; + + userColumns: string[] = ['select', 'user', 'privileged', 'frozen', 'operation']; + + avatarChangedUsers: Set; + avatarRefreshParam = ''; + + codeDataSource: CodeDataSource; + codeSelection = new SelectionModel(true, []); + codeUpdating = false; + + codeColumns: string[] = ['select', 'code', 'times', 'operation']; + + getRefresh(id: string): string { + if (this.avatarChangedUsers.has(id)) { + return '?refresh=' + this.avatarRefreshParam; + } else { + return ''; + } + } + + constructor(private scroll: ScrollService, + public locale: LocaleService, + private api: ApiService, + private msg: MessageService, + private dialog: MatDialog, + private title: TitleService) { + this.destroy$ = new Subject(); + } + + userIsAllSelected(): boolean { + const numSelected = this.userSelection.selected.length; + const numRows = this.userDataSource.data.length; + return numRows && numSelected === numRows; + } + + userMasterToggle(): void { + this.userIsAllSelected() ? + this.userSelection.clear() : + this.userDataSource.data.forEach(row => this.userSelection.select(row)); + } + + userApplyFilter(): void { + this.userDataSource.keyword = this.userFilter; + this.userSelection.clear(); + } + + userSetAvatar(user: User): void { + this.dialog.open(ChangeAvatarComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + data: user.id, + maxHeight: '80vh', + }).afterClosed().subscribe(result => { + if (result) { + this.avatarChangedUsers.add(user.id); + } + }); + } + + userSetPermission(user: User, privileged: boolean, frozen: boolean): void { + this.userUpdating = true; + + this.api.SetUserPermission({ + user_id: user.id, + privileged: privileged ? !user.privileged : user.privileged, + frozen: frozen ? !user.frozen : user.frozen, + }).subscribe({ + next: _ => { + if (privileged) { + this.msg.SendMessage(this.locale.dict.admin_user_privileged_set); + } + + if (frozen) { + this.msg.SendMessage(this.locale.dict.admin_user_frozen_set); + } + + this.userDataSource.reload(); + this.userUpdating = false; + }, + error: err => { + if (privileged) { + this.msg.SendMessage(this.locale.dict.admin_user_privileged_failed.replace( + '%1', this.locale.dict[err] || this.locale.dict[9999])); + } + + if (frozen) { + this.msg.SendMessage(this.locale.dict.admin_user_frozen_failed.replace( + '%1', this.locale.dict[err] || this.locale.dict[9999])); + } + + this.userUpdating = false; + } + }); + } + + userSetPassword(user: User): void { + this.dialog.open(SinglePromptComponent, { + data: { + type: 'set_password', + input: 'text' + } + }).afterClosed().subscribe(password => { + if (!password) { + return; + } + + this.api.SetPassword({ + user_id: user.id, + password + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.admin_user_password_ok); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.admin_user_password_failed.replace('%1', stringError.call(this, err))); + } + }); + }); + } + + userDelete(user: User | User[], multiple: boolean): void { + this.dialog.open(ChoiceComponent, { + data: { + type: multiple ? 'delete_users' : 'delete_user', + severe: true, + } + }).afterClosed().subscribe(adopt => { + this.dialog.open(ConfirmationComponent, { + data: { + type: multiple ? 'delete_users' : 'delete_user', + severe: true, + } + }).afterClosed().subscribe(decision => { + if (!decision) { + return; + } + + this.userUpdating = true; + this.api.RemoveUser({ + users: multiple ? (user as User[]).map(u => u.id) : [(user as User).id], + cascade: !adopt + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict[multiple ? 'admin_user_delete_selected_ok' : 'admin_user_delete_ok']); + this.userSelection.clear(); + this.userDataSource.reload(); + this.userUpdating = false; + }, + error: err => { + this.msg.SendMessage(this.locale.dict[multiple ? 'admin_user_delete_selected_failed' : 'admin_user_delete_failed'] + .replace('%1', stringError.call(this, err))); + this.userSelection.clear(); + this.userDataSource.reload(); + this.userUpdating = false; + } + }); + }); + }); + } + + userAdd(): void { + this.dialog.open(AddUserComponent).afterClosed().subscribe(result => { + if (result) { + this.userDataSource.reload(); + this.userSelection.clear(); + } + }); + } + + codeIsAllSelected(): boolean { + const numSelected = this.codeSelection.selected.length; + const numRows = this.codeDataSource.data.length; + return numRows && numSelected === numRows; + } + + codeMasterToggle(): void { + this.codeIsAllSelected() ? + this.codeSelection.clear() : + this.codeDataSource.data.forEach(row => this.codeSelection.select(row)); + } + + codeAdd(): void { + this.dialog.open(AddCodeComponent).afterClosed().subscribe(result => { + if (result) { + this.codeDataSource.reload(); + this.codeSelection.clear(); + } + }); + } + + codeDelete(code: InviteCode): void { + this.dialog.open(ConfirmationComponent, { + data: { + type: 'delete_code', + severe: true, + } + }).afterClosed().subscribe(decision => { + if (!decision) { + return; + } + + this.codeUpdating = true; + this.api.RemoveInvite(code.code).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.admin_code_delete_ok); + this.codeSelection.clear(); + this.codeDataSource.reload(); + this.codeUpdating = false; + }, + error: err => { + this.msg.SendMessage(this.locale.dict.admin_code_delete_failed + .replace('%1', stringError.call(this, err))); + this.codeSelection.clear(); + this.codeDataSource.reload(); + this.codeUpdating = false; + } + }); + }); + } + + codeSetTimes(code: InviteCode): void { + this.dialog.open(SinglePromptComponent, { + data: { + type: 'code_times', + input: 'number' + } + }).afterClosed().subscribe(times => { + if (!times) { + return; + } + + this.codeUpdating = true; + + this.api.SetInviteTimes({ + code: code.code, + times: Number(times) + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.admin_times_set_ok); + this.codeUpdating = false; + this.codeDataSource.reload(); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.admin_times_set_failed.replace('%1', stringError.call(this, err))); + this.codeUpdating = false; + this.codeDataSource.reload(); + } + }); + }); + } + + ngOnInit(): void { + this.avatarChangedUsers = new Set(); + this.userDataSource = new UserDataSource(this.api, this.locale, this.msg); + this.codeDataSource = new CodeDataSource(this.api, this.locale, this.msg); + this.avatarRefreshParam = new Date().getTime().toString(10); + this.locale.dict$.pipe(takeUntil(this.destroy$)).subscribe(dict => { + this.title.firstPart = dict.title_admin; + }); + } + + ngAfterViewInit(): void { + this.userDataSource.paginator = this.userPaginator; + this.codeDataSource.paginator = this.codePaginator; + this.scroll.WatchOn(this.container.nativeElement); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.scroll.UnwatchOn(this.container.nativeElement); + } + +} + +export class UserDataSource implements DataSource { + forceChangeS: Subscriber; + paginatorChangeS: Subscriber; + data: User[]; + dataStreamS: Subscriber>; + changeSubscription: Subscription; + paginatorSubscription: Subscription; + // tslint:disable-next-line:variable-name + _keyword = ''; + // tslint:disable-next-line:variable-name + _paginator: MatPaginator; + + get paginator(): MatPaginator { + return this._paginator; + } + + set paginator(paginator: MatPaginator) { + this._paginator = paginator; + this.paginatorSubscription?.unsubscribe(); + this.paginatorSubscription = paginator.page.subscribe(e => this.paginatorChangeS?.next(e)); + this.forceChangeS?.next(); + } + + get keyword(): string { + return this._keyword; + } + + set keyword(keyword: string) { + this._keyword = keyword; + this.reload(); + } + + reload(): void { + this.paginator.pageIndex = 0; + this.forceChangeS?.next(); + } + + fetch(range: ListRange): void { + this.api.ListUser({ + keyword: this.keyword, + offset: range.start, + limit: range.end > 100 ? 10 : range.end + }).subscribe({ + next: value => { + this.data = value.users; + this.dataStreamS.next(value.users); + if (this.paginator) { + this.paginator.length = value.total; + } + }, + error: err => { + this.msg.SendMessage(this.locale.dict.admin_user_fetch_failed.replace( + '%1', this.locale.dict[err] || this.locale.dict[9999])); + this.dataStreamS.next([]); + if (this.paginator) { + this.paginator.length = 0; + this.paginator.pageIndex = 0; + } + } + }); + } + + connect(collectionViewer: CollectionViewer): Observable> { + this.changeSubscription = combineLatest([ + merge( + collectionViewer.viewChange, + new Observable(s => this.paginatorChangeS = s).pipe( + map(e => ({start: e.pageIndex * e.pageSize, end: (e.pageIndex + 1) * e.pageSize} as ListRange)) + ) + ), + new Observable(s => this.forceChangeS = s) + ]).subscribe(([range, _]) => this.fetch(range)); + return new Observable(s => this.dataStreamS = s); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.changeSubscription.unsubscribe(); + } + + constructor(private api: ApiService, + private locale: LocaleService, + private msg: MessageService) { + + } +} + +export class CodeDataSource implements DataSource { + forceChangeS: Subscriber; + paginatorChangeS: Subscriber; + data: InviteCode[]; + dataStreamS: Subscriber>; + changeSubscription: Subscription; + paginatorSubscription: Subscription; + // tslint:disable-next-line:variable-name + _paginator: MatPaginator; + + get paginator(): MatPaginator { + return this._paginator; + } + + set paginator(paginator: MatPaginator) { + this._paginator = paginator; + this.paginatorSubscription?.unsubscribe(); + this.paginatorSubscription = paginator.page.subscribe(e => this.paginatorChangeS?.next(e)); + this.forceChangeS?.next(); + } + + reload(): void { + this.paginator.pageIndex = 0; + this.forceChangeS?.next(); + } + + fetch(range: ListRange): void { + this.api.ListInvite({ + offset: range.start, + limit: range.end > 100 ? 10 : range.end + }).subscribe({ + next: value => { + this.data = value.codes; + this.dataStreamS.next(value.codes); + if (this.paginator) { + this.paginator.length = value.total; + } + }, + error: err => { + this.msg.SendMessage(this.locale.dict.admin_code_fetch_failed.replace( + '%1', this.locale.dict[err] || this.locale.dict[9999])); + this.dataStreamS.next([]); + if (this.paginator) { + this.paginator.length = 0; + this.paginator.pageIndex = 0; + } + } + }); + } + + connect(collectionViewer: CollectionViewer): Observable> { + this.changeSubscription = combineLatest([ + merge( + collectionViewer.viewChange, + new Observable(s => this.paginatorChangeS = s).pipe( + map(e => ({start: e.pageIndex * e.pageSize, end: (e.pageIndex + 1) * e.pageSize} as ListRange)) + ) + ), + new Observable(s => this.forceChangeS = s) + ]).subscribe(([range, _]) => this.fetch(range)); + return new Observable(s => this.dataStreamS = s); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.changeSubscription.unsubscribe(); + } + + constructor(private api: ApiService, + private locale: LocaleService, + private msg: MessageService) { + } +} + +export class LocalePaginatorIntl implements MatPaginatorIntl { + private dict: { [key: string]: string } = {other: 'Loading...'}; + readonly changes = new Subject(); + + get firstPageLabel(): string { + return this.dict.paginator_first_page || this.dict.other || ''; + } + + get itemsPerPageLabel(): string { + return this.dict.paginator_per_page || this.dict.other || ''; + } + + get lastPageLabel(): string { + return this.dict.paginator_last_page || this.dict.other || ''; + } + + get nextPageLabel(): string { + return this.dict.paginator_next_page || this.dict.other || ''; + } + + get previousPageLabel(): string { + return this.dict.paginator_previous_page || this.dict.other || ''; + } + + getRangeLabel(page: number, pageSize: number, length: number): string { + length = Math.max(length, 0); + + if (length === 0) { + return this.dict.paginator_position_none || this.dict.other || ''; + } else if (pageSize === 0) { + return (this.dict.paginator_position_zero && this.dict.paginator_position_zero.replace('%1', String(length))) + || this.dict.other || ''; + } else { + const startIndex = page * pageSize; + const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize; + return (this.dict.paginator_position && + this.dict.paginator_position + .replace('%1', String(startIndex + 1)) + .replace('%2', String(endIndex)) + .replace('%3', String(length))) || this.dict.other || ''; + } + } + + constructor(private locale: LocaleService) { + this.locale.dict$.subscribe(dict => { + this.dict = dict; + this.changes.next(); + }); + } +} + +export const LOCALE_PAGINATOR_INTL_PROVIDER = { + provide: MatPaginatorIntl, + deps: [LocaleService], + useFactory: (locale: LocaleService) => new LocalePaginatorIntl(locale) +}; diff --git a/front/src/app/components/aware-sticky/aware-sticky.component.html b/front/src/app/components/aware-sticky/aware-sticky.component.html new file mode 100644 index 0000000..cb2e6a6 --- /dev/null +++ b/front/src/app/components/aware-sticky/aware-sticky.component.html @@ -0,0 +1,5 @@ +
+ +
+ + diff --git a/front/src/app/components/aware-sticky/aware-sticky.component.spec.ts b/front/src/app/components/aware-sticky/aware-sticky.component.spec.ts new file mode 100644 index 0000000..4f369b3 --- /dev/null +++ b/front/src/app/components/aware-sticky/aware-sticky.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {AwareStickyComponent} from './aware-sticky.component'; + +describe('AwareStickyComponent', () => { + let component: AwareStickyComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AwareStickyComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AwareStickyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/aware-sticky/aware-sticky.component.styl b/front/src/app/components/aware-sticky/aware-sticky.component.styl new file mode 100644 index 0000000..99b0f07 --- /dev/null +++ b/front/src/app/components/aware-sticky/aware-sticky.component.styl @@ -0,0 +1,19 @@ +*[awareSticky] + position sticky + top var(--sticky-top, 0px) + margin-bottom var(--sticky-bottom, 0px) + +.__aware-sticky-sentinel + position absolute + background transparent + visibility hidden + width 100% + left 0 + + &.__aware-sticky-sentinel-before + top calc(-1 * calc(2px + var(--sticky-top, 0px))) + height 1px + + &.__aware-sticky-sentinel-after + bottom calc(-1 * calc(2px + var(--sticky-bottom, 0px))) + height 1px diff --git a/front/src/app/components/aware-sticky/aware-sticky.component.ts b/front/src/app/components/aware-sticky/aware-sticky.component.ts new file mode 100644 index 0000000..5e93b32 --- /dev/null +++ b/front/src/app/components/aware-sticky/aware-sticky.component.ts @@ -0,0 +1,210 @@ +import { + AfterViewInit, + Component, + ElementRef, + EventEmitter, + HostBinding, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; + +export declare type StickyState = +// the element behaves like a static element + 'static' // before: false, after true + // the element is stuck at top + | 'stuck' // before: true, after true + // the element's bottom has reached the container's bottom, so got pushed up + | 'push-up' // before: true, after false + // the element's size is bigger than the container, so it overflowed like a static element + | 'overflow'; // before: false, after false + +export class StickyStateChangedEvent { + state: StickyState; +} + +@Component({ + // tslint:disable-next-line:component-selector + selector: '*[awareSticky]', + templateUrl: './aware-sticky.component.html', + styleUrls: ['./aware-sticky.component.styl'], + encapsulation: ViewEncapsulation.None +}) +export class AwareStickyComponent implements OnInit, AfterViewInit, OnDestroy { + + @Input() stickyTop: number | string = 0; + @Input() stickyBottom: number | string = 0; + @Input() stickyContainer: HTMLElement | null = null; + + @Input() stickyClassPrefix: string | null = null; + @Input() stickyStaticClass: string | null = null; + @Input() stickyStuckClass: string | null = null; + @Input() stickyPushUpClass: string | null = null; + @Input() stickyOverflowClass: string | null = null; + + private bindClass = false; + private staticClass: string; + private stuckClass: string; + private pushUpClass: string; + private overflowClass: string; + + get bindingClasses(): string { + switch (this.stickyState) { + case 'static': + return this.staticClass || ''; + case 'stuck': + return this.stuckClass || ''; + case 'push-up': + return this.pushUpClass || ''; + case 'overflow': + return this.overflowClass || ''; + } + } + + @Output() stateChanged = new EventEmitter(); + + @HostBinding('style.--sticky-top') get stickyTopRegularized(): string { + return AwareStickyComponent.lengthRegularize(this.stickyTop); + } + + @HostBinding('style.--sticky-bottom') get stickyBottomRegularized(): string { + return AwareStickyComponent.lengthRegularize(this.stickyBottom); + } + + @ViewChild('before', {static: true}) before: ElementRef; + @ViewChild('after', {static: true}) after: ElementRef; + + private beforeStatus: boolean | undefined; + private afterStatus: boolean | undefined; + + get stickyState(): StickyState { + if (this.beforeStatus) { + if (this.afterStatus) { + return 'stuck'; + } else { + return 'push-up'; + } + } else { + if (this.afterStatus) { + return 'static'; + } else { + return 'overflow'; + } + } + } + + private observer: IntersectionObserver; + + private static lengthRegularize(s: string | number): string { + if (!s) { + return '0px'; + } + + const n = Number.parseFloat(s as string); + if (!Number.isNaN(n)) { + return `${n}px`; + } else { + return s as string; + } + } + + private emitState(): void { + if (this.beforeStatus === undefined || this.afterStatus === undefined) { + return; + } + + this.stateChanged.emit({state: this.stickyState}); + } + + constructor(private host: ElementRef) { + } + + ngOnInit(): void { + this.beforeStatus = undefined; + this.afterStatus = undefined; + + if (!this.stickyClassPrefix && + !this.stickyStaticClass && + !this.stickyStuckClass && + !this.stickyPushUpClass && + !this.stickyOverflowClass) { + this.bindClass = false; + } else { + this.bindClass = true; + + if (this.stickyClassPrefix) { + this.staticClass = this.stickyClassPrefix + '__static'; + this.stuckClass = this.stickyClassPrefix + '__stuck'; + this.pushUpClass = this.stickyClassPrefix + '__push-up'; + this.overflowClass = this.stickyClassPrefix + '__overflow'; + } + + if (this.stickyStaticClass) { + this.staticClass = this.stickyStaticClass; + } + + if (this.stickyStuckClass) { + this.stuckClass = this.stickyStuckClass; + } + + if (this.stickyPushUpClass) { + this.pushUpClass = this.stickyPushUpClass; + } + + if (this.stickyOverflowClass) { + this.overflowClass = this.stickyOverflowClass; + } + } + + if (this.bindClass) { + let lastClass = ''; + this.stateChanged.subscribe(_ => { + const newClass = this.bindingClasses; + if (lastClass === newClass) { + return; + } + + if (lastClass) { + this.host.nativeElement.classList.remove(lastClass); + } + + lastClass = newClass; + + if (lastClass) { + this.host.nativeElement.classList.add(lastClass); + } + }); + } + } + + ngAfterViewInit(): void { + this.observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + switch (entry.target.getAttribute('sentinel-type')) { + case 'before': + this.beforeStatus = entry.intersectionRatio !== 0; + this.emitState(); + break; + case 'after': + this.afterStatus = entry.intersectionRatio !== 0; + this.emitState(); + break; + } + }); + }, { + root: this.stickyContainer ? this.stickyContainer : null, + rootMargin: '0px', + threshold: [0, 1] + }); + + this.observer.observe(this.before.nativeElement); + this.observer.observe(this.after.nativeElement); + } + + ngOnDestroy(): void { + this.observer.disconnect(); + } +} diff --git a/front/src/app/components/gallery-image/gallery-image.component.html b/front/src/app/components/gallery-image/gallery-image.component.html new file mode 100644 index 0000000..3c886f5 --- /dev/null +++ b/front/src/app/components/gallery-image/gallery-image.component.html @@ -0,0 +1,33 @@ +
+
+
+
+ broken_image +
+
+ {{'gallery_broken_image' | i18nSelect:locale.dict}} +
+
+
+
+ +
+
+
+
+ +
+
+ {{'gallery_loading' | i18nSelect:locale.dict}} +
+
+
+
+ +
+
diff --git a/front/src/app/components/gallery-image/gallery-image.component.spec.ts b/front/src/app/components/gallery-image/gallery-image.component.spec.ts new file mode 100644 index 0000000..6ef91f6 --- /dev/null +++ b/front/src/app/components/gallery-image/gallery-image.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {GalleryImageComponent} from './gallery-image.component'; + +describe('GalleryImageComponent', () => { + let component: GalleryImageComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GalleryImageComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GalleryImageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/gallery-image/gallery-image.component.styl b/front/src/app/components/gallery-image/gallery-image.component.styl new file mode 100644 index 0000000..f7eab9f --- /dev/null +++ b/front/src/app/components/gallery-image/gallery-image.component.styl @@ -0,0 +1,77 @@ +:host + contain strict + +.image-container + position relative + height 0 + width 100% + padding-top 100% + + .hint, + .image + position absolute + top 0 + left 0 + width 100% + height 100% + background #eee + display flex + justify-content center + align-items center + color #888 + + &.load + background var(--accent-lighter) + + .image + img + width 100% + height 100% + will-change transform + transition transform 0.2s cubic-bezier(0.4, 0.0, 0.2, 1) + + &.selected img + transform scale(0.9) + transform-origin 50% 50% + + .hint-icon-container + text-align center + height 30px + width 30px + margin 0 auto 10px + + .hint-icon + font-size 30px + line-height 30px + height 30px + width 30px + margin 0 + + .hint-message + text-align center + + .select-overlay + position absolute + top 0 + left 0 + width 100% + height 100% + background-image linear-gradient(to bottom, rgba(0, 0, 0, .26), transparent 56px, transparent) + transition opacity 0.2s, background 0.2s + opacity 0 + + &.show, &:hover + opacity 1 + + &.selected + background transparent + + .select-button + transition color 0.2s + + &:not(.mat-accent) + color rgba(100, 100, 100, 0.6) + + &:hover .select-button:not(.mat-accent) + color white + diff --git a/front/src/app/components/gallery-image/gallery-image.component.ts b/front/src/app/components/gallery-image/gallery-image.component.ts new file mode 100644 index 0000000..9f9fdd6 --- /dev/null +++ b/front/src/app/components/gallery-image/gallery-image.component.ts @@ -0,0 +1,95 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {animate, style, transition, trigger} from '@angular/animations'; +import {Image} from '../../types'; + +export class GalleryImageSelectEvent { + selected: boolean; + image: Image; +} + +@Component({ + selector: 'app-gallery-image', + templateUrl: './gallery-image.component.html', + styleUrls: ['./gallery-image.component.styl'], + animations: [ + trigger('loading', [ + transition(':leave', [ + style({opacity: 1}), + animate('0.15s ease-out', + style({opacity: 0})) + ]) + ]) + ] +}) +export class GalleryImageComponent implements OnInit { + + constructor(public locale: LocaleService) { + } + + @Input() image: Image | null; + @Input() size = '100%'; + @Input() selected = false; + + @Input() showSelect = true; + @Input() alwaysShowSelect = false; + + @Output() toggled = new EventEmitter(); + @Output() clicked = new EventEmitter(); + + loaded = false; + error = false; + + disableClick = false; + enableClickTimeoutId: number; + + onload = () => this.loaded = true; + onerror = () => { + this.error = true; + this.loaded = true; + } + + selectClick = (event?: Event) => { + event?.stopPropagation(); + this.toggled.emit({selected: !this.selected, image: this.image}); + } + + longPressStart = () => { + if (this.enableClickTimeoutId) { + clearTimeout(this.enableClickTimeoutId); + this.enableClickTimeoutId = 0; + } + + this.disableClick = true; + } + + longPressEnd = (e?: Event) => { + e.preventDefault(); + if (this.enableClickTimeoutId) { + clearTimeout(this.enableClickTimeoutId); + } + + this.enableClickTimeoutId = setTimeout(() => { + this.disableClick = false; + this.enableClickTimeoutId = 0; + }, 100); + } + + overlayClick = (event?: Event) => { + if (this.disableClick) { + return; + } + + if (this.selected || this.alwaysShowSelect) { + this.selectClick(event); + } else { + this.clicked.emit(this.image); + } + } + + ngOnInit(): void { + this.loaded = false; + this.error = false; + } + +} diff --git a/front/src/app/components/gallery-overlay/gallery-overlay.component.html b/front/src/app/components/gallery-overlay/gallery-overlay.component.html new file mode 100644 index 0000000..ad49b11 --- /dev/null +++ b/front/src/app/components/gallery-overlay/gallery-overlay.component.html @@ -0,0 +1,84 @@ +
+ +
+
+
+ +
+
+ {{'gallery_loading' | i18nSelect:locale.dict}} +
+
+
+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
diff --git a/front/src/app/components/gallery-overlay/gallery-overlay.component.spec.ts b/front/src/app/components/gallery-overlay/gallery-overlay.component.spec.ts new file mode 100644 index 0000000..9e1357e --- /dev/null +++ b/front/src/app/components/gallery-overlay/gallery-overlay.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {GalleryOverlayComponent} from './gallery-overlay.component'; + +describe('GalleryOverlayComponent', () => { + let component: GalleryOverlayComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GalleryOverlayComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GalleryOverlayComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/gallery-overlay/gallery-overlay.component.styl b/front/src/app/components/gallery-overlay/gallery-overlay.component.styl new file mode 100644 index 0000000..ad6269f --- /dev/null +++ b/front/src/app/components/gallery-overlay/gallery-overlay.component.styl @@ -0,0 +1,49 @@ +:host + display flex + transition box-shadow 0.2s + +.image-loading:host + box-shadow none + +.image-wrapper + min-width 200px + min-height 200px + position relative + display flex + + .overlay-image + max-width 90vw + max-height 90vh + + @media (max-width 599px) + max-width 100vw + max-height 100vh + + .hint + position absolute + top 0 + left 0 + width 100% + height 100% + display flex + justify-content center + align-items center + + .hint-message + color #eee + font-size 16px + text-shadow 0.02em 0.02em 0.05em #333333 + margin-top 25px + + .hint-icon-container + text-align center + height 75px + width 75px + margin 0 auto 25px + + .hint-icon + font-size 75px + line-height 75px + height 75px + width 75px + margin 0 diff --git a/front/src/app/components/gallery-overlay/gallery-overlay.component.ts b/front/src/app/components/gallery-overlay/gallery-overlay.component.ts new file mode 100644 index 0000000..c052cee --- /dev/null +++ b/front/src/app/components/gallery-overlay/gallery-overlay.component.ts @@ -0,0 +1,273 @@ +import {Component, Inject, OnDestroy, OnInit} from '@angular/core'; +import {Image} from '../../types'; +import {LocaleService} from '../../services/locale.service'; +import {animate, group, query, stagger, state, style, transition, trigger} from '@angular/animations'; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog'; +import {filter} from 'rxjs/operators'; +import {merge, Observable, Subscriber, Subscription} from 'rxjs'; +import {GenCodeSingleComponent} from '../../dialogs/gen-code-single/gen-code-single.component'; +import {EditImagesComponent, ImagesOriginalData, ImagesOriginalDataResult} from '../../dialogs/edit-images/edit-images.component'; +import {stringError} from '../../utils'; +import {ApiService} from '../../services/api.service'; +import {MessageService} from '../../services/message.service'; +import {ConfirmationComponent} from '../../dialogs/confirmation/confirmation.component'; +import {ImageInfoComponent} from '../../dialogs/image-info/image-info.component'; +import {AuthService} from '../../services/auth.service'; + +// noinspection DuplicatedCode +@Component({ + selector: 'app-gallery-overlay', + templateUrl: './gallery-overlay.component.html', + styleUrls: ['./gallery-overlay.component.styl'], + animations: [ + trigger('panelFrame', [ + state('hide', style( + {opacity: 0, transform: 'translateY(100px)'} + )), + state('show', style( + {opacity: 1, transform: 'none'} + )), + state('closed', style( + {opacity: 0, transform: 'none'} + )), + transition('hide => show', [ + group([ + animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', + style({opacity: 1, transform: 'none'})), + query('.action-button-container', [ + style({opacity: 0, transform: 'translateY(100px)'}), + stagger(30, [ + animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', style({opacity: 1, transform: 'none'})) + ]) + ]) + ]), + ]), + transition('show => hide', [ + group([ + animate('400ms 200ms cubic-bezier(0.35, 0, 0.25, 1)', + style({opacity: 0, transform: 'translateY(100px)'})), + ]), + ]), + transition('show => closed', [ + group([ + animate('200ms cubic-bezier(0.35, 0, 0.25, 1)', + style({opacity: 0, transform: 'translateY(100px)'})), + ]), + ]) + ]), + trigger('loading', [ + transition(':leave', [ + style({opacity: 1}), + animate('0.15s ease-out', + style({opacity: 0})) + ]) + ]) + ] +}) +export class GalleryOverlayComponent implements OnInit, OnDestroy { + + init = true; + loading = true; + imageChanged = false; + imageDeleted = false; + + decreaseTimeouts: Set; + showPanelVote = 0; + closeFired = false; + imageCloseClickedS: Subscriber; + disableClick = false; + enableClickTimeoutId: number; + clickSubscription: Subscription; + + get canEdit(): boolean { + return this.auth?.user?.name === 'admin' || this.auth?.user?.id === this.image.user_id || false; + } + + increaseShowPanel(): void { + this.showPanelVote++; + this.dialogRef.disableClose = true; + } + + decreaseShowPanel(): void { + if (this.showPanelVote > 0) { + this.showPanelVote--; + } + + if (this.showPanelVote === 0) { + this.dialogRef.disableClose = false; + } + } + + waitDecreaseShowPanel(): void { + const timeoutId = setTimeout(() => { + if (this.decreaseTimeouts.has(timeoutId)) { + this.decreaseShowPanel(); + this.decreaseTimeouts.delete(timeoutId); + } + + }, 500); + + this.decreaseTimeouts.add(timeoutId); + } + + hidePanel(): void { + this.decreaseTimeouts.forEach(id => clearTimeout(id)); + this.decreaseTimeouts.clear(); + this.showPanelVote = 0; + this.dialogRef.disableClose = false; + } + + imageLoad(): void { + this.loading = false; + this.dialogRef.removePanelClass('gallery-overlay-loading'); + } + + imageLongPress(event?: Event): void { + if (this.enableClickTimeoutId) { + clearTimeout(this.enableClickTimeoutId); + this.enableClickTimeoutId = 0; + } + + this.disableClick = true; + + event?.stopPropagation(); + this.increaseShowPanel(); + } + + imageLongPressEnd(e?: Event): void { + e?.preventDefault(); + if (this.enableClickTimeoutId) { + clearTimeout(this.enableClickTimeoutId); + } + + this.enableClickTimeoutId = setTimeout(() => { + this.disableClick = false; + this.enableClickTimeoutId = 0; + }, 100); + } + + imageClicked(event?: MouseEvent): void { + if (this.disableClick) { + return; + } + + event?.stopPropagation(); + + this.imageCloseClickedS.next(event); + } + + close(): void { + this.closeFired = true; + setTimeout(() => { + this.dialogRef.close({changed: this.imageChanged, deleted: this.imageDeleted}); + }, 200); + } + + showInfo(): void { + this.dialog.open(ImageInfoComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + data: this.image, + maxHeight: '80vh', + }); + } + + editImage(): void { + const original: ImagesOriginalData = { + tag: this.image.tag, + origins: this.image.origins + }; + + this.dialog.open(EditImagesComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + data: original, + maxHeight: '80vh', + }).afterClosed().subscribe((result?: ImagesOriginalDataResult) => { + if (!result) { + return; + } + + let data: string; + if (result.field === 'tag') { + data = result.tag; + } else { + data = result.origins.join(','); + } + + this.api.SetImageInfo({ + data, + field: result.field, + targets: [this.image.id] + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.edit_images_result_success); + this.imageChanged = true; + this.close(); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.edit_images_result_failed.replace('%1', stringError.call(this, err))); + } + }); + }); + } + + deleteImage(): void { + this.dialog.open(ConfirmationComponent, { + data: { + type: 'delete_this', + severe: true, + } + }).afterClosed().subscribe(decision => { + if (!decision) { + return; + } + + this.api.RemoveImage([this.image.id]).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.delete_images_result_success); + this.imageDeleted = true; + this.close(); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.delete_images_result_failed.replace('%1', stringError.call(this, err))); + } + }); + }); + } + + showCode(): void { + this.dialog.open(GenCodeSingleComponent, { + data: this.image.id + }); + } + + constructor(public locale: LocaleService, + private dialog: MatDialog, + private api: ApiService, + private msg: MessageService, + private auth: AuthService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public image?: Image) { + } + + ngOnInit(): void { + this.decreaseTimeouts = new Set(); + + this.clickSubscription = merge( + this.dialogRef.backdropClick(), + new Observable(s => this.imageCloseClickedS = s) + ).pipe( + filter(() => this.dialogRef.disableClose) + ).subscribe(_ => { + this.hidePanel(); + }); + + this.dialogRef.addPanelClass('gallery-overlay-loading'); + setTimeout(() => { + this.init = false; + }, 1000); + } + + ngOnDestroy(): void { + this.clickSubscription.unsubscribe(); + } +} diff --git a/front/src/app/components/gallery/gallery.component.html b/front/src/app/components/gallery/gallery.component.html new file mode 100644 index 0000000..118049f --- /dev/null +++ b/front/src/app/components/gallery/gallery.component.html @@ -0,0 +1,182 @@ +
+
+

+ +
+
+
+ {{(isSelf ? 'gallery_empty' : 'gallery_empty_other') | i18nSelect:locale.dict}} + +
+
+ +
+
+
+
+ +

{{'gallery_loading' | i18nSelect:locale.dict}}

+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ diff --git a/front/src/app/components/gallery/gallery.component.spec.ts b/front/src/app/components/gallery/gallery.component.spec.ts new file mode 100644 index 0000000..72554a0 --- /dev/null +++ b/front/src/app/components/gallery/gallery.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {GalleryComponent} from './gallery.component'; + +describe('GalleryComponent', () => { + let component: GalleryComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GalleryComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GalleryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/gallery/gallery.component.styl b/front/src/app/components/gallery/gallery.component.styl new file mode 100644 index 0000000..3c074d2 --- /dev/null +++ b/front/src/app/components/gallery/gallery.component.styl @@ -0,0 +1,200 @@ +.page-container + height 100vh + width 90vw + overflow-x hidden + overflow-y auto + padding 0 5vw + + .gallery-filter + display grid + grid-template-columns 1fr 1fr 0.8fr 1.2fr + grid-gap 24px + gap 24px + + @media (max-width 1023px) + grid-template-columns 0.85fr 1.15fr + grid-gap 12px + gap 12px + + @media (max-width 599px) + grid-template-columns 1fr + + .tag-form + grid-column-start 1 + grid-column-end 3 + display flex + justify-content center + align-items center + + @media (max-width 599px) + grid-column-end 2 + + .tag-form-field + width 100% + + .filter-chooser + display flex + justify-content flex-start + align-items center + + .choose-label + margin-right 8px + + .time-chooser + opacity 0 + visibility hidden + transition 0.3s + + &.visible + opacity 1 + visibility visible + + .galleries + width 100% + margin-bottom 80px + + .gallery-wall + margin 25px 0 + + .gallery-header-wrapper + z-index 1 + + .gallery-header + margin -5px -5px -5px -15px + padding 5px + background-color white + border-radius 5px + transition-duration 0.2s + line-height 40px + display inline-flex + align-items center + + &.gallery-header-active .gallery-header + box-shadow 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 1px 8px 0 rgba(0, 0, 0, 0.12) + + .header-text-container + height 40px + margin 0 10px + + .header-text + height 40px + line-height 40px + padding 0 + margin 0 + max-width 90vw + white-space nowrap + overflow hidden + text-overflow ellipsis + transition max-width 0.2s ease-in-out + + &.select-toggle-show + max-width calc(90vw - 120px) + overflow-x auto + text-overflow unset + + @media (pointer: fine) and (hover: hover) + &::-webkit-scrollbar + height 5px + + &.gallery-header-active .header-text-container .header-text + max-width calc(95vw - 81px) + overflow-x auto + text-overflow unset + + &.select-toggle-show + max-width calc(95vw - 201px) + + .select-toggle-container + overflow hidden + + .select-toggle + height 40px + width 120px + + @media (min-width 600px) + .gallery-images + display flex + flex-wrap wrap + + .gallery-image + height 130px + width 130px + margin 0 5px 5px 0 + + @media (min-width 1000px) + .gallery-images .gallery-image + height 160px + width 160px + + @media (max-width 600px) + .gallery-images + display grid + grid-template-columns repeat(4, 1fr) + grid-gap 5px + gap 5px + + .gallery-image + height 100% + width 100% + + @media (max-width 399px) + .gallery-images + grid-template-columns repeat(3, 1fr) + + .loading-indicator + position relative + display flex + margin 20px 20px 40px + justify-content center + + .loading-indicator-sentinel + position absolute + height 186px + width 100% + top -186px + //top -82px !important + visibility hidden + + @media (min-width 600px) + top -222px + height 222px + + @media (min-width 1000px) + height 252px + top -252px + + @media (max-width 600px) + height calc(22.5vw + 83.25px) + top calc(-22.5vw - 83.25px) + + @media (max-width 399px) + height calc(30vw + 83.667px) + top calc(-30vw - 83.667px) + + .indicator-container + display flex + flex-direction column + align-items center + + .loading-indicator-info + margin 20px 0 0 + + .empty-indicator + margin-top 64px + display flex + color #555 + justify-content center + + .empty-button-upload + margin-left 10px + +.action-panel-container-6 + transition visibility 0s 0s + + &.hidden + transition visibility 0s 400ms + visibility hidden + +.info + height 25px + width 100% diff --git a/front/src/app/components/gallery/gallery.component.ts b/front/src/app/components/gallery/gallery.component.ts new file mode 100644 index 0000000..9fe2803 --- /dev/null +++ b/front/src/app/components/gallery/gallery.component.ts @@ -0,0 +1,381 @@ +import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {ScrollService} from '../../services/scroll.service'; +import {FormControl} from '@angular/forms'; +import {LocaleService} from '../../services/locale.service'; +import {BrokerService} from '../../services/broker.service'; +import {combineLatest, Observable, of, Subject, Subscription} from 'rxjs'; +import {ActivatedRoute, Router} from '@angular/router'; +import {AuthService} from '../../services/auth.service'; +import {catchError, first, map, startWith, takeUntil} from 'rxjs/operators'; +import {ApiService} from '../../services/api.service'; +import {MessageService} from '../../services/message.service'; +import {GalleryManagerService, ImageGroup} from '../../services/gallery-manager.service'; +import {PreferenceService} from '../../services/preference.service'; +import {SelectionModel} from '@angular/cdk/collections'; +import {Image} from '../../types'; +import {GalleryImageSelectEvent} from '../gallery-image/gallery-image.component'; +import {animate, group, query, sequence, stagger, state, style, transition, trigger} from '@angular/animations'; +import {MatDialog} from '@angular/material/dialog'; +import {Overlay} from '@angular/cdk/overlay'; +import {GalleryOverlayComponent} from '../gallery-overlay/gallery-overlay.component'; +import {EditImagesComponent, ImagesOriginalData, ImagesOriginalDataResult} from '../../dialogs/edit-images/edit-images.component'; +import {ConfirmationComponent} from '../../dialogs/confirmation/confirmation.component'; +import {stringError} from '../../utils'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import {GenCodeMultiComponent} from '../../dialogs/gen-code-multi/gen-code-multi.component'; +import {TitleService} from '../../services/title.service'; + +@Component({ + selector: 'app-gallery', + templateUrl: './gallery.component.html', + styleUrls: ['./gallery.component.styl'], + animations: [ + trigger('selectToggle', [ + transition(':enter', [ + style({width: '0', opacity: '0'}), + group([ + animate('0.2s ease-in-out', + style({width: '*'})), + animate('0.2s 0.1s ease-in-out', + style({opacity: 1})) + ]) + ]), + transition(':leave', [ + style({width: '*', opacity: '1'}), + sequence([ + animate('0.2s ease-in-out', + style({opacity: 0})), + animate('0.2s ease-in-out', + style({width: '0'})) + ]) + ]) + ]), + trigger('panelFrame', [ + state('hide', style( + {opacity: 0, transform: 'translateY(100px)'} + )), + state('show', style( + {opacity: 1, transform: 'none'} + )), + transition('hide => show', [ + group([ + animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', + style({opacity: 1, transform: 'none'})), + query('.action-button-container', [ + style({opacity: 0, transform: 'translateY(100px)'}), + stagger(30, [ + animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', style({opacity: 1, transform: 'none'})) + ]) + ]) + ]), + ]), + transition('show => hide', [ + animate('400ms 200ms cubic-bezier(0.35, 0, 0.25, 1)', + style({opacity: 0, transform: 'translateY(100px)'})), + ]) + ]), + trigger('imageContainer', [ + transition('* => *', [ + query('.gallery-image:enter', [ + style({opacity: 0, transform: 'translateY(100px)'}), + stagger(30, animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', + style({opacity: 1, transform: 'none'}))) + ], {optional: true}) + ]) + ]), + ] +}) +export class GalleryComponent implements OnInit, OnDestroy { + + constructor(private scroll: ScrollService, + public locale: LocaleService, + public broker: BrokerService, + private route: ActivatedRoute, + private auth: AuthService, + private api: ApiService, + private msg: MessageService, + public gMgr: GalleryManagerService, + public pref: PreferenceService, + private dialog: MatDialog, + private router: Router, + private overlay: Overlay, + private title: TitleService) { + this.destroy$ = new Subject(); + } + + @ViewChild('container', {static: true}) container: ElementRef; + + readonly destroy$: Subject; + + selection = new SelectionModel(true, []); + + userId: string; + isSelf = false; + canEdit = false; + + tagForm = new FormControl(''); + + groupMode: 'tag' | 'upload' = 'tag'; + timeSpan: 'day' | 'week' | 'month' | 'year' = 'day'; + + filteredTags$: Observable; + + reloadSubscription: Subscription; + + toInt = i => Number.parseInt(i, 10); + + forItemCheckBy(index: number, item: Image): string { + return item.id; + } + + spanFormat(dict: { [key: string]: string }): string { + return dict['time_' + this.timeSpan]; + } + + postFormat(s: string, dict: { [key: string]: string }): string { + return dict['time_' + this.timeSpan + '_post'].replace('%1', s); + } + + changeGroup(value: 'tag' | 'upload'): void { + this.gMgr.setGrouper(value); + this.reload(); + } + + changeSpan(value: 'day' | 'week' | 'month' | 'year'): void { + this.gMgr.setSpan(value); + } + + setTagByEnter(): void { + this.setTag(this.tagForm.value); + } + + setTagByAutoComplete(e: MatAutocompleteSelectedEvent): void { + this.setTag(e.option.value); + } + + setTag(value: string): void { + if (String(value) === this.gMgr.useTag) { + return; + } + + if (value) { + this.gMgr.useTag = value; + this.gMgr.onlyTag = true; + } else { + this.gMgr.useTag = ''; + this.gMgr.onlyTag = false; + } + + this.reload(); + } + + toggleImage(event: GalleryImageSelectEvent): void { + if (event.selected) { + this.selection.select(event.image); + } else { + this.selection.deselect(event.image); + } + } + + selectMany(images: Image[]): void { + this.selection.select(...images); + } + + deselectMany(images: Image[]): void { + this.selection.deselect(...images); + } + + selectAll(): void { + this.selection.select(...this.gMgr.images); + } + + reverseSelect(total?: Image[]): void { + const selectedSet = new Set(this.selection.selected); + + if (!total) { + this.selection.clear(); + this.selection.select(...this.gMgr.images.filter(image => !selectedSet.has(image))); + } else { + this.selection.deselect(...total); + this.selection.select(...total.filter(image => !selectedSet.has(image))); + } + + } + + reload(): void { + this.gMgr.reset(); + this.selection.clear(); + } + + editSelect(): void { + if (this.selection.selected.length === 0) { + return; + } + + let original: ImagesOriginalData; + if (this.selection.selected.length === 1) { + const image = this.selection.selected[0]; + original = { + tag: image.tag, + origins: image.origins + }; + } + + this.dialog.open(EditImagesComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + data: original, + maxHeight: '80vh', + }).afterClosed().subscribe((result?: ImagesOriginalDataResult) => { + if (!result) { + return; + } + + let data: string; + if (result.field === 'tag') { + data = result.tag; + } else { + data = result.origins.join(','); + } + + this.api.SetImageInfo({ + data, + field: result.field, + targets: this.selection.selected.map(i => i.id) + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.edit_images_result_success); + this.reload(); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.edit_images_result_failed.replace('%1', stringError.call(this, err))); + } + }); + }); + } + + deleteSelect(): void { + this.dialog.open(ConfirmationComponent, { + data: { + type: 'delete_select', + severe: true, + } + }).afterClosed().subscribe(decision => { + if (!decision) { + return; + } + + this.api.RemoveImage(this.selection.selected.map(i => i.id)).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.delete_images_result_success); + this.reload(); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.delete_images_result_failed.replace('%1', stringError.call(this, err))); + } + }); + }); + } + + openOverlay(image: Image, gallery: ImageGroup): void { + this.dialog.open(GalleryOverlayComponent, { + panelClass: 'gallery-overlay-panel', + data: image, + closeOnNavigation: true, + autoFocus: false, + maxWidth: '100vw', + maxHeight: '100vh', + scrollStrategy: this.overlay.scrollStrategies.block() + }).afterClosed().subscribe(r => { + const result = r as { changed: boolean, deleted: boolean }; + if (!result) { + return; + } + + if (result.deleted) { + this.gMgr.images.splice(this.gMgr.images.indexOf(image), 1); + gallery.images.splice(gallery.images.indexOf(image), 1); + + if (gallery.images.length === 0) { + this.gMgr.groups.splice(this.gMgr.groups.indexOf(gallery), 1); + } + } else if (result.changed) { + this.reload(); + } + }); + } + + loadMore(): void { + if (this.gMgr.all) { + return; + } + + this.gMgr.requireLoading = true; + + if (!this.gMgr.loading) { + this.gMgr.fetchMore(); + } + } + + cancelLoadMore(): void { + this.gMgr.requireLoading = false; + } + + showCode(): void { + this.dialog.open(GenCodeMultiComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + data: this.selection.selected.map(i => i.id), + maxHeight: '80vh', + }); + } + + showUpload(): void { + this.router.navigate([{outlets: {upload: 'show'}}]).then(); + } + + ngOnInit(): void { + this.reloadSubscription = this.gMgr.informReload.subscribe(_ => this.reload()); + this.route.paramMap.subscribe(param => { + this.tagForm.reset(); + this.groupMode = 'tag'; + this.timeSpan = 'day'; + this.gMgr.clean(); + + this.scroll.WatchOn(this.container.nativeElement); + this.userId = param.get('id'); + if (!this.userId) { + this.userId = this.auth.user.id; + this.isSelf = true; + } + + this.locale.dict$.pipe(takeUntil(this.destroy$)).subscribe(dict => { + this.title.firstPart = this.isSelf ? dict.title_gallery_me : dict.title_gallery; + }); + + this.canEdit = this.auth.user?.id === this.userId || this.auth.user?.name === 'admin'; + + this.gMgr.setID(this.userId); + + this.filteredTags$ = combineLatest([this.api.ListImageTags(this.userId).pipe( + catchError(error => { + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.gallery_tag_failed_message.replace('%1', dict[error])); + }); + return of([] as string[]); + }), + ), + this.tagForm.valueChanges.pipe(startWith([undefined])) + ]).pipe(map(([tags, _]) => { + const filterValue = this.tagForm.value || ''; + return tags.filter(tag => tag && tag.toLowerCase().includes(filterValue.toLowerCase())); + })); + }); + } + + ngOnDestroy(): void { + this.scroll.UnwatchOn(this.container.nativeElement); + this.gMgr.reset(); + this.reloadSubscription.unsubscribe(); + this.destroy$.next(); + } + +} diff --git a/front/src/app/components/header/header.component.html b/front/src/app/components/header/header.component.html new file mode 100644 index 0000000..0701cb4 --- /dev/null +++ b/front/src/app/components/header/header.component.html @@ -0,0 +1,51 @@ +
+
+
+ + +
+ + admin_panel_settings + + + + collections + +
+ avatar +
+ person +
+
+ + + + + +
+
+
diff --git a/front/src/app/components/header/header.component.spec.ts b/front/src/app/components/header/header.component.spec.ts new file mode 100644 index 0000000..e39c333 --- /dev/null +++ b/front/src/app/components/header/header.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {HeaderComponent} from './header.component'; + +describe('HeaderComponent', () => { + let component: HeaderComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [HeaderComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/header/header.component.styl b/front/src/app/components/header/header.component.styl new file mode 100644 index 0000000..2f2d91d --- /dev/null +++ b/front/src/app/components/header/header.component.styl @@ -0,0 +1,60 @@ +.header-container + width 100% + position relative + + .stuff-wrapper + position absolute + width calc(100vw - 24px) + right 0 + top 0 + height 40px + padding 12px + background-color var(--primary-lighter) + overflow hidden + + .stuff-container + display flex + flex-direction row + float right + width calc(100vw - 24px) + + .header-avatar + height 40px + width 40px + top 0 + right 0 + flex 0 0 auto + margin-left 24px + + .avatar-image + height 40px + width 40px + border-radius 20px + background #ddd + + .pseudo-avatar-image + color #555 + display flex + justify-content center + align-items center + user-select none + + .header-upload, + .header-gallery, + .header-preference + margin-left 12px + + &[disabled] + color #ccc + + .header-admin, + .header-preference, + .header-gallery, + .header-language, + .header-upload + flex 0 0 auto + color white + + .inflate + flex 1 1 auto + diff --git a/front/src/app/components/header/header.component.ts b/front/src/app/components/header/header.component.ts new file mode 100644 index 0000000..3a1e913 --- /dev/null +++ b/front/src/app/components/header/header.component.ts @@ -0,0 +1,181 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {AuthService} from '../../services/auth.service'; +import {auditTime, map, takeUntil} from 'rxjs/operators'; +import {animate, sequence, state, style, transition, trigger} from '@angular/animations'; +import {LocaleService} from '../../services/locale.service'; +import {Observable, Subscriber} from 'rxjs'; +import {ScrollService} from '../../services/scroll.service'; +import {BrokerService} from '../../services/broker.service'; +import {MatDialog} from '@angular/material/dialog'; +import {ChangePasswordComponent} from '../../dialogs/change-password/change-password.component'; +import {Router} from '@angular/router'; +import {ChangeAvatarComponent} from '../../dialogs/change-avatar/change-avatar.component'; +import {FormatPreferenceComponent} from '../../dialogs/format-preference/format-preference.component'; + +@Component({ + selector: 'app-header', + templateUrl: './header.component.html', + styleUrls: ['./header.component.styl'], + animations: [ + trigger('header', [ + state('minified', style( + {width: '40px', right: '12px', top: '12px', padding: '0', borderRadius: '20px', backgroundColor: 'transparent'} + )), + state('expanded', style( + {width: '*', right: '0', top: '0', borderRadius: '0', padding: '12px', backgroundColor: '*'} + )), + transition('minified => expanded', sequence([ + animate('10ms', style( + {width: '40px', right: '12px', top: '12px', borderRadius: '20px', padding: '0', backgroundColor: '*'} + )), + animate('225ms ease-in-out', style( + {width: '40px', right: '0', top: '0', borderRadius: '5px', padding: '12px', backgroundColor: '*'} + )), + animate('225ms ease-in-out', style( + {width: '*', right: '0', top: '0', borderRadius: '0', padding: '12px', backgroundColor: '*'} + )), + ])), + transition('expanded => minified', sequence([ + animate('225ms ease-in-out', style( + {width: '40px', right: '0', top: '0', borderRadius: '5px', padding: '12px', backgroundColor: '*'} + )), + animate('225ms ease-in-out', style( + {width: '40px', right: '12px', top: '12px', borderRadius: '20px', padding: '0', backgroundColor: '*'} + )), + animate('10ms', style( + {width: '40px', right: '12px', top: '12px', padding: '0', borderRadius: '20px', backgroundColor: 'transparent'} + )), + ])) + ]), + ], +}) +export class HeaderComponent implements OnInit, OnDestroy { + + constructor(public auth: AuthService, + public locale: LocaleService, + private scroll: ScrollService, + private dialog: MatDialog, + private router: Router, + public broker: BrokerService) { + this.destroy$ = new Observable(s => { + this.destroyS = s; + }); + } + + isLogin$ = this.auth.user$.pipe(map(user => user && user.id)); + isAdmin$ = this.auth.user$.pipe(map(user => user && user.name === 'admin')); + isFrozen$ = this.auth.user$.pipe(map(user => user && user.frozen)); + + destroy$: Observable; + destroyS: Subscriber; + + scrollExpand = true; + hoverExpand = false; + subExpand = 0; + lastEvent = 0; + lastScroll: number; + + avatarError = false; + refresh = ''; + + ngOnDestroy(): void { + this.destroyS.next(); + } + + updateData(): void { + this.broker.set('header-status', this.scrollExpand || this.hoverExpand || this.subExpand); + } + + enterHeader(): void { + if (this.lastEvent) { + clearTimeout(this.lastEvent); + this.lastEvent = 0; + } + + this.hoverExpand = true; + this.updateData(); + } + + leaveHeader(): void { + if (this.lastEvent) { + clearTimeout(this.lastEvent); + } + + this.lastEvent = setTimeout(() => { + this.hoverExpand = false; + this.updateData(); + this.lastEvent = 0; + }, 500); + } + + openSub(): void { + this.subExpand++; + this.updateData(); + } + + closeSub(): void { + if (this.subExpand > 0) { + setTimeout(() => { + this.subExpand--; + this.updateData(); + }, 1000); + } + } + + changeAvatar(): void { + this.dialog.open(ChangeAvatarComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + maxHeight: '80vh', + }).afterClosed().subscribe(result => { + if (result) { + this.avatarError = false; + this.refresh = '?refresh=' + new Date().getTime(); + } + }); + } + + changePassword(): void { + this.dialog.open(ChangePasswordComponent, { + panelClass: ['scrollable-inner-y', 'scrollable-inner'], + maxHeight: '80vh', + }); + } + + logout(): void { + this.auth.clear().subscribe(_ => { + this.router.navigateByUrl('/login').then(); + }); + } + + showUpload(): void { + this.router.navigate([{outlets: {upload: 'show'}}]).then(); + } + + openPreference(): void { + this.dialog.open(FormatPreferenceComponent); + } + + ngOnInit(): void { + this.lastScroll = this.scroll.status.offsetH; + + if (this.lastScroll !== 0) { + this.scrollExpand = false; + } + + this.updateData(); + + this.scroll.status$.pipe(auditTime(50), takeUntil(this.destroy$)).subscribe(e => { + if (e.offsetH > this.lastScroll) { + this.scrollExpand = false; + this.updateData(); + } else if (e.offsetH + 150 < this.lastScroll || e.offsetH === 0) { + this.scrollExpand = true; + this.updateData(); + } + + this.lastScroll = e.offsetH; + }); + } + +} + diff --git a/front/src/app/components/info/info.component.html b/front/src/app/components/info/info.component.html new file mode 100644 index 0000000..4ff578f --- /dev/null +++ b/front/src/app/components/info/info.component.html @@ -0,0 +1,6 @@ + diff --git a/front/src/app/components/info/info.component.spec.ts b/front/src/app/components/info/info.component.spec.ts new file mode 100644 index 0000000..3dc2667 --- /dev/null +++ b/front/src/app/components/info/info.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {InfoComponent} from './info.component'; + +describe('InfoComponent', () => { + let component: InfoComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [InfoComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(InfoComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/info/info.component.styl b/front/src/app/components/info/info.component.styl new file mode 100644 index 0000000..f5b4b8c --- /dev/null +++ b/front/src/app/components/info/info.component.styl @@ -0,0 +1,25 @@ +.info-container + display flex + justify-content center + align-items center + height 100% + width 100% + + .info-para + font-size 12px + line-height 12px + margin 0 + + .info-item + color #555 + text-decoration none + transition color 0.3s + + &:hover + color #777 + + &:not(:last-child)::after + content ' | ' + font-family var(--font-mono) + user-select none + color #888 diff --git a/front/src/app/components/info/info.component.ts b/front/src/app/components/info/info.component.ts new file mode 100644 index 0000000..81956d2 --- /dev/null +++ b/front/src/app/components/info/info.component.ts @@ -0,0 +1,17 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; + +@Component({ + selector: 'app-info', + templateUrl: './info.component.html', + styleUrls: ['./info.component.styl'] +}) +export class InfoComponent implements OnInit { + + constructor(public locale: LocaleService) { + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/components/language-picker/language-picker.component.html b/front/src/app/components/language-picker/language-picker.component.html new file mode 100644 index 0000000..72e7f0f --- /dev/null +++ b/front/src/app/components/language-picker/language-picker.component.html @@ -0,0 +1,14 @@ +
+ + + + + +
diff --git a/front/src/app/components/language-picker/language-picker.component.spec.ts b/front/src/app/components/language-picker/language-picker.component.spec.ts new file mode 100644 index 0000000..601490d --- /dev/null +++ b/front/src/app/components/language-picker/language-picker.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {LanguagePickerComponent} from './language-picker.component'; + +describe('LanguagePickerComponent', () => { + let component: LanguagePickerComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [LanguagePickerComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LanguagePickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/language-picker/language-picker.component.styl b/front/src/app/components/language-picker/language-picker.component.styl new file mode 100644 index 0000000..e69de29 diff --git a/front/src/app/components/language-picker/language-picker.component.ts b/front/src/app/components/language-picker/language-picker.component.ts new file mode 100644 index 0000000..39d050a --- /dev/null +++ b/front/src/app/components/language-picker/language-picker.component.ts @@ -0,0 +1,38 @@ +import {AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; +import {AvailableLanguages} from '../../langs'; +import {LocaleService} from '../../services/locale.service'; +import {PreferenceService} from '../../services/preference.service'; +import {MatMenuTrigger} from '@angular/material/menu'; + +@Component({ + selector: 'app-language-picker', + templateUrl: './language-picker.component.html', + styleUrls: ['./language-picker.component.styl'] +}) +export class LanguagePickerComponent implements OnInit, AfterViewInit { + + @ViewChild('menuTrigger') menuTrigger: MatMenuTrigger; + + @Input() iconStyle: 'fab' | 'icon' = 'fab'; + @Output() menuOpened = new EventEmitter(); + @Output() menuClosed = new EventEmitter(); + + languages = Object.entries(AvailableLanguages); + + setLanguage(lang: string): void { + this.pref.set('locale', lang).subscribe(); + } + + constructor(private pref: PreferenceService, + public locale: LocaleService) { + } + + ngOnInit(): void { + } + + ngAfterViewInit(): void { + this.menuTrigger.menuOpened.subscribe(i => this.menuOpened.emit(i)); + this.menuTrigger.menuClosed.subscribe(i => this.menuClosed.emit(i)); + } + +} diff --git a/front/src/app/components/login/login.component.html b/front/src/app/components/login/login.component.html new file mode 100644 index 0000000..e9f1c2f --- /dev/null +++ b/front/src/app/components/login/login.component.html @@ -0,0 +1,46 @@ +
+
+ + + +
+
+ +
+
+ +
+
diff --git a/front/src/app/components/login/login.component.spec.ts b/front/src/app/components/login/login.component.spec.ts new file mode 100644 index 0000000..d09dd4b --- /dev/null +++ b/front/src/app/components/login/login.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {LoginComponent} from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [LoginComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/login/login.component.styl b/front/src/app/components/login/login.component.styl new file mode 100644 index 0000000..2059e65 --- /dev/null +++ b/front/src/app/components/login/login.component.styl @@ -0,0 +1,80 @@ +.language-picker + position fixed + left 20px + bottom 20px + +.info + position absolute + height 25px + width 100% + bottom 0 + left 0 + +.page-wrapper + height 100vh + width 100vw + display flex + justify-content center + align-items center + + .page-container + max-height calc(100vh - 50px) + padding 40px 0 + width 386px + max-width 100vw + box-sizing border-box + border-radius 8px + border 1px solid #dadce0 + display flex + flex-direction column + + .site-header + margin 0 40px 20px + + .login-form + flex 0 1 auto + display flex + flex-direction column + overflow hidden + + .form-inputs + flex 0 1 auto + overflow-x hidden + overflow-y auto + + .form-inputs-inner + margin 20px 40px + + .form-field + width 302px + + .login-recaptcha-container + height 78px + margin-bottom 20px + + .operations + flex 0 0 auto + margin 20px 40px 0 + display flex + + .operations-alter, + .operations-main + flex 0 0 auto + + .inflate + flex 1 1 auto + +@media (max-width 599px) + .page-wrapper + align-items unset + + .page-container + width 304px + border none + + .site-header, + .form-inputs .form-inputs-inner, + .operations + margin-left 0 + margin-right 0 + diff --git a/front/src/app/components/login/login.component.ts b/front/src/app/components/login/login.component.ts new file mode 100644 index 0000000..a3ec17a --- /dev/null +++ b/front/src/app/components/login/login.component.ts @@ -0,0 +1,84 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {FormControl, FormGroup, FormGroupDirective, Validators} from '@angular/forms'; +import {AuthService} from '../../services/auth.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {ApiService} from '../../services/api.service'; +import {MessageService} from '../../services/message.service'; +import {first, takeUntil} from 'rxjs/operators'; +import {TitleService} from '../../services/title.service'; +import {Subject} from 'rxjs'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.styl'] +}) +export class LoginComponent implements OnInit, OnDestroy { + + constructor(public locale: LocaleService, + private auth: AuthService, + private api: ApiService, + private msg: MessageService, + private router: Router, + private route: ActivatedRoute, + private title: TitleService) { + this.destroy$ = new Subject(); + } + + loginForm = new FormGroup({ + username: new FormControl('', { + validators: Validators.required, + updateOn: 'blur' + }), + password: new FormControl('', { + validators: Validators.required, + updateOn: 'blur' + }), + recaptcha: new FormControl(null, Validators.required) + }); + + readonly destroy$: Subject; + + login(directive: FormGroupDirective): void { + this.api.Login({ + name: this.loginForm.controls.username.value, + password: this.loginForm.controls.password.value, + recaptcha: this.loginForm.controls.recaptcha.value + }).subscribe({ + next: user => { + this.auth.updateUser(user).subscribe(_ => { + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.login_success_message); + this.router.navigateByUrl(this.auth.getRedirect() || '/gallery/me').then(); + }); + }); + }, + error: e => { + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.login_failed_message.replace('%1', dict[e])); + this.loginForm.reset(); + directive.resetForm(); + }); + } + }); + } + + ngOnInit(): void { + this.route.paramMap.subscribe(_ => { + this.locale.dict$.pipe(takeUntil(this.destroy$)).subscribe(dict => { + this.title.firstPart = dict.title_login; + }); + if (this.auth.user && this.auth.user.id) { + this.router.navigateByUrl('/gallery').then(); + } + + this.loginForm.reset(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + } + +} diff --git a/front/src/app/components/main/main.component.html b/front/src/app/components/main/main.component.html new file mode 100644 index 0000000..cec161a --- /dev/null +++ b/front/src/app/components/main/main.component.html @@ -0,0 +1,28 @@ +
+
+

Chromatic

+

{{'main_subtitle' | i18nSelect:locale.dict}}

+ +
+
+ +
+
+ +
+
diff --git a/front/src/app/components/main/main.component.spec.ts b/front/src/app/components/main/main.component.spec.ts new file mode 100644 index 0000000..ad08052 --- /dev/null +++ b/front/src/app/components/main/main.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {MainComponent} from './main.component'; + +describe('MainComponent', () => { + let component: MainComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [MainComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MainComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/main/main.component.styl b/front/src/app/components/main/main.component.styl new file mode 100644 index 0000000..0641f77 --- /dev/null +++ b/front/src/app/components/main/main.component.styl @@ -0,0 +1,55 @@ +.page-wrapper + height 100vh + width 100vw + display flex + justify-content center + align-items center + background linear-gradient(230deg, #4b79cf, #29b6f6, #4db6ac) + background-size 300% 300% + animation main-bg 60s ease infinite + + .page-container + display flex + flex-direction column + + .site-header, + .site-subheader + color white + margin 20px + text-align center + + .operations + margin 100px 20px 20px + color white + display flex + + .operation-container + flex 0 0 50% + display flex + justify-content center + align-items center + + .operation-button + font-size 20px + line-height 48px + width 140px + text-align center + + .language-picker + position fixed + left 20px + bottom 20px + + .image-preference + position fixed + left 96px + bottom 20px + +@keyframes main-bg + 0% + background-position 0 84% + 50% + background-position 100% 16% + 100% + background-position 0 84% + diff --git a/front/src/app/components/main/main.component.ts b/front/src/app/components/main/main.component.ts new file mode 100644 index 0000000..8b5a36d --- /dev/null +++ b/front/src/app/components/main/main.component.ts @@ -0,0 +1,27 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {FormatPreferenceComponent} from '../../dialogs/format-preference/format-preference.component'; +import {MatDialog} from '@angular/material/dialog'; +import {TitleService} from '../../services/title.service'; + +@Component({ + selector: 'app-main', + templateUrl: './main.component.html', + styleUrls: ['./main.component.styl'] +}) +export class MainComponent implements OnInit { + + openPreference(): void { + this.dialog.open(FormatPreferenceComponent); + } + + constructor(public locale: LocaleService, + private dialog: MatDialog, + private title: TitleService) { + } + + ngOnInit(): void { + this.title.firstPart = ''; + } + +} diff --git a/front/src/app/components/not-found/not-found.component.html b/front/src/app/components/not-found/not-found.component.html new file mode 100644 index 0000000..d4f1417 --- /dev/null +++ b/front/src/app/components/not-found/not-found.component.html @@ -0,0 +1,13 @@ +
+
+

{{'not_found_title' | i18nSelect:locale.dict}}

+

{{'not_found_word' | i18nSelect:locale.dict}}

+ +
+
diff --git a/front/src/app/components/not-found/not-found.component.spec.ts b/front/src/app/components/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..a89fcbe --- /dev/null +++ b/front/src/app/components/not-found/not-found.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {NotFoundComponent} from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [NotFoundComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/not-found/not-found.component.styl b/front/src/app/components/not-found/not-found.component.styl new file mode 100644 index 0000000..4f8aa3b --- /dev/null +++ b/front/src/app/components/not-found/not-found.component.styl @@ -0,0 +1,44 @@ +.page-wrapper + height 100vh + width 100vw + display flex + justify-content center + align-items center + background linear-gradient(230deg, #4b79cf, #29b6f6, #4db6ac) + background-size 300% 300% + animation main-bg 60s ease infinite + + .page-container + display flex + flex-direction column + + .site-header, + .site-subheader + color white + margin 20px + text-align center + + .operations + margin 100px 20px 20px + color white + display flex + + .operation-container + flex 0 0 100% + display flex + justify-content center + align-items center + + .operation-button + font-size 20px + line-height 48px + width 180px + text-align center + +@keyframes main-bg + 0% + background-position 0 84% + 50% + background-position 100% 16% + 100% + background-position 0 84% diff --git a/front/src/app/components/not-found/not-found.component.ts b/front/src/app/components/not-found/not-found.component.ts new file mode 100644 index 0000000..f04c2b3 --- /dev/null +++ b/front/src/app/components/not-found/not-found.component.ts @@ -0,0 +1,28 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {ScrollService} from '../../services/scroll.service'; +import {TitleService} from '../../services/title.service'; + +@Component({ + selector: 'app-not-found', + templateUrl: './not-found.component.html', + styleUrls: ['./not-found.component.styl'] +}) +export class NotFoundComponent implements OnInit { + + constructor(public locale: LocaleService, + private scroll: ScrollService, + private title: TitleService) { + } + + ngOnInit(): void { + this.title.firstPart = this.locale.dict.title_not_found; + this.scroll.PseudoStatus({ + height: 1, + width: 1, + offsetH: 1, + offsetW: 0, + }); + } + +} diff --git a/front/src/app/components/plural/plural.component.spec.ts b/front/src/app/components/plural/plural.component.spec.ts new file mode 100644 index 0000000..6670611 --- /dev/null +++ b/front/src/app/components/plural/plural.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {PluralComponent} from './plural.component'; + +describe('PluralComponent', () => { + let component: PluralComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PluralComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PluralComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/plural/plural.component.ts b/front/src/app/components/plural/plural.component.ts new file mode 100644 index 0000000..34f6e2c --- /dev/null +++ b/front/src/app/components/plural/plural.component.ts @@ -0,0 +1,34 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; + +@Component({ + // tslint:disable-next-line:component-selector + selector: '[app-plural]', + template: ` + + + {{(pluralSource + '_0') | i18nSelect:locale.dict}} + + + {{(pluralSource + '_1') | i18nSelect:locale.dict}} + + + {{value}} + {{(pluralSource + '_other') | i18nSelect:locale.dict}} + + + `, +}) +export class PluralComponent implements OnInit { + + // tslint:disable-next-line:no-input-rename + @Input('app-plural') value: number; + @Input() pluralSource: string; + + constructor(public locale: LocaleService) { + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/components/register/register.component.html b/front/src/app/components/register/register.component.html new file mode 100644 index 0000000..dff06f1 --- /dev/null +++ b/front/src/app/components/register/register.component.html @@ -0,0 +1,84 @@ +
+
+ + +
+
+
+ + + + person + + {{'register_error_user_exist' | i18nSelect:locale.dict}} + + + + + + + + {{'register_hint_password' | i18nSelect:locale.dict}} + + + {{'register_error_password_weak' | i18nSelect:locale.dict}} + + + + + + + + + + {{'register_error_password_mismatch' | i18nSelect:locale.dict}} + + + + + + code + +
+ +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
diff --git a/front/src/app/components/register/register.component.spec.ts b/front/src/app/components/register/register.component.spec.ts new file mode 100644 index 0000000..aa82df9 --- /dev/null +++ b/front/src/app/components/register/register.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {RegisterComponent} from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RegisterComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/register/register.component.styl b/front/src/app/components/register/register.component.styl new file mode 100644 index 0000000..cafb799 --- /dev/null +++ b/front/src/app/components/register/register.component.styl @@ -0,0 +1,81 @@ +.language-picker + position fixed + left 20px + bottom 20px + +.info + position absolute + height 25px + width 100% + bottom 0 + left 0 + +.page-wrapper + height 100vh + width 100vw + display flex + justify-content center + align-items center + + .page-container + max-height calc(100vh - 50px) + padding 40px 0 + width 386px + max-width 100vw + box-sizing border-box + border-radius 8px + border 1px solid #dadce0 + display flex + flex-direction column + + .site-header + margin 0 40px 20px + + .register-form + flex 0 1 auto + display flex + flex-direction column + overflow hidden + + .form-inputs + flex 0 1 auto + overflow-x hidden + overflow-y auto + + .form-inputs-inner + margin 20px 40px + + .form-field + width 302px + + .form-field-extend + margin-bottom 1.5em + + .password-visible-toggle + line-height 24px + transform translate(4px,0) + + .register-recaptcha-container + height 78px + margin-bottom 20px + + .operations + flex 0 0 auto + margin 20px 40px 0 + display flex + + .operations-alter, + .operations-main + flex 0 0 auto + + .inflate + flex 1 1 auto + +@media (max-width 599px) + .page-wrapper + align-items unset + + .page-container + width 304px + padding 5vh 0 0 + border none diff --git a/front/src/app/components/register/register.component.ts b/front/src/app/components/register/register.component.ts new file mode 100644 index 0000000..8c819b4 --- /dev/null +++ b/front/src/app/components/register/register.component.ts @@ -0,0 +1,146 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {AbstractControl, FormControl, FormGroup, FormGroupDirective, NgForm, ValidationErrors, Validators} from '@angular/forms'; +import {catchError, map, takeUntil} from 'rxjs/operators'; +import {LocaleService} from '../../services/locale.service'; +import {AuthService} from '../../services/auth.service'; +import {ApiService} from '../../services/api.service'; +import {MessageService} from '../../services/message.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {Observable, of, Subject} from 'rxjs'; +import {ErrorStateMatcher} from '@angular/material/core'; +import {TitleService} from '../../services/title.service'; + +@Component({ + selector: 'app-register', + templateUrl: './register.component.html', + styleUrls: ['./register.component.styl'] +}) +export class RegisterComponent implements OnInit, OnDestroy { + + readonly destroy$: Subject; + + registerForm: FormGroup; + + passwordVisible = false; + passwordErrorStateMatcher = new RepeatedErrorStateMatcher(); + + register(directive: FormGroupDirective): void { + this.api.Register({ + name: this.registerForm.controls.username.value, + password: this.registerForm.controls.password.value, + invite_code: this.registerForm.controls.invite_code.value, + recaptcha: this.registerForm.controls.recaptcha.value + }).subscribe({ + next: user => { + this.auth.updateUser(user).subscribe(_ => { + this.msg.SendMessage(this.locale.dict.login_success_message); + this.router.navigateByUrl('/login').then(); + }); + }, + error: e => { + this.msg.SendMessage(this.locale.dict.login_failed_message.replace('%1', + this.locale.dict[e] || this.locale.dict[9999])); + this.registerForm.reset(); + directive.resetForm(); + } + }); + } + + UserExistValidator = ( + control: AbstractControl + ): Promise | Observable => { + if (!control.value) { + return null; + } + return this.api.UserExist(control.value).pipe( + map(exist => exist ? {userExist: {value: control.value}} : null), + catchError(_ => of(null)) + ); + } + + PasswordStrengthValidator(control: AbstractControl): ValidationErrors | null { + if (!control.value) { + return null; + } + const value = control.value.toString(); + if (value.length < 8) { + return {weakPassword: {value}}; + } + let hasNumber = false; + let hasLowerCase = false; + let hasUpperCase = false; + let i = value.length; + while (i--) { + const ord = value.charCodeAt(i); + if (ord > 96 && ord < 123) { + hasLowerCase = true; + } else if (ord > 64 && ord < 91) { + hasUpperCase = true; + } else if (ord > 47 && ord < 58) { + hasNumber = true; + } + } + return (hasNumber && hasUpperCase && hasLowerCase) ? null : {weakPassword: {value}}; + } + + PasswordIdenticalValidator(control: FormGroup): ValidationErrors | null { + const password = control.value.password; + const passwordConfirm = control.value.passwordConfirm; + return password && passwordConfirm && password === passwordConfirm ? null : {passwordMismatch: true}; + } + + constructor(public locale: LocaleService, + private auth: AuthService, + private api: ApiService, + private msg: MessageService, + private router: Router, + private route: ActivatedRoute, + private title: TitleService) { + this.registerForm = new FormGroup({ + username: new FormControl(null, { + validators: Validators.required, + asyncValidators: this.UserExistValidator, + updateOn: 'blur' + }), + password: new FormControl(null, { + validators: [Validators.required, this.PasswordStrengthValidator], + updateOn: 'blur' + }), + passwordConfirm: new FormControl(null, { + validators: Validators.required, + updateOn: 'blur' + }), + invite_code: new FormControl(null, Validators.required), + recaptcha: new FormControl(null, Validators.required) + }, { + validators: this.PasswordIdenticalValidator, + }); + + this.destroy$ = new Subject(); + } + + ngOnInit(): void { + this.route.paramMap.subscribe(_ => { + this.locale.dict$.pipe(takeUntil(this.destroy$)).subscribe(dict => { + this.title.firstPart = dict.title_register; + }); + + if (this.auth.user && this.auth.user.id) { + this.router.navigateByUrl('/gallery').then(); + } + + this.registerForm.reset(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + } + +} + +export class RepeatedErrorStateMatcher implements ErrorStateMatcher { + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { + return !!(control.value && form.hasError('passwordMismatch')); + } +} diff --git a/front/src/app/components/upload/upload.component.html b/front/src/app/components/upload/upload.component.html new file mode 100644 index 0000000..fae355d --- /dev/null +++ b/front/src/app/components/upload/upload.component.html @@ -0,0 +1,277 @@ +
+
+
+
+
+
+

+ + {{'upload_title_select_files_p1' | i18nSelect:locale.dict}} + + {{'upload_title_select_files_p2' | i18nSelect:locale.dict}} + + + {{'upload_title_uploading_fine_p1' | i18nSelect:locale.dict}} + {{totalProgress | percent:'1.0-1':(pref.get('locale') | async)}} + {{'upload_title_uploading_fine_p2' | i18nSelect:locale.dict}} + + {{'upload_title_uploading_fine_p3' | i18nSelect:locale.dict}} + + + {{'upload_title_uploading_some_error_p1' | i18nSelect:locale.dict}} + {{totalProgress | percent:'1.0-1':(pref.get('locale') | async)}} + {{'upload_title_uploading_some_error_p2' | i18nSelect:locale.dict}} + + {{'upload_title_uploading_some_error_p3' | i18nSelect:locale.dict}} + + {{'upload_title_uploading_some_error_p4' | i18nSelect:locale.dict}} + + + {{'upload_title_uploaded_fine' | i18nSelect:locale.dict}} + + + {{'upload_title_uploaded_some_error_p1' | i18nSelect:locale.dict}} + + {{'upload_title_uploaded_some_error_p2' | i18nSelect:locale.dict}} + + {{'upload_title_uploaded_some_error_p3' | i18nSelect:locale.dict}} + + + {{'upload_title_idle' | i18nSelect:locale.dict}} + +

+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ + {{'upload_preview_limit_height' | i18nSelect:locale.dict}} + + {{'upload_preview_type_header' | i18nSelect:locale.dict}} + + {{'upload_preview_type_grid' | i18nSelect:locale.dict}} + {{'upload_preview_type_list' | i18nSelect:locale.dict}} + +
+

{{'upload_preview_title' | i18nSelect:locale.dict}}

+
+
+
+ + +
+
+
+ +
+
+
+
+
+ + +
+
+ check_circle +
+
+ error +
+
+
+
+ + + {{file?.name || ('upload_file_name_unknown' | i18nSelect:locale.dict)}} + + · + + {{(file?.size || 0) | fileSize:'number' | number:'1.0-2':(pref.get('locale') | async)}} + {{(file?.size || 0) | fileSize:'unit'}} + + · + + {{(file?.type || 'image/' + ('upload_file_type_unknown' | i18nSelect:locale.dict)) | slice:6 | uppercase}} + + +
+
+
+

{{'upload_info_form_title' | i18nSelect:locale.dict}}

+
+
+ + {{'upload_info_tag_header' | i18nSelect:locale.dict}} + +
+
+ + help + +
+
+ + + + + + + + {{option || ('gallery_tag_empty' | i18nSelect:locale.dict)}} + + + {{'upload_info_tag_header_hint' | i18nSelect:locale.dict}} + + + +
+
+ + {{'upload_info_allow_origins' | i18nSelect:locale.dict}} + +
+
+ + help + +
+
+ + + + + + + {{domain}} + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + cancel + + + + + + + {{domain.origin}} + + + + {{'upload_info_allow_origins_exact' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_wildcard' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_bad' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_exist' |i18nSelect:locale.dict}} + + + + + +
+
+
+ + +
+
+
+
+ {{'upload_drop_file_pre' | i18nSelect:locale.dict}} +
+ + +
+ {{'upload_drop_file_post' | i18nSelect:locale.dict}} +
+ +
+
+
+ diff --git a/front/src/app/components/upload/upload.component.spec.ts b/front/src/app/components/upload/upload.component.spec.ts new file mode 100644 index 0000000..9e8243d --- /dev/null +++ b/front/src/app/components/upload/upload.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {UploadComponent} from './upload.component'; + +describe('UploadComponent', () => { + let component: UploadComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [UploadComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UploadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/components/upload/upload.component.styl b/front/src/app/components/upload/upload.component.styl new file mode 100644 index 0000000..514149d --- /dev/null +++ b/front/src/app/components/upload/upload.component.styl @@ -0,0 +1,404 @@ +:host + display block !important + +.upload-container + width 50vw + max-width 750px + height 450px + max-height calc(100vh - 128px) + position absolute + bottom 0 + right 0 + z-index 10 + background-color white + + .upload-header + width 100% + height 56px + background-color var(--primary-lighter) + position relative + transition background-color 0.3s + + .header-content-wrapper + display flex + + .flex-element + height 56px + overflow hidden + + .header-text + flex 1 100 auto + + .header-text-inner + width calc(50vw - 112px) + max-width 638px + overflow hidden + text-overflow ellipsis + box-sizing border-box + padding 8px + line-height 40px + color white + height 56px + + .header-h2 + margin 0 0 0 12px + text-overflow ellipsis + line-height 40px + overflow hidden + white-space nowrap + + .header-min-max + flex 0 0 auto + + .header-close + flex 0 0.01 auto + + .header-button-inner + height 56px + width 56px + display flex + justify-content center + align-items center + + .header-button + color white + + .header-progress-indicator + position absolute + z-index 0 + top 0 + left 0 + height 56px + transition width 0.1s linear + + &.show-progress + background-color rgba(255, 255, 255, 0.3) + + &.succeed + background-color var(--status-green) + + &.has-error + background-color var(--accent-default) + + &.errored + background-color var(--warn-default) + + @media (max-width 799px) + width 66.7vw + + .upload-header .header-content-wrapper .header-text .header-text-inner + width calc(66.7vw - 112px) + max-width 66.7vw + + @media (max-width 599px) + width 100vw + + .upload-header .header-content-wrapper .header-text .header-text-inner + width calc(100vw - 112px) + max-width 100vw + + .upload-content + width 100% + height calc(100% - 56px) + overflow hidden + position relative + + .upload-content-wrapper + width 50vw + max-width 750px + height 394px + max-height calc(100vh - 184px) + overflow-x hidden + overflow-y auto + + #upload-drop-file-area + width 50vw + max-width 750px + height 394px + max-height calc(100vh - 184px) + top 0 + left 0 + position absolute + display flex + justify-content center + align-items center + background #eee + z-index 1 + + &::before + position absolute + top 0 + left 0 + font-size 24px + content attr(file-drop-hint) " " + width 100% + height 100% + box-sizing border-box + background white + z-index 2 + border 10px dashed #ccc + display flex + justify-content center + align-items center + opacity 0 + visibility hidden + transition 0.3s + + &.drag-over::before + opacity 1 + visibility visible + + .hint-wrapper + display flex + line-height 36px + font-weight 600 + + #file-picker + margin 0 5px + + #real-file-picker + visibility hidden + height 0 + width 0 + + .advanced-button + position absolute + top 10px + right 10px + + .preview-mode-toggle + display flex + justify-content flex-end + align-items center + margin 20px + + .choose-label + margin-right 10px + + .preview-switch-height + margin-right 5px + + .upload-title + margin-left 20px + + .upload-files + margin 0 20px 20px + width calc(100% - 40px) + + &.grid + display grid + grid-template-columns repeat(4, 1fr) + grid-gap 10px + gap 10px + + .upload-file + width 100% + transition opacity 0.3s + background-color #eee + + .preview-image-container + position relative + display flex + height 100% + + .preview-image + width 100% + object-fit contain + + .remove-button, + .result-hint + position absolute + right 0 + top 0 + + &.limit-height .upload-file + height 0 + padding-top 100% + position relative + + .preview-image-container + position absolute + width 100% + top 0 + left 0 + + .preview-image + height 100% + object-fit contain + + .ghost-upload-file + opacity 0.5 + + &.list + .upload-file + width 100% + transition opacity 0.3s + height 75px + display flex + padding 8px 0 + + &:not(:last-child) + border-bottom 1px solid #ddd + + .preview-image-container + position relative + flex 0 0 75px + display flex + height 100% + + .preview-image + width 100% + object-fit contain + + .remove-button, + .result-hint + position absolute + right -10px + top -10px + + .preview-info-container + flex 1 1 auto + display flex + justify-content flex-start + align-items center + margin-left 10px + + .preview-info + flex 1 1 auto + display inline-flex + + .preview-filename + flex 0 1 auto + overflow hidden + white-space nowrap + text-overflow ellipsis + word-break break-all + max-width calc(100% - 160px) + + .preview-size + flex 0 0 auto + color var(--primary-darker) + + .preview-type + flex 0 0 auto + color var(--accent-darker) + + .ghost-upload-file + opacity 0.5 + + .preview-image-container + &::after + content "" + position absolute + top 0 + bottom 0 + right 0 + transition width 0.1s + + &.uploading::after + width calc(100% - var(--upload-status)) + background rgba(0,0,0,0.3) + + &.errored::after + width 100% + background rgba(255,0,0,0.3) + + .loading-hint + position absolute + top 0 + left 0 + width 100% + height 100% + background #eee + display flex + justify-content center + align-items center + color #888 + opacity 1 + visibility visible + transition opacity 0.3s, visibility 0.3s + + &.loaded + opacity 0 + visibility hidden + + .hint-icon-container + text-align center + height 30px + width 30px + margin 0 auto 10px + + .hint-icon + font-size 30px + line-height 30px + height 30px + width 30px + margin 0 + + .remove-button + z-index 2 + + .result-hint + width 40px + height 40px + display flex + justify-content center + align-items center + z-index 1 + + .done + color var(--status-green) + + .error + color var(--warn-default) + + #upload-settings-area + display grid + grid-template-columns auto auto 1fr + grid-gap 12px + gap 12px + margin 0 20px 20px + + .field-header + .field-hint + display flex + align-items center + + .field-input + display flex + + .form-field + flex 1 1 auto + + .field-tag-input + .special-domain-hint, + .origin-select-hint + font-weight 300 + color #777 + margin-left 5px + + .final-operations + margin 0 20px 40px + display flex + justify-content flex-end + + button:not(:last-child) + margin-right 20px + + @media (max-width 799px) + width 66.7vw + + .upload-content + .upload-content-wrapper + #upload-drop-file-area + width 66.7vw + max-width 66.7vw + + @media (max-width 599px) + width 100vw + + .upload-content + .upload-content-wrapper + #upload-drop-file-area + width 100vw + max-width 100vw diff --git a/front/src/app/components/upload/upload.component.ts b/front/src/app/components/upload/upload.component.ts new file mode 100644 index 0000000..4c910bf --- /dev/null +++ b/front/src/app/components/upload/upload.component.ts @@ -0,0 +1,504 @@ +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {EnvironmentService} from '../../services/environment.service'; +import {animate, sequence, state, style, transition, trigger} from '@angular/animations'; +import {FormControl, FormGroup} from '@angular/forms'; +import {combineLatest, merge, Observable, of, Subscriber} from 'rxjs'; +import {PreferenceService} from '../../services/preference.service'; +import {Options} from 'sortablejs'; +import {catchError, first, map, startWith} from 'rxjs/operators'; +import {ApiService, Response} from '../../services/api.service'; +import {AuthService} from '../../services/auth.service'; +import {MessageService} from '../../services/message.service'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {COMMA, ENTER} from '@angular/cdk/keycodes'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import {HttpEventType, HttpProgressEvent, HttpResponse} from '@angular/common/http'; +import {EOk} from '../../errors'; +import {ActivatedRoute, Router} from '@angular/router'; +import {GalleryManagerService} from '../../services/gallery-manager.service'; +import {generateOriginHints} from '../../utils'; +import {MatDialog} from '@angular/material/dialog'; +import {UploadAdvancedComponent} from '../../dialogs/upload-advanced/upload-advanced.component'; +import {TitleService} from '../../services/title.service'; + +interface Progress { + progress: number; + error: string; + done: boolean; +} + +@Component({ + selector: 'app-upload', + templateUrl: './upload.component.html', + styleUrls: ['./upload.component.styl'], + animations: [ + trigger('panel', [ + state('expand', style( + {bottom: '0', width: '*', height: '*'} + )), + state('mini', style( + {bottom: '0', width: '*', height: '56px'} + )), + state('pico', style( + {bottom: '60px', width: '56px', height: '56px'} + )), + transition('expand => mini', + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({height: '56px'}))), + transition('mini => expand', + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({height: '*'}))), + transition('expand => pico', sequence([ + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({height: '116px'})), + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({bottom: '60px', height: '56px'})), + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({width: '56px'})), + ])), + transition('pico => expand', sequence([ + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({width: '*'})), + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({bottom: '0', height: '*'})), + ])), + transition('pico => mini', + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({width: '*', bottom: '0'}))), + transition('mini => pico', + animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)', + style({width: '56px', bottom: '60px'}))), + ]), + trigger('fade', [ + transition(':leave', [ + style({opacity: 1}), + animate('0.2s ease', + style({opacity: 0})) + ]), + transition(':enter', [ + style({opacity: 0}), + animate('0.2s ease', + style({opacity: 1})) + ]) + ]) + ] +}) +export class UploadComponent implements OnInit { + + constructor(public locale: LocaleService, + public env: EnvironmentService, + public pref: PreferenceService, + private api: ApiService, + private auth: AuthService, + private msg: MessageService, + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private gMgr: GalleryManagerService, + private title: TitleService) { + + } + + private readonly AcceptedFileTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/avif']); + + @ViewChild('filePicker') filePicker: ElementRef; + @ViewChild('originsInput') originsInput: ElementRef; + + showAdvanced = false; + + expand = true; + dragArea = true; + dragOver = false; + dragTimeoutId = 0; + + files: File[] = []; + + detail = false; + limitHeight = true; + + uploading = false; + uploaded = true; + totalUploaded: number; + totalSize: number; + + get totalProgress(): number { + const p = this.totalUploaded / this.totalSize; + return p > 1 ? 1 : p; + } + + eachStatus: Map; + finishedCount = 0; + errorCount = 0; + + dragOptions: Options = { + group: 'upload-files', + animation: 250, + easing: 'cubic-bezier(0, 0, 0.2, 1)', + delayOnTouchOnly: true, + dragClass: 'dragging-upload-file', + delay: 100, + disabled: false, + ghostClass: 'ghost-upload-file' + }; + + uploadInfoForm = new FormGroup({ + tag: new FormControl(), + }); + + originsControl = new FormControl(); + + originsChangedS: Subscriber; + origins: string[]; + originsSet: Set; + separatorKeysCodes: number[] = [ENTER, COMMA]; + + filteredTags$: Observable; + suggestOrigins$: Observable<{ origin: string, type: string, bad: boolean }[]>; + + get status(): string { + if (this.uploading) { + if (this.errorCount === 0) { + return 'uploading_fine'; + } else { + return 'uploading_some_error'; + } + } else if (this.uploaded) { + if (this.errorCount === 0) { + return 'uploaded_fine'; + } else { + return 'uploaded_some_error'; + } + } else { + if (this.files.length === 0) { + return 'idle'; + } else { + return 'select_files'; + } + } + } + + goAdvanced(): void { + if (!this.showAdvanced) { + return; + } + + this.dialog.open(UploadAdvancedComponent, { + maxHeight: '80vh', + disableClose: true, + }).afterClosed().subscribe(r => { + if (r) { + this.gMgr.informReload.emit(); + } + }); + } + + fileSelectClick(): void { + this.filePicker.nativeElement.click(); + } + + noDefault(e?: Event): void { + e?.preventDefault(); + e?.stopPropagation(); + } + + fileDragEnter(e?: Event): void { + this.noDefault(e); + if (this.dragTimeoutId) { + clearTimeout(this.dragTimeoutId); + this.dragTimeoutId = 0; + } + + this.dragOver = true; + } + + fileDragLeave(e?: Event): void { + + this.noDefault(e); + if (this.dragTimeoutId) { + clearTimeout(this.dragTimeoutId); + } + this.dragTimeoutId = setTimeout(() => { + this.dragOver = false; + }, 100); + } + + submitByDrop(e: DragEvent): void { + this.noDefault(e); + + const files: File[] = []; + + if (e.dataTransfer.items) { + for (const item of e.dataTransfer.items as any as Iterable) { + if (item.kind === 'file' && this.AcceptedFileTypes.has(item.type)) { + files.push(item.getAsFile()); + } + } + } else { + for (const file of e.dataTransfer.files as any as Iterable) { + if (this.AcceptedFileTypes.has(file.type)) { + files.push(file); + } + } + } + + if (files.length > 0) { + this.dragArea = false; + this.files = files; + } else { + this.msg.SendMessage(this.locale.dict.upload_submit_no_valid); + } + + this.dragOver = false; + } + + submitByButton(): void { + const filePicker = this.filePicker.nativeElement; + if (!filePicker || !filePicker.files || filePicker.files.length === 0) { + return; + } + + const files: File[] = []; + + for (const file of filePicker.files as any as Iterable) { + if (this.AcceptedFileTypes.has(file.type)) { + files.push(file); + } + } + + if (files.length > 0) { + this.dragArea = false; + this.files = files; + } else { + this.msg.SendMessage(this.locale.dict.upload_submit_no_valid); + } + } + + hideLoading(el: HTMLElement): void { + el.classList.add('loaded'); + } + + removeFile(file: File): void { + this.files.splice(this.files.indexOf(file), 1); + + if (this.files.length === 0) { + this.dragArea = true; + } + } + + originsInputSubmit(e: MatChipInputEvent): void { + const input = e.input; + + if (input) { + input.value = ''; + } + + this.originsControl.setValue(null); + } + + addDomain(e: MatAutocompleteSelectedEvent): void { + const domain = e.option.value as string; + this.origins.push(domain); + this.originsSet.add(domain); + this.originsInput.nativeElement.value = ''; + this.originsControl.setValue(null); + this.originsChangedS.next(); + } + + removeDomain(domain: string): void { + this.origins.splice(this.origins.indexOf(domain), 1); + this.originsSet.delete(domain); + this.originsChangedS.next(); + } + + reset(): void { + this.origins = ['*']; + this.originsSet = new Set(this.origins); + this.expand = true; + this.dragArea = true; + this.dragOver = false; + this.dragTimeoutId = 0; + this.dragOptions.disabled = false; + + this.files = []; + + this.detail = false; + this.limitHeight = true; + + this.uploadInfoForm.reset(); + this.uploadInfoForm.enable(); + this.originsControl.reset(); + this.originsControl.enable(); + + this.uploading = false; + this.uploaded = false; + this.totalSize = 0; + this.totalUploaded = 0; + this.eachStatus = new Map(); + this.finishedCount = 0; + this.errorCount = 0; + + this.title.secondPart = this.locale.dict.title_upload; + } + + errorFile(file: File, error: string): void { + this.eachStatus.set(file, {progress: -1, error, done: true}); + this.errorCount += 1; + } + + post(): void { + this.uploading = true; + this.dragOptions.disabled = true; + this.uploadInfoForm.disable(); + this.originsControl.disable(); + + const tag = this.uploadInfoForm.controls.tag.value || ''; + const origins = this.origins.join(','); + + this.totalSize = 0; + this.totalUploaded = 0; + for (const file of this.files) { + this.eachStatus.set(file, {progress: 0, error: '', done: false}); + this.totalSize += file.size; + } + + const uploadNth = (index: number) => { + if (index === this.files.length) { + this.uploading = false; + this.uploaded = true; + this.gMgr.informReload.emit(); + if (this.errorCount === 0) { + this.title.secondPart = this.locale.dict.title_uploaded.replace('%1', String(this.finishedCount)); + } else { + this.title.secondPart = this.locale.dict.title_uploaded_error + .replace('%1', String(this.finishedCount)) + .replace('%2', String(this.errorCount)); + } + return; + } + + if (this.errorCount === 0) { + this.title.secondPart = this.locale.dict.title_uploading.replace('%1', String(this.finishedCount)); + } else { + this.title.secondPart = this.locale.dict.title_uploading_error + .replace('%1', String(this.finishedCount)) + .replace('%2', String(this.errorCount)); + } + + const file = this.files[index]; + + const thisSize = file.size; + let lastSize = 0; + const updateStatus = (loaded: number, done: boolean = false) => { + if (!this.eachStatus.get(file).error) { + this.eachStatus.set(file, {progress: loaded / thisSize, error: '', done}); + } + }; + + this.api.UploadSimple(tag, origins, file).subscribe({ + next: event => { + switch (event.type) { + case HttpEventType.UploadProgress: + const progress = event as HttpProgressEvent; + this.totalUploaded += (progress.loaded - lastSize); + lastSize = progress.loaded; + updateStatus(progress.loaded); + return; + case HttpEventType.Response: + const response = event as unknown as HttpResponse; + if (response.status !== 200) { + this.errorFile(file, this.locale.dict.http_error.replace('%1', String(response.status))); + break; + } + + const body = response.body; + + if (!body.status || !body.data) { + this.errorFile(file, this.locale.dict['9999']); + break; + } else if (body.status !== EOk) { + this.errorFile(file, this.locale.dict[body.status] || this.locale.dict['9999']); + break; + } + + // if (!environment.production) { + // console.log(`File #${index} updated successfully, with id ${body.data}`); + // } + + break; + // default: + // if (!environment.production) { + // console.log(`Other event type: ${HttpEventType[event.type]} when posting image #${index}:`, event); + // } + } + + updateStatus(thisSize); + }, + error: _ => { + this.errorFile(file, this.locale.dict['9999']); + // if (!environment.production) { + // console.log(`Failed uploading file #${index} with unexpected error: `, error); + // } + + updateStatus(thisSize); + uploadNth(index + 1); + }, + complete: () => { + // if (!environment.production) { + // console.log(`File #${index} uploaded.`); + // } + + this.finishedCount++; + + updateStatus(thisSize, true); + uploadNth(index + 1); + } + }); + }; + + uploadNth(0); + } + + ngOnInit(): void { + this.route.paramMap.subscribe(__ => { + this.reset(); + + this.auth.user$.subscribe(user => { + if (user.privileged) { + this.showAdvanced = true; + } + this.filteredTags$ = combineLatest([this.api.ListImageTags(user.id).pipe( + catchError(error => { + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.gallery_tag_failed_message.replace('%1', dict[error])); + }); + return of([] as string[]); + }), + ), + this.uploadInfoForm.controls.tag.valueChanges.pipe(startWith([undefined])) + ]).pipe(map(([tags, _]) => { + const filterValue = this.uploadInfoForm.controls.tag.value || ''; + return tags.filter(tag => tag.toLowerCase().includes(filterValue.toLowerCase())); + })); + }); + + this.suggestOrigins$ = merge( + this.originsControl.valueChanges, + new Observable(s => this.originsChangedS = s) + ).pipe( + startWith([undefined]), + map(_ => { + const input = this.originsControl.value || ''; + return generateOriginHints.call(this, input); + })); + }); + + } + + close(): void { + this.title.secondPart = ''; + this.router.navigate([{outlets: {upload: null}}]).then(); + } + +} + diff --git a/front/src/app/dialogs/add-code/add-code.component.html b/front/src/app/dialogs/add-code/add-code.component.html new file mode 100644 index 0000000..60872ce --- /dev/null +++ b/front/src/app/dialogs/add-code/add-code.component.html @@ -0,0 +1,32 @@ +

{{'add_code_title' | i18nSelect:locale.dict}}

+ +
+ + + + + + + + + + + + +
+
+
+ + +
diff --git a/front/src/app/dialogs/add-code/add-code.component.spec.ts b/front/src/app/dialogs/add-code/add-code.component.spec.ts new file mode 100644 index 0000000..ef54ce0 --- /dev/null +++ b/front/src/app/dialogs/add-code/add-code.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {AddCodeComponent} from './add-code.component'; + +describe('AddCodeComponent', () => { + let component: AddCodeComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AddCodeComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddCodeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/add-code/add-code.component.styl b/front/src/app/dialogs/add-code/add-code.component.styl new file mode 100644 index 0000000..08787cf --- /dev/null +++ b/front/src/app/dialogs/add-code/add-code.component.styl @@ -0,0 +1,8 @@ +.dialog-content + max-width calc(80vw - 48px) + width 300px + padding-bottom 10px + display flex + + .form-field + width 100% diff --git a/front/src/app/dialogs/add-code/add-code.component.ts b/front/src/app/dialogs/add-code/add-code.component.ts new file mode 100644 index 0000000..8b7650a --- /dev/null +++ b/front/src/app/dialogs/add-code/add-code.component.ts @@ -0,0 +1,50 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {MessageService} from '../../services/message.service'; +import {ApiService} from '../../services/api.service'; +import {MatDialogRef} from '@angular/material/dialog'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; +import {stringError} from '../../utils'; + +@Component({ + selector: 'app-add-code', + templateUrl: './add-code.component.html', + styleUrls: ['./add-code.component.styl'] +}) +export class AddCodeComponent implements OnInit { + + codeForm = new FormGroup({ + code: new FormControl(null, Validators.required), + times: new FormControl(0, Validators.min(0)) + }); + + addCode(): void { + if (this.codeForm.invalid) { + return; + } + + this.api.AddInvite({ + code: this.codeForm.controls.code.value, + times: this.codeForm.controls.times.value + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.add_code_success_msg.replace('%1', this.codeForm.controls.code.value)); + this.dialogRef.close(true); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.add_code_error_msg.replace('%1', stringError.call(this, err))); + this.dialogRef.close(false); + } + }); + } + + constructor(public locale: LocaleService, + private msg: MessageService, + private api: ApiService, + private dialogRef: MatDialogRef) { + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/add-user/add-user.component.html b/front/src/app/dialogs/add-user/add-user.component.html new file mode 100644 index 0000000..0e43445 --- /dev/null +++ b/front/src/app/dialogs/add-user/add-user.component.html @@ -0,0 +1,40 @@ +

{{'add_user_title' | i18nSelect:locale.dict}}

+ +
+ + + + + + + {{'add_user_error_user_exist' | i18nSelect:locale.dict}} + + + + + + + + +
+ + {{'add_user_privileged' | i18nSelect:locale.dict}} + +
+
+
+
+ + +
diff --git a/front/src/app/dialogs/add-user/add-user.component.spec.ts b/front/src/app/dialogs/add-user/add-user.component.spec.ts new file mode 100644 index 0000000..bbc52c1 --- /dev/null +++ b/front/src/app/dialogs/add-user/add-user.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {AddUserComponent} from './add-user.component'; + +describe('AddUserComponent', () => { + let component: AddUserComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AddUserComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/add-user/add-user.component.styl b/front/src/app/dialogs/add-user/add-user.component.styl new file mode 100644 index 0000000..e8d408f --- /dev/null +++ b/front/src/app/dialogs/add-user/add-user.component.styl @@ -0,0 +1,11 @@ +.dialog-content + max-width calc(80vw - 48px) + width 300px + padding-bottom 10px + display flex + + .form-field + width 100% + + .privileged-toggle + margin 20px 0 diff --git a/front/src/app/dialogs/add-user/add-user.component.ts b/front/src/app/dialogs/add-user/add-user.component.ts new file mode 100644 index 0000000..402e45c --- /dev/null +++ b/front/src/app/dialogs/add-user/add-user.component.ts @@ -0,0 +1,71 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {ApiService} from '../../services/api.service'; +import {AbstractControl, FormControl, FormGroup, ValidationErrors, Validators} from '@angular/forms'; +import {Observable, of} from 'rxjs'; +import {catchError, map} from 'rxjs/operators'; +import {stringError} from '../../utils'; +import {MessageService} from '../../services/message.service'; +import {MatDialogRef} from '@angular/material/dialog'; + +@Component({ + selector: 'app-add-user', + templateUrl: './add-user.component.html', + styleUrls: ['./add-user.component.styl'] +}) +export class AddUserComponent implements OnInit { + + userForm: FormGroup; + + UserExistValidator = ( + control: AbstractControl + ): Promise | Observable => { + if (!control.value) { + return null; + } + return this.api.UserExist(control.value).pipe( + map(exist => exist ? {userExist: {value: control.value}} : null), + catchError(_ => of(null)) + ); + } + + addUser(): void { + if (this.userForm.invalid) { + return; + } + + this.api.AddUser({ + name: this.userForm.controls.username.value, + password: this.userForm.controls.password.value, + privileged: this.userForm.controls.privileged.value + }).subscribe({ + next: _ => { + this.msg.SendMessage(this.locale.dict.add_user_success_msg.replace('%1', this.userForm.controls.username.value)); + this.dialogRef.close(true); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.add_user_error_msg.replace('%1', stringError.call(this, err))); + this.dialogRef.close(false); + } + }); + } + + constructor(public locale: LocaleService, + private msg: MessageService, + private api: ApiService, + private dialogRef: MatDialogRef) { + this.userForm = new FormGroup({ + username: new FormControl(null, { + validators: Validators.required, + asyncValidators: this.UserExistValidator, + updateOn: 'blur' + }), + password: new FormControl(null, Validators.required), + privileged: new FormControl(false), + }); + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/change-avatar/change-avatar.component.html b/front/src/app/dialogs/change-avatar/change-avatar.component.html new file mode 100644 index 0000000..bc7d64e --- /dev/null +++ b/front/src/app/dialogs/change-avatar/change-avatar.component.html @@ -0,0 +1,46 @@ +

{{'change_password_title' | i18nSelect:locale.dict}}

+ +

{{'change_avatar_title_pick_file' | i18nSelect:locale.dict}}

+
+ + +
+

{{'change_avatar_title_crop' | i18nSelect:locale.dict}}

+
+
+ +
+ +
+ {{'change_avatar_preview_no_file' | i18nSelect:locale.dict}} +
+ +
+ {{'change_avatar_preview' | i18nSelect:locale.dict}} + cropped image +
+
+ +
+
+
+ + +
diff --git a/front/src/app/dialogs/change-avatar/change-avatar.component.spec.ts b/front/src/app/dialogs/change-avatar/change-avatar.component.spec.ts new file mode 100644 index 0000000..e21b29b --- /dev/null +++ b/front/src/app/dialogs/change-avatar/change-avatar.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {ChangeAvatarComponent} from './change-avatar.component'; + +describe('ChangeAvatarComponent', () => { + let component: ChangeAvatarComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ChangeAvatarComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChangeAvatarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/change-avatar/change-avatar.component.styl b/front/src/app/dialogs/change-avatar/change-avatar.component.styl new file mode 100644 index 0000000..0f2d663 --- /dev/null +++ b/front/src/app/dialogs/change-avatar/change-avatar.component.styl @@ -0,0 +1,65 @@ +.dialog-content + max-width calc(80vw - 48px) + +.file-picker + position relative + margin-bottom 20px + + #real-file-picker + position absolute + height 0 + width 0 + opacity 0 + visibility hidden + +.note-crop + margin-bottom 0 + +.image-cropper + display flex + padding-bottom 50px + + @media (max-width 599px) + flex-direction column + + .cropper-container + margin-right 20px + width 256px + max-height 256px + + @media (max-width 380px) + width calc(80vw - 48px) + max-height calc(80vw - 48px) + + .cropper + max-width 256px + max-height 256px + margin 20px auto 0 + padding 0 + + @media (max-width 380px) + max-width calc(80vw - 48px) + max-height calc(80vw - 48px) + + .cropped + position relative + width 128px + height 128px + margin-top 20px + + .cropped-image + width 128px + height 128px + + .cropped-overlay + position absolute + top 0 + left 0 + width 128px + height 128px + background radial-gradient(transparent 69%, rgba(0,0,0,0.5) 71%) + + .cropped-hint + position absolute + bottom 128px + left 0 diff --git a/front/src/app/dialogs/change-avatar/change-avatar.component.ts b/front/src/app/dialogs/change-avatar/change-avatar.component.ts new file mode 100644 index 0000000..82d1079 --- /dev/null +++ b/front/src/app/dialogs/change-avatar/change-avatar.component.ts @@ -0,0 +1,93 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {ImageCroppedEvent} from 'ngx-image-cropper'; +import {Observable} from 'rxjs'; +import {MessageService} from '../../services/message.service'; +import {ApiService} from '../../services/api.service'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; + +@Component({ + selector: 'app-change-avatar', + templateUrl: './change-avatar.component.html', + styleUrls: ['./change-avatar.component.styl'] +}) +export class ChangeAvatarComponent implements OnInit { + + imageChangedEvent: Event = null; + imageCroppedEvent: ImageCroppedEvent = null; + cropDirty = false; + croppedImage: any = ''; + uploading = false; + + fileChangeEvent(event: any): void { + this.imageChangedEvent = event; + this.cropperUp(); + } + + imageCropped(event: ImageCroppedEvent): void { + this.imageCroppedEvent = event; + this.cropDirty = true; + } + + // Workaround to make preview refresh correctly + cropperUp(): void { + setTimeout(() => { + if (this.cropDirty) { + this.croppedImage = this.imageCroppedEvent.base64; + this.cropDirty = false; + } + }, 100); + } + + loadImageFailed(): void { + this.msg.SendMessage(this.locale.dict.change_avatar_file_bad); + } + + getCroppedBlob(): Observable { + return new Observable(sub => { + fetch(this.croppedImage).then( + resp => resp.blob() + ).then( + blob => sub.next(blob) + ).catch(err => sub.error(err)); + }); + } + + setAvatar(): void { + if (!this.croppedImage || this.uploading) { + return; + } + + this.uploading = true; + + this.getCroppedBlob().subscribe(f => { + let resp: Observable; + if (!this.id) { + resp = this.api.SetAvatar(f); + } else { + resp = this.api.SetAvatarP(f, this.id); + } + resp.subscribe({ + next: () => { + this.msg.SendMessage(this.locale.dict.change_avatar_success); + this.dialogRef.close(true); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.change_avatar_failed.replace( + '%1', this.locale.dict[err] || this.locale.dict[9999])); + this.dialogRef.close(false); + } + }); + }); + } + + constructor(private msg: MessageService, + public locale: LocaleService, + private api: ApiService, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public readonly id?: string) { + } + + ngOnInit(): void { + } +} diff --git a/front/src/app/dialogs/change-password/change-password.component.html b/front/src/app/dialogs/change-password/change-password.component.html new file mode 100644 index 0000000..2f00b14 --- /dev/null +++ b/front/src/app/dialogs/change-password/change-password.component.html @@ -0,0 +1,57 @@ +

{{'change_password_title' | i18nSelect:locale.dict}}

+
+ +
+ + + + vpn_key + + + + + + + {{'register_hint_password' | i18nSelect:locale.dict}} + + + {{'register_error_password_weak' | i18nSelect:locale.dict}} + + + + + + + + + + {{'register_error_password_mismatch' | i18nSelect:locale.dict}} + + +
+
+
+ + +
+
diff --git a/front/src/app/dialogs/change-password/change-password.component.spec.ts b/front/src/app/dialogs/change-password/change-password.component.spec.ts new file mode 100644 index 0000000..6a26047 --- /dev/null +++ b/front/src/app/dialogs/change-password/change-password.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {ChangePasswordComponent} from './change-password.component'; + +describe('ChangePasswordComponent', () => { + let component: ChangePasswordComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ChangePasswordComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChangePasswordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/change-password/change-password.component.styl b/front/src/app/dialogs/change-password/change-password.component.styl new file mode 100644 index 0000000..dc1b08b --- /dev/null +++ b/front/src/app/dialogs/change-password/change-password.component.styl @@ -0,0 +1,29 @@ +.dialog-content + width 302px + max-width calc(80vw - 48px) + display flex + flex-direction column + + .register-form + flex 0 1 auto + display flex + flex-direction column + overflow hidden + + .form-inputs + flex 0 1 auto + overflow-x hidden + overflow-y auto + + .form-inputs-inner + margin 20px 0 + + .form-field + width 302px + + .form-field-extend + margin-bottom 1.5em + + .password-visible-toggle + line-height 24px + transform translate(4px,0) diff --git a/front/src/app/dialogs/change-password/change-password.component.ts b/front/src/app/dialogs/change-password/change-password.component.ts new file mode 100644 index 0000000..fd81dab --- /dev/null +++ b/front/src/app/dialogs/change-password/change-password.component.ts @@ -0,0 +1,101 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {AbstractControl, FormControl, FormGroup, FormGroupDirective, ValidationErrors, Validators} from '@angular/forms'; +import {RepeatedErrorStateMatcher} from '../../components/register/register.component'; +import {ApiService} from '../../services/api.service'; +import {AuthService} from '../../services/auth.service'; +import {MessageService} from '../../services/message.service'; +import {Router} from '@angular/router'; +import {MatDialogRef} from '@angular/material/dialog'; + +@Component({ + selector: 'app-change-password', + templateUrl: './change-password.component.html', + styleUrls: ['./change-password.component.styl'] +}) +export class ChangePasswordComponent implements OnInit { + + changePasswordForm: FormGroup; + passwordVisible = false; + passwordErrorStateMatcher = new RepeatedErrorStateMatcher(); + + changePassword(directive: FormGroupDirective): void { + this.api.ChangePassword({ + old_password: this.changePasswordForm.value.oldPassword, + new_password: this.changePasswordForm.value.password, + }).subscribe({ + next: _ => { + this.auth.clear().subscribe(() => { + this.msg.SendMessage(this.locale.dict.change_password_succeed); + this.router.navigateByUrl('/login').then(); + this.dialogRef.close(); + }); + }, + error: err => { + this.msg.SendMessage(this.locale.dict.change_password_failed.replace( + '%1', this.locale.dict[err] || this.locale.dict[9999])); + this.changePasswordForm.reset(); + directive.resetForm(); + } + }); + } + + constructor(public locale: LocaleService, + private api: ApiService, + private auth: AuthService, + private router: Router, + private msg: MessageService, + private dialogRef: MatDialogRef) { + } + + PasswordStrengthValidator(control: AbstractControl): ValidationErrors | null { + if (!control.value) { + return null; + } + const value = control.value.toString(); + if (value.length < 8) { + return {weakPassword: {value}}; + } + let hasNumber = false; + let hasLowerCase = false; + let hasUpperCase = false; + let i = value.length; + while (i--) { + const ord = value.charCodeAt(i); + if (ord > 96 && ord < 123) { + hasLowerCase = true; + } else if (ord > 64 && ord < 91) { + hasUpperCase = true; + } else if (ord > 47 && ord < 58) { + hasNumber = true; + } + } + return (hasNumber && hasUpperCase && hasLowerCase) ? null : {weakPassword: {value}}; + } + + PasswordIdenticalValidator(control: FormGroup): ValidationErrors | null { + const password = control.value.password; + const passwordConfirm = control.value.passwordConfirm; + return password && passwordConfirm && password === passwordConfirm ? null : {passwordMismatch: true}; + } + + ngOnInit(): void { + this.changePasswordForm = new FormGroup({ + oldPassword: new FormControl(null, { + validators: [Validators.required], + updateOn: 'blur' + }), + password: new FormControl(null, { + validators: [Validators.required, this.PasswordStrengthValidator], + updateOn: 'blur' + }), + passwordConfirm: new FormControl(null, { + validators: Validators.required, + updateOn: 'blur' + }), + }, { + validators: this.PasswordIdenticalValidator, + }); + } + +} diff --git a/front/src/app/dialogs/choice/choice.component.html b/front/src/app/dialogs/choice/choice.component.html new file mode 100644 index 0000000..75bab8c --- /dev/null +++ b/front/src/app/dialogs/choice/choice.component.html @@ -0,0 +1,18 @@ +

{{(config.type + '_choice_title') | i18nSelect:locale.dict}}

+ +

+ {{(config.type + '_choice_word') | i18nSelect:locale.dict}} +

+
+
+ + + +
diff --git a/front/src/app/dialogs/choice/choice.component.spec.ts b/front/src/app/dialogs/choice/choice.component.spec.ts new file mode 100644 index 0000000..8231956 --- /dev/null +++ b/front/src/app/dialogs/choice/choice.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {ChoiceComponent} from './choice.component'; + +describe('ChoiceComponent', () => { + let component: ChoiceComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ChoiceComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChoiceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/choice/choice.component.styl b/front/src/app/dialogs/choice/choice.component.styl new file mode 100644 index 0000000..0ae7058 --- /dev/null +++ b/front/src/app/dialogs/choice/choice.component.styl @@ -0,0 +1,3 @@ +.dialog-content + max-width calc(80vw - 48px) + width 300px diff --git a/front/src/app/dialogs/choice/choice.component.ts b/front/src/app/dialogs/choice/choice.component.ts new file mode 100644 index 0000000..e6d6f35 --- /dev/null +++ b/front/src/app/dialogs/choice/choice.component.ts @@ -0,0 +1,29 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; + +export interface ChoiceConfig { + type?: string; + different?: boolean; +} + +@Component({ + selector: 'app-choice', + templateUrl: './choice.component.html', + styleUrls: ['./choice.component.styl'] +}) +export class ChoiceComponent implements OnInit { + + constructor(public locale: LocaleService, + @Inject(MAT_DIALOG_DATA) public readonly config?: ChoiceConfig) { + if (!config) { + this.config = {type: 'default', different: false}; + } else if (!config.type) { + this.config.type = 'default'; + } + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/confirmation/confirmation.component.html b/front/src/app/dialogs/confirmation/confirmation.component.html new file mode 100644 index 0000000..18c2d83 --- /dev/null +++ b/front/src/app/dialogs/confirmation/confirmation.component.html @@ -0,0 +1,15 @@ +

{{(config.type + '_confirm_title') | i18nSelect:locale.dict}}

+ +

+ {{(config.type + '_confirm_word') | i18nSelect:locale.dict}} +

+
+
+ + +
diff --git a/front/src/app/dialogs/confirmation/confirmation.component.spec.ts b/front/src/app/dialogs/confirmation/confirmation.component.spec.ts new file mode 100644 index 0000000..d70689d --- /dev/null +++ b/front/src/app/dialogs/confirmation/confirmation.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {ConfirmationComponent} from './confirmation.component'; + +describe('ConfirmationComponent', () => { + let component: ConfirmationComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ConfirmationComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfirmationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/confirmation/confirmation.component.styl b/front/src/app/dialogs/confirmation/confirmation.component.styl new file mode 100644 index 0000000..0ae7058 --- /dev/null +++ b/front/src/app/dialogs/confirmation/confirmation.component.styl @@ -0,0 +1,3 @@ +.dialog-content + max-width calc(80vw - 48px) + width 300px diff --git a/front/src/app/dialogs/confirmation/confirmation.component.ts b/front/src/app/dialogs/confirmation/confirmation.component.ts new file mode 100644 index 0000000..ce28aa3 --- /dev/null +++ b/front/src/app/dialogs/confirmation/confirmation.component.ts @@ -0,0 +1,29 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {LocaleService} from '../../services/locale.service'; + +export interface ConfirmationConfig { + type?: string; + severe?: boolean; +} + +@Component({ + selector: 'app-confirmation', + templateUrl: './confirmation.component.html', + styleUrls: ['./confirmation.component.styl'] +}) +export class ConfirmationComponent implements OnInit { + + constructor(public locale: LocaleService, + @Inject(MAT_DIALOG_DATA) public readonly config?: ConfirmationConfig) { + if (!config) { + this.config = {type: 'default', severe: false}; + } else if (!config.type) { + this.config.type = 'default'; + } + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/edit-images/edit-images.component.html b/front/src/app/dialogs/edit-images/edit-images.component.html new file mode 100644 index 0000000..d792606 --- /dev/null +++ b/front/src/app/dialogs/edit-images/edit-images.component.html @@ -0,0 +1,98 @@ +

{{'edit_images_title' | i18nSelect:locale.dict}}

+ + + +
+

{{'edit_images_title_tag' | i18nSelect:locale.dict}}

+ + + + + + + + {{option || ('gallery_tag_empty' | i18nSelect:locale.dict)}} + + + {{'upload_info_tag_header_hint' | i18nSelect:locale.dict}} + + + +
+
+ +
+

{{'edit_images_title_origins' | i18nSelect:locale.dict}}

+ + + + + + + {{domain}} + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + cancel + + + + + + + {{domain.origin}} + + + + {{'upload_info_allow_origins_exact' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_wildcard' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_bad' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_exist' |i18nSelect:locale.dict}} + + + + + +
+
+
+
+
+ + +
diff --git a/front/src/app/dialogs/edit-images/edit-images.component.spec.ts b/front/src/app/dialogs/edit-images/edit-images.component.spec.ts new file mode 100644 index 0000000..aec2be8 --- /dev/null +++ b/front/src/app/dialogs/edit-images/edit-images.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {EditImagesComponent} from './edit-images.component'; + +describe('EditImagesComponent', () => { + let component: EditImagesComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [EditImagesComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditImagesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/edit-images/edit-images.component.styl b/front/src/app/dialogs/edit-images/edit-images.component.styl new file mode 100644 index 0000000..1cd8b29 --- /dev/null +++ b/front/src/app/dialogs/edit-images/edit-images.component.styl @@ -0,0 +1,10 @@ +.dialog-content + width 600px + max-width calc(80vw - 48px) + +.field-input + width 100% + margin 20px 0 + + .form-field + width 100% diff --git a/front/src/app/dialogs/edit-images/edit-images.component.ts b/front/src/app/dialogs/edit-images/edit-images.component.ts new file mode 100644 index 0000000..4587df4 --- /dev/null +++ b/front/src/app/dialogs/edit-images/edit-images.component.ts @@ -0,0 +1,125 @@ +import {Component, ElementRef, Inject, OnInit, ViewChild} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {FormControl} from '@angular/forms'; +import {combineLatest, merge, Observable, of, Subscriber} from 'rxjs'; +import {COMMA, ENTER} from '@angular/cdk/keycodes'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import {catchError, first, map, startWith} from 'rxjs/operators'; +import {AuthService} from '../../services/auth.service'; +import {MessageService} from '../../services/message.service'; +import {ApiService} from '../../services/api.service'; +import {generateOriginHints} from '../../utils'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; + +export interface ImagesOriginalData { + origins: string[]; + tag: string; +} + +export interface ImagesOriginalDataResult { + origins: string[]; + tag: string; + field: string; +} + +@Component({ + selector: 'app-edit-images', + templateUrl: './edit-images.component.html', + styleUrls: ['./edit-images.component.styl'] +}) +export class EditImagesComponent implements OnInit { + + @ViewChild('originsInput') originsInput: ElementRef; + + selectedTab = 0; + tagControl = new FormControl(); + originsControl = new FormControl(); + + originsChangedS: Subscriber; + origins: string[] = ['*']; + originsSet: Set = new Set(['*']); + separatorKeysCodes: number[] = [ENTER, COMMA]; + + filteredTags$: Observable; + suggestOrigins$: Observable<{ origin: string, type: string, bad: boolean }[]>; + + originsInputSubmit(e: MatChipInputEvent): void { + const input = e.input; + + if (input) { + input.value = ''; + } + + this.originsControl.setValue(null); + } + + addDomain(e: MatAutocompleteSelectedEvent): void { + const domain = e.option.value as string; + this.origins.push(domain); + this.originsSet.add(domain); + this.originsInput.nativeElement.value = ''; + this.originsControl.setValue(null); + this.originsChangedS.next(); + } + + removeDomain(domain: string): void { + this.origins.splice(this.origins.indexOf(domain), 1); + this.originsSet.delete(domain); + this.originsChangedS.next(); + } + + get result(): ImagesOriginalDataResult { + return { + tag: this.tagControl.value, + origins: this.origins, + field: this.selectedTab === 0 ? 'tag' : 'origins' + }; + } + + constructor(public locale: LocaleService, + private auth: AuthService, + private api: ApiService, + private msg: MessageService, + @Inject(MAT_DIALOG_DATA) public data?: ImagesOriginalData) { + if (data) { + if (data.tag) { + this.tagControl.setValue(data.tag); + } + + if (data.origins) { + this.origins = data.origins; + this.originsSet = new Set(this.origins); + } + } + } + + ngOnInit(): void { + this.auth.user$.subscribe(user => { + this.filteredTags$ = combineLatest([this.api.ListImageTags(user.id).pipe( + catchError(error => { + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.gallery_tag_failed_message.replace('%1', dict[error])); + }); + return of([] as string[]); + }), + ), + this.tagControl.valueChanges.pipe(startWith([undefined])) + ]).pipe(map(([tags, _]) => { + const filterValue = this.tagControl.value || ''; + return tags.filter(tag => tag.toLowerCase().includes(filterValue.toLowerCase())); + })); + }); + + this.suggestOrigins$ = merge( + this.originsControl.valueChanges, + new Observable(s => this.originsChangedS = s) + ).pipe( + startWith([undefined]), + map(_ => { + const input = this.originsControl.value || ''; + return generateOriginHints.call(this, input); + })); + } + +} diff --git a/front/src/app/dialogs/format-preference/format-preference.component.html b/front/src/app/dialogs/format-preference/format-preference.component.html new file mode 100644 index 0000000..536bd43 --- /dev/null +++ b/front/src/app/dialogs/format-preference/format-preference.component.html @@ -0,0 +1,72 @@ +

{{'format_pref_title' | i18nSelect:locale.dict}}

+ +

+ + {{'format_pref_note_title' | i18nSelect:locale.dict}} + + {{'format_pref_note_content' | i18nSelect:locale.dict}} +

+
+

{{'format_pref_support_status' | i18nSelect:locale.dict}}

+

WEBP: + + + + {{'format_pref_support_status_detecting' | i18nSelect:locale.dict}} + + + done + {{'format_pref_support_status_supported' | i18nSelect:locale.dict}} + + + clear + {{'format_pref_support_status_not_supported' | i18nSelect:locale.dict}} + + +

+

AVIF: + + + + {{'format_pref_support_status_detecting' | i18nSelect:locale.dict}} + + + done + {{'format_pref_support_status_supported' | i18nSelect:locale.dict}} + + + clear + {{'format_pref_support_status_not_supported' | i18nSelect:locale.dict}} + + +

+

{{'format_pref_support_status_jpeg_png' | i18nSelect:locale.dict}}

+
+
+

{{'format_pref_drag_hint' | i18nSelect:locale.dict}}

+
+
+ {{format}} +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+ +
diff --git a/front/src/app/dialogs/format-preference/format-preference.component.spec.ts b/front/src/app/dialogs/format-preference/format-preference.component.spec.ts new file mode 100644 index 0000000..38cb3fc --- /dev/null +++ b/front/src/app/dialogs/format-preference/format-preference.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {FormatPreferenceComponent} from './format-preference.component'; + +describe('FormatPreferenceComponent', () => { + let component: FormatPreferenceComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [FormatPreferenceComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FormatPreferenceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/format-preference/format-preference.component.styl b/front/src/app/dialogs/format-preference/format-preference.component.styl new file mode 100644 index 0000000..3f60f95 --- /dev/null +++ b/front/src/app/dialogs/format-preference/format-preference.component.styl @@ -0,0 +1,51 @@ +.dialog-content + width 600px + max-width calc(80vw - 48px) + + .info-header + font-weight 600 + + .good-result + color var(--status-green) + + .bad-result + color var(--warn-default) + + .status-icon + vertical-align bottom + + &.status-spinner + display inline-block + vertical-align middle + + .sorter-container + width 100% + display flex + border 1px solid #ddd + background-color white + border-radius 5px + margin-bottom 20px + + .sort-item + flex 1 1 auto + display flex + justify-content center + align-items center + user-select none + cursor move + border-right 1px solid #ddd + padding 10px + + &:last-child + border none + +.button-wrapper-normal + flex 0 1 auto + +.button-wrapper-wide + flex 1 1 auto + display flex + justify-content flex-end + +.button-cancel + margin-right 10px diff --git a/front/src/app/dialogs/format-preference/format-preference.component.ts b/front/src/app/dialogs/format-preference/format-preference.component.ts new file mode 100644 index 0000000..abb776c --- /dev/null +++ b/front/src/app/dialogs/format-preference/format-preference.component.ts @@ -0,0 +1,78 @@ +import {Component, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {Options} from 'sortablejs'; +import {MatDialogRef} from '@angular/material/dialog'; +import {MessageService} from '../../services/message.service'; + +const TestWebp = `data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA`; +const TestAvif = `data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUEAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABsAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgS0AAAAAABNjb2xybmNseAACAAIAAQAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACNtZGF0EgAKBzgADtAgIBEyDhAQAABAAAAAAFey6gmI`; + +@Component({ + selector: 'app-format-preference', + templateUrl: './format-preference.component.html', + styleUrls: ['./format-preference.component.styl'] +}) +export class FormatPreferenceComponent implements OnInit { + + supportWebp: boolean = null; + supportAvif: boolean = null; + + currentPreference: string[] = ['avif', 'webp', 'jpeg', 'png']; + + dragOptions: Options = { + group: 'file-links', + animation: 250, + easing: 'cubic-bezier(0, 0, 0.2, 1)', + delayOnTouchOnly: true, + dragClass: 'dragging-file', + delay: 100, + disabled: false, + ghostClass: 'ghost-file' + }; + + get cookie(): string { + return decodeURIComponent(document.cookie.replace(/(?:(?:^|.*;\s*)Preference\s*=\s*([^;]*).*$)|^.*$/, '$1')); + } + + set cookie(value: string) { + if (!value) { + document.cookie = 'Preference=; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;SameSite=None;Secure'; + } else { + document.cookie = `Preference=${encodeURIComponent(value)};expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/;SameSite=None;Secure`; + } + } + + save(): void { + this.cookie = JSON.stringify(this.currentPreference); + this.msg.SendMessage(this.locale.dict.format_pref_save_done); + this.dialogRef.close(); + } + + reset(): void { + this.cookie = ''; + this.msg.SendMessage(this.locale.dict.format_pref_reset_done); + this.dialogRef.close(); + } + + constructor(public locale: LocaleService, + private dialogRef: MatDialogRef, + private msg: MessageService) { + } + + ngOnInit(): void { + if (this.cookie) { + this.currentPreference = JSON.parse(this.cookie); + } + + const webp = new Image(); + webp.onerror = () => this.supportWebp = false; + webp.onload = () => this.supportWebp = (webp.width > 0 && webp.height > 0); + webp.src = TestWebp; + + const avif = new Image(); + avif.onerror = () => this.supportAvif = false; + avif.onload = () => this.supportAvif = (avif.width > 0 && avif.height > 0); + avif.src = TestAvif; + } + +} diff --git a/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.html b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.html new file mode 100644 index 0000000..2f654c2 --- /dev/null +++ b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.html @@ -0,0 +1,77 @@ +

{{'generate_code_title' | i18nSelect:locale.dict}}

+ +

+ {{'generate_sort_title' | i18nSelect:locale.dict}} + +

+
+
+
+ +
+
+
+

+ {{'generate_code_type' | i18nSelect:locale.dict}} +

+
+ + + {{'generate_type_html' | i18nSelect:locale.dict}} + + + {{'generate_type_bbcode' | i18nSelect:locale.dict}} + + + {{'generate_type_url' | i18nSelect:locale.dict}} + + + {{'generate_type_custom' | i18nSelect:locale.dict}} + + +
+
+ + + + + {{'generate_custom_template_hint' | i18nSelect:locale.dict}} + + + + + + + {{'generate_custom_separator_hint' | i18nSelect:locale.dict}} + + +
+
+
+ +
+
+ +
+
+
+
+ +
diff --git a/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.spec.ts b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.spec.ts new file mode 100644 index 0000000..fe5e134 --- /dev/null +++ b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {GenCodeMultiComponent} from './gen-code-multi.component'; + +describe('GenCodeMultiComponent', () => { + let component: GenCodeMultiComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GenCodeMultiComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GenCodeMultiComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.styl b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.styl new file mode 100644 index 0000000..0efd3ce --- /dev/null +++ b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.styl @@ -0,0 +1,90 @@ +.dialog-content + overflow-x hidden + overflow-y auto + width 1024px + max-width calc(80vw - 48px) + +.sort-reverse + float right + +.type-radio-chooser + line-height 30px + + .mat-radio-button:not(:last-child) + margin-right 10px + +.custom-input + display flex + flex-direction column + margin-top 20px + + .separator-field + margin-top 20px + +.code-area-wrapper + margin-top 20px + background-color #eee + border 1px solid #ccc + border-radius 5px + position relative + + .code-area + white-space nowrap + overflow auto + padding 10px + max-height 300px + min-height 30px + + .result-code + font-family var(--font-mono) + user-select all + + .copy-button + position absolute + top 5px + right 15px + opacity 0 + visibility hidden + transition opacity 0.3s 0.5s, visibility 0.3s 0.5s + background-color #eee + border-radius 20px + + &:hover .copy-button + opacity 1 + visibility visible + transition opacity 0.3s, visibility 0.3s + +.sort-images-container + margin-bottom 20px + display grid + grid-template-columns repeat(6, 1fr) + grid-gap 5px + gap 5px + + @media (max-width 900px) + grid-template-columns repeat(5, 1fr) + + @media (max-width 600px) + grid-template-columns repeat(4, 1fr) + + @media (max-width 400px) + grid-template-columns repeat(3, 1fr) + + .sort-image-wrapper + position relative + width 100% + height 0 + padding-top 100% + + .sort-image-container + position absolute + width 100% + height 100% + top 0 + left 0 + background-color #eee + + .sort-image + width 100% + height 100% + object-fit contain diff --git a/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.ts b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.ts new file mode 100644 index 0000000..d117587 --- /dev/null +++ b/front/src/app/dialogs/gen-code-multi/gen-code-multi.component.ts @@ -0,0 +1,87 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {MessageService} from '../../services/message.service'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {Options} from 'sortablejs'; + +const htmlTemplate = ``; +const htmlBetween = `
\n`; + +const bbcodeTemplate = `[img]%url%[/img]`; +const bbcodeBetween = `\n`; + +const urlTemplate = `%url%`; +const urlBetween = `\n`; + +@Component({ + selector: 'app-gen-code-multi', + templateUrl: './gen-code-multi.component.html', + styleUrls: ['./gen-code-multi.component.styl'] +}) +export class GenCodeMultiComponent implements OnInit { + + type = 'html'; + + customizeTemplate = `%url%`; + customizeBetween = `\n`; + + dragOptions: Options = { + group: 'file-links', + animation: 250, + easing: 'cubic-bezier(0, 0, 0.2, 1)', + delayOnTouchOnly: true, + dragClass: 'dragging-file', + delay: 100, + disabled: false, + ghostClass: 'ghost-file' + }; + + private static get host(): string { + return window.location.host; + } + + private static url(id: string): string { + return `https://${GenCodeMultiComponent.host}/image/${id}.i`; + } + + get code(): string { + let template: string; + let between: string; + switch (this.type) { + case 'html': + template = htmlTemplate; + between = htmlBetween; + break; + case 'bbcode': + template = bbcodeTemplate; + between = bbcodeBetween; + break; + case 'url': + template = urlTemplate; + between = urlBetween; + break; + case 'custom': + template = this.customizeTemplate; + between = this.customizeBetween; + } + + return this.ids.map(id => template.replace('%url%', GenCodeMultiComponent.url(id))).join(between); + } + + copied(): void { + this.msg.SendMessage(this.locale.dict.generate_result_copied); + } + + reverse(): void { + this.ids.reverse(); + } + + constructor(public locale: LocaleService, + private msg: MessageService, + @Inject(MAT_DIALOG_DATA) public ids?: string[]) { + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/gen-code-single/gen-code-single.component.html b/front/src/app/dialogs/gen-code-single/gen-code-single.component.html new file mode 100644 index 0000000..1cae032 --- /dev/null +++ b/front/src/app/dialogs/gen-code-single/gen-code-single.component.html @@ -0,0 +1,37 @@ +

{{'generate_code_title' | i18nSelect:locale.dict}}

+ +

+ {{'generate_code_type' | i18nSelect:locale.dict}} +

+
+ + + {{'generate_type_html' | i18nSelect:locale.dict}} + + + {{'generate_type_bbcode' | i18nSelect:locale.dict}} + + + {{'generate_type_url' | i18nSelect:locale.dict}} + + +
+
+
+ +
+
+ +
+
+
+
+ +
diff --git a/front/src/app/dialogs/gen-code-single/gen-code-single.component.spec.ts b/front/src/app/dialogs/gen-code-single/gen-code-single.component.spec.ts new file mode 100644 index 0000000..9e4e096 --- /dev/null +++ b/front/src/app/dialogs/gen-code-single/gen-code-single.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {GenCodeSingleComponent} from './gen-code-single.component'; + +describe('GenCodeSingleComponent', () => { + let component: GenCodeSingleComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GenCodeSingleComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GenCodeSingleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/gen-code-single/gen-code-single.component.styl b/front/src/app/dialogs/gen-code-single/gen-code-single.component.styl new file mode 100644 index 0000000..e301285 --- /dev/null +++ b/front/src/app/dialogs/gen-code-single/gen-code-single.component.styl @@ -0,0 +1,44 @@ +.dialog-content + overflow-x hidden + overflow-y auto + width 1024px + max-width calc(80vw - 48px) + +.type-radio-chooser + line-height 30px + + .mat-radio-button:not(:last-child) + margin-right 10px + +.code-area-wrapper + margin-top 20px + background-color #eee + border 1px solid #ccc + border-radius 5px + min-height 20px + position relative + + .code-area + white-space nowrap + overflow auto + padding 10px + max-height 300px + + .result-code + font-family var(--font-mono) + user-select all + + .copy-button + position absolute + top 0 + right 0 + opacity 0 + visibility hidden + transition opacity 0.3s 0.5s, visibility 0.3s 0.5s + background-color #eee + border-radius 20px + + &:hover .copy-button + opacity 1 + visibility visible + transition opacity 0.3s, visibility 0.3s diff --git a/front/src/app/dialogs/gen-code-single/gen-code-single.component.ts b/front/src/app/dialogs/gen-code-single/gen-code-single.component.ts new file mode 100644 index 0000000..dad23ff --- /dev/null +++ b/front/src/app/dialogs/gen-code-single/gen-code-single.component.ts @@ -0,0 +1,46 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {MessageService} from '../../services/message.service'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; + +@Component({ + selector: 'app-gen-code-single', + templateUrl: './gen-code-single.component.html', + styleUrls: ['./gen-code-single.component.styl'] +}) +export class GenCodeSingleComponent implements OnInit { + + type = 'html'; + + private static get host(): string { + return window.location.host; + } + + private get url(): string { + return `https://${GenCodeSingleComponent.host}/image/${this.id}.i`; + } + + get code(): string { + switch (this.type) { + case 'html': + return ``; + case 'bbcode': + return `[img]${this.url}[/img]`; + case 'url': + return this.url; + } + } + + copied(): void { + this.msg.SendMessage(this.locale.dict.generate_result_copied); + } + + constructor(public locale: LocaleService, + private msg: MessageService, + @Inject(MAT_DIALOG_DATA) public id?: string) { + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/image-info/image-info.component.html b/front/src/app/dialogs/image-info/image-info.component.html new file mode 100644 index 0000000..7ea0d4f --- /dev/null +++ b/front/src/app/dialogs/image-info/image-info.component.html @@ -0,0 +1,75 @@ +

{{'info_title' | i18nSelect:locale.dict}}

+ +
+ {{'info_user' | i18nSelect:locale.dict}} +
+
+ + {{image.user_name}} + +
+ avatar +
+ person +
+
+
+
+ {{'info_tag' | i18nSelect:locale.dict}} +
+
+ + {{image.tag || ('gallery_tag_empty' | i18nSelect:locale.dict)}} + +
+
+ {{'info_upload_time' | i18nSelect:locale.dict}} +
+
+ + {{image.upload | date:'medium':null:(pref.get('locale') | async)}} + +
+
+ {{'info_view' | i18nSelect:locale.dict}} +
+
+ + {{image.view}} + +
+
+ {{'info_origins' | i18nSelect:locale.dict}} +
+
+ + + {{domain}} + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + + +
+
+ {{'info_formats' | i18nSelect:locale.dict}} +
+
+ + + {{format.format}} + + +
+
+
+ +
diff --git a/front/src/app/dialogs/image-info/image-info.component.spec.ts b/front/src/app/dialogs/image-info/image-info.component.spec.ts new file mode 100644 index 0000000..2f46ea7 --- /dev/null +++ b/front/src/app/dialogs/image-info/image-info.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {ImageInfoComponent} from './image-info.component'; + +describe('ImageInfoComponent', () => { + let component: ImageInfoComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ImageInfoComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ImageInfoComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/image-info/image-info.component.styl b/front/src/app/dialogs/image-info/image-info.component.styl new file mode 100644 index 0000000..643c1d4 --- /dev/null +++ b/front/src/app/dialogs/image-info/image-info.component.styl @@ -0,0 +1,39 @@ +.dialog-content + width 800px + padding-bottom 10px + max-width calc(80vw - 48px) + display grid + grid-template-columns auto 1fr + grid-gap 12px + gap 12px + + .header + display flex + align-items center + justify-content flex-start + min-height 32px + + .content + display flex + align-items center + justify-content flex-start + flex-wrap wrap + min-height 32px + + .user-avatar + height 30px + width 30px + margin-left 12px + + .avatar-image + height 30px + width 30px + border-radius 15px + background #ddd + + .pseudo-avatar-image + color #555 + display flex + justify-content center + align-items center + user-select none diff --git a/front/src/app/dialogs/image-info/image-info.component.ts b/front/src/app/dialogs/image-info/image-info.component.ts new file mode 100644 index 0000000..5bf6b41 --- /dev/null +++ b/front/src/app/dialogs/image-info/image-info.component.ts @@ -0,0 +1,30 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {Image} from '../../types'; +import {PreferenceService} from '../../services/preference.service'; +import {ApiService} from '../../services/api.service'; + +@Component({ + selector: 'app-image-info', + templateUrl: './image-info.component.html', + styleUrls: ['./image-info.component.styl'] +}) +export class ImageInfoComponent implements OnInit { + + avatarError = false; + toInt = i => Number.parseInt(i, 10); + + constructor(public locale: LocaleService, + public pref: PreferenceService, + public api: ApiService, + @Inject(MAT_DIALOG_DATA) public image: Image) { + } + + ngOnInit(): void { + if (this.image && this.image.id) { + this.api.GetImage(this.image.id).subscribe(i => this.image = i); + } + } + +} diff --git a/front/src/app/dialogs/single-prompt/single-prompt.component.html b/front/src/app/dialogs/single-prompt/single-prompt.component.html new file mode 100644 index 0000000..ae320f4 --- /dev/null +++ b/front/src/app/dialogs/single-prompt/single-prompt.component.html @@ -0,0 +1,22 @@ +

{{(config.type + '_prompt_title') | i18nSelect:locale.dict}}

+ + + + + + + + +
+ + +
diff --git a/front/src/app/dialogs/single-prompt/single-prompt.component.spec.ts b/front/src/app/dialogs/single-prompt/single-prompt.component.spec.ts new file mode 100644 index 0000000..cd083ac --- /dev/null +++ b/front/src/app/dialogs/single-prompt/single-prompt.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {SinglePromptComponent} from './single-prompt.component'; + +describe('SinglePromptComponent', () => { + let component: SinglePromptComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [SinglePromptComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SinglePromptComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/single-prompt/single-prompt.component.styl b/front/src/app/dialogs/single-prompt/single-prompt.component.styl new file mode 100644 index 0000000..ec75cff --- /dev/null +++ b/front/src/app/dialogs/single-prompt/single-prompt.component.styl @@ -0,0 +1,7 @@ +.dialog-content + max-width calc(80vw - 48px) + width 300px + display flex + + .form-field + width 100% diff --git a/front/src/app/dialogs/single-prompt/single-prompt.component.ts b/front/src/app/dialogs/single-prompt/single-prompt.component.ts new file mode 100644 index 0000000..a8c5cbe --- /dev/null +++ b/front/src/app/dialogs/single-prompt/single-prompt.component.ts @@ -0,0 +1,34 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {FormControl, Validators} from '@angular/forms'; + +export interface PromptConfig { + type?: string; + input?: string; +} + +@Component({ + selector: 'app-single-prompt', + templateUrl: './single-prompt.component.html', + styleUrls: ['./single-prompt.component.styl'] +}) +export class SinglePromptComponent implements OnInit { + + inputValue = new FormControl(null, Validators.required); + + constructor(public locale: LocaleService, + @Inject(MAT_DIALOG_DATA) public readonly config?: PromptConfig) { + if (!config) { + this.config = {type: 'default', input: 'text'}; + } else if (!config.type) { + this.config.type = 'default'; + } else if (!config.input) { + this.config.input = 'text'; + } + } + + ngOnInit(): void { + } + +} diff --git a/front/src/app/dialogs/upload-advanced/upload-advanced.component.html b/front/src/app/dialogs/upload-advanced/upload-advanced.component.html new file mode 100644 index 0000000..79a2226 --- /dev/null +++ b/front/src/app/dialogs/upload-advanced/upload-advanced.component.html @@ -0,0 +1,163 @@ +

{{'upload_advanced_title' | i18nSelect:locale.dict}}

+ +

+ + {{'upload_advanced_warning_head' | i18nSelect:locale.dict}} + + {{'upload_advanced_warning_text' | i18nSelect:locale.dict}} +

+
+

{{'upload_advanced_file_title' | i18nSelect:locale.dict}}

+
+ +
{{format | uppercase}}
+
+ + +
+
+ + {{'upload_advanced_status_not_selected' | i18nSelect:locale.dict}} + + + {{'upload_advanced_status_before' | i18nSelect:locale.dict}} + {{(files[format]?.size || 0) | fileSize:'number' | number:'1.0-2':(pref.get('locale') | async)}} + {{(files[format]?.size || 0) | fileSize:'unit'}} + {{'upload_advanced_status_after' | i18nSelect:locale.dict}} + +
+
+
+

{{'upload_advanced_info_title' | i18nSelect:locale.dict}}

+
+
+ {{'upload_info_tag_header' | i18nSelect:locale.dict}} +
+
+ + + + + + + + {{option || ('gallery_tag_empty' | i18nSelect:locale.dict)}} + + + {{'upload_info_tag_header_hint' | i18nSelect:locale.dict}} + + + +
+
+ {{'upload_info_allow_origins' | i18nSelect:locale.dict}} +
+
+ + + + + + + {{domain}} + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + cancel + + + + + + + {{domain.origin}} + + + + {{'upload_info_allow_origins_exact' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_wildcard' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_any' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_none' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_bad' |i18nSelect:locale.dict}} + + + {{'upload_info_allow_origins_exist' |i18nSelect:locale.dict}} + + + + + +
+
+
+
+

+ {{'upload_advanced_progress' | i18nSelect:locale.dict}} +

+ +
+

+ {{('upload_advanced_failed_message' | i18nSelect:locale.dict).replace('%1', errorMessage)}} +

+

+ {{'upload_advanced_success_message' | i18nSelect:locale.dict}} +

+
+
+ + +
diff --git a/front/src/app/dialogs/upload-advanced/upload-advanced.component.spec.ts b/front/src/app/dialogs/upload-advanced/upload-advanced.component.spec.ts new file mode 100644 index 0000000..f9f7411 --- /dev/null +++ b/front/src/app/dialogs/upload-advanced/upload-advanced.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import {UploadAdvancedComponent} from './upload-advanced.component'; + +describe('UploadAdvancedComponent', () => { + let component: UploadAdvancedComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [UploadAdvancedComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UploadAdvancedComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/src/app/dialogs/upload-advanced/upload-advanced.component.styl b/front/src/app/dialogs/upload-advanced/upload-advanced.component.styl new file mode 100644 index 0000000..ec66259 --- /dev/null +++ b/front/src/app/dialogs/upload-advanced/upload-advanced.component.styl @@ -0,0 +1,64 @@ +.dialog-content + width 600px + max-width calc(80vw - 48px) + + .advanced-warning + font-weight 600 + + .warning-header + color var(--warn-default) + + .upload-files + display grid + grid-template-columns auto auto 1fr + grid-gap 8px + gap 8px + margin-bottom 20px + + .file-format, + .upload-button, + .upload-file + display flex + justify-content flex-start + align-items center + min-height 32px + + .upload-button + width 100px + position relative + + .file-picker + flex-grow 1 + + .real-file-picker + position absolute + height 0 + width 0 + opacity 0 + visibility hidden + + .upload-settings + display grid + grid-template-columns auto 1fr + grid-gap 8px + gap 8px + margin-bottom 20px + + .setting-header, + .setting-input + display flex + align-items center + justify-content flex-start + + .setting-input .form-field + flex-grow 1 + + .error-message + margin-top 12px + font-size 16px + font-weight 600 + color var(--warn-default) + + .ok-message + font-size 16px + font-weight 600 diff --git a/front/src/app/dialogs/upload-advanced/upload-advanced.component.ts b/front/src/app/dialogs/upload-advanced/upload-advanced.component.ts new file mode 100644 index 0000000..c5ef1b2 --- /dev/null +++ b/front/src/app/dialogs/upload-advanced/upload-advanced.component.ts @@ -0,0 +1,229 @@ +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {LocaleService} from '../../services/locale.service'; +import {PreferenceService} from '../../services/preference.service'; +import {MatDialog, MatDialogRef} from '@angular/material/dialog'; +import {ConfirmationComponent} from '../confirmation/confirmation.component'; +import {ApiService, Response} from '../../services/api.service'; +import {FormControl} from '@angular/forms'; +import {combineLatest, merge, Observable, of, Subscriber} from 'rxjs'; +import {COMMA, ENTER} from '@angular/cdk/keycodes'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import {ImagesOriginalDataResult} from '../edit-images/edit-images.component'; +import {catchError, first, map, startWith} from 'rxjs/operators'; +import {generateOriginHints} from '../../utils'; +import {AuthService} from '../../services/auth.service'; +import {MessageService} from '../../services/message.service'; +import {HttpEventType, HttpProgressEvent, HttpResponse} from '@angular/common/http'; +import {EOk} from '../../errors'; +import {animate, style, transition, trigger} from '@angular/animations'; + +@Component({ + selector: 'app-upload-advanced', + templateUrl: './upload-advanced.component.html', + styleUrls: ['./upload-advanced.component.styl'], + animations: [ + trigger('enter', [ + transition(':enter', [ + style({height: '0', opacity: '0', overflow: 'hidden'}), + animate('0.2s ease-in-out', + style({height: '*', opacity: '1'})), + ]), + transition(':leave', [ + style({overflow: 'hidden'}), + animate('0.2s ease-in-out', + style({height: '0', opacity: '0'})), + ]), + ]) + ] +}) +export class UploadAdvancedComponent implements OnInit { + + uploading = false; + uploaded = false; + errored = false; + errorMessage = ''; + + readonly formats = ['jpeg', 'png', 'webp', 'avif']; + + progress = 0; + + files: { [key: string]: Blob | null } = { + jpeg: null, + png: null, + webp: null, + avif: null, + }; + + @ViewChild('originsInput') originsInput: ElementRef; + + selectedTab = 0; + tagControl = new FormControl(''); + originsControl = new FormControl(); + + originsChangedS: Subscriber; + origins: string[] = ['*']; + originsSet: Set = new Set(['*']); + separatorKeysCodes: number[] = [ENTER, COMMA]; + + filteredTags$: Observable; + suggestOrigins$: Observable<{ origin: string, type: string, bad: boolean }[]>; + + originsInputSubmit(e: MatChipInputEvent): void { + const input = e.input; + + if (input) { + input.value = ''; + } + + this.originsControl.setValue(null); + } + + addDomain(e: MatAutocompleteSelectedEvent): void { + const domain = e.option.value as string; + this.origins.push(domain); + this.originsSet.add(domain); + this.originsInput.nativeElement.value = ''; + this.originsControl.setValue(null); + this.originsChangedS.next(); + } + + removeDomain(domain: string): void { + this.origins.splice(this.origins.indexOf(domain), 1); + this.originsSet.delete(domain); + this.originsChangedS.next(); + } + + get result(): ImagesOriginalDataResult { + return { + tag: this.tagControl.value, + origins: this.origins, + field: this.selectedTab === 0 ? 'tag' : 'origins' + }; + } + + setFile(el: HTMLInputElement, format: string): void { + if (this.files[format] === undefined) { + return; + } + + if (!(el && el.files && el.files.length > 0)) { + return; + } + + this.files[format] = el.files[0]; + } + + close(): void { + if (!this.errored && (this.uploading || (!this.uploaded && this.hasFile))) { + this.dialog.open(ConfirmationComponent, { + data: { + type: 'upload', + severe: true, + } + }).afterClosed().subscribe(decision => { + if (decision) { + this.dialogRef.close(true); + } + }); + } else { + this.dialogRef.close(this.uploaded); + } + } + + upload(): void { + if (this.uploaded || this.errored) { + this.close(); + } + + if (this.uploading) { + return; + } + + this.uploading = true; + + this.api.UploadAdvanced(this.tagControl.value, this.origins.join(','), this.files).subscribe({ + next: event => { + switch (event.type) { + case HttpEventType.UploadProgress: + const progress = event as HttpProgressEvent; + this.progress = progress.loaded / progress.total; + return; + case HttpEventType.Response: + const response = event as unknown as HttpResponse; + if (response.status !== 200) { + this.errored = true; + this.uploading = false; + this.errorMessage = this.locale.dict.http_error.replace('%1', String(response.status)); + break; + } + + const body = response.body; + + if (!body.status || !body.data) { + this.errored = true; + this.uploading = false; + this.errorMessage = this.locale.dict['9999']; + break; + } else if (body.status !== EOk) { + this.errored = true; + this.uploading = false; + this.errorMessage = this.locale.dict[body.status] || this.locale.dict['9999']; + break; + } + } + }, + error: _ => { + this.errored = true; + this.uploading = false; + this.errorMessage = this.locale.dict['9999']; + }, + complete: () => { + this.uploading = false; + this.uploaded = true; + } + }); + } + + get hasFile(): boolean { + return Object.values(this.files).reduce((a, c) => !!(a || c), false); + } + + constructor(public locale: LocaleService, + public pref: PreferenceService, + private dialog: MatDialog, + private api: ApiService, + private auth: AuthService, + private msg: MessageService, + public dialogRef: MatDialogRef) { + } + + ngOnInit(): void { + this.auth.user$.subscribe(user => { + this.filteredTags$ = combineLatest([this.api.ListImageTags(user.id).pipe( + catchError(error => { + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.gallery_tag_failed_message.replace('%1', dict[error])); + }); + return of([] as string[]); + }), + ), + this.tagControl.valueChanges.pipe(startWith([undefined])) + ]).pipe(map(([tags, _]) => { + const filterValue = this.tagControl.value || ''; + return tags.filter(tag => tag.toLowerCase().includes(filterValue.toLowerCase())); + })); + }); + + this.suggestOrigins$ = merge( + this.originsControl.valueChanges, + new Observable(s => this.originsChangedS = s) + ).pipe( + startWith([undefined]), + map(_ => { + const input = this.originsControl.value || ''; + return generateOriginHints.call(this, input); + })); + } + +} diff --git a/front/src/app/directives/intersection.directive.spec.ts b/front/src/app/directives/intersection.directive.spec.ts new file mode 100644 index 0000000..a3b6d80 --- /dev/null +++ b/front/src/app/directives/intersection.directive.spec.ts @@ -0,0 +1,8 @@ +import {IntersectionDirective} from './intersection.directive'; + +describe('IntersectionDirective', () => { + it('should create an instance', () => { + const directive = new IntersectionDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/front/src/app/directives/intersection.directive.ts b/front/src/app/directives/intersection.directive.ts new file mode 100644 index 0000000..01b2889 --- /dev/null +++ b/front/src/app/directives/intersection.directive.ts @@ -0,0 +1,41 @@ +import {AfterViewInit, Directive, ElementRef, EventEmitter, Input, OnDestroy, Output} from '@angular/core'; + +@Directive({ + selector: '[appIntersection]' +}) +export class IntersectionDirective implements AfterViewInit, OnDestroy { + + // tslint:disable-next-line:no-input-rename + @Input('intersectionContainer') container: HTMLElement | null = null; + @Output() enter = new EventEmitter(); + @Output() leave = new EventEmitter(); + + private observer: IntersectionObserver; + + constructor(private el: ElementRef) { + } + + ngAfterViewInit(): void { + this.observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.target === this.el.nativeElement) { + if (entry.intersectionRatio > 0) { + this.enter?.emit(entry); + } else { + this.leave?.emit(entry); + } + } + }); + }, { + root: this.container ? this.container : null, + rootMargin: '0px', + threshold: [0, 1] + }); + + this.observer.observe(this.el.nativeElement); + } + + ngOnDestroy(): void { + this.observer.disconnect(); + } +} diff --git a/front/src/app/directives/long-press.directive.spec.ts b/front/src/app/directives/long-press.directive.spec.ts new file mode 100644 index 0000000..029a88e --- /dev/null +++ b/front/src/app/directives/long-press.directive.spec.ts @@ -0,0 +1,8 @@ +import {LongPressDirective} from './long-press.directive'; + +describe('LongPressDirective', () => { + it('should create an instance', () => { + const directive = new LongPressDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/front/src/app/directives/long-press.directive.ts b/front/src/app/directives/long-press.directive.ts new file mode 100644 index 0000000..6d8cf66 --- /dev/null +++ b/front/src/app/directives/long-press.directive.ts @@ -0,0 +1,96 @@ +import {Directive, EventEmitter, HostBinding, HostListener, Input, Output} from '@angular/core'; + +@Directive({ + // tslint:disable-next-line:directive-selector + selector: '[longPress]' +}) +export class LongPressDirective { + pressingStart = false; + pressingState = false; + touchStart: {X: number, Y: number} = {X: 0, Y: 0}; + + timeoutId: number; + intervalId: number; + + @Input() longPress = 0; + @Input() pressingInterval = 0; + + @Output() longPressStart = new EventEmitter(); + @Output() longPressEnd = new EventEmitter(); + @Output() longPressing = new EventEmitter(); + + @HostBinding('class.pressing') get press(): boolean { + return this.pressingStart; + } + + @HostBinding('class.long-pressing') get longPressState(): boolean { + return this.pressingState; + } + + @HostListener('touchstart', ['$event']) + @HostListener('mousedown', ['$event']) startPress(event: Event): void { + // don't handle multi touches + const touch = event as TouchEvent; + if (touch.touches) { + if (touch.touches.length !== 1) { + return; + } else { + this.touchStart = { + X: touch.touches[0].clientX, + Y: touch.touches[0].clientY + }; + } + } + + this.pressingStart = true; + this.pressingState = false; + this.timeoutId = setTimeout(() => { + this.pressingState = true; + this.longPressStart.emit(event); + this.longPressing.emit(event); + if (this.pressingInterval > 0) { + this.intervalId = setInterval(() => { + this.longPressing.emit(event); + }, this.pressingInterval); + } + }, this.longPress || 500); + } + + @HostListener('touchmove', ['$event']) touchMove(event: TouchEvent): void { + if (event.touches.length !== 1) { + return; + } + + const dX = this.touchStart.X - event.touches[0].clientX; + const dY = this.touchStart.Y - event.touches[0].clientY; + + if (dX * dX + dY * dY > 200 && this.timeoutId) { + this.pressingStart = false; + this.pressingState = false; + clearTimeout(this.timeoutId); + this.timeoutId = 0; + } + } + + @HostListener('touchend', ['$event']) + @HostListener('mouseup', ['$event']) + @HostListener('mouseleave', ['$event']) endPress(event: Event): void { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.timeoutId = 0; + } + + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = 0; + } + + if (this.pressingState) { + event.preventDefault(); + this.longPressEnd.emit(event); + } + + this.pressingStart = false; + this.pressingState = false; + } +} diff --git a/front/src/app/errors.ts b/front/src/app/errors.ts new file mode 100644 index 0000000..4187eec --- /dev/null +++ b/front/src/app/errors.ts @@ -0,0 +1,38 @@ +export const EOk = '0000'; +export const EWeakPassword = '0001'; +export const EUserExist = '0002'; +export const EInvalidInviteCode = '0003'; +export const EBadCredential = '0004'; +export const EUserNotExist = '0005'; +export const EInviteCodeExist = '0006'; +export const EInviteCodeNotExist = '0007'; +export const EBadRequest = '0008'; +export const EImageNotExist = '0009'; +export const EBadImage = '0010'; +export const EEncodeFailed = '0011'; +export const EMissingOriginal = '0012'; +export const EMissingAuthorization = '0013'; +export const ECredentialExpired = '0014'; +export const EPermissionDenied = '0015'; + +export const EUnknown = '9999'; + +export const ErrorList = [ + EOk, + EWeakPassword, + EUserExist, + EInvalidInviteCode, + EBadCredential, + EUserNotExist, + EInviteCodeExist, + EInviteCodeNotExist, + EBadRequest, + EImageNotExist, + EBadImage, + EEncodeFailed, + EMissingOriginal, + EMissingAuthorization, + ECredentialExpired, + EPermissionDenied, + EUnknown, +]; diff --git a/front/src/app/guards/admin.guard.spec.ts b/front/src/app/guards/admin.guard.spec.ts new file mode 100644 index 0000000..72af478 --- /dev/null +++ b/front/src/app/guards/admin.guard.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {AdminGuard} from './admin.guard'; + +describe('AdminGuard', () => { + let guard: AdminGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(AdminGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/front/src/app/guards/admin.guard.ts b/front/src/app/guards/admin.guard.ts new file mode 100644 index 0000000..c36f225 --- /dev/null +++ b/front/src/app/guards/admin.guard.ts @@ -0,0 +1,27 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot, UrlTree} from '@angular/router'; +import {Observable} from 'rxjs'; +import {AuthService} from '../services/auth.service'; +import {first, map} from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class AdminGuard implements CanActivate, CanActivateChild { + constructor(private auth: AuthService) { + } + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.auth.user$.pipe(first(), map(user => { + return user && user.name === 'admin'; + })); + } + + canActivateChild( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.canActivate(next, state); + } +} diff --git a/front/src/app/guards/deactivate.guard.spec.ts b/front/src/app/guards/deactivate.guard.spec.ts new file mode 100644 index 0000000..0eed2c2 --- /dev/null +++ b/front/src/app/guards/deactivate.guard.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {DeactivateGuard} from './deactivate.guard'; + +describe('DeactivateGuard', () => { + let guard: DeactivateGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(DeactivateGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/front/src/app/guards/deactivate.guard.ts b/front/src/app/guards/deactivate.guard.ts new file mode 100644 index 0000000..355720c --- /dev/null +++ b/front/src/app/guards/deactivate.guard.ts @@ -0,0 +1,26 @@ +import {EventEmitter, Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot, UrlTree} from '@angular/router'; +import {Observable} from 'rxjs'; + +export interface CanComponentDeactivate { + deactivateHint?: EventEmitter; + canDeactivate: () => Observable | Promise | boolean; +} + +@Injectable({ + providedIn: 'root' +}) +export class DeactivateGuard implements CanDeactivate { + canDeactivate( + component: CanComponentDeactivate, + currentRoute: ActivatedRouteSnapshot, + currentState: RouterStateSnapshot, + nextState?: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + if (component.deactivateHint) { + component.deactivateHint.emit(nextState); + } + + return component.canDeactivate ? component.canDeactivate() : true; + } + +} diff --git a/front/src/app/guards/login.guard.spec.ts b/front/src/app/guards/login.guard.spec.ts new file mode 100644 index 0000000..4f55b4a --- /dev/null +++ b/front/src/app/guards/login.guard.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {LoginGuard} from './login.guard'; + +describe('LoginGuard', () => { + let guard: LoginGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(LoginGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/front/src/app/guards/login.guard.ts b/front/src/app/guards/login.guard.ts new file mode 100644 index 0000000..75f4e9a --- /dev/null +++ b/front/src/app/guards/login.guard.ts @@ -0,0 +1,37 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree} from '@angular/router'; +import {Observable} from 'rxjs'; +import {AuthService} from '../services/auth.service'; +import {first, map} from 'rxjs/operators'; +import {MessageService} from '../services/message.service'; +import {LocaleService} from '../services/locale.service'; + +@Injectable({ + providedIn: 'root' +}) +export class LoginGuard implements CanActivate, CanActivateChild { + constructor(private auth: AuthService, private router: Router, private msg: MessageService, private locale: LocaleService) { + } + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.auth.user$.pipe(first(), map(user => { + if (user && user.id) { + return true; + } else { + this.auth.setRedirect(state.url); + this.locale.dict$.pipe(first()).subscribe(dict => { + this.msg.SendMessage(dict.guard_need_login_message); + }); + return this.router.parseUrl('/login'); + } + })); + } + + canActivateChild( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.canActivate(next, state); + } +} diff --git a/front/src/app/guards/non-login.guard.spec.ts b/front/src/app/guards/non-login.guard.spec.ts new file mode 100644 index 0000000..8d7c85e --- /dev/null +++ b/front/src/app/guards/non-login.guard.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {NonLoginGuard} from './non-login.guard'; + +describe('UnLoginGuard', () => { + let guard: NonLoginGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(NonLoginGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/front/src/app/guards/non-login.guard.ts b/front/src/app/guards/non-login.guard.ts new file mode 100644 index 0000000..fdef23e --- /dev/null +++ b/front/src/app/guards/non-login.guard.ts @@ -0,0 +1,32 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree} from '@angular/router'; +import {Observable} from 'rxjs'; +import {AuthService} from '../services/auth.service'; +import {first, map} from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class NonLoginGuard implements CanActivate, CanActivateChild { + constructor(private auth: AuthService, private router: Router) { + } + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.auth.user$.pipe(first(), map(user => { + if (user && user.id) { + return this.router.parseUrl('/gallery/me'); + } else { + return true; + } + })); + } + + canActivateChild( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.canActivate(next, state); + } +} + diff --git a/front/src/app/interceptors/index.ts b/front/src/app/interceptors/index.ts new file mode 100644 index 0000000..1d753f0 --- /dev/null +++ b/front/src/app/interceptors/index.ts @@ -0,0 +1,8 @@ +/* "Barrel" of Http Interceptors */ +import {HTTP_INTERCEPTORS} from '@angular/common/http'; +import {TokenInterceptor} from './token.interceptor'; + +/** Http interceptor providers in outside-in order */ +export const httpInterceptorProviders = [ + {provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true}, +]; diff --git a/front/src/app/interceptors/token.interceptor.spec.ts b/front/src/app/interceptors/token.interceptor.spec.ts new file mode 100644 index 0000000..1766d4a --- /dev/null +++ b/front/src/app/interceptors/token.interceptor.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {TokenInterceptor} from './token.interceptor'; + +describe('TokenInterceptor', () => { + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + TokenInterceptor + ] + })); + + it('should be created', () => { + const interceptor: TokenInterceptor = TestBed.inject(TokenInterceptor); + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/front/src/app/interceptors/token.interceptor.ts b/front/src/app/interceptors/token.interceptor.ts new file mode 100644 index 0000000..99c7461 --- /dev/null +++ b/front/src/app/interceptors/token.interceptor.ts @@ -0,0 +1,82 @@ +import {Injectable} from '@angular/core'; +import {HttpEvent, HttpEventType, HttpHandler, HttpHeaderResponse, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; +import {Observable} from 'rxjs'; +import {AuthService} from '../services/auth.service'; +import {first, tap} from 'rxjs/operators'; +import {Router} from '@angular/router'; +import {ApiService, Response} from '../services/api.service'; +import {ECredentialExpired} from '../errors'; +import {MessageService} from '../services/message.service'; +import {LocaleService} from '../services/locale.service'; + +@Injectable() +export class TokenInterceptor implements HttpInterceptor { + + constructor(private auth: AuthService, + private router: Router, + private locale: LocaleService, + private msg: MessageService, + private api: ApiService) { + } + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + if (request.url.includes('/api/')) { + return new Observable>(observer => { + this.auth.get().subscribe(token => { + if (token) { + const authReq = request.clone({ + setHeaders: {Authorization: 'Bearer ' + token} + }); + + next.handle(authReq).subscribe({ + next: i => { + this.updateToken(i); + observer.next(i); + }, + complete: () => observer.complete(), + error: e => observer.error(e) + }); + } else { + next.handle(request).subscribe({ + next: i => { + this.updateToken(i); + observer.next(i); + }, + complete: () => observer.complete(), + error: e => observer.error(e) + }); + } + }); + }); + } else { + return next.handle(request).pipe( + tap(this.updateToken) + ); + } + } + + private updateToken(httpEvent: HttpEvent): void { + if (httpEvent.type === HttpEventType.Response || httpEvent.type === HttpEventType.ResponseHeader) { + const token = (httpEvent as HttpHeaderResponse).headers.get('X-Update-Authorization'); + if (token) { + this.auth.set(token).subscribe(_ => { + this.auth.user$.pipe(first()).subscribe(oldUser => { + if (oldUser?.id) { + this.api.GetUser(oldUser.id).subscribe(user => this.auth.updateUser(user).subscribe()); + } + }); + }); + } + + if (httpEvent.type === HttpEventType.Response) { + const response = (httpEvent as HttpResponse); + if (response && response.body && response.body.status === ECredentialExpired) { + this.auth.clear().subscribe(_ => { + this.router.navigateByUrl('/login').then(); + this.msg.SendMessage(this.locale.dict.login_expired); + }); + } + } + } + } +} diff --git a/front/src/app/langs.ts b/front/src/app/langs.ts new file mode 100644 index 0000000..c891089 --- /dev/null +++ b/front/src/app/langs.ts @@ -0,0 +1,1289 @@ +export const AvailableLanguages: { [key: string]: string } = { + en: 'English', + 'zh-CN': '简体中文', + 'zh-TW': '正體中文', +}; + +/* +export const TransTable: {[key: string]: {[key: string]: string}} = { + en: { + main_subtitle: 'Share your images. Optimized for web.', + main_register: 'Register', + main_login: 'Login', + language_switch: 'Switch Language', + + header_admin: 'Admin area', + header_upload: 'Upload', + header_upload_frozen: 'Your account is suspended.', + header_change_avatar: 'Change avatar', + header_change_password: 'Change Password', + header_logout: 'Logout', + header_image_pref: 'Image preference', + header_gallery: 'Gallery', + + login_title: 'Login', + login_username: 'Username', + login_password: 'Password', + login_action_login: 'Login', + login_action_register: 'Register account', + login_success_message: 'Login successful.', + login_failed_message: 'Login failed: %1.', + + register_title: 'Register', + register_username: 'Username', + register_password: 'Password', + register_password_repeat: 'Repeat password', + register_password_see: 'Unmask password', + register_password_hide: 'Mask password', + register_code: 'Invite code', + register_hint_password: 'At least 8 characters, contains upper, lower letter and number', + register_error_user_exist: 'User already exist', + register_error_password_weak: 'Password too weak', + register_error_password_mismatch: 'Passwords didn\'t match', + register_action_login: 'I have account', + register_action_register: 'Register', + + guard_need_login_message: 'You must login to access this page.', + + gallery_broken_image: 'Image not available', + gallery_loading: 'Loading...', + gallery_tag_search: 'Show specified tag only...', + gallery_tag_placeholder: 'Input tag here', + gallery_tag_failed_message: 'Failed loading tags: %1.', + gallery_tag_empty: '(Empty tag)', + gallery_group_label: 'Group images by: ', + gallery_group_time: 'Time', + gallery_group_tag: 'Tag', + gallery_group_time_label: 'Time span: ', + gallery_group_time_day: 'Day', + gallery_group_time_week: 'Week', + gallery_group_time_month: 'Month', + gallery_group_time_year: 'Year', + gallery_select_all_typed: 'Select all of this group', + gallery_deselect_all_typed: 'Deselect all of this group', + gallery_reverse_all_typed: 'Reverse select images of this group', + gallery_action_edit: 'Edit selected images', + gallery_action_code: 'Get code of selected images to publish', + gallery_action_delete: 'Delete selected images', + gallery_action_select_all: 'Select all images', + gallery_action_select_reverse: 'Reverse selection', + gallery_action_done: 'Done', + gallery_overlay_action_info: 'Image info', + gallery_overlay_action_open: 'Open image file in new page', + gallery_overlay_action_edit: 'Edit this image', + gallery_overlay_action_code: 'Get code of this image to publish', + gallery_overlay_action_delete: 'Delete this image', + gallery_overlay_action_close: 'Return to gallery', + gallery_fetch_failed: 'Failed loading images: %1.', + gallery_empty: 'Your gallery is empty.', + gallery_empty_other: 'Nothing here.', + gallery_empty_upload: 'Upload image', + + upload_minimize: 'Minimize', + upload_expand: 'Expand', + upload_close: 'Close', + upload_title_file_plural_0: 'no file', + upload_title_file_plural_1: '1 file', + upload_title_file_plural_other: 'files', + upload_title_idle: 'Upload', + upload_title_select_files_p1: '', + upload_title_select_files_p2: 'selected for upload', + upload_title_uploading_fine_p1: 'Uploading files,', + upload_title_uploading_fine_p2: ', ', + upload_title_uploading_fine_p3: 'finished', + upload_title_uploading_some_error_p1: 'Uploading files,', + upload_title_uploading_some_error_p2: ',', + upload_title_uploading_some_error_p3: 'finished,', + upload_title_uploading_some_error_p4: 'failed', + upload_title_uploaded_fine: 'Upload finished, all files succeed', + upload_title_uploaded_some_error_p1: 'Upload finished,', + upload_title_uploaded_some_error_p2: 'succeed,', + upload_title_uploaded_some_error_p3: 'failed', + upload_drop_file_pre: 'Drop image files here, or ', + upload_drop_file_picker: 'Select files', + upload_drop_file_post: '.', + upload_drop_file_release: 'Release your mouse to submit files.', + upload_file_name_unknown: '(Unknown filename)', + upload_file_type_unknown: '(Unknown type)', + upload_submit_no_valid: 'No valid image file found in your selected files.', + upload_preview_type_header: 'Display mode: ', + upload_preview_type_grid: 'Grid', + upload_preview_type_list: 'List', + upload_preview_limit_height: 'Limit height', + upload_preview_title: 'Image Preview', + upload_info_form_title: 'Settings', + upload_info_tag_header: 'Tag for images', + upload_info_tag_header_help: 'Tag is the primary way to group your images', + upload_info_tag_header_placeholder: 'Type tag here, or keep blank to use empty tag', + upload_info_tag_header_hint: '(Default: tag will set to your input)', + upload_info_allow_origins: 'Allow origins', + upload_info_allow_origins_help: 'User can only access images from these sites', + upload_info_allow_origins_placeholder: 'Type domain here, split with comma or enter', + upload_info_allow_origins_any: ' (Any site)', + upload_info_allow_origins_none: ' (Direct access)', + upload_info_allow_origins_exact: ' (Exact match)', + upload_info_allow_origins_wildcard: ' (Wildcard match)', + upload_info_allow_origins_bad: ' (Current input is not a valid domain)', + upload_info_allow_origins_exist: ' (Already covered)', + upload_failed_info: 'Upload failed: %1', + upload_operation_reset: 'Reset', + upload_operation_submit: 'Upload', + + upload_advanced_button: 'Advanced upload', + upload_advanced_title: 'Advanced upload', + upload_advanced_warning_head: 'Warning: ', + upload_advanced_warning_text: 'Advanced upload allows you to upload files in different formats of the same image,' + + ' and serve to the user as is. Server will not do any check, even not checking if it is a valid image file,' + + ' so as it called "Advanced". Please be cautious.', + upload_advanced_file_title: 'Pick files', + upload_advanced_select_file: 'Select file', + upload_advanced_selected: 'Reselect', + upload_advanced_status_not_selected: 'Not selected', + upload_advanced_status_before: 'File size:', + upload_advanced_status_after: '.', + upload_advanced_info_title: 'Settings', + upload_advanced_operation_cancel: 'Cancel', + upload_advanced_operation_upload: 'Upload', + upload_advanced_operation_uploading: 'Uploading', + upload_advanced_operation_uploaded: 'Close', + upload_advanced_operation_exit: 'Exit', + upload_advanced_failed_message: 'Uploading file failed: %1.', + upload_advanced_success_message: 'Upload successful.', + upload_advanced_progress: 'Upload Progress', + + edit_images_title: 'Edit images', + edit_images_tab_tag: 'Tag', + edit_images_title_tag: 'Change tag of images', + edit_images_tab_origins: 'Origins', + edit_images_title_origins: 'Change origins of images', + + edit_images_result_success: 'Image edit successful.', + edit_images_result_failed: 'Image edit failed: %1.', + + dialog_cancel: 'Cancel', + dialog_submit: 'OK', + + default_confirm_title: 'Confirmation', + default_confirm_word: 'Are you sure to process?', + default_confirm_yes: 'Confirm', + default_confirm_no: 'Cancel', + + delete_select_confirm_title: 'Delete images', + delete_select_confirm_word: 'Are you sure to delete selected images?', + delete_select_confirm_yes: 'Delete', + delete_select_confirm_no: 'Cancel', + + delete_this_confirm_title: 'Delete this image', + delete_this_confirm_word: 'Are you sure to delete this image?', + delete_this_confirm_yes: 'Delete', + delete_this_confirm_no: 'Cancel', + + upload_confirm_title: 'Abort upload and exit', + upload_confirm_word: 'Are you sure to exit? Some files may not be uploaded if you exit now.', + upload_confirm_yes: 'Exit', + upload_confirm_no: 'Cancel', + + delete_images_result_success: 'Image deleted.', + delete_images_result_failed: 'Image delete failed: %1.', + + generate_code_title: 'Generate Code for Publish', + generate_code_type: 'Code type:', + generate_code_type_label: 'Select a code type', + generate_type_html: 'HTML', + generate_type_bbcode: 'BBCode', + generate_type_url: 'URL', + generate_type_custom: 'Customize', + generate_custom_template: 'Template', + generate_custom_template_placeholder: 'Template code for each image', + generate_custom_template_hint: 'Use %url% to mark the position to place url', + generate_custom_separator: 'Separator', + generate_custom_separator_placeholder: 'Separator code between images', + generate_custom_separator_hint: 'Separator code will be placed between codes of each image', + generate_result_copy: 'Copy', + generate_result_copied: 'Code copied.', + generate_code_done: 'Done', + generate_sort_title: 'Sort images', + generate_sort_reverse: 'Reverse', + + info_title: 'Image info', + info_user: 'Uploader', + info_tag: 'Image tag', + info_upload_time: 'Upload time', + info_view: 'View count', + info_origins: 'Allowed origin sites', + info_formats: 'Available formats', + info_formats_available: 'This format is available', + info_formats_encoding: 'This format is being transcode, and will be available soon', + info_done: 'Close', + + change_password_title: 'Change Password', + change_password_old: 'Old password', + change_password_new: 'New password', + change_password_cancel: 'Cancel', + change_password_submit: 'Change', + change_password_failed: 'Change password failed: %1.', + change_password_succeed: 'Password changed. Please re-login.', + + change_avatar_title: 'Change Avatar', + change_avatar_title_pick_file: '1. Pick an image', + change_avatar_file_selector: 'Select image', + change_avatar_file_bad: 'Failed loading image. Is this a valid image file?', + change_avatar_title_crop: '2. Crop your image', + change_avatar_preview_no_file: 'Please select an image first.', + change_avatar_preview: 'Avatar preview', + change_avatar_success: 'Avatar changed.', + change_avatar_failed: 'Failed change avatar: %1', + change_avatar_cancel: 'Cancel', + change_avatar_submit: 'Save', + + admin_user: 'User', + admin_code: 'Code', + admin_user_search: 'Show users name contains keyword only...', + admin_user_search_placeholder: 'Input keyword here', + admin_user_fetch_failed: 'Failed fetching user list: %1.', + admin_user_username: 'User', + admin_user_privileged: 'Privileged', + admin_user_privileged_set: 'User privilege status changed.', + admin_user_privileged_failed: 'Failed changing user privilege status: %1.', + admin_user_frozen: 'Frozen', + admin_user_frozen_set: 'User freeze status changed.', + admin_user_frozen_failed: 'Failed changing user freeze status: %1.', + admin_operation: 'Operations', + admin_user_set_password: 'Set Password', + admin_user_password_ok: 'User password changed.', + admin_user_password_failed: 'Failed changing user password: %1.', + admin_user_set_avatar: 'Set Avatar', + admin_user_check_gallery: 'Check Gallery', + admin_delete: 'Delete', + admin_user_delete_ok: 'User deleted.', + admin_user_delete_failed: 'Failed deleting user: %1.', + admin_user_delete_selected: 'Delete selected users', + admin_user_delete_selected_ok: 'Selected users deleted.', + admin_user_delete_selected_failed: 'Failed deleting selected users: %1.', + admin_user_add: 'Add user', + admin_code_code: 'Invite code', + admin_code_times: 'Available times', + admin_code_set_times: 'Set available times', + admin_code_delete_selected: 'Delete selected codes', + admin_code_add: 'Add invite code', + admin_code_fetch_failed: 'Failed fetching invite code list: %1.', + admin_code_delete_ok: 'Invite code deleted.', + admin_code_delete_failed: 'Failed deleting invite code: %1.', + admin_times_set_ok: 'Available times updated.', + admin_times_set_failed: 'Failed updating available times: %1.', + + paginator_first_page: 'First page', + paginator_last_page: 'Last page', + paginator_previous_page: 'Previous page', + paginator_next_page: 'Next page', + paginator_per_page: 'Items per page: ', + paginator_position_none: 'No item', + paginator_position_zero: '0, total %1', + paginator_position: '%1 – %2 of %3', + + default_prompt_title: 'Input a value', + default_prompt_label: 'The value', + default_prompt_placeholder: 'Input value here', + default_prompt_yes: 'Submit', + default_prompt_no: 'Cancel', + + set_password_prompt_title: 'Set user password', + set_password_prompt_label: 'New Password', + set_password_prompt_placeholder: 'Input new password here', + set_password_prompt_yes: 'Set', + set_password_prompt_no: 'Cancel', + + delete_user_confirm_title: 'Delete user', + delete_user_confirm_word: 'Are you sure to delete this user?', + delete_user_confirm_yes: 'Delete', + delete_user_confirm_no: 'Cancel', + + delete_users_confirm_title: 'Delete selected users', + delete_users_confirm_word: 'Are you sure to delete selected users?', + delete_users_confirm_yes: 'Delete', + delete_users_confirm_no: 'Cancel', + + default_choice_title: 'Select one value', + default_choice_word: 'Please select one of the two values', + default_choice_right: 'Right', + default_choice_left: 'Left', + + delete_user_choice_title: 'Image handling', + delete_user_choice_word: 'How to handle images (if any) of this user?', + delete_user_choice_right: 'Adopt', + delete_user_choice_left: 'Delete', + + delete_users_choice_title: 'Image handling', + delete_users_choice_word: 'How to handle images (if any) of the selected users?', + delete_users_choice_right: 'Adopt', + delete_users_choice_left: 'Delete', + + add_user_title: 'Add user', + add_user_username: 'Username', + add_user_password: 'Password', + add_user_privileged: 'Privilege this user', + add_user_error_user_exist: 'User already exist', + add_user_cancel: 'Cancel', + add_user_submit: 'Add', + add_user_success_msg: 'User %1 added.', + add_user_error_msg: 'Failed adding user: %1.', + + add_code_title: 'Add invite code', + add_code_code: 'Code token', + add_code_times: 'Available times', + add_code_error_code_exist: 'Code already exist', + add_code_cancel: 'Cancel', + add_code_submit: 'Add', + add_code_success_msg: 'Code %1 added.', + add_code_error_msg: 'Failed adding code: %1.', + + code_times_prompt_title: 'Set available times', + code_times_prompt_label: 'Available times', + code_times_prompt_placeholder: 'Input a number here', + code_times_prompt_yes: 'Save', + code_times_prompt_no: 'Cancel', + + delete_code_confirm_title: 'Delete code', + delete_code_confirm_word: 'Are you sure to delete this code?', + delete_code_confirm_yes: 'Delete', + delete_code_confirm_no: 'Cancel', + + not_found_title: 'Not found', + not_found_word: 'Nothing available here.', + not_found_back: 'To main page', + + format_pref_title: 'Format preference settings', + format_pref_note_title: 'Note: ', + format_pref_note_content: 'Here you can set your image format preference. By default we serve you image in the format' + + ' that has minimal size while supported by your browser, and this means CPU will do more work to decode the image.' + + ' Usually the difference is small, but smaller file loads significantly faster, so we recommend you to keep the default.' + + ' However, if you are using a low-end device or you really cares battery life, you can manually set your preference below,' + + ' and we will respect your settings and try to serve you images with formats in your desired order.', + format_pref_support_status: 'Your browser\'s support to different formats: ', + format_pref_support_status_detecting: 'Detecting...', + format_pref_support_status_supported: 'Supported', + format_pref_support_status_not_supported: 'Not supported', + format_pref_support_status_jpeg_png: 'Every browser supports PNG and JPEG.', + format_pref_drag_hint: 'Drag to sort the formats:', + format_pref_cancel: 'Cancel', + format_pref_reset: 'Reset', + format_pref_save: 'Save', + format_pref_save_done: 'Preference saved.', + format_pref_reset_done: 'Preference cleared.', + + about_title: 'About', + about_content: 'Chromatic is an image store platform specially targeted on optimizing web access experience.', + about_privacy: 'Privacy', + about_privacy_content: 'We value your privacy, and collects only necessary data. We do anonymous count when you are requesting images' + + ' from us, and the data is solely used to decide if we should put more resource to optimize load experience. If you set the' + + ' format preference, then a cookie will be sent everytime you request images from us (include from other sites), to let us know' + + ' your preference. We won\'t use the cookie to do any other thing.', + about_based_on: 'Credits', + about_based_content: 'Chromatic is made possible by many open source projects.', + about_license_other: 'Other', + + info_main: 'Main Page', + info_about: 'About', + + title_about: 'About', + title_admin: 'Admin', + title_gallery: 'Gallery', + title_gallery_me: 'My gallery', + title_login: 'Login', + title_register: 'Register', + title_not_found: 'Not Found', + title_upload: 'Upload', + title_uploading: 'Uploading, %1 files finished', + title_uploading_error: 'Uploading, %1 files finished, %2 files failed', + title_uploaded: '%1 files uploaded', + title_uploaded_error: '%1 files uploaded, %2 files failed', + + time_year: 'y', + time_year_post: 'Year %1', + time_month: 'MMM, y', + time_month_post: '%1', + time_week: 'w, y', + time_week_post: 'Week %1', + time_day: 'mediumDate', + time_day_post: '%1', + + '0000': 'Successful', + '0001': 'Password strength too weak', + '0002': 'User already exist', + '0003': 'The invite code is invalid', + '0004': 'Wrong account or password', + '0005': 'The specified user doesn\'t exist', + '0006': 'The specified invite code already exist', + '0007': 'The specified invite code doesn\'t exist', + '0008': 'Bad request', + '0009': 'The specified image doesn\'t exist', + '0010': 'The image is broken or unsupported', + '0011': 'Failed encoding image', + '0012': 'Original image doesn\'t exist', + '0013': 'Request being made without credential', + '0014': 'Credential used in request has expired', + '0015': 'Permission denied', + + 9999: 'Unknown error', + + http_error: 'HTTP %1', + net_error: 'Network error', + action_retry: 'Retry', + login_expired: 'Your login info has expired and you have to login.', + other: '(!Missing!)' + }, + 'zh-CN': { + main_subtitle: '分享你的图片。为Web访问优化。', + main_register: '注册', + main_login: '登录', + language_switch: '切换语言', + + header_admin: '管理界面', + header_upload: '上传', + header_upload_frozen: '你的账号已被停用。', + header_change_avatar: '更换头像', + header_change_password: '更改密码', + header_logout: '退出登录', + header_image_pref: '图像格式偏好', + header_gallery: '图片库', + + login_title: '登录', + login_username: '用户名', + login_password: '密码', + login_action_login: '登录', + login_action_register: '注册新帐号', + login_success_message: '登录成功。', + login_failed_message: '登录失败:%1。', + + register_title: '注册', + register_username: '用户名', + register_password: '密码', + register_password_repeat: '重复密码', + register_password_see: '显示密码', + register_password_hide: '隐藏密码', + register_code: '邀请码', + register_hint_password: '密码至少8位长,须同时包含数字及大小写字母', + register_error_user_exist: '用户已经存在', + register_error_password_weak: '密码强度太弱', + register_error_password_mismatch: '两次输入的密码不一致', + register_action_login: '登录已有帐号', + register_action_register: '注册', + + guard_need_login_message: '你必须登录才可浏览此界面。', + + gallery_broken_image: '图片不可用', + gallery_loading: '正在加载……', + gallery_tag_search: '仅显示特定标签的图片', + gallery_tag_placeholder: '输入标签', + gallery_tag_failed_message: '标签加载失败:%1。', + gallery_tag_empty: '(空标签)', + gallery_group_label: '分组依据:', + gallery_group_time: '时间', + gallery_group_tag: '标签', + gallery_group_time_label: '分组时长:', + gallery_group_time_day: '天', + gallery_group_time_week: '周', + gallery_group_time_month: '月', + gallery_group_time_year: '年', + gallery_select_all_typed: '全选本组', + gallery_deselect_all_typed: '取消选择本组', + gallery_reverse_all_typed: '反选本组', + gallery_action_edit: '编辑所选图片', + gallery_action_code: '获取所选图片的发布代码', + gallery_action_delete: '删除所选图片', + gallery_action_select_all: '选择当前所有图片', + gallery_action_select_reverse: '反选所有图片', + gallery_action_done: '完成', + gallery_overlay_action_info: '图片信息', + gallery_overlay_action_open: '在新标签页内打开图片', + gallery_overlay_action_edit: '编辑此图片信息', + gallery_overlay_action_code: '获取此图片的发布代码', + gallery_overlay_action_delete: '删除此图片', + gallery_overlay_action_close: '回到列表', + gallery_fetch_failed: '图片列表加载失败:%1。', + gallery_empty: '你还没有上传图片。', + gallery_empty_other: '无图片。', + gallery_empty_upload: '上传', + + upload_minimize: '最小化', + upload_expand: '还原', + upload_close: '关闭', + upload_title_file_plural_0: '尚无文件', + upload_title_file_plural_1: '1个文件', + upload_title_file_plural_other: '个文件', + upload_title_idle: '上传', + upload_title_select_files_p1: '已选择', + upload_title_select_files_p2: '', + upload_title_uploading_fine_p1: '正在上传,', + upload_title_uploading_fine_p2: ',', + upload_title_uploading_fine_p3: '已上传完毕。', + upload_title_uploading_some_error_p1: '正在上传,', + upload_title_uploading_some_error_p2: ',', + upload_title_uploading_some_error_p3: '已上传完毕,', + upload_title_uploading_some_error_p4: '上传失败。', + upload_title_uploaded_fine: '上传完毕,全部文件上传成功。', + upload_title_uploaded_some_error_p1: '上传完毕,', + upload_title_uploaded_some_error_p2: '上传成功,', + upload_title_uploaded_some_error_p3: '上传失败。', + upload_drop_file_pre: '将文件拖拽到此处,或', + upload_drop_file_picker: '选择文件', + upload_drop_file_post: '以上传。', + upload_drop_file_release: '松开鼠标即可上传', + upload_file_name_unknown: '(未知文件名)', + upload_file_type_unknown: '(未知格式)', + upload_submit_no_valid: '你选择的文件里没有图片。', + upload_preview_type_header: '显示模式:', + upload_preview_type_grid: '网格', + upload_preview_type_list: '列表', + upload_preview_limit_height: '限制预览高度', + upload_preview_title: '图片预览', + upload_info_form_title: '设置', + upload_info_tag_header: '图片标签', + upload_info_tag_header_help: '标签是用来归类图像的主要方式', + upload_info_tag_header_placeholder: '在此输入标签,或留空以使用空标签', + upload_info_tag_header_hint: '(默认:创建新标签)', + upload_info_allow_origins: '可访问网站', + upload_info_allow_origins_help: '浏览者仅能从这些网站访问你的图片', + upload_info_allow_origins_placeholder: '在此输入域名,使用逗号或回车分隔', + upload_info_allow_origins_any: '(任何网站)', + upload_info_allow_origins_none: '(直接打开)', + upload_info_allow_origins_exact: '(精确匹配)', + upload_info_allow_origins_wildcard: '(允许子站)', + upload_info_allow_origins_bad: '(当前输入不是一个有效域名)', + upload_info_allow_origins_exist: '(允许列表已包含此域名)', + upload_failed_info: '上传失败:%1', + upload_operation_reset: '重置', + upload_operation_submit: '上传', + + upload_advanced_button: '高级上传', + upload_advanced_title: '高级上传', + upload_advanced_warning_head: '警告:', + upload_advanced_warning_text: '高级上传允许你手动上传不同格式的同一张图片,并直接将上传的文件提供给用户。' + + '服务端不会对上传的文件进行任何校验,因此本功能仅面向高级用户。在操作时请小心。', + upload_advanced_file_title: '上传文件', + upload_advanced_select_file: '选择文件', + upload_advanced_selected: '重新选择', + upload_advanced_status_not_selected: '尚未选择文件', + upload_advanced_status_before: '文件大小:', + upload_advanced_status_after: '。', + upload_advanced_info_title: '设置', + upload_advanced_operation_cancel: '取消', + upload_advanced_operation_upload: '上传', + upload_advanced_operation_uploading: '正在上传', + upload_advanced_operation_uploaded: '关闭', + upload_advanced_operation_exit: '退出', + upload_advanced_failed_message: '上传失败: %1。', + upload_advanced_success_message: '上传成功', + upload_advanced_progress: '上传进度', + + edit_images_title: '编辑图片', + edit_images_tab_tag: '标签', + edit_images_title_tag: '更改图片标签', + edit_images_tab_origins: '可访问网站', + edit_images_title_origins: '更改图片可访问网站', + + edit_images_result_success: '图片编辑成功。', + edit_images_result_failed: '图片编辑失败:%1。', + + dialog_cancel: '取消', + dialog_submit: '确定', + + default_confirm_title: '确认', + default_confirm_word: '确实要执行动作吗?', + default_confirm_yes: '确定', + default_confirm_no: '取消', + + delete_select_confirm_title: '删除图片', + delete_select_confirm_word: '确实要删除选择的图片吗?', + delete_select_confirm_yes: '删除', + delete_select_confirm_no: '取消', + + delete_this_confirm_title: '删除此图片', + delete_this_confirm_word: '确实要删除这张图片吗?', + delete_this_confirm_yes: '删除', + delete_this_confirm_no: '取消', + + upload_confirm_title: '中止上传并退出', + upload_confirm_word: '确实要退出吗?部分文件可能不会被上传。', + upload_confirm_yes: '退出', + upload_confirm_no: '取消', + + delete_images_result_success: '图片已删除。', + delete_images_result_failed: '图片删除失败:%1。', + + generate_code_title: '生成发布代码', + generate_code_type: '代码类型', + generate_code_type_label: '选择一个代码类型', + generate_type_html: 'HTML', + generate_type_bbcode: 'BBCode', + generate_type_url: '链接', + generate_type_custom: '自定义', + generate_custom_template: '模板', + generate_custom_template_placeholder: '每张图片的代码模板', + generate_custom_template_hint: '请使用%url%标注放置图片链接的位置', + generate_custom_separator: '分隔内容', + generate_custom_separator_placeholder: '置于图片间的代码', + generate_custom_separator_hint: '分隔代码将置于每两张图片代码的中间', + generate_result_copy: '复制', + generate_result_copied: '代码已复制', + generate_code_done: '完成', + generate_sort_title: '排序图片', + generate_sort_reverse: '反序', + + info_title: '图片信息', + info_user: '所有者', + info_tag: '标签', + info_upload_time: '上传时间', + info_view: '浏览量', + info_origins: '允许访问的站点', + info_formats: '可用格式', + info_formats_available: '此格式可用', + info_formats_encoding: '此格式正在转码,即将可用', + info_done: '关闭', + + change_password_title: '更改密码', + change_password_old: '旧密码', + change_password_new: '新密码', + change_password_cancel: '取消', + change_password_submit: '更改', + change_password_failed: '更改密码失败:%1。', + change_password_succeed: '密码已更改。请重新登录。', + + change_avatar_title: '更换头像', + change_avatar_title_pick_file: '第一步:请选择一张图片', + change_avatar_file_selector: '选择图片', + change_avatar_file_bad: '图片加载失败。请确认这是一个图片文件。', + change_avatar_title_crop: '第二步:裁剪图片', + change_avatar_preview_no_file: '请先选择一张图片,进行裁剪。', + change_avatar_preview: '头像预览', + change_avatar_success: '头像已更换。', + change_avatar_failed: '头像更换失败:%1', + change_avatar_cancel: '取消', + change_avatar_submit: '保存', + + admin_user: '用户', + admin_code: '邀请码', + admin_user_search: '仅显示用户名包含关键字的用户', + admin_user_search_placeholder: '输入关键字', + admin_user_fetch_failed: '用户列表获取失败:%1。', + admin_user_username: '用户名', + admin_user_privileged: '高级功能权限', + admin_user_privileged_set: '已修改用户权限。', + admin_user_privileged_failed: '用户权限修改失败:%1。', + admin_user_frozen: '冻结', + admin_user_frozen_set: '已修改用户冻结状态。', + admin_user_frozen_failed: '用户冻结状态修改失败:%1。', + admin_operation: '操作', + admin_user_set_password: '设置密码', + admin_user_password_ok: '密码已修改。', + admin_user_password_failed: '密码修改失败:%1。', + admin_user_set_avatar: '设置头像', + admin_user_check_gallery: '查看用户图片库', + admin_delete: '删除', + admin_user_delete_ok: '用户已删除。', + admin_user_delete_failed: '用户删除失败:%1。', + admin_user_delete_selected: '删除已选用户', + admin_user_delete_selected_ok: '所选用户已删除。', + admin_user_delete_selected_failed: '所选用户删除失败:%1。', + admin_user_add: '添加用户', + admin_code_code: '邀请码', + admin_code_times: '可用次数', + admin_code_set_times: '设置可用次数', + admin_code_delete_selected: '删除已选邀请码', + admin_code_add: '添加邀请码', + admin_code_fetch_failed: '邀请码列表加载失败:%1。', + admin_code_delete_ok: '已删除邀请码。', + admin_code_delete_failed: '邀请码删除失败:%1。', + admin_times_set_ok: '已更新可用次数。', + admin_times_set_failed: '可用次数更新失败:%1。', + + paginator_first_page: '第一页', + paginator_last_page: '最后一页', + paginator_previous_page: '上一页', + paginator_next_page: '下一页', + paginator_per_page: '每页条目:', + paginator_position_none: '无项目', + paginator_position_zero: '当前列表无项目,共有%1个项目', + paginator_position: '第%1到%2个项目,共%3个项目', + + default_prompt_title: '输入一个值', + default_prompt_label: '值', + default_prompt_placeholder: '在此输入值', + default_prompt_yes: '确定', + default_prompt_no: '取消', + + set_password_prompt_title: '设置用户密码', + set_password_prompt_label: '新密码', + set_password_prompt_placeholder: '在此输入新密码', + set_password_prompt_yes: '设置', + set_password_prompt_no: '取消', + + delete_user_confirm_title: '删除用户', + delete_user_confirm_word: '你确定要删除这个用户吗?', + delete_user_confirm_yes: '删除', + delete_user_confirm_no: '取消', + + delete_users_confirm_title: '删除已选用户', + delete_users_confirm_word: '你确定要删除这些用户吗?', + delete_users_confirm_yes: '删除', + delete_users_confirm_no: '取消', + + default_choice_title: '选择一个值', + default_choice_word: '请从下面两个值中选择一个', + default_choice_right: '右', + default_choice_left: '左', + + delete_user_choice_title: '处理用户图片', + delete_user_choice_word: '如何处理该用户上传的图片?', + delete_user_choice_right: '归入此帐号', + delete_user_choice_left: '删除', + + delete_users_choice_title: '处理用户图片', + delete_users_choice_word: '如何处理所选用户上传的图片?', + delete_users_choice_right: '归入此帐号', + delete_users_choice_left: '删除', + + add_user_title: '添加用户', + add_user_username: '用户名', + add_user_password: '密码', + add_user_privileged: '赋予高级功能权限', + add_user_error_user_exist: '用户已存在', + add_user_cancel: '取消', + add_user_submit: '添加', + add_user_success_msg: '已添加用户%1。', + add_user_error_msg: '用户添加失败:%1。', + + add_code_title: '添加邀请码', + add_code_code: '邀请代码', + add_code_times: '可用次数', + add_code_error_code_exist: '该邀请码已存在', + add_code_cancel: '取消', + add_code_submit: '添加', + add_code_success_msg: '已添加邀请码%1。', + add_code_error_msg: '邀请码添加失败:%1。', + + code_times_prompt_title: '设置可用次数', + code_times_prompt_label: '可用次数', + code_times_prompt_placeholder: '输入一个数字', + code_times_prompt_yes: '保存', + code_times_prompt_no: '取消', + + delete_code_confirm_title: '删除邀请码', + delete_code_confirm_word: '确实要删除这个邀请码吗?', + delete_code_confirm_yes: '删除', + delete_code_confirm_no: '取消', + + not_found_title: 'Not found', + not_found_word: '此页面没有内容。', + not_found_back: '返回主页', + + format_pref_title: '图像格式偏好设置', + format_pref_note_title: '提示:', + format_pref_note_content: '你可以在这里设置你对不同格式图像的偏好。默认情况下服务器会以你浏览器所支持,且文件大小最小的格式提供图片,' + + '但这需要CPU进行更多运算以显示图片。一般情况下解码时间的差异可以忽略不计,但更小的文件可以显著提高加载速度,因此我们推荐你使用默认设置。' + + '如果你正在使用一个低性能设备,或你十分关心设备的续航时间,你可以在此手动设置你对各种格式的偏好,我们在提供图片时会优先考虑你设置的顺序。', + format_pref_support_status: '你浏览器对各种图片格式的支持:', + format_pref_support_status_detecting: '正在检测……', + format_pref_support_status_supported: '支持', + format_pref_support_status_not_supported: '不支持', + format_pref_support_status_jpeg_png: '所有浏览器都支持PNG和JPEG格式。', + format_pref_drag_hint: '拖拽以改变顺序:', + format_pref_cancel: '取消', + format_pref_reset: '重置', + format_pref_save: '保存', + format_pref_save_done: '偏好已保存。', + format_pref_reset_done: '偏好已重置。', + + about_title: '关于', + about_content: 'Chromatic是一个专注于优化Web访问体验的图片存储平台。', + about_privacy: '隐私', + about_privacy_content: '我们注重对你的隐私保护,仅收集最低限度的必要数据。在你浏览图片时,我们会匿名统计图片的访问次数,' + + '并依此决定我们是否应该使用更多服务器资源进一步优化浏览体验。如果你设置了图片格式偏好' + + ',则每当你请求图片(包括在其他网站加载来自本平台的图片)时,都会向服务器发送一个Cookie以告知你的偏好。此Cookie不会被用作其他用途。', + about_based_on: '鸣谢', + about_based_content: 'Chromatic的诞生离不开许许多多开源项目的支持。', + about_license_other: '其他许可', + + info_main: '主页', + info_about: '关于', + + title_about: '关于', + title_admin: '管理', + title_gallery: '图片库', + title_gallery_me: '我的图片库', + title_login: '登录', + title_register: '注册', + title_not_found: '未知页面', + title_upload: '上传图片', + title_uploading: '正在上传,%1个文件已上传', + title_uploading_error: '正在上传,%1个文件已上传,%2个文件上传失败', + title_uploaded: '%1个文件上传成功', + title_uploaded_error: '%1个文件上传成功,%2个文件上传失败', + + time_year: 'y年', + time_year_post: '%1', + time_month: 'y年MMM', + time_month_post: '%1', + time_week: 'y年第w周', + time_week_post: '%1', + time_day: 'mediumDate', + time_day_post: '%1', + + '0000': '成功', + '0001': '密码强度过弱', + '0002': '用户已经存在', + '0003': '邀请码无效', + '0004': '用户名或密码不正确', + '0005': '所选用户不存在', + '0006': '指定的邀请码已经存在', + '0007': '指定的邀请码不存在', + '0008': '请求不合法', + '0009': '指定的图片不存在', + '0010': '图片文件已损坏,或不支持此格式', + '0011': '图片编码失败', + '0012': '原始图片不存在', + '0013': '请求缺少认证代码', + '0014': '认证代码已过期', + '0015': '权限不足', + + 9999: '未知错误', + http_error: 'HTTP %1', + net_error: '网络错误', + action_retry: '重试', + login_expired: '你的登录信息已过期。请重新登录。', + other: '(!缺失!)' + }, + 'zh-TW': { + main_subtitle: '分享你的圖片。為Web訪問最佳化。', + main_register: '註冊', + main_login: '登入', + language_switch: '切換語言', + + header_admin: '管理界面', + header_upload: '上傳', + header_upload_frozen: '你的帳號已被停用。', + header_change_avatar: '更換頭像', + header_change_password: '更改密碼', + header_logout: '退出登入', + header_image_pref: '圖像格式偏好', + header_gallery: '圖片庫', + + login_title: '登入', + login_username: '使用者名稱', + login_password: '密碼', + login_action_login: '登入', + login_action_register: '註冊新帳號', + login_success_message: '登入成功。', + login_failed_message: '登入失敗:%1。', + + register_title: '註冊', + register_username: '使用者名稱', + register_password: '密碼', + register_password_repeat: '重複密碼', + register_password_see: '顯示密碼', + register_password_hide: '隱藏密碼', + register_code: '邀請碼', + register_hint_password: '密碼至少8位長,須同時包含數字及大小寫字母', + register_error_user_exist: '用戶已經存在', + register_error_password_weak: '密碼強度太弱', + register_error_password_mismatch: '兩次輸入的密碼不一致', + register_action_login: '登入已有帳號', + register_action_register: '註冊', + + guard_need_login_message: '你必須登入才可瀏覽此界面。', + + gallery_broken_image: '圖片不可用', + gallery_loading: '正在載入……', + gallery_tag_search: '僅顯示特定標籤的圖片', + gallery_tag_placeholder: '輸入標籤', + gallery_tag_failed_message: '標籤載入失敗:%1。', + gallery_tag_empty: '(空標籤)', + gallery_group_label: '分組依據:', + gallery_group_time: '時間', + gallery_group_tag: '標籤', + gallery_group_time_label: '分組時長:', + gallery_group_time_day: '天', + gallery_group_time_week: '周', + gallery_group_time_month: '月', + gallery_group_time_year: '年', + gallery_select_all_typed: '全選本組', + gallery_deselect_all_typed: '取消選擇本組', + gallery_reverse_all_typed: '反選本組', + gallery_action_edit: '編輯所選圖片', + gallery_action_code: '獲取所選圖片的發布代碼', + gallery_action_delete: '刪除所選圖片', + gallery_action_select_all: '選擇當前所有圖片', + gallery_action_select_reverse: '反選所有圖片', + gallery_action_done: '完成', + gallery_overlay_action_info: '圖片訊息', + gallery_overlay_action_open: '在新標籤頁內打開圖片', + gallery_overlay_action_edit: '編輯此圖片訊息', + gallery_overlay_action_code: '獲取此圖片的發布代碼', + gallery_overlay_action_delete: '刪除此圖片', + gallery_overlay_action_close: '回到列表', + gallery_fetch_failed: '圖片列表載入失敗:%1。', + gallery_empty: '你還沒有上傳圖片。', + gallery_empty_other: '無圖片。', + gallery_empty_upload: '上傳', + + upload_minimize: '最小化', + upload_expand: '還原', + upload_close: '關閉', + upload_title_file_plural_0: '尚無文件', + upload_title_file_plural_1: '1個文件', + upload_title_file_plural_other: '個文件', + upload_title_idle: '上傳', + upload_title_select_files_p1: '已選擇', + upload_title_select_files_p2: '', + upload_title_uploading_fine_p1: '正在上傳,', + upload_title_uploading_fine_p2: ',', + upload_title_uploading_fine_p3: '已上傳完畢。', + upload_title_uploading_some_error_p1: '正在上傳,', + upload_title_uploading_some_error_p2: ',', + upload_title_uploading_some_error_p3: '已上傳完畢,', + upload_title_uploading_some_error_p4: '上傳失敗。', + upload_title_uploaded_fine: '上傳完畢,全部文件上傳成功。', + upload_title_uploaded_some_error_p1: '上傳完畢,', + upload_title_uploaded_some_error_p2: '上傳成功,', + upload_title_uploaded_some_error_p3: '上傳失敗。', + upload_drop_file_pre: '將文件拖拽到此處,或', + upload_drop_file_picker: '選擇文件', + upload_drop_file_post: '以上傳。', + upload_drop_file_release: '鬆開滑鼠即可上傳', + upload_file_name_unknown: '(未知檔案名)', + upload_file_type_unknown: '(未知格式)', + upload_submit_no_valid: '你選擇的文件裡沒有圖片。', + upload_preview_type_header: '顯示模式:', + upload_preview_type_grid: '網格', + upload_preview_type_list: '列表', + upload_preview_limit_height: '限制預覽高度', + upload_preview_title: '圖片預覽', + upload_info_form_title: '設置', + upload_info_tag_header: '圖片標籤', + upload_info_tag_header_help: '標籤是用來歸類圖像的主要方式', + upload_info_tag_header_placeholder: '在此輸入標籤,或留空以使用空標籤', + upload_info_tag_header_hint: '(默認:創建新標籤)', + upload_info_allow_origins: '可訪問網站', + upload_info_allow_origins_help: '瀏覽者僅能從這些網站訪問你的圖片', + upload_info_allow_origins_placeholder: '在此輸入域名,使用逗號或回車分隔', + upload_info_allow_origins_any: '(任何網站)', + upload_info_allow_origins_none: '(直接打開)', + upload_info_allow_origins_exact: '(精確匹配)', + upload_info_allow_origins_wildcard: '(允許子站)', + upload_info_allow_origins_bad: '(當前輸入不是一個有效域名)', + upload_info_allow_origins_exist: '(允許列表已包含此域名)', + upload_failed_info: '上傳失敗:%1', + upload_operation_reset: '重設', + upload_operation_submit: '上傳', + + upload_advanced_button: '高級上傳', + upload_advanced_title: '高級上傳', + upload_advanced_warning_head: '警告:', + upload_advanced_warning_text: '高級上傳允許你手動上傳不同格式的同一張圖片,並直接將上傳的文件提供給用戶。' + + '服務端不會對上傳的文件進行任何校驗,因此本功能僅面向高級用戶。在操作時請小心。', + upload_advanced_file_title: '上傳文件', + upload_advanced_select_file: '選擇文件', + upload_advanced_selected: '重新選擇', + upload_advanced_status_not_selected: '尚未選擇文件', + upload_advanced_status_before: '檔案大小:', + upload_advanced_status_after: '。', + upload_advanced_info_title: '設置', + upload_advanced_operation_cancel: '取消', + upload_advanced_operation_upload: '上傳', + upload_advanced_operation_uploading: '正在上傳', + upload_advanced_operation_uploaded: '關閉', + upload_advanced_operation_exit: '退出', + upload_advanced_failed_message: '上傳失敗: %1。', + upload_advanced_success_message: '上傳成功', + upload_advanced_progress: '上傳進度', + + edit_images_title: '編輯圖片', + edit_images_tab_tag: '標籤', + edit_images_title_tag: '更改圖片標籤', + edit_images_tab_origins: '可訪問網站', + edit_images_title_origins: '更改圖片可訪問網站', + + edit_images_result_success: '圖片編輯成功。', + edit_images_result_failed: '圖片編輯失敗:%1。', + + dialog_cancel: '取消', + dialog_submit: '確定', + + default_confirm_title: '確認', + default_confirm_word: '確定要執行動作嗎?', + default_confirm_yes: '確定', + default_confirm_no: '取消', + + delete_select_confirm_title: '刪除圖片', + delete_select_confirm_word: '確定要刪除選擇的圖片嗎?', + delete_select_confirm_yes: '刪除', + delete_select_confirm_no: '取消', + + delete_this_confirm_title: '刪除此圖片', + delete_this_confirm_word: '確定要刪除這張圖片嗎?', + delete_this_confirm_yes: '刪除', + delete_this_confirm_no: '取消', + + upload_confirm_title: '中止上傳並退出', + upload_confirm_word: '確定要退出嗎?部分文件可能不會被上傳。', + upload_confirm_yes: '退出', + upload_confirm_no: '取消', + + delete_images_result_success: '圖片已刪除。', + delete_images_result_failed: '圖片刪除失敗:%1。', + + generate_code_title: '生成發布代碼', + generate_code_type: '代碼類型', + generate_code_type_label: '選擇一個代碼類型', + generate_type_html: 'HTML', + generate_type_bbcode: 'BBCode', + generate_type_url: '連結', + generate_type_custom: '自訂', + generate_custom_template: '模板', + generate_custom_template_placeholder: '每張圖片的代碼模板', + generate_custom_template_hint: '請使用%url%標註放置圖片連結的位置', + generate_custom_separator: '分隔內容', + generate_custom_separator_placeholder: '置於圖片間的代碼', + generate_custom_separator_hint: '分隔代碼將置於每兩張圖片代碼的中間', + generate_result_copy: '複製', + generate_result_copied: '代碼已複製', + generate_code_done: '完成', + generate_sort_title: '排序圖片', + generate_sort_reverse: '反序', + + info_title: '圖片訊息', + info_user: '所有者', + info_tag: '標籤', + info_upload_time: '上傳時間', + info_view: '瀏覽量', + info_origins: '允許訪問的站點', + info_formats: '可用格式', + info_formats_available: '此格式可用', + info_formats_encoding: '此格式正在轉檔,即將可用', + info_done: '關閉', + + change_password_title: '更改密碼', + change_password_old: '舊密碼', + change_password_new: '新密碼', + change_password_cancel: '取消', + change_password_submit: '更改', + change_password_failed: '更改密碼失敗:%1。', + change_password_succeed: '密碼已更改。請重新登入。', + + change_avatar_title: '更換頭像', + change_avatar_title_pick_file: '第一步:請選擇一張圖片', + change_avatar_file_selector: '選擇圖片', + change_avatar_file_bad: '圖片載入失敗。請確認這是一個圖片文件。', + change_avatar_title_crop: '第二步:裁剪圖片', + change_avatar_preview_no_file: '請先選擇一張圖片,進行裁剪。', + change_avatar_preview: '頭像預覽', + change_avatar_success: '頭像已更換。', + change_avatar_failed: '頭像更換失敗:%1', + change_avatar_cancel: '取消', + change_avatar_submit: '保存', + + admin_user: '用戶', + admin_code: '邀請碼', + admin_user_search: '僅顯示使用者名稱包含關鍵字的用戶', + admin_user_search_placeholder: '輸入關鍵字', + admin_user_fetch_failed: '用戶列表獲取失敗:%1。', + admin_user_username: '使用者名稱', + admin_user_privileged: '進階功能權限', + admin_user_privileged_set: '已修改用戶權限。', + admin_user_privileged_failed: '用戶權限修改失敗:%1。', + admin_user_frozen: '凍結', + admin_user_frozen_set: '已修改用戶凍結狀態。', + admin_user_frozen_failed: '用戶凍結狀態修改失敗:%1。', + admin_operation: '操作', + admin_user_set_password: '設置密碼', + admin_user_password_ok: '密碼已修改。', + admin_user_password_failed: '密碼修改失敗:%1。', + admin_user_set_avatar: '設置頭像', + admin_user_check_gallery: '查看用戶圖片庫', + admin_delete: '刪除', + admin_user_delete_ok: '用戶已刪除。', + admin_user_delete_failed: '用戶刪除失敗:%1。', + admin_user_delete_selected: '刪除已選用戶', + admin_user_delete_selected_ok: '所選用戶已刪除。', + admin_user_delete_selected_failed: '所選用戶刪除失敗:%1。', + admin_user_add: '添加用戶', + admin_code_code: '邀請碼', + admin_code_times: '可用次數', + admin_code_set_times: '設置可用次數', + admin_code_delete_selected: '刪除已選邀請碼', + admin_code_add: '添加邀請碼', + admin_code_fetch_failed: '邀請碼列表載入失敗:%1。', + admin_code_delete_ok: '已刪除邀請碼。', + admin_code_delete_failed: '邀請碼刪除失敗:%1。', + admin_times_set_ok: '已更新可用次數。', + admin_times_set_failed: '可用次數更新失敗:%1。', + + paginator_first_page: '第一頁', + paginator_last_page: '最後一頁', + paginator_previous_page: '上一頁', + paginator_next_page: '下一頁', + paginator_per_page: '每頁條目:', + paginator_position_none: '無項目', + paginator_position_zero: '當前列表無項目,共有%1個項目', + paginator_position: '第%1到%2個項目,共%3個項目', + + default_prompt_title: '輸入一個值', + default_prompt_label: '值', + default_prompt_placeholder: '在此輸入值', + default_prompt_yes: '確定', + default_prompt_no: '取消', + + set_password_prompt_title: '設置用戶密碼', + set_password_prompt_label: '新密碼', + set_password_prompt_placeholder: '在此輸入新密碼', + set_password_prompt_yes: '設置', + set_password_prompt_no: '取消', + + delete_user_confirm_title: '刪除用戶', + delete_user_confirm_word: '你確定要刪除這個用戶嗎?', + delete_user_confirm_yes: '刪除', + delete_user_confirm_no: '取消', + + delete_users_confirm_title: '刪除已選用戶', + delete_users_confirm_word: '你確定要刪除這些用戶嗎?', + delete_users_confirm_yes: '刪除', + delete_users_confirm_no: '取消', + + default_choice_title: '選擇一個值', + default_choice_word: '請從下面兩個值中選擇一個', + default_choice_right: '右', + default_choice_left: '左', + + delete_user_choice_title: '處理用戶圖片', + delete_user_choice_word: '如何處理該用戶上傳的圖片?', + delete_user_choice_right: '歸入此帳號', + delete_user_choice_left: '刪除', + + delete_users_choice_title: '處理用戶圖片', + delete_users_choice_word: '如何處理所選用戶上傳的圖片?', + delete_users_choice_right: '歸入此帳號', + delete_users_choice_left: '刪除', + + add_user_title: '添加用戶', + add_user_username: '使用者名稱', + add_user_password: '密碼', + add_user_privileged: '賦予進階功能權限', + add_user_error_user_exist: '用戶已存在', + add_user_cancel: '取消', + add_user_submit: '添加', + add_user_success_msg: '已添加用戶%1。', + add_user_error_msg: '用戶添加失敗:%1。', + + add_code_title: '添加邀請碼', + add_code_code: '邀請代碼', + add_code_times: '可用次數', + add_code_error_code_exist: '該邀請碼已存在', + add_code_cancel: '取消', + add_code_submit: '添加', + add_code_success_msg: '已添加邀請碼%1。', + add_code_error_msg: '邀請碼添加失敗:%1。', + + code_times_prompt_title: '設置可用次數', + code_times_prompt_label: '可用次數', + code_times_prompt_placeholder: '輸入一個數字', + code_times_prompt_yes: '保存', + code_times_prompt_no: '取消', + + delete_code_confirm_title: '刪除邀請碼', + delete_code_confirm_word: '確定要刪除這個邀請碼嗎?', + delete_code_confirm_yes: '刪除', + delete_code_confirm_no: '取消', + + not_found_title: 'Not found', + not_found_word: '此頁面沒有內容。', + not_found_back: '返回首頁', + + format_pref_title: '圖像格式偏好設置', + format_pref_note_title: '提示:', + format_pref_note_content: '你可以在這裡設置你對不同格式圖像的偏好。默認情況下伺服器會以你瀏覽器所支持,且檔案大小最小的格式提供圖片,' + + '但這需要CPU進行更多運算以顯示圖片。一般情況下解碼時間的差異可以忽略不計,但更小的文件可以顯著提高載入速度,因此我們推薦你使用默認設置。' + + '如果你正在使用一個低性能設備,或你十分關心設備的續航時間,你可以在此手動設置你對各種格式的偏好,我們在提供圖片時會優先考慮你設置的順序。', + format_pref_support_status: '你瀏覽器對各種圖片格式的支持:', + format_pref_support_status_detecting: '正在檢測……', + format_pref_support_status_supported: '支持', + format_pref_support_status_not_supported: '不支持', + format_pref_support_status_jpeg_png: '所有瀏覽器都支持PNG和JPEG格式。', + format_pref_drag_hint: '拖拽以改變順序:', + format_pref_cancel: '取消', + format_pref_reset: '重設', + format_pref_save: '保存', + format_pref_save_done: '偏好已保存。', + format_pref_reset_done: '偏好已重設。', + + about_title: '關於', + about_content: 'Chromatic是一個專注於最佳化Web訪問體驗的圖片儲存平台。', + about_privacy: '隱私', + about_privacy_content: '我們注重對你的隱私保護,僅收集最低限度的必要數據。在你瀏覽圖片時,我們會匿名統計圖片的訪問次數,' + + '並依此決定我們是否應該使用更多伺服器資源進一步最佳化瀏覽體驗。如果你設置了圖片格式偏好' + + ',則每當你請求圖片(包括在其他網站載入來自本平台的圖片)時,都會向伺服器發送一個Cookie以告知你的偏好。此Cookie不會被用作其他用途。', + about_based_on: '鳴謝', + about_based_content: 'Chromatic的誕生離不開許許多多開源項目的支持。', + about_license_other: '其他許可', + + info_main: '首頁', + info_about: '關於', + + title_about: '關於', + title_admin: '管理', + title_gallery: '圖片庫', + title_gallery_me: '我的圖片庫', + title_login: '登入', + title_register: '註冊', + title_not_found: '未知頁面', + title_upload: '上傳圖片', + title_uploading: '正在上傳,%1個文件已上傳', + title_uploading_error: '正在上傳,%1個文件已上傳,%2個文件上傳失敗', + title_uploaded: '%1個文件上傳成功', + title_uploaded_error: '%1個文件上傳成功,%2個文件上傳失敗', + + time_year: 'y年', + time_year_post: '%1', + time_month: 'y年MMM', + time_month_post: '%1', + time_week: 'y年第w周', + time_week_post: '%1', + time_day: 'mediumDate', + time_day_post: '%1', + + '0000': '成功', + '0001': '密碼強度太弱', + '0002': '用戶已經存在', + '0003': '邀請碼無效', + '0004': '使用者名稱或密碼不正確', + '0005': '所選用戶不存在', + '0006': '指定的邀請碼已經存在', + '0007': '指定的邀請碼不存在', + '0008': '請求不合法', + '0009': '指定的圖片不存在', + '0010': '圖片文件已損壞,或不支持此格式', + '0011': '圖片編碼失敗', + '0012': '原始圖片不存在', + '0013': '請求缺少認證代碼', + '0014': '認證代碼已過期', + '0015': '權限不足', + + 9999: '未知錯誤', + http_error: 'HTTP %1', + net_error: '網路錯誤', + action_retry: '重試', + login_expired: '你的登入訊息已過期。請重新登入。', + other: '(!缺失!)' + } +}; + +console.log(JSON.stringify(TransTable)); +*/ +// tslint:disable-next-line:max-line-length +const langJSON = `{"en":{"9999":"Unknown error","main_subtitle":"Share your images. Optimized for web.","main_register":"Register","main_login":"Login","language_switch":"Switch Language","header_admin":"Admin area","header_upload":"Upload","header_upload_frozen":"Your account is suspended.","header_change_avatar":"Change avatar","header_change_password":"Change Password","header_logout":"Logout","header_image_pref":"Image preference","header_gallery":"Gallery","login_title":"Login","login_username":"Username","login_password":"Password","login_action_login":"Login","login_action_register":"Register account","login_success_message":"Login successful.","login_failed_message":"Login failed: %1.","register_title":"Register","register_username":"Username","register_password":"Password","register_password_repeat":"Repeat password","register_password_see":"Unmask password","register_password_hide":"Mask password","register_code":"Invite code","register_hint_password":"At least 8 characters, contains upper, lower letter and number","register_error_user_exist":"User already exist","register_error_password_weak":"Password too weak","register_error_password_mismatch":"Passwords didn't match","register_action_login":"I have account","register_action_register":"Register","guard_need_login_message":"You must login to access this page.","gallery_broken_image":"Image not available","gallery_loading":"Loading...","gallery_tag_search":"Show specified tag only...","gallery_tag_placeholder":"Input tag here","gallery_tag_failed_message":"Failed loading tags: %1.","gallery_tag_empty":"(Empty tag)","gallery_group_label":"Group images by: ","gallery_group_time":"Time","gallery_group_tag":"Tag","gallery_group_time_label":"Time span: ","gallery_group_time_day":"Day","gallery_group_time_week":"Week","gallery_group_time_month":"Month","gallery_group_time_year":"Year","gallery_select_all_typed":"Select all of this group","gallery_deselect_all_typed":"Deselect all of this group","gallery_reverse_all_typed":"Reverse select images of this group","gallery_action_edit":"Edit selected images","gallery_action_code":"Get code of selected images to publish","gallery_action_delete":"Delete selected images","gallery_action_select_all":"Select all images","gallery_action_select_reverse":"Reverse selection","gallery_action_done":"Done","gallery_overlay_action_info":"Image info","gallery_overlay_action_open":"Open image file in new page","gallery_overlay_action_edit":"Edit this image","gallery_overlay_action_code":"Get code of this image to publish","gallery_overlay_action_delete":"Delete this image","gallery_overlay_action_close":"Return to gallery","gallery_fetch_failed":"Failed loading images: %1.","gallery_empty":"Your gallery is empty.","gallery_empty_other":"Nothing here.","gallery_empty_upload":"Upload image","upload_minimize":"Minimize","upload_expand":"Expand","upload_close":"Close","upload_title_file_plural_0":"no file","upload_title_file_plural_1":"1 file","upload_title_file_plural_other":"files","upload_title_idle":"Upload","upload_title_select_files_p1":"","upload_title_select_files_p2":"selected for upload","upload_title_uploading_fine_p1":"Uploading files,","upload_title_uploading_fine_p2":", ","upload_title_uploading_fine_p3":"finished","upload_title_uploading_some_error_p1":"Uploading files,","upload_title_uploading_some_error_p2":",","upload_title_uploading_some_error_p3":"finished,","upload_title_uploading_some_error_p4":"failed","upload_title_uploaded_fine":"Upload finished, all files succeed","upload_title_uploaded_some_error_p1":"Upload finished,","upload_title_uploaded_some_error_p2":"succeed,","upload_title_uploaded_some_error_p3":"failed","upload_drop_file_pre":"Drop image files here, or ","upload_drop_file_picker":"Select files","upload_drop_file_post":".","upload_drop_file_release":"Release your mouse to submit files.","upload_file_name_unknown":"(Unknown filename)","upload_file_type_unknown":"(Unknown type)","upload_submit_no_valid":"No valid image file found in your selected files.","upload_preview_type_header":"Display mode: ","upload_preview_type_grid":"Grid","upload_preview_type_list":"List","upload_preview_limit_height":"Limit height","upload_preview_title":"Image Preview","upload_info_form_title":"Settings","upload_info_tag_header":"Tag for images","upload_info_tag_header_help":"Tag is the primary way to group your images","upload_info_tag_header_placeholder":"Type tag here, or keep blank to use empty tag","upload_info_tag_header_hint":"(Default: tag will set to your input)","upload_info_allow_origins":"Allow origins","upload_info_allow_origins_help":"User can only access images from these sites","upload_info_allow_origins_placeholder":"Type domain here, split with comma or enter","upload_info_allow_origins_any":" (Any site)","upload_info_allow_origins_none":" (Direct access)","upload_info_allow_origins_exact":" (Exact match)","upload_info_allow_origins_wildcard":" (Wildcard match)","upload_info_allow_origins_bad":" (Current input is not a valid domain)","upload_info_allow_origins_exist":" (Already covered)","upload_failed_info":"Upload failed: %1","upload_operation_reset":"Reset","upload_operation_submit":"Upload","upload_advanced_button":"Advanced upload","upload_advanced_title":"Advanced upload","upload_advanced_warning_head":"Warning: ","upload_advanced_warning_text":"Advanced upload allows you to upload files in different formats of the same image, and serve to the user as is. Server will not do any check, even not checking if it is a valid image file, so as it called \\"Advanced\\". Please be cautious.","upload_advanced_file_title":"Pick files","upload_advanced_select_file":"Select file","upload_advanced_selected":"Reselect","upload_advanced_status_not_selected":"Not selected","upload_advanced_status_before":"File size:","upload_advanced_status_after":".","upload_advanced_info_title":"Settings","upload_advanced_operation_cancel":"Cancel","upload_advanced_operation_upload":"Upload","upload_advanced_operation_uploading":"Uploading","upload_advanced_operation_uploaded":"Close","upload_advanced_operation_exit":"Exit","upload_advanced_failed_message":"Uploading file failed: %1.","upload_advanced_success_message":"Upload successful.","upload_advanced_progress":"Upload Progress","edit_images_title":"Edit images","edit_images_tab_tag":"Tag","edit_images_title_tag":"Change tag of images","edit_images_tab_origins":"Origins","edit_images_title_origins":"Change origins of images","edit_images_result_success":"Image edit successful.","edit_images_result_failed":"Image edit failed: %1.","dialog_cancel":"Cancel","dialog_submit":"OK","default_confirm_title":"Confirmation","default_confirm_word":"Are you sure to process?","default_confirm_yes":"Confirm","default_confirm_no":"Cancel","delete_select_confirm_title":"Delete images","delete_select_confirm_word":"Are you sure to delete selected images?","delete_select_confirm_yes":"Delete","delete_select_confirm_no":"Cancel","delete_this_confirm_title":"Delete this image","delete_this_confirm_word":"Are you sure to delete this image?","delete_this_confirm_yes":"Delete","delete_this_confirm_no":"Cancel","upload_confirm_title":"Abort upload and exit","upload_confirm_word":"Are you sure to exit? Some files may not be uploaded if you exit now.","upload_confirm_yes":"Exit","upload_confirm_no":"Cancel","delete_images_result_success":"Image deleted.","delete_images_result_failed":"Image delete failed: %1.","generate_code_title":"Generate Code for Publish","generate_code_type":"Code type:","generate_code_type_label":"Select a code type","generate_type_html":"HTML","generate_type_bbcode":"BBCode","generate_type_url":"URL","generate_type_custom":"Customize","generate_custom_template":"Template","generate_custom_template_placeholder":"Template code for each image","generate_custom_template_hint":"Use %url% to mark the position to place url","generate_custom_separator":"Separator","generate_custom_separator_placeholder":"Separator code between images","generate_custom_separator_hint":"Separator code will be placed between codes of each image","generate_result_copy":"Copy","generate_result_copied":"Code copied.","generate_code_done":"Done","generate_sort_title":"Sort images","generate_sort_reverse":"Reverse","info_title":"Image info","info_user":"Uploader","info_tag":"Image tag","info_upload_time":"Upload time","info_view":"View count","info_origins":"Allowed origin sites","info_formats":"Available formats","info_formats_available":"This format is available","info_formats_encoding":"This format is being transcode, and will be available soon","info_done":"Close","change_password_title":"Change Password","change_password_old":"Old password","change_password_new":"New password","change_password_cancel":"Cancel","change_password_submit":"Change","change_password_failed":"Change password failed: %1.","change_password_succeed":"Password changed. Please re-login.","change_avatar_title":"Change Avatar","change_avatar_title_pick_file":"1. Pick an image","change_avatar_file_selector":"Select image","change_avatar_file_bad":"Failed loading image. Is this a valid image file?","change_avatar_title_crop":"2. Crop your image","change_avatar_preview_no_file":"Please select an image first.","change_avatar_preview":"Avatar preview","change_avatar_success":"Avatar changed.","change_avatar_failed":"Failed change avatar: %1","change_avatar_cancel":"Cancel","change_avatar_submit":"Save","admin_user":"User","admin_code":"Code","admin_user_search":"Show users name contains keyword only...","admin_user_search_placeholder":"Input keyword here","admin_user_fetch_failed":"Failed fetching user list: %1.","admin_user_username":"User","admin_user_privileged":"Privileged","admin_user_privileged_set":"User privilege status changed.","admin_user_privileged_failed":"Failed changing user privilege status: %1.","admin_user_frozen":"Frozen","admin_user_frozen_set":"User freeze status changed.","admin_user_frozen_failed":"Failed changing user freeze status: %1.","admin_operation":"Operations","admin_user_set_password":"Set Password","admin_user_password_ok":"User password changed.","admin_user_password_failed":"Failed changing user password: %1.","admin_user_set_avatar":"Set Avatar","admin_user_check_gallery":"Check Gallery","admin_delete":"Delete","admin_user_delete_ok":"User deleted.","admin_user_delete_failed":"Failed deleting user: %1.","admin_user_delete_selected":"Delete selected users","admin_user_delete_selected_ok":"Selected users deleted.","admin_user_delete_selected_failed":"Failed deleting selected users: %1.","admin_user_add":"Add user","admin_code_code":"Invite code","admin_code_times":"Available times","admin_code_set_times":"Set available times","admin_code_delete_selected":"Delete selected codes","admin_code_add":"Add invite code","admin_code_fetch_failed":"Failed fetching invite code list: %1.","admin_code_delete_ok":"Invite code deleted.","admin_code_delete_failed":"Failed deleting invite code: %1.","admin_times_set_ok":"Available times updated.","admin_times_set_failed":"Failed updating available times: %1.","paginator_first_page":"First page","paginator_last_page":"Last page","paginator_previous_page":"Previous page","paginator_next_page":"Next page","paginator_per_page":"Items per page: ","paginator_position_none":"No item","paginator_position_zero":"0, total %1","paginator_position":"%1 – %2 of %3","default_prompt_title":"Input a value","default_prompt_label":"The value","default_prompt_placeholder":"Input value here","default_prompt_yes":"Submit","default_prompt_no":"Cancel","set_password_prompt_title":"Set user password","set_password_prompt_label":"New Password","set_password_prompt_placeholder":"Input new password here","set_password_prompt_yes":"Set","set_password_prompt_no":"Cancel","delete_user_confirm_title":"Delete user","delete_user_confirm_word":"Are you sure to delete this user?","delete_user_confirm_yes":"Delete","delete_user_confirm_no":"Cancel","delete_users_confirm_title":"Delete selected users","delete_users_confirm_word":"Are you sure to delete selected users?","delete_users_confirm_yes":"Delete","delete_users_confirm_no":"Cancel","default_choice_title":"Select one value","default_choice_word":"Please select one of the two values","default_choice_right":"Right","default_choice_left":"Left","delete_user_choice_title":"Image handling","delete_user_choice_word":"How to handle images (if any) of this user?","delete_user_choice_right":"Adopt","delete_user_choice_left":"Delete","delete_users_choice_title":"Image handling","delete_users_choice_word":"How to handle images (if any) of the selected users?","delete_users_choice_right":"Adopt","delete_users_choice_left":"Delete","add_user_title":"Add user","add_user_username":"Username","add_user_password":"Password","add_user_privileged":"Privilege this user","add_user_error_user_exist":"User already exist","add_user_cancel":"Cancel","add_user_submit":"Add","add_user_success_msg":"User %1 added.","add_user_error_msg":"Failed adding user: %1.","add_code_title":"Add invite code","add_code_code":"Code token","add_code_times":"Available times","add_code_error_code_exist":"Code already exist","add_code_cancel":"Cancel","add_code_submit":"Add","add_code_success_msg":"Code %1 added.","add_code_error_msg":"Failed adding code: %1.","code_times_prompt_title":"Set available times","code_times_prompt_label":"Available times","code_times_prompt_placeholder":"Input a number here","code_times_prompt_yes":"Save","code_times_prompt_no":"Cancel","delete_code_confirm_title":"Delete code","delete_code_confirm_word":"Are you sure to delete this code?","delete_code_confirm_yes":"Delete","delete_code_confirm_no":"Cancel","not_found_title":"Not found","not_found_word":"Nothing available here.","not_found_back":"To main page","format_pref_title":"Format preference settings","format_pref_note_title":"Note: ","format_pref_note_content":"Here you can set your image format preference. By default we serve you image in the format that has minimal size while supported by your browser, and this means CPU will do more work to decode the image. Usually the difference is small, but smaller file loads significantly faster, so we recommend you to keep the default. However, if you are using a low-end device or you really cares battery life, you can manually set your preference below, and we will respect your settings and try to serve you images with formats in your desired order.","format_pref_support_status":"Your browser's support to different formats: ","format_pref_support_status_detecting":"Detecting...","format_pref_support_status_supported":"Supported","format_pref_support_status_not_supported":"Not supported","format_pref_support_status_jpeg_png":"Every browser supports PNG and JPEG.","format_pref_drag_hint":"Drag to sort the formats:","format_pref_cancel":"Cancel","format_pref_reset":"Reset","format_pref_save":"Save","format_pref_save_done":"Preference saved.","format_pref_reset_done":"Preference cleared.","about_title":"About","about_content":"Chromatic is an image store platform specially targeted on optimizing web access experience.","about_privacy":"Privacy","about_privacy_content":"We value your privacy, and collects only necessary data. We do anonymous count when you are requesting images from us, and the data is solely used to decide if we should put more resource to optimize load experience. If you set the format preference, then a cookie will be sent everytime you request images from us (include from other sites), to let us know your preference. We won't use the cookie to do any other thing.","about_based_on":"Credits","about_based_content":"Chromatic is made possible by many open source projects.","about_license_other":"Other","info_main":"Main Page","info_about":"About","title_about":"About","title_admin":"Admin","title_gallery":"Gallery","title_gallery_me":"My gallery","title_login":"Login","title_register":"Register","title_not_found":"Not Found","title_upload":"Upload","title_uploading":"Uploading, %1 files finished","title_uploading_error":"Uploading, %1 files finished, %2 files failed","title_uploaded":"%1 files uploaded","title_uploaded_error":"%1 files uploaded, %2 files failed","time_year":"y","time_year_post":"Year %1","time_month":"MMM, y","time_month_post":"%1","time_week":"w, y","time_week_post":"Week %1","time_day":"mediumDate","time_day_post":"%1","0000":"Successful","0001":"Password strength too weak","0002":"User already exist","0003":"The invite code is invalid","0004":"Wrong account or password","0005":"The specified user doesn't exist","0006":"The specified invite code already exist","0007":"The specified invite code doesn't exist","0008":"Bad request","0009":"The specified image doesn't exist","0010":"The image is broken or unsupported","0011":"Failed encoding image","0012":"Original image doesn't exist","0013":"Request being made without credential","0014":"Credential used in request has expired","0015":"Permission denied","http_error":"HTTP %1","net_error":"Network error","action_retry":"Retry","login_expired":"Your login info has expired and you have to login.","other":"(!Missing!)"},"zh-CN":{"9999":"未知错误","main_subtitle":"分享你的图片。为Web访问优化。","main_register":"注册","main_login":"登录","language_switch":"切换语言","header_admin":"管理界面","header_upload":"上传","header_upload_frozen":"你的账号已被停用。","header_change_avatar":"更换头像","header_change_password":"更改密码","header_logout":"退出登录","header_image_pref":"图像格式偏好","header_gallery":"图片库","login_title":"登录","login_username":"用户名","login_password":"密码","login_action_login":"登录","login_action_register":"注册新帐号","login_success_message":"登录成功。","login_failed_message":"登录失败:%1。","register_title":"注册","register_username":"用户名","register_password":"密码","register_password_repeat":"重复密码","register_password_see":"显示密码","register_password_hide":"隐藏密码","register_code":"邀请码","register_hint_password":"密码至少8位长,须同时包含数字及大小写字母","register_error_user_exist":"用户已经存在","register_error_password_weak":"密码强度太弱","register_error_password_mismatch":"两次输入的密码不一致","register_action_login":"登录已有帐号","register_action_register":"注册","guard_need_login_message":"你必须登录才可浏览此界面。","gallery_broken_image":"图片不可用","gallery_loading":"正在加载……","gallery_tag_search":"仅显示特定标签的图片","gallery_tag_placeholder":"输入标签","gallery_tag_failed_message":"标签加载失败:%1。","gallery_tag_empty":"(空标签)","gallery_group_label":"分组依据:","gallery_group_time":"时间","gallery_group_tag":"标签","gallery_group_time_label":"分组时长:","gallery_group_time_day":"天","gallery_group_time_week":"周","gallery_group_time_month":"月","gallery_group_time_year":"年","gallery_select_all_typed":"全选本组","gallery_deselect_all_typed":"取消选择本组","gallery_reverse_all_typed":"反选本组","gallery_action_edit":"编辑所选图片","gallery_action_code":"获取所选图片的发布代码","gallery_action_delete":"删除所选图片","gallery_action_select_all":"选择当前所有图片","gallery_action_select_reverse":"反选所有图片","gallery_action_done":"完成","gallery_overlay_action_info":"图片信息","gallery_overlay_action_open":"在新标签页内打开图片","gallery_overlay_action_edit":"编辑此图片信息","gallery_overlay_action_code":"获取此图片的发布代码","gallery_overlay_action_delete":"删除此图片","gallery_overlay_action_close":"回到列表","gallery_fetch_failed":"图片列表加载失败:%1。","gallery_empty":"你还没有上传图片。","gallery_empty_other":"无图片。","gallery_empty_upload":"上传","upload_minimize":"最小化","upload_expand":"还原","upload_close":"关闭","upload_title_file_plural_0":"尚无文件","upload_title_file_plural_1":"1个文件","upload_title_file_plural_other":"个文件","upload_title_idle":"上传","upload_title_select_files_p1":"已选择","upload_title_select_files_p2":"","upload_title_uploading_fine_p1":"正在上传,","upload_title_uploading_fine_p2":",","upload_title_uploading_fine_p3":"已上传完毕。","upload_title_uploading_some_error_p1":"正在上传,","upload_title_uploading_some_error_p2":",","upload_title_uploading_some_error_p3":"已上传完毕,","upload_title_uploading_some_error_p4":"上传失败。","upload_title_uploaded_fine":"上传完毕,全部文件上传成功。","upload_title_uploaded_some_error_p1":"上传完毕,","upload_title_uploaded_some_error_p2":"上传成功,","upload_title_uploaded_some_error_p3":"上传失败。","upload_drop_file_pre":"将文件拖拽到此处,或","upload_drop_file_picker":"选择文件","upload_drop_file_post":"以上传。","upload_drop_file_release":"松开鼠标即可上传","upload_file_name_unknown":"(未知文件名)","upload_file_type_unknown":"(未知格式)","upload_submit_no_valid":"你选择的文件里没有图片。","upload_preview_type_header":"显示模式:","upload_preview_type_grid":"网格","upload_preview_type_list":"列表","upload_preview_limit_height":"限制预览高度","upload_preview_title":"图片预览","upload_info_form_title":"设置","upload_info_tag_header":"图片标签","upload_info_tag_header_help":"标签是用来归类图像的主要方式","upload_info_tag_header_placeholder":"在此输入标签,或留空以使用空标签","upload_info_tag_header_hint":"(默认:创建新标签)","upload_info_allow_origins":"可访问网站","upload_info_allow_origins_help":"浏览者仅能从这些网站访问你的图片","upload_info_allow_origins_placeholder":"在此输入域名,使用逗号或回车分隔","upload_info_allow_origins_any":"(任何网站)","upload_info_allow_origins_none":"(直接打开)","upload_info_allow_origins_exact":"(精确匹配)","upload_info_allow_origins_wildcard":"(允许子站)","upload_info_allow_origins_bad":"(当前输入不是一个有效域名)","upload_info_allow_origins_exist":"(允许列表已包含此域名)","upload_failed_info":"上传失败:%1","upload_operation_reset":"重置","upload_operation_submit":"上传","upload_advanced_button":"高级上传","upload_advanced_title":"高级上传","upload_advanced_warning_head":"警告:","upload_advanced_warning_text":"高级上传允许你手动上传不同格式的同一张图片,并直接将上传的文件提供给用户。服务端不会对上传的文件进行任何校验,因此本功能仅面向高级用户。在操作时请小心。","upload_advanced_file_title":"上传文件","upload_advanced_select_file":"选择文件","upload_advanced_selected":"重新选择","upload_advanced_status_not_selected":"尚未选择文件","upload_advanced_status_before":"文件大小:","upload_advanced_status_after":"。","upload_advanced_info_title":"设置","upload_advanced_operation_cancel":"取消","upload_advanced_operation_upload":"上传","upload_advanced_operation_uploading":"正在上传","upload_advanced_operation_uploaded":"关闭","upload_advanced_operation_exit":"退出","upload_advanced_failed_message":"上传失败: %1。","upload_advanced_success_message":"上传成功","upload_advanced_progress":"上传进度","edit_images_title":"编辑图片","edit_images_tab_tag":"标签","edit_images_title_tag":"更改图片标签","edit_images_tab_origins":"可访问网站","edit_images_title_origins":"更改图片可访问网站","edit_images_result_success":"图片编辑成功。","edit_images_result_failed":"图片编辑失败:%1。","dialog_cancel":"取消","dialog_submit":"确定","default_confirm_title":"确认","default_confirm_word":"确实要执行动作吗?","default_confirm_yes":"确定","default_confirm_no":"取消","delete_select_confirm_title":"删除图片","delete_select_confirm_word":"确实要删除选择的图片吗?","delete_select_confirm_yes":"删除","delete_select_confirm_no":"取消","delete_this_confirm_title":"删除此图片","delete_this_confirm_word":"确实要删除这张图片吗?","delete_this_confirm_yes":"删除","delete_this_confirm_no":"取消","upload_confirm_title":"中止上传并退出","upload_confirm_word":"确实要退出吗?部分文件可能不会被上传。","upload_confirm_yes":"退出","upload_confirm_no":"取消","delete_images_result_success":"图片已删除。","delete_images_result_failed":"图片删除失败:%1。","generate_code_title":"生成发布代码","generate_code_type":"代码类型","generate_code_type_label":"选择一个代码类型","generate_type_html":"HTML","generate_type_bbcode":"BBCode","generate_type_url":"链接","generate_type_custom":"自定义","generate_custom_template":"模板","generate_custom_template_placeholder":"每张图片的代码模板","generate_custom_template_hint":"请使用%url%标注放置图片链接的位置","generate_custom_separator":"分隔内容","generate_custom_separator_placeholder":"置于图片间的代码","generate_custom_separator_hint":"分隔代码将置于每两张图片代码的中间","generate_result_copy":"复制","generate_result_copied":"代码已复制","generate_code_done":"完成","generate_sort_title":"排序图片","generate_sort_reverse":"反序","info_title":"图片信息","info_user":"所有者","info_tag":"标签","info_upload_time":"上传时间","info_view":"浏览量","info_origins":"允许访问的站点","info_formats":"可用格式","info_formats_available":"此格式可用","info_formats_encoding":"此格式正在转码,即将可用","info_done":"关闭","change_password_title":"更改密码","change_password_old":"旧密码","change_password_new":"新密码","change_password_cancel":"取消","change_password_submit":"更改","change_password_failed":"更改密码失败:%1。","change_password_succeed":"密码已更改。请重新登录。","change_avatar_title":"更换头像","change_avatar_title_pick_file":"第一步:请选择一张图片","change_avatar_file_selector":"选择图片","change_avatar_file_bad":"图片加载失败。请确认这是一个图片文件。","change_avatar_title_crop":"第二步:裁剪图片","change_avatar_preview_no_file":"请先选择一张图片,进行裁剪。","change_avatar_preview":"头像预览","change_avatar_success":"头像已更换。","change_avatar_failed":"头像更换失败:%1","change_avatar_cancel":"取消","change_avatar_submit":"保存","admin_user":"用户","admin_code":"邀请码","admin_user_search":"仅显示用户名包含关键字的用户","admin_user_search_placeholder":"输入关键字","admin_user_fetch_failed":"用户列表获取失败:%1。","admin_user_username":"用户名","admin_user_privileged":"高级功能权限","admin_user_privileged_set":"已修改用户权限。","admin_user_privileged_failed":"用户权限修改失败:%1。","admin_user_frozen":"冻结","admin_user_frozen_set":"已修改用户冻结状态。","admin_user_frozen_failed":"用户冻结状态修改失败:%1。","admin_operation":"操作","admin_user_set_password":"设置密码","admin_user_password_ok":"密码已修改。","admin_user_password_failed":"密码修改失败:%1。","admin_user_set_avatar":"设置头像","admin_user_check_gallery":"查看用户图片库","admin_delete":"删除","admin_user_delete_ok":"用户已删除。","admin_user_delete_failed":"用户删除失败:%1。","admin_user_delete_selected":"删除已选用户","admin_user_delete_selected_ok":"所选用户已删除。","admin_user_delete_selected_failed":"所选用户删除失败:%1。","admin_user_add":"添加用户","admin_code_code":"邀请码","admin_code_times":"可用次数","admin_code_set_times":"设置可用次数","admin_code_delete_selected":"删除已选邀请码","admin_code_add":"添加邀请码","admin_code_fetch_failed":"邀请码列表加载失败:%1。","admin_code_delete_ok":"已删除邀请码。","admin_code_delete_failed":"邀请码删除失败:%1。","admin_times_set_ok":"已更新可用次数。","admin_times_set_failed":"可用次数更新失败:%1。","paginator_first_page":"第一页","paginator_last_page":"最后一页","paginator_previous_page":"上一页","paginator_next_page":"下一页","paginator_per_page":"每页条目:","paginator_position_none":"无项目","paginator_position_zero":"当前列表无项目,共有%1个项目","paginator_position":"第%1到%2个项目,共%3个项目","default_prompt_title":"输入一个值","default_prompt_label":"值","default_prompt_placeholder":"在此输入值","default_prompt_yes":"确定","default_prompt_no":"取消","set_password_prompt_title":"设置用户密码","set_password_prompt_label":"新密码","set_password_prompt_placeholder":"在此输入新密码","set_password_prompt_yes":"设置","set_password_prompt_no":"取消","delete_user_confirm_title":"删除用户","delete_user_confirm_word":"你确定要删除这个用户吗?","delete_user_confirm_yes":"删除","delete_user_confirm_no":"取消","delete_users_confirm_title":"删除已选用户","delete_users_confirm_word":"你确定要删除这些用户吗?","delete_users_confirm_yes":"删除","delete_users_confirm_no":"取消","default_choice_title":"选择一个值","default_choice_word":"请从下面两个值中选择一个","default_choice_right":"右","default_choice_left":"左","delete_user_choice_title":"处理用户图片","delete_user_choice_word":"如何处理该用户上传的图片?","delete_user_choice_right":"归入此帐号","delete_user_choice_left":"删除","delete_users_choice_title":"处理用户图片","delete_users_choice_word":"如何处理所选用户上传的图片?","delete_users_choice_right":"归入此帐号","delete_users_choice_left":"删除","add_user_title":"添加用户","add_user_username":"用户名","add_user_password":"密码","add_user_privileged":"赋予高级功能权限","add_user_error_user_exist":"用户已存在","add_user_cancel":"取消","add_user_submit":"添加","add_user_success_msg":"已添加用户%1。","add_user_error_msg":"用户添加失败:%1。","add_code_title":"添加邀请码","add_code_code":"邀请代码","add_code_times":"可用次数","add_code_error_code_exist":"该邀请码已存在","add_code_cancel":"取消","add_code_submit":"添加","add_code_success_msg":"已添加邀请码%1。","add_code_error_msg":"邀请码添加失败:%1。","code_times_prompt_title":"设置可用次数","code_times_prompt_label":"可用次数","code_times_prompt_placeholder":"输入一个数字","code_times_prompt_yes":"保存","code_times_prompt_no":"取消","delete_code_confirm_title":"删除邀请码","delete_code_confirm_word":"确实要删除这个邀请码吗?","delete_code_confirm_yes":"删除","delete_code_confirm_no":"取消","not_found_title":"Not found","not_found_word":"此页面没有内容。","not_found_back":"返回主页","format_pref_title":"图像格式偏好设置","format_pref_note_title":"提示:","format_pref_note_content":"你可以在这里设置你对不同格式图像的偏好。默认情况下服务器会以你浏览器所支持,且文件大小最小的格式提供图片,但这需要CPU进行更多运算以显示图片。一般情况下解码时间的差异可以忽略不计,但更小的文件可以显著提高加载速度,因此我们推荐你使用默认设置。如果你正在使用一个低性能设备,或你十分关心设备的续航时间,你可以在此手动设置你对各种格式的偏好,我们在提供图片时会优先考虑你设置的顺序。","format_pref_support_status":"你浏览器对各种图片格式的支持:","format_pref_support_status_detecting":"正在检测……","format_pref_support_status_supported":"支持","format_pref_support_status_not_supported":"不支持","format_pref_support_status_jpeg_png":"所有浏览器都支持PNG和JPEG格式。","format_pref_drag_hint":"拖拽以改变顺序:","format_pref_cancel":"取消","format_pref_reset":"重置","format_pref_save":"保存","format_pref_save_done":"偏好已保存。","format_pref_reset_done":"偏好已重置。","about_title":"关于","about_content":"Chromatic是一个专注于优化Web访问体验的图片存储平台。","about_privacy":"隐私","about_privacy_content":"我们注重对你的隐私保护,仅收集最低限度的必要数据。在你浏览图片时,我们会匿名统计图片的访问次数,并依此决定我们是否应该使用更多服务器资源进一步优化浏览体验。如果你设置了图片格式偏好,则每当你请求图片(包括在其他网站加载来自本平台的图片)时,都会向服务器发送一个Cookie以告知你的偏好。此Cookie不会被用作其他用途。","about_based_on":"鸣谢","about_based_content":"Chromatic的诞生离不开许许多多开源项目的支持。","about_license_other":"其他许可","info_main":"主页","info_about":"关于","title_about":"关于","title_admin":"管理","title_gallery":"图片库","title_gallery_me":"我的图片库","title_login":"登录","title_register":"注册","title_not_found":"未知页面","title_upload":"上传图片","title_uploading":"正在上传,%1个文件已上传","title_uploading_error":"正在上传,%1个文件已上传,%2个文件上传失败","title_uploaded":"%1个文件上传成功","title_uploaded_error":"%1个文件上传成功,%2个文件上传失败","time_year":"y年","time_year_post":"%1","time_month":"y年MMM","time_month_post":"%1","time_week":"y年第w周","time_week_post":"%1","time_day":"mediumDate","time_day_post":"%1","0000":"成功","0001":"密码强度过弱","0002":"用户已经存在","0003":"邀请码无效","0004":"用户名或密码不正确","0005":"所选用户不存在","0006":"指定的邀请码已经存在","0007":"指定的邀请码不存在","0008":"请求不合法","0009":"指定的图片不存在","0010":"图片文件已损坏,或不支持此格式","0011":"图片编码失败","0012":"原始图片不存在","0013":"请求缺少认证代码","0014":"认证代码已过期","0015":"权限不足","http_error":"HTTP %1","net_error":"网络错误","action_retry":"重试","login_expired":"你的登录信息已过期。请重新登录。","other":"(!缺失!)"},"zh-TW":{"9999":"未知錯誤","main_subtitle":"分享你的圖片。為Web訪問最佳化。","main_register":"註冊","main_login":"登入","language_switch":"切換語言","header_admin":"管理界面","header_upload":"上傳","header_upload_frozen":"你的帳號已被停用。","header_change_avatar":"更換頭像","header_change_password":"更改密碼","header_logout":"退出登入","header_image_pref":"圖像格式偏好","header_gallery":"圖片庫","login_title":"登入","login_username":"使用者名稱","login_password":"密碼","login_action_login":"登入","login_action_register":"註冊新帳號","login_success_message":"登入成功。","login_failed_message":"登入失敗:%1。","register_title":"註冊","register_username":"使用者名稱","register_password":"密碼","register_password_repeat":"重複密碼","register_password_see":"顯示密碼","register_password_hide":"隱藏密碼","register_code":"邀請碼","register_hint_password":"密碼至少8位長,須同時包含數字及大小寫字母","register_error_user_exist":"用戶已經存在","register_error_password_weak":"密碼強度太弱","register_error_password_mismatch":"兩次輸入的密碼不一致","register_action_login":"登入已有帳號","register_action_register":"註冊","guard_need_login_message":"你必須登入才可瀏覽此界面。","gallery_broken_image":"圖片不可用","gallery_loading":"正在載入……","gallery_tag_search":"僅顯示特定標籤的圖片","gallery_tag_placeholder":"輸入標籤","gallery_tag_failed_message":"標籤載入失敗:%1。","gallery_tag_empty":"(空標籤)","gallery_group_label":"分組依據:","gallery_group_time":"時間","gallery_group_tag":"標籤","gallery_group_time_label":"分組時長:","gallery_group_time_day":"天","gallery_group_time_week":"周","gallery_group_time_month":"月","gallery_group_time_year":"年","gallery_select_all_typed":"全選本組","gallery_deselect_all_typed":"取消選擇本組","gallery_reverse_all_typed":"反選本組","gallery_action_edit":"編輯所選圖片","gallery_action_code":"獲取所選圖片的發布代碼","gallery_action_delete":"刪除所選圖片","gallery_action_select_all":"選擇當前所有圖片","gallery_action_select_reverse":"反選所有圖片","gallery_action_done":"完成","gallery_overlay_action_info":"圖片訊息","gallery_overlay_action_open":"在新標籤頁內打開圖片","gallery_overlay_action_edit":"編輯此圖片訊息","gallery_overlay_action_code":"獲取此圖片的發布代碼","gallery_overlay_action_delete":"刪除此圖片","gallery_overlay_action_close":"回到列表","gallery_fetch_failed":"圖片列表載入失敗:%1。","gallery_empty":"你還沒有上傳圖片。","gallery_empty_other":"無圖片。","gallery_empty_upload":"上傳","upload_minimize":"最小化","upload_expand":"還原","upload_close":"關閉","upload_title_file_plural_0":"尚無文件","upload_title_file_plural_1":"1個文件","upload_title_file_plural_other":"個文件","upload_title_idle":"上傳","upload_title_select_files_p1":"已選擇","upload_title_select_files_p2":"","upload_title_uploading_fine_p1":"正在上傳,","upload_title_uploading_fine_p2":",","upload_title_uploading_fine_p3":"已上傳完畢。","upload_title_uploading_some_error_p1":"正在上傳,","upload_title_uploading_some_error_p2":",","upload_title_uploading_some_error_p3":"已上傳完畢,","upload_title_uploading_some_error_p4":"上傳失敗。","upload_title_uploaded_fine":"上傳完畢,全部文件上傳成功。","upload_title_uploaded_some_error_p1":"上傳完畢,","upload_title_uploaded_some_error_p2":"上傳成功,","upload_title_uploaded_some_error_p3":"上傳失敗。","upload_drop_file_pre":"將文件拖拽到此處,或","upload_drop_file_picker":"選擇文件","upload_drop_file_post":"以上傳。","upload_drop_file_release":"鬆開滑鼠即可上傳","upload_file_name_unknown":"(未知檔案名)","upload_file_type_unknown":"(未知格式)","upload_submit_no_valid":"你選擇的文件裡沒有圖片。","upload_preview_type_header":"顯示模式:","upload_preview_type_grid":"網格","upload_preview_type_list":"列表","upload_preview_limit_height":"限制預覽高度","upload_preview_title":"圖片預覽","upload_info_form_title":"設置","upload_info_tag_header":"圖片標籤","upload_info_tag_header_help":"標籤是用來歸類圖像的主要方式","upload_info_tag_header_placeholder":"在此輸入標籤,或留空以使用空標籤","upload_info_tag_header_hint":"(默認:創建新標籤)","upload_info_allow_origins":"可訪問網站","upload_info_allow_origins_help":"瀏覽者僅能從這些網站訪問你的圖片","upload_info_allow_origins_placeholder":"在此輸入域名,使用逗號或回車分隔","upload_info_allow_origins_any":"(任何網站)","upload_info_allow_origins_none":"(直接打開)","upload_info_allow_origins_exact":"(精確匹配)","upload_info_allow_origins_wildcard":"(允許子站)","upload_info_allow_origins_bad":"(當前輸入不是一個有效域名)","upload_info_allow_origins_exist":"(允許列表已包含此域名)","upload_failed_info":"上傳失敗:%1","upload_operation_reset":"重設","upload_operation_submit":"上傳","upload_advanced_button":"高級上傳","upload_advanced_title":"高級上傳","upload_advanced_warning_head":"警告:","upload_advanced_warning_text":"高級上傳允許你手動上傳不同格式的同一張圖片,並直接將上傳的文件提供給用戶。服務端不會對上傳的文件進行任何校驗,因此本功能僅面向高級用戶。在操作時請小心。","upload_advanced_file_title":"上傳文件","upload_advanced_select_file":"選擇文件","upload_advanced_selected":"重新選擇","upload_advanced_status_not_selected":"尚未選擇文件","upload_advanced_status_before":"檔案大小:","upload_advanced_status_after":"。","upload_advanced_info_title":"設置","upload_advanced_operation_cancel":"取消","upload_advanced_operation_upload":"上傳","upload_advanced_operation_uploading":"正在上傳","upload_advanced_operation_uploaded":"關閉","upload_advanced_operation_exit":"退出","upload_advanced_failed_message":"上傳失敗: %1。","upload_advanced_success_message":"上傳成功","upload_advanced_progress":"上傳進度","edit_images_title":"編輯圖片","edit_images_tab_tag":"標籤","edit_images_title_tag":"更改圖片標籤","edit_images_tab_origins":"可訪問網站","edit_images_title_origins":"更改圖片可訪問網站","edit_images_result_success":"圖片編輯成功。","edit_images_result_failed":"圖片編輯失敗:%1。","dialog_cancel":"取消","dialog_submit":"確定","default_confirm_title":"確認","default_confirm_word":"確定要執行動作嗎?","default_confirm_yes":"確定","default_confirm_no":"取消","delete_select_confirm_title":"刪除圖片","delete_select_confirm_word":"確定要刪除選擇的圖片嗎?","delete_select_confirm_yes":"刪除","delete_select_confirm_no":"取消","delete_this_confirm_title":"刪除此圖片","delete_this_confirm_word":"確定要刪除這張圖片嗎?","delete_this_confirm_yes":"刪除","delete_this_confirm_no":"取消","upload_confirm_title":"中止上傳並退出","upload_confirm_word":"確定要退出嗎?部分文件可能不會被上傳。","upload_confirm_yes":"退出","upload_confirm_no":"取消","delete_images_result_success":"圖片已刪除。","delete_images_result_failed":"圖片刪除失敗:%1。","generate_code_title":"生成發布代碼","generate_code_type":"代碼類型","generate_code_type_label":"選擇一個代碼類型","generate_type_html":"HTML","generate_type_bbcode":"BBCode","generate_type_url":"連結","generate_type_custom":"自訂","generate_custom_template":"模板","generate_custom_template_placeholder":"每張圖片的代碼模板","generate_custom_template_hint":"請使用%url%標註放置圖片連結的位置","generate_custom_separator":"分隔內容","generate_custom_separator_placeholder":"置於圖片間的代碼","generate_custom_separator_hint":"分隔代碼將置於每兩張圖片代碼的中間","generate_result_copy":"複製","generate_result_copied":"代碼已複製","generate_code_done":"完成","generate_sort_title":"排序圖片","generate_sort_reverse":"反序","info_title":"圖片訊息","info_user":"所有者","info_tag":"標籤","info_upload_time":"上傳時間","info_view":"瀏覽量","info_origins":"允許訪問的站點","info_formats":"可用格式","info_formats_available":"此格式可用","info_formats_encoding":"此格式正在轉檔,即將可用","info_done":"關閉","change_password_title":"更改密碼","change_password_old":"舊密碼","change_password_new":"新密碼","change_password_cancel":"取消","change_password_submit":"更改","change_password_failed":"更改密碼失敗:%1。","change_password_succeed":"密碼已更改。請重新登入。","change_avatar_title":"更換頭像","change_avatar_title_pick_file":"第一步:請選擇一張圖片","change_avatar_file_selector":"選擇圖片","change_avatar_file_bad":"圖片載入失敗。請確認這是一個圖片文件。","change_avatar_title_crop":"第二步:裁剪圖片","change_avatar_preview_no_file":"請先選擇一張圖片,進行裁剪。","change_avatar_preview":"頭像預覽","change_avatar_success":"頭像已更換。","change_avatar_failed":"頭像更換失敗:%1","change_avatar_cancel":"取消","change_avatar_submit":"保存","admin_user":"用戶","admin_code":"邀請碼","admin_user_search":"僅顯示使用者名稱包含關鍵字的用戶","admin_user_search_placeholder":"輸入關鍵字","admin_user_fetch_failed":"用戶列表獲取失敗:%1。","admin_user_username":"使用者名稱","admin_user_privileged":"進階功能權限","admin_user_privileged_set":"已修改用戶權限。","admin_user_privileged_failed":"用戶權限修改失敗:%1。","admin_user_frozen":"凍結","admin_user_frozen_set":"已修改用戶凍結狀態。","admin_user_frozen_failed":"用戶凍結狀態修改失敗:%1。","admin_operation":"操作","admin_user_set_password":"設置密碼","admin_user_password_ok":"密碼已修改。","admin_user_password_failed":"密碼修改失敗:%1。","admin_user_set_avatar":"設置頭像","admin_user_check_gallery":"查看用戶圖片庫","admin_delete":"刪除","admin_user_delete_ok":"用戶已刪除。","admin_user_delete_failed":"用戶刪除失敗:%1。","admin_user_delete_selected":"刪除已選用戶","admin_user_delete_selected_ok":"所選用戶已刪除。","admin_user_delete_selected_failed":"所選用戶刪除失敗:%1。","admin_user_add":"添加用戶","admin_code_code":"邀請碼","admin_code_times":"可用次數","admin_code_set_times":"設置可用次數","admin_code_delete_selected":"刪除已選邀請碼","admin_code_add":"添加邀請碼","admin_code_fetch_failed":"邀請碼列表載入失敗:%1。","admin_code_delete_ok":"已刪除邀請碼。","admin_code_delete_failed":"邀請碼刪除失敗:%1。","admin_times_set_ok":"已更新可用次數。","admin_times_set_failed":"可用次數更新失敗:%1。","paginator_first_page":"第一頁","paginator_last_page":"最後一頁","paginator_previous_page":"上一頁","paginator_next_page":"下一頁","paginator_per_page":"每頁條目:","paginator_position_none":"無項目","paginator_position_zero":"當前列表無項目,共有%1個項目","paginator_position":"第%1到%2個項目,共%3個項目","default_prompt_title":"輸入一個值","default_prompt_label":"值","default_prompt_placeholder":"在此輸入值","default_prompt_yes":"確定","default_prompt_no":"取消","set_password_prompt_title":"設置用戶密碼","set_password_prompt_label":"新密碼","set_password_prompt_placeholder":"在此輸入新密碼","set_password_prompt_yes":"設置","set_password_prompt_no":"取消","delete_user_confirm_title":"刪除用戶","delete_user_confirm_word":"你確定要刪除這個用戶嗎?","delete_user_confirm_yes":"刪除","delete_user_confirm_no":"取消","delete_users_confirm_title":"刪除已選用戶","delete_users_confirm_word":"你確定要刪除這些用戶嗎?","delete_users_confirm_yes":"刪除","delete_users_confirm_no":"取消","default_choice_title":"選擇一個值","default_choice_word":"請從下面兩個值中選擇一個","default_choice_right":"右","default_choice_left":"左","delete_user_choice_title":"處理用戶圖片","delete_user_choice_word":"如何處理該用戶上傳的圖片?","delete_user_choice_right":"歸入此帳號","delete_user_choice_left":"刪除","delete_users_choice_title":"處理用戶圖片","delete_users_choice_word":"如何處理所選用戶上傳的圖片?","delete_users_choice_right":"歸入此帳號","delete_users_choice_left":"刪除","add_user_title":"添加用戶","add_user_username":"使用者名稱","add_user_password":"密碼","add_user_privileged":"賦予進階功能權限","add_user_error_user_exist":"用戶已存在","add_user_cancel":"取消","add_user_submit":"添加","add_user_success_msg":"已添加用戶%1。","add_user_error_msg":"用戶添加失敗:%1。","add_code_title":"添加邀請碼","add_code_code":"邀請代碼","add_code_times":"可用次數","add_code_error_code_exist":"該邀請碼已存在","add_code_cancel":"取消","add_code_submit":"添加","add_code_success_msg":"已添加邀請碼%1。","add_code_error_msg":"邀請碼添加失敗:%1。","code_times_prompt_title":"設置可用次數","code_times_prompt_label":"可用次數","code_times_prompt_placeholder":"輸入一個數字","code_times_prompt_yes":"保存","code_times_prompt_no":"取消","delete_code_confirm_title":"刪除邀請碼","delete_code_confirm_word":"確定要刪除這個邀請碼嗎?","delete_code_confirm_yes":"刪除","delete_code_confirm_no":"取消","not_found_title":"Not found","not_found_word":"此頁面沒有內容。","not_found_back":"返回首頁","format_pref_title":"圖像格式偏好設置","format_pref_note_title":"提示:","format_pref_note_content":"你可以在這裡設置你對不同格式圖像的偏好。默認情況下伺服器會以你瀏覽器所支持,且檔案大小最小的格式提供圖片,但這需要CPU進行更多運算以顯示圖片。一般情況下解碼時間的差異可以忽略不計,但更小的文件可以顯著提高載入速度,因此我們推薦你使用默認設置。如果你正在使用一個低性能設備,或你十分關心設備的續航時間,你可以在此手動設置你對各種格式的偏好,我們在提供圖片時會優先考慮你設置的順序。","format_pref_support_status":"你瀏覽器對各種圖片格式的支持:","format_pref_support_status_detecting":"正在檢測……","format_pref_support_status_supported":"支持","format_pref_support_status_not_supported":"不支持","format_pref_support_status_jpeg_png":"所有瀏覽器都支持PNG和JPEG格式。","format_pref_drag_hint":"拖拽以改變順序:","format_pref_cancel":"取消","format_pref_reset":"重設","format_pref_save":"保存","format_pref_save_done":"偏好已保存。","format_pref_reset_done":"偏好已重設。","about_title":"關於","about_content":"Chromatic是一個專注於最佳化Web訪問體驗的圖片儲存平台。","about_privacy":"隱私","about_privacy_content":"我們注重對你的隱私保護,僅收集最低限度的必要數據。在你瀏覽圖片時,我們會匿名統計圖片的訪問次數,並依此決定我們是否應該使用更多伺服器資源進一步最佳化瀏覽體驗。如果你設置了圖片格式偏好,則每當你請求圖片(包括在其他網站載入來自本平台的圖片)時,都會向伺服器發送一個Cookie以告知你的偏好。此Cookie不會被用作其他用途。","about_based_on":"鳴謝","about_based_content":"Chromatic的誕生離不開許許多多開源項目的支持。","about_license_other":"其他許可","info_main":"首頁","info_about":"關於","title_about":"關於","title_admin":"管理","title_gallery":"圖片庫","title_gallery_me":"我的圖片庫","title_login":"登入","title_register":"註冊","title_not_found":"未知頁面","title_upload":"上傳圖片","title_uploading":"正在上傳,%1個文件已上傳","title_uploading_error":"正在上傳,%1個文件已上傳,%2個文件上傳失敗","title_uploaded":"%1個文件上傳成功","title_uploaded_error":"%1個文件上傳成功,%2個文件上傳失敗","time_year":"y年","time_year_post":"%1","time_month":"y年MMM","time_month_post":"%1","time_week":"y年第w周","time_week_post":"%1","time_day":"mediumDate","time_day_post":"%1","0000":"成功","0001":"密碼強度太弱","0002":"用戶已經存在","0003":"邀請碼無效","0004":"使用者名稱或密碼不正確","0005":"所選用戶不存在","0006":"指定的邀請碼已經存在","0007":"指定的邀請碼不存在","0008":"請求不合法","0009":"指定的圖片不存在","0010":"圖片文件已損壞,或不支持此格式","0011":"圖片編碼失敗","0012":"原始圖片不存在","0013":"請求缺少認證代碼","0014":"認證代碼已過期","0015":"權限不足","http_error":"HTTP %1","net_error":"網路錯誤","action_retry":"重試","login_expired":"你的登入訊息已過期。請重新登入。","other":"(!缺失!)"}}`; +export const TransTable: { [key: string]: { [key: string]: string } } = JSON.parse(langJSON); + +TransTable.zh = TransTable['zh-CN']; +TransTable['zh-HK'] = TransTable['zh-TW']; +TransTable['zh-MO'] = TransTable['zh-TW']; +TransTable['zh-Hant'] = TransTable['zh-TW']; + diff --git a/front/src/app/pipes/as-data-url.pipe.spec.ts b/front/src/app/pipes/as-data-url.pipe.spec.ts new file mode 100644 index 0000000..2fe372f --- /dev/null +++ b/front/src/app/pipes/as-data-url.pipe.spec.ts @@ -0,0 +1,8 @@ +import {AsDataUrlPipe} from './as-data-url.pipe'; + +describe('AsDataUrlPipe', () => { + it('create an instance', () => { + const pipe = new AsDataUrlPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/front/src/app/pipes/as-data-url.pipe.ts b/front/src/app/pipes/as-data-url.pipe.ts new file mode 100644 index 0000000..f409d87 --- /dev/null +++ b/front/src/app/pipes/as-data-url.pipe.ts @@ -0,0 +1,17 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import {Observable} from 'rxjs'; +import {FileReaderService} from '../services/file-reader.service'; + +@Pipe({ + name: 'asDataUrl' +}) +export class AsDataUrlPipe implements PipeTransform { + + transform(value: Blob): Observable { + return this.reader.read(value, 'dataUrl'); + } + + constructor(private reader: FileReaderService) { + } + +} diff --git a/front/src/app/pipes/file-size.pipe.spec.ts b/front/src/app/pipes/file-size.pipe.spec.ts new file mode 100644 index 0000000..a09e02f --- /dev/null +++ b/front/src/app/pipes/file-size.pipe.spec.ts @@ -0,0 +1,8 @@ +import {FileSizePipe} from './file-size.pipe'; + +describe('FileSizePipe', () => { + it('create an instance', () => { + const pipe = new FileSizePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/front/src/app/pipes/file-size.pipe.ts b/front/src/app/pipes/file-size.pipe.ts new file mode 100644 index 0000000..5f03beb --- /dev/null +++ b/front/src/app/pipes/file-size.pipe.ts @@ -0,0 +1,35 @@ +import {Pipe, PipeTransform} from '@angular/core'; + +@Pipe({ + name: 'fileSize' +}) +export class FileSizePipe implements PipeTransform { + + private static readonly UnitList: string[] = [ + '', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB' + ]; + + transform(value: number | string, type?: string, precision?: number): number | string { + type = type || 'both'; + precision = precision === undefined ? 0 : precision; + + let v = Number(value); + let index = 0; + while (v > 1024) { + v /= 1024; + index += 1; + } + + switch (type) { + case 'both': + return `${v.toFixed(index === 0 ? 0 : precision)}${FileSizePipe.UnitList[index]}`; + case 'number': + return precision === undefined ? v : v.toFixed(index === 0 ? 0 : precision); + case 'unit': + return FileSizePipe.UnitList[index]; + default: + return ''; + } + } + +} diff --git a/front/src/app/services/api.service.spec.ts b/front/src/app/services/api.service.spec.ts new file mode 100644 index 0000000..b517af4 --- /dev/null +++ b/front/src/app/services/api.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {ApiService} from './api.service'; + +describe('ApiService', () => { + let service: ApiService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ApiService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/api.service.ts b/front/src/app/services/api.service.ts new file mode 100644 index 0000000..c89ba4b --- /dev/null +++ b/front/src/app/services/api.service.ts @@ -0,0 +1,255 @@ +import {Injectable} from '@angular/core'; +import {HttpClient, HttpEvent, HttpParams, HttpRequest} from '@angular/common/http'; +import {Observable} from 'rxjs'; +import {EOk} from '../errors'; +import { + AddUserQ, + ChangePasswordQ, + Image, + InviteCode, ListImageContainsTagQ, + ListImageP, + ListImageQ, + ListImageWithTagQ, + ListInviteP, + ListInviteQ, + ListUserP, + ListUserQ, + LoginQ, + RegisterQ, + RemoveUserQ, + SetImageInfoQ, + SetPasswordQ, + SetUserPermissionQ, + User +} from '../types'; + +export interface Response { + status: string; + data: any; +} + +declare type PostBody = + string + | { [key: string]: any } + | Blob + | FormData + | ArrayBuffer + | ArrayBufferView + | URLSearchParams + | ReadableStream; + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + + private get(url: string): Observable { + return new Observable(observer => { + this.http.get(url).subscribe({ + next: resp => { + if (resp.status === EOk) { + observer.next(resp.data); + observer.complete(); + } else { + observer.error(resp.status); + } + }, + error: err => { + observer.error(err); + } + }); + }); + } + + private post(url: string, body: PostBody): Observable { + return new Observable(observer => { + this.http.post(url, body).subscribe({ + next: resp => { + if (resp.status === EOk) { + observer.next(resp.data); + observer.complete(); + } else { + observer.error(resp.status); + } + }, + error: err => { + observer.error(err); + } + }); + }); + } + + private postProgress(url: string, body: PostBody): Observable> { + const req = new HttpRequest('POST', url, body, { + reportProgress: true + }); + + return this.http.request(req); + + // return new Observable(observer => { + // this.http.request(req).subscribe(event => { + // switch (event.type) { + // case HttpEventType.Sent: + // return event as HttpSentEvent; + // case HttpEventType.DownloadProgress: + // return event as HttpProgressEvent; + // case HttpEventType.Response: + // return (event as HttpResponse).body; + // default: + // if (!environment.production) { + // console.log(`Unexpected event type: ${HttpEventType[event.type]} when posting to ${url}.`, body, event); + // } + // } + // }); + // }); + } + + public Login(r: LoginQ): Observable { + return this.post('/api/user/login', r); + } + + public Register(r: RegisterQ): Observable { + return this.post('/api/user/register', r); + } + + public UserExist(r: string): Observable { + return this.get(`/api/user/exist/${r}`); + } + + public GetUser(r: string): Observable { + return this.get(`/api/user/get/${r}`); + } + + public ChangePassword(r: ChangePasswordQ): Observable { + return this.post('/api/user/changePassword', r); + } + + public SetAvatar(r: Blob): Observable { + const form = new FormData(); + form.append('avatar', r, 'avatar.png'); + + return this.post('/api/user/setAvatar', form); + } + + public ResetAvatar(): Observable { + return this.get('/api/user/resetAvatar'); + } + + public SetAvatarP(r: Blob, id: string): Observable { + const form = new FormData(); + form.append('id', id); + form.append('avatar', r, 'avatar.png'); + + return this.post('/api/user/admin/setAvatar', form); + } + + public ResetAvatarP(r: string): Observable { + return this.get(`/api/user/resetAvatarP/${r}`); + } + + public ListUser(r: ListUserQ): Observable { + return this.post('/api/user/admin/list', r); + } + + public AddUser(r: AddUserQ): Observable { + return this.post('/api/user/admin/add', r); + } + + public RemoveUser(r: RemoveUserQ): Observable { + return this.post('/api/user/admin/remove', r); + } + + public SetPassword(r: SetPasswordQ): Observable { + return this.post('/api/user/admin/password', r); + } + + public SetUserPermission(r: SetUserPermissionQ): Observable { + return this.post('/api/user/admin/permission', r); + } + + public ListInvite(r: ListInviteQ): Observable { + return this.post('/api/invite/list', r); + } + + public AddInvite(r: InviteCode): Observable { + return this.post('/api/invite/add', r); + } + + public RemoveInvite(r: string): Observable { + return this.get(`/api/invite/remove/${r}`); + } + + public SetInviteTimes(r: InviteCode): Observable { + return this.post('/api/invite/setTimes', r); + } + + public ListImage(r: ListImageQ): Observable { + return this.post('/api/gallery/list', r); + } + + public ListImageTags(r: string): Observable { + return this.get(`/api/gallery/listTags/${r}`); + } + + public ListImageWithTag(r: ListImageWithTagQ): Observable { + return this.post('/api/gallery/listWithTag', r); + } + + public ListImageContainsTag(r: ListImageContainsTagQ): Observable { + return this.post('/api/gallery/listContainsTag', r); + } + + public SetImageInfo(r: SetImageInfoQ): Observable { + return this.post('/api/gallery/set', r); + } + + public GetImage(r: string): Observable { + return this.get(`/api/image/get/${r}`); + } + + public RemoveImage(r: string[]): Observable { + return this.post('/api/image/remove', {ids: r}); + } + + public UploadSimple(tag: string, origins: string, image: Blob): Observable> { + const form = new FormData(); + form.append('tag', tag); + form.append('origins', origins); + const name = (image as File).name || 'image'; + form.append('image', image, name); + + return this.postProgress('/api/upload/simple', form); + } + + public UploadAdvanced(tag: string, origins: string, formats: { [key: string]: Blob }): Observable> { + const form = new FormData(); + form.append('tag', tag); + form.append('origins', origins); + for (const [format, image] of Object.entries(formats)) { + if (image) { + form.append('images', image, format); + } + } + + return this.postProgress('/api/upload/advanced', form); + } + + public UpdateImage(id: string, formats: { [key: string]: Blob }): Observable> { + const form = new FormData(); + form.append('id', id); + for (const [format, image] of Object.entries(formats)) { + form.append('images', image, format); + } + + return this.postProgress('/api/upload/update', form); + } + + public SetPreference(preference: string): Observable { + const body = new HttpParams().set('preference', preference); + + return this.post('/preference', body); + } + + constructor(private http: HttpClient) { + } +} diff --git a/front/src/app/services/auth.service.spec.ts b/front/src/app/services/auth.service.spec.ts new file mode 100644 index 0000000..4e00c0a --- /dev/null +++ b/front/src/app/services/auth.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {AuthService} from './auth.service'; + +describe('ApiService', () => { + let service: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/auth.service.ts b/front/src/app/services/auth.service.ts new file mode 100644 index 0000000..1779146 --- /dev/null +++ b/front/src/app/services/auth.service.ts @@ -0,0 +1,72 @@ +import {Injectable} from '@angular/core'; +import {LocalStorage} from '@ngx-pwa/local-storage'; +import {Observable, ReplaySubject} from 'rxjs'; +import {first} from 'rxjs/operators'; +import {User} from '../types'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + private token$ = new ReplaySubject(1); + public user$ = new ReplaySubject(1); + public user: User; + private loginRedirect: string | null = null; + + constructor(private storage: LocalStorage) { + this.storage.getItem('token').subscribe((token: string) => { + this.token$.next(token ? token : null); + }); + this.storage.getItem('user').subscribe((user: User) => { + this.user$.next(user); + }); + this.user$.subscribe((user: User) => this.user = user); + } + + public updateUser(user: User): Observable { + return new Observable(observer => { + this.storage.setItem('user', user).subscribe(_ => { + this.user$.next(user); + observer.next(); + observer.complete(); + }); + }); + } + + public getRedirect(): string | null { + const redirectUrl = this.loginRedirect; + this.loginRedirect = null; + return redirectUrl; + } + + public setRedirect(redirectUrl: string): void { + this.loginRedirect = redirectUrl; + } + + public get(): Observable { + return this.token$.pipe(first()); + } + + public set(token: string): Observable { + return new Observable(observer => { + this.storage.setItem('token', token).subscribe(_ => { + this.token$.next(token); + observer.next(); + observer.complete(); + }); + }); + } + + public clear(): Observable { + return new Observable(observer => { + this.storage.setItem('token', null).subscribe(_ => { + this.token$.next(null); + this.updateUser(null).subscribe(() => { + observer.next(); + observer.complete(); + }); + }); + }); + } +} diff --git a/front/src/app/services/broker.service.spec.ts b/front/src/app/services/broker.service.spec.ts new file mode 100644 index 0000000..36041cb --- /dev/null +++ b/front/src/app/services/broker.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {BrokerService} from './broker.service'; + +describe('BrokerService', () => { + let service: BrokerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(BrokerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/broker.service.ts b/front/src/app/services/broker.service.ts new file mode 100644 index 0000000..a789f26 --- /dev/null +++ b/front/src/app/services/broker.service.ts @@ -0,0 +1,20 @@ +import {Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class BrokerService { + + private data: { [key: string]: any } = {}; + + public get(key: string): any { + return this.data[key]; + } + + public set(key: string, value: any): void { + this.data[key] = value; + } + + constructor() { + } +} diff --git a/front/src/app/services/environment.service.spec.ts b/front/src/app/services/environment.service.spec.ts new file mode 100644 index 0000000..95600b6 --- /dev/null +++ b/front/src/app/services/environment.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {EnvironmentService} from './environment.service'; + +describe('EnvironmentService', () => { + let service: EnvironmentService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(EnvironmentService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/environment.service.ts b/front/src/app/services/environment.service.ts new file mode 100644 index 0000000..1045075 --- /dev/null +++ b/front/src/app/services/environment.service.ts @@ -0,0 +1,19 @@ +import {Injectable} from '@angular/core'; +import {fromEvent, ReplaySubject} from 'rxjs'; +import {auditTime, startWith} from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class EnvironmentService { + + public mode: 'mobile' | 'desktop' = 'desktop'; + public mode$ = new ReplaySubject<'mobile' | 'desktop'>(1); + + constructor() { + fromEvent(window, 'resize').pipe(auditTime(500), startWith([undefined])).subscribe(_ => { + this.mode = window.innerWidth > 599 ? 'desktop' : 'mobile'; + this.mode$.next(this.mode); + }); + } +} diff --git a/front/src/app/services/file-reader.service.spec.ts b/front/src/app/services/file-reader.service.spec.ts new file mode 100644 index 0000000..fdef240 --- /dev/null +++ b/front/src/app/services/file-reader.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {FileReaderService} from './file-reader.service'; + +describe('FileReaderService', () => { + let service: FileReaderService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(FileReaderService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/file-reader.service.ts b/front/src/app/services/file-reader.service.ts new file mode 100644 index 0000000..c1d94f8 --- /dev/null +++ b/front/src/app/services/file-reader.service.ts @@ -0,0 +1,70 @@ +import {Inject, Injectable, InjectionToken, Optional} from '@angular/core'; +import {Observable, Subscriber} from 'rxjs'; +import {first} from 'rxjs/operators'; + +export const FILE_READER_CONCURRENT = new InjectionToken('file-reader-concurrent'); + +@Injectable({ + providedIn: 'root' +}) +export class FileReaderService { + + ResourceBorrow$: Observable; + ResourceReturnS: Subscriber; + + read(file: Blob, type: 'dataUrl' | 'text'): Observable; + + read(file: Blob, type: 'binaryString' | 'arrayBuffer'): Observable; + + read(file: Blob, type: 'dataUrl' | 'text' | 'binaryString' | 'arrayBuffer'): Observable { + return new Observable(observer => { + this.ResourceBorrow$.pipe(first()).subscribe(reader => { + reader.onload = _ => { + switch (type) { + case 'dataUrl': + case 'text': + observer.next(reader.result as string); + break; + case 'binaryString': + case 'arrayBuffer': + observer.next(reader.result as ArrayBuffer); + } + observer.complete(); + this.ResourceReturnS.next(reader); + }; + + reader.onerror = _ => { + observer.error(reader.error); + this.ResourceReturnS.next(reader); + }; + + switch (type) { + case 'dataUrl': + reader.readAsDataURL(file); + break; + case 'text': + reader.readAsText(file); + break; + case 'binaryString': + reader.readAsBinaryString(file); + break; + case 'arrayBuffer': + reader.readAsArrayBuffer(file); + } + }); + }); + } + + constructor(@Optional() @Inject(FILE_READER_CONCURRENT) private readonly concurrent: number) { + if (!concurrent) { + this.concurrent = 1; + } + + this.ResourceBorrow$ = new Observable(s => { + this.ResourceReturnS = s; + for (let i = 0; i < this.concurrent; i++) { + s.next(new FileReader()); + } + }); + } +} diff --git a/front/src/app/services/gallery-manager.service.spec.ts b/front/src/app/services/gallery-manager.service.spec.ts new file mode 100644 index 0000000..63fb9bf --- /dev/null +++ b/front/src/app/services/gallery-manager.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {GalleryManagerService} from './gallery-manager.service'; + +describe('GalleryManagerService', () => { + let service: GalleryManagerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(GalleryManagerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/gallery-manager.service.ts b/front/src/app/services/gallery-manager.service.ts new file mode 100644 index 0000000..cc81e46 --- /dev/null +++ b/front/src/app/services/gallery-manager.service.ts @@ -0,0 +1,229 @@ +import {EventEmitter, Inject, Injectable, InjectionToken, Optional} from '@angular/core'; +import {ApiService} from './api.service'; +import {Image, ListImageP} from '../types'; +import {Observable} from 'rxjs'; +import {MessageService} from './message.service'; +import {LocaleService} from './locale.service'; +import {stringError} from '../utils'; + +const MAX_SAFE_INTEGER = 9007199254740991; + +function groupBy(arr: T[], by: string): { [key: string]: T[] } { + return arr.reduce((rv: { [key: string]: T[] }, x: T) => { + (rv[x[by]] = rv[x[by]] || []).push(x); + return rv; + }, {}); +} + +function groupByFunc(arr: T[], by: (v: T) => string): { [key: string]: T[] } { + return arr.reduce((rv: { [key: string]: T[] }, x: T) => { + const byV = by(x); + (rv[byV] = rv[byV] || []).push(x); + return rv; + }, {}); +} + +export declare type TimeSpan = 'day' | 'week' | 'month' | 'year'; + +function NearestDay(stamp: string | number): number { + return new Date(stamp).setHours(0, 0, 0, 0); +} + +function NearestWeek(stamp: string | number): number { + const d = new Date(stamp); + d.setHours(0, 0, 0, 0); + return d.setDate(d.getDate() - (d.getDay() + 6) % 7 - 1); +} + +function NearestMonth(stamp: string | number): number { + const d = new Date(stamp); + d.setHours(0, 0, 0, 0); + return d.setDate(1); +} + +function NearestYear(stamp: string | number): number { + const d = new Date(stamp); + d.setHours(0, 0, 0, 0); + return d.setMonth(0, 1); +} + +export declare type ImageGroup = { by: string, images: Image[] }; + +export const GALLERY_LOAD_BATCH = new InjectionToken('gallery_load_batch'); + +@Injectable({ + providedIn: 'root' +}) +export class GalleryManagerService { + + constructor(private api: ApiService, + private locale: LocaleService, + private msg: MessageService, + @Optional() @Inject(GALLERY_LOAD_BATCH) private readonly loadBatch: number) { + if (!loadBatch) { + this.loadBatch = 10; + } + } + + informReload = new EventEmitter(); + + id: string; + + images: Image[]; + groups: ImageGroup[]; + groupMode: 'tag' | 'upload' = 'tag'; + grouper: (i: Image[]) => ImageGroup[] = GalleryManagerService.byTag; + keyFunction: (time: string) => number = NearestDay; + + current = 0; + total: number = MAX_SAFE_INTEGER; + loading = false; + requireLoading = false; + + onlyTag = false; + useTag = ''; + + get all(): boolean { + return this.current >= this.total; + } + + private static byTag(i: Image[]): ImageGroup[] { + return Object.entries(groupBy(i, 'tag')) + .sort((a, b) => ((a[0] === b[0]) ? 0 : ((a[0] > b[0]) ? 1 : -1))) + .map(([tag, images]) => { + return {by: tag, images}; + }); + } + + private byTime(i: Image[]): ImageGroup[] { + return Object.entries(groupByFunc(i, j => this.keyFunction(j.upload).toString())) + .map(([timeString, images]) => [Number(timeString), images]) + .sort((a: [number, Image[]], b: [number, Image[]]) => b[0] - a[0]) + .map(([time, images]: [number, Image[]]) => { + return {by: time.toString(), images}; + }); + } + + public reGroup(): void { + if (this.images.length > 0) { + this.groups = this.grouper(this.images); + } else { + this.groups = []; + } + } + + public clean(): void { + this.reset(); + this.id = ''; + this.groupMode = 'tag'; + this.grouper = GalleryManagerService.byTag; + this.keyFunction = NearestDay; + } + + public setGrouper(g: 'tag' | 'upload'): void { + this.reset(); + this.groupMode = g; + + switch (g) { + case 'tag': + this.grouper = GalleryManagerService.byTag; + break; + case 'upload': + this.grouper = this.byTime; + } + + this.reGroup(); + } + + public setID(id: string): void { + this.id = id; + this.reset(); + } + + public setSpan(s: 'day' | 'week' | 'month' | 'year'): void { + switch (s) { + case 'day': + this.keyFunction = NearestDay; + break; + case 'week': + this.keyFunction = NearestWeek; + break; + case 'month': + this.keyFunction = NearestMonth; + break; + case 'year': + this.keyFunction = NearestYear; + } + + this.reGroup(); + } + + public reset(): void { + this.images = []; + this.groups = []; + this.current = 0; + this.total = Number.POSITIVE_INFINITY; + } + + public fetchMore(): void { + if (this.all || this.loading) { + return; + } + + this.loading = true; + + let result: Observable; + + if (this.onlyTag) { + result = this.api.ListImageContainsTag({ + id: this.id, + limit: this.loadBatch, + offset: this.current, + tag: this.useTag, + }); + } else { + result = this.api.ListImage({ + id: this.id, + limit: this.loadBatch, + offset: this.current, + sort: this.groupMode + }); + } + + result.subscribe({ + next: resp => { + this.total = resp.total; + if (resp.images) { + this.push(resp.images); + } + this.current += resp.count; + this.loading = false; + + setTimeout(() => { + if (this.requireLoading) { + this.fetchMore(); + } + }, 500); + }, + error: err => { + this.msg.SendMessage( + this.locale.dict.gallery_fetch_failed.replace('%1', stringError.call(this, err)), + this.locale.dict.action_retry).onAction().subscribe(_ => { + this.fetchMore(); + }); + } + }); + + } + + private push(images: Image[]): void { + this.images = this.images.concat(images); + const newGroup = this.grouper(images); + if (this.groups.length > 0 && this.groups[this.groups.length - 1].by === newGroup[0].by) { + this.groups[this.groups.length - 1].images = this.groups[this.groups.length - 1].images.concat(newGroup[0].images); + this.groups = this.groups.concat(newGroup.slice(1)); + } else { + this.groups = this.groups.concat(newGroup); + } + } +} diff --git a/front/src/app/services/locale.service.spec.ts b/front/src/app/services/locale.service.spec.ts new file mode 100644 index 0000000..f958c54 --- /dev/null +++ b/front/src/app/services/locale.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {LocaleService} from './locale.service'; + +describe('LocaleService', () => { + let service: LocaleService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(LocaleService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/locale.service.ts b/front/src/app/services/locale.service.ts new file mode 100644 index 0000000..9bf55d3 --- /dev/null +++ b/front/src/app/services/locale.service.ts @@ -0,0 +1,65 @@ +import {Inject, Injectable} from '@angular/core'; +import {ReplaySubject} from 'rxjs'; +import {TransTable} from '../langs'; +import {map} from 'rxjs/operators'; +import {PreferenceService} from './preference.service'; +import {environment} from '../../environments/environment'; +import {DOCUMENT} from '@angular/common'; + +@Injectable({ + providedIn: 'root' +}) +export class LocaleService { + public dict$ = new ReplaySubject<{ [key: string]: string }>(1); + public dict: { [key: string]: string } = {other: 'Loading...'}; + + private static getUsersLocale(defaultValue: string = 'en'): string { + if (!environment.production) { + return 'en'; + } + + if (typeof window === 'undefined' || typeof window.navigator === 'undefined') { + return defaultValue; + } + const wn = window.navigator as any; + let lang = wn.languages ? wn.languages[0] : defaultValue; + lang = lang || wn.language || wn.browserLanguage || wn.userLanguage; + return lang; + } + + constructor(private pref: PreferenceService, + @Inject(DOCUMENT) private document: Document) { + + this.dict$.next(TransTable.en); + this.pref.listen('locale').pipe( + map((locale?: string) => { + if (!locale) { + locale = LocaleService.getUsersLocale(); + } + + let dict: { [key: string]: string } = TransTable[locale]; + + if (!dict) { + locale = locale.split('-')[0]; + dict = TransTable[locale]; + } + + if (!dict) { + locale = 'en'; + dict = TransTable.en; + } + + if (!environment.production) { + console.log('Current locale: ', locale, ', Current dict: ', dict); + } + + this.document.documentElement.lang = locale; + + return dict; + }), + ).subscribe(dict => { + this.dict$.next(dict); + this.dict = dict; + }); + } +} diff --git a/front/src/app/services/message.service.spec.ts b/front/src/app/services/message.service.spec.ts new file mode 100644 index 0000000..ce1f5d1 --- /dev/null +++ b/front/src/app/services/message.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {MessageService} from './message.service'; + +describe('MessageService', () => { + let service: MessageService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MessageService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/message.service.ts b/front/src/app/services/message.service.ts new file mode 100644 index 0000000..c8a6e5f --- /dev/null +++ b/front/src/app/services/message.service.ts @@ -0,0 +1,21 @@ +import {Injectable} from '@angular/core'; +import {MatSnackBar, MatSnackBarConfig, MatSnackBarRef, SimpleSnackBar} from '@angular/material/snack-bar'; +import {EnvironmentService} from './environment.service'; + +@Injectable({ + providedIn: 'root' +}) +export class MessageService { + + constructor(private snackBar: MatSnackBar, private env: EnvironmentService) { + } + + public SendMessage(message: string, action?: string): MatSnackBarRef { + const config: MatSnackBarConfig = { + duration: 5000, + horizontalPosition: this.env.mode === 'mobile' ? 'center' : 'left', + verticalPosition: 'bottom', + }; + return this.snackBar.open(message, action, config); + } +} diff --git a/front/src/app/services/preference.service.spec.ts b/front/src/app/services/preference.service.spec.ts new file mode 100644 index 0000000..82685a4 --- /dev/null +++ b/front/src/app/services/preference.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {PreferenceService} from './preference.service'; + +describe('PreferenceService', () => { + let service: PreferenceService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PreferenceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/preference.service.ts b/front/src/app/services/preference.service.ts new file mode 100644 index 0000000..2d281f0 --- /dev/null +++ b/front/src/app/services/preference.service.ts @@ -0,0 +1,82 @@ +import {Injectable} from '@angular/core'; +import {Observable, ReplaySubject} from 'rxjs'; +import {filter, first, map} from 'rxjs/operators'; +import {LocalStorage} from '@ngx-pwa/local-storage'; + +export interface Preferences { + locale?: string; + + [key: string]: any; +} + +@Injectable({ + providedIn: 'root' +}) +export class PreferenceService { + + public pref$ = new ReplaySubject(1); + private pref: Preferences = {}; + + constructor(private storage: LocalStorage) { + this.pref$.next({}); + this.storage.getItem('preference').subscribe((token: Preferences) => { + this.pref$.next(token ? token : {}); + }); + + this.pref$.subscribe(pref => this.pref = pref); + } + + public get(key: string): Observable { + return this.pref$.pipe(first(), map(pref => pref[key])); + } + + public listen(key: string): Observable { + return this.pref$.pipe(filter(pref => pref && pref !== {}), map(pref => pref[key])); + } + + public getCurrent(key: string): any { + return this.pref[key]; + } + + public set(key: string, value: any): Observable { + const newPreference = this.pref; + newPreference[key] = value; + return this.setPreference(newPreference); + } + + public clear(key: string): Observable { + return new Observable(observer => { + const newPreference = this.pref; + newPreference[key] = undefined; + this.storage.setItem('preference', newPreference).subscribe(_ => { + this.pref$.next(newPreference); + observer.next(); + observer.complete(); + }); + }); + } + + public getPreference(): Observable { + return this.pref$.pipe(first()); + } + + public setPreference(preference: Preferences): Observable { + return new Observable(observer => { + this.storage.setItem('preference', preference).subscribe(_ => { + this.pref$.next(preference); + observer.next(); + observer.complete(); + }); + }); + } + + public clearPreference(): Observable { + return new Observable(observer => { + this.storage.setItem('preference', {}).subscribe(_ => { + this.pref$.next({}); + observer.next(); + observer.complete(); + }); + }); + } +} diff --git a/front/src/app/services/scroll.service.spec.ts b/front/src/app/services/scroll.service.spec.ts new file mode 100644 index 0000000..d372776 --- /dev/null +++ b/front/src/app/services/scroll.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {ScrollService} from './scroll.service'; + +describe('ScrollService', () => { + let service: ScrollService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ScrollService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/scroll.service.ts b/front/src/app/services/scroll.service.ts new file mode 100644 index 0000000..278cfa9 --- /dev/null +++ b/front/src/app/services/scroll.service.ts @@ -0,0 +1,63 @@ +import {Injectable} from '@angular/core'; +import {fromEvent, Subject} from 'rxjs'; +import {startWith, takeUntil} from 'rxjs/operators'; + +export interface ScrollStatus { + height: number; + width: number; + offsetH: number; + offsetW: number; +} + +@Injectable({ + providedIn: 'root' +}) +export class ScrollService { + + status$: Subject; + status: ScrollStatus = {height: 0, width: 0, offsetH: 0, offsetW: 0}; + private current: HTMLElement | Window | undefined; + + private cancelLast$: Subject; + + public WatchOn(e: HTMLElement | Window): void { + if (this.current) { + this.cancelLast$.next(); + } + this.current = e; + fromEvent(e, 'scroll').pipe(startWith([undefined]), takeUntil(this.cancelLast$)).subscribe(_ => { + const status = { + height: (e as HTMLElement).scrollHeight || (e as Window).document.body.scrollHeight || 0, + width: (e as HTMLElement).scrollWidth || (e as Window).document.body.scrollWidth || 0, + offsetH: (e as HTMLElement).scrollTop || (e as Window).pageYOffset || 0, + offsetW: (e as HTMLElement).scrollLeft || (e as Window).pageXOffset || 0 + }; + this.status$.next(status); + this.status = status; + }); + } + + public UnwatchOn(e: HTMLElement | Window): void { + if (e === this.current) { + this.cancelLast$.next(); + this.current = undefined; + } + } + + public UnWatch(): void { + if (this.current) { + this.cancelLast$.next(); + this.current = undefined; + } + } + + public PseudoStatus(s: ScrollStatus): void { + this.status$.next(s); + this.status = s; + } + + constructor() { + this.cancelLast$ = new Subject(); + this.status$ = new Subject(); + } +} diff --git a/front/src/app/services/title.service.spec.ts b/front/src/app/services/title.service.spec.ts new file mode 100644 index 0000000..590d10c --- /dev/null +++ b/front/src/app/services/title.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {TitleService} from './title.service'; + +describe('TitleService', () => { + let service: TitleService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TitleService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/front/src/app/services/title.service.ts b/front/src/app/services/title.service.ts new file mode 100644 index 0000000..42f5faf --- /dev/null +++ b/front/src/app/services/title.service.ts @@ -0,0 +1,48 @@ +import {Injectable} from '@angular/core'; +import {Title} from '@angular/platform-browser'; + +@Injectable({ + providedIn: 'root' +}) +export class TitleService { + + // tslint:disable-next-line:variable-name + _firstPart = ''; + // tslint:disable-next-line:variable-name + _secondPart = ''; + + get firstPart(): string { + return this._firstPart; + } + + set firstPart(value: string) { + this._firstPart = value; + this.updateTitle(); + } + + get secondPart(): string { + return this._secondPart; + } + + set secondPart(value: string) { + this._secondPart = value; + this.updateTitle(); + } + + updateTitle(): void { + const t = ['Chromatic']; + if (this._firstPart) { + t.push(this._firstPart); + } + + if (this._secondPart) { + t.push(this._secondPart); + } + + this.title.setTitle(t.join(' - ')); + } + + constructor(private title: Title) { + this.updateTitle(); + } +} diff --git a/front/src/app/types.ts b/front/src/app/types.ts new file mode 100644 index 0000000..bb370ba --- /dev/null +++ b/front/src/app/types.ts @@ -0,0 +1,125 @@ +export interface LoginQ { + name: string; + password: string; + recaptcha: string; +} + +export interface ListUserQ { + offset: number; + limit: number; + keyword: string; +} + +export interface ListImageQ { + id: string; // ObjectID + sort: string; + offset: number; + limit: number; +} + +export interface ListUserP { + total: number; + count: number; + users: User[] | null; +} + +export interface RemoveUserQ { + users: string[] | null; // ObjectID + cascade: boolean; +} + +export interface SetUserPermissionQ { + user_id: string; // ObjectID + privileged: boolean; + frozen: boolean; +} + +export interface ListInviteP { + total: number; + count: number; + codes: InviteCode[] | null; +} + +export interface ImageFormat { + format: string; + hash: number; +} + +export interface SetImageInfoQ { + targets: string[] | null; // ObjectID + field: string; + data: string; +} + +export interface RegisterQ { + name: string; + password: string; + invite_code: string; + recaptcha: string; +} + +export interface User { + id: string; // ObjectID + name: string; + privileged: boolean; + frozen: boolean; +} + +export interface AddUserQ { + name: string; + password: string; + privileged: boolean; +} + +export interface SetPasswordQ { + user_id: string; // ObjectID + password: string; +} + +export interface ListInviteQ { + offset: number; + limit: number; +} + +export interface ListImageP { + total: number; + count: number; + images: Image[] | null; +} + +export interface ChangePasswordQ { + old_password: string; + new_password: string; +} + +export interface InviteCode { + code: string; + times: number; +} + +export interface Image { + id: string; // ObjectID + user_id: string; // ObjectID + user_name: string; + tag: string; + upload: string; // DateTime + view: number; + origins: string[] | null; + original: boolean; + files?: ImageFormat[]; +} + +export interface ListImageWithTagQ { + id: string; // ObjectID + tag: string; + offset: number; + limit: number; +} + +export interface ListImageContainsTagQ { + id: string; // ObjectID + tag: string; + offset: number; + limit: number; +} + diff --git a/front/src/app/utils.ts b/front/src/app/utils.ts new file mode 100644 index 0000000..ff44e37 --- /dev/null +++ b/front/src/app/utils.ts @@ -0,0 +1,76 @@ +import * as isIp from 'is-ip'; +import {ErrorList} from './errors'; + +export function generateOriginMatch(input: string): string { + if (!input) { + return ''; + } + + if (input === ' ' || input === '*') { + return input; + } + + let ip: string; + if (input.charAt(0) === '[' && input.charAt(input.length - 1) === ']') { + ip = input.substring(1, input.length - 1); + } + + if (isIp(ip)) { + return ip; + } + + const domain = input.charAt(input.length - 1) === '.' ? input.slice(0, -1) : input; + const parts = domain.split('.'); + for (const [index, part] of Object.entries(parts)) { + if (part.length === 0) { + return ''; + } + + if (part === '*') { + if (index !== '0') { + return ''; + } else { + continue; + } + } + + if (!/^[a-zA-Z0-9\-:]+$/.test(part)) { + return ''; + } + } + + return domain; +} + +export function generateOriginHints(input: string): { origin: string, type: string, bad: boolean }[] { + const hints: { origin: string, type: string, bad: boolean }[] = []; + + const domain = generateOriginMatch(input); + const hasAny = this.originsSet.has('*'); + + if (domain.length > 0) { + if (domain.charAt(0) === '*') { + hints.push({origin: domain, type: 'wildcard', bad: hasAny || this.originsSet.has(domain)}); + } else { + hints.push({origin: domain, type: 'exact', bad: hasAny || this.originsSet.has(domain)}); + hints.push({origin: '*.' + domain, type: 'wildcard', bad: hasAny || this.originsSet.has('*.' + domain)}); + } + } else { + hints.push({origin: '-', type: 'bad', bad: true}); + } + + hints.push({origin: '*', type: 'any', bad: hasAny}); + hints.push({origin: ' ', type: 'none', bad: hasAny || this.originsSet.has(' ')}); + + return hints; +} + +export function stringError(err: any): string { + if (ErrorList.includes(err)) { + return this.locale.dict[err]; + } else if (err.status_code) { + return this.locale.dict.http_error.replace('%1', String(err.status_code)); + } else { + return this.locale.dict.network_error; + } +} diff --git a/front/src/assets/.gitkeep b/front/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/front/src/assets/action-upload-144x144.png b/front/src/assets/action-upload-144x144.png new file mode 100644 index 0000000..5826a1d Binary files /dev/null and b/front/src/assets/action-upload-144x144.png differ diff --git a/front/src/assets/action-upload-144x144.webp b/front/src/assets/action-upload-144x144.webp new file mode 100644 index 0000000..21b98f0 Binary files /dev/null and b/front/src/assets/action-upload-144x144.webp differ diff --git a/front/src/assets/action-upload-192x192.png b/front/src/assets/action-upload-192x192.png new file mode 100644 index 0000000..7dfeec0 Binary files /dev/null and b/front/src/assets/action-upload-192x192.png differ diff --git a/front/src/assets/action-upload-192x192.webp b/front/src/assets/action-upload-192x192.webp new file mode 100644 index 0000000..600cb42 Binary files /dev/null and b/front/src/assets/action-upload-192x192.webp differ diff --git a/front/src/assets/action-upload-36x36.png b/front/src/assets/action-upload-36x36.png new file mode 100644 index 0000000..d869892 Binary files /dev/null and b/front/src/assets/action-upload-36x36.png differ diff --git a/front/src/assets/action-upload-36x36.webp b/front/src/assets/action-upload-36x36.webp new file mode 100644 index 0000000..4efa05d Binary files /dev/null and b/front/src/assets/action-upload-36x36.webp differ diff --git a/front/src/assets/action-upload-48x48.png b/front/src/assets/action-upload-48x48.png new file mode 100644 index 0000000..dad6adc Binary files /dev/null and b/front/src/assets/action-upload-48x48.png differ diff --git a/front/src/assets/action-upload-48x48.webp b/front/src/assets/action-upload-48x48.webp new file mode 100644 index 0000000..70ba1bb Binary files /dev/null and b/front/src/assets/action-upload-48x48.webp differ diff --git a/front/src/assets/action-upload-72x72.png b/front/src/assets/action-upload-72x72.png new file mode 100644 index 0000000..1f2b5f9 Binary files /dev/null and b/front/src/assets/action-upload-72x72.png differ diff --git a/front/src/assets/action-upload-72x72.webp b/front/src/assets/action-upload-72x72.webp new file mode 100644 index 0000000..2d7db1d Binary files /dev/null and b/front/src/assets/action-upload-72x72.webp differ diff --git a/front/src/assets/action-upload-96x96.png b/front/src/assets/action-upload-96x96.png new file mode 100644 index 0000000..484d5ea Binary files /dev/null and b/front/src/assets/action-upload-96x96.png differ diff --git a/front/src/assets/action-upload-96x96.webp b/front/src/assets/action-upload-96x96.webp new file mode 100644 index 0000000..6ee888e Binary files /dev/null and b/front/src/assets/action-upload-96x96.webp differ diff --git a/front/src/assets/icon-114x114.png b/front/src/assets/icon-114x114.png new file mode 100644 index 0000000..916ac80 Binary files /dev/null and b/front/src/assets/icon-114x114.png differ diff --git a/front/src/assets/icon-120x120.png b/front/src/assets/icon-120x120.png new file mode 100644 index 0000000..97b6894 Binary files /dev/null and b/front/src/assets/icon-120x120.png differ diff --git a/front/src/assets/icon-144x144.png b/front/src/assets/icon-144x144.png new file mode 100644 index 0000000..1d25069 Binary files /dev/null and b/front/src/assets/icon-144x144.png differ diff --git a/front/src/assets/icon-144x144.webp b/front/src/assets/icon-144x144.webp new file mode 100644 index 0000000..9d018f1 Binary files /dev/null and b/front/src/assets/icon-144x144.webp differ diff --git a/front/src/assets/icon-150x150.png b/front/src/assets/icon-150x150.png new file mode 100644 index 0000000..0256914 Binary files /dev/null and b/front/src/assets/icon-150x150.png differ diff --git a/front/src/assets/icon-152x152.png b/front/src/assets/icon-152x152.png new file mode 100644 index 0000000..8d62bab Binary files /dev/null and b/front/src/assets/icon-152x152.png differ diff --git a/front/src/assets/icon-16x16.png b/front/src/assets/icon-16x16.png new file mode 100644 index 0000000..817857b Binary files /dev/null and b/front/src/assets/icon-16x16.png differ diff --git a/front/src/assets/icon-180x180.png b/front/src/assets/icon-180x180.png new file mode 100644 index 0000000..bb4ba1a Binary files /dev/null and b/front/src/assets/icon-180x180.png differ diff --git a/front/src/assets/icon-192x192.png b/front/src/assets/icon-192x192.png new file mode 100644 index 0000000..8826644 Binary files /dev/null and b/front/src/assets/icon-192x192.png differ diff --git a/front/src/assets/icon-192x192.webp b/front/src/assets/icon-192x192.webp new file mode 100644 index 0000000..1aca22c Binary files /dev/null and b/front/src/assets/icon-192x192.webp differ diff --git a/front/src/assets/icon-310x310.png b/front/src/assets/icon-310x310.png new file mode 100644 index 0000000..a6f7acf Binary files /dev/null and b/front/src/assets/icon-310x310.png differ diff --git a/front/src/assets/icon-32x32.png b/front/src/assets/icon-32x32.png new file mode 100644 index 0000000..80fad9d Binary files /dev/null and b/front/src/assets/icon-32x32.png differ diff --git a/front/src/assets/icon-36x36.png b/front/src/assets/icon-36x36.png new file mode 100644 index 0000000..d8c77a1 Binary files /dev/null and b/front/src/assets/icon-36x36.png differ diff --git a/front/src/assets/icon-36x36.webp b/front/src/assets/icon-36x36.webp new file mode 100644 index 0000000..a0176b5 Binary files /dev/null and b/front/src/assets/icon-36x36.webp differ diff --git a/front/src/assets/icon-48x48.png b/front/src/assets/icon-48x48.png new file mode 100644 index 0000000..687ee18 Binary files /dev/null and b/front/src/assets/icon-48x48.png differ diff --git a/front/src/assets/icon-48x48.webp b/front/src/assets/icon-48x48.webp new file mode 100644 index 0000000..e2136a7 Binary files /dev/null and b/front/src/assets/icon-48x48.webp differ diff --git a/front/src/assets/icon-512x512.png b/front/src/assets/icon-512x512.png new file mode 100644 index 0000000..e789b43 Binary files /dev/null and b/front/src/assets/icon-512x512.png differ diff --git a/front/src/assets/icon-512x512.webp b/front/src/assets/icon-512x512.webp new file mode 100644 index 0000000..47fdaa6 Binary files /dev/null and b/front/src/assets/icon-512x512.webp differ diff --git a/front/src/assets/icon-57x57.png b/front/src/assets/icon-57x57.png new file mode 100644 index 0000000..020aa49 Binary files /dev/null and b/front/src/assets/icon-57x57.png differ diff --git a/front/src/assets/icon-60x60.png b/front/src/assets/icon-60x60.png new file mode 100644 index 0000000..d0792c5 Binary files /dev/null and b/front/src/assets/icon-60x60.png differ diff --git a/front/src/assets/icon-70x70.png b/front/src/assets/icon-70x70.png new file mode 100644 index 0000000..d58bd9b Binary files /dev/null and b/front/src/assets/icon-70x70.png differ diff --git a/front/src/assets/icon-72x72.png b/front/src/assets/icon-72x72.png new file mode 100644 index 0000000..8feff24 Binary files /dev/null and b/front/src/assets/icon-72x72.png differ diff --git a/front/src/assets/icon-72x72.webp b/front/src/assets/icon-72x72.webp new file mode 100644 index 0000000..b163898 Binary files /dev/null and b/front/src/assets/icon-72x72.webp differ diff --git a/front/src/assets/icon-76x76.png b/front/src/assets/icon-76x76.png new file mode 100644 index 0000000..15a80b4 Binary files /dev/null and b/front/src/assets/icon-76x76.png differ diff --git a/front/src/assets/icon-96x96.png b/front/src/assets/icon-96x96.png new file mode 100644 index 0000000..0017b2d Binary files /dev/null and b/front/src/assets/icon-96x96.png differ diff --git a/front/src/assets/icon-96x96.webp b/front/src/assets/icon-96x96.webp new file mode 100644 index 0000000..b6d4521 Binary files /dev/null and b/front/src/assets/icon-96x96.webp differ diff --git a/front/src/assets/icon-maskable-144x144.png b/front/src/assets/icon-maskable-144x144.png new file mode 100644 index 0000000..4a78327 Binary files /dev/null and b/front/src/assets/icon-maskable-144x144.png differ diff --git a/front/src/assets/icon-maskable-144x144.webp b/front/src/assets/icon-maskable-144x144.webp new file mode 100644 index 0000000..73e3f2f Binary files /dev/null and b/front/src/assets/icon-maskable-144x144.webp differ diff --git a/front/src/assets/icon-maskable-192x192.png b/front/src/assets/icon-maskable-192x192.png new file mode 100644 index 0000000..ef581e3 Binary files /dev/null and b/front/src/assets/icon-maskable-192x192.png differ diff --git a/front/src/assets/icon-maskable-192x192.webp b/front/src/assets/icon-maskable-192x192.webp new file mode 100644 index 0000000..9d1e36f Binary files /dev/null and b/front/src/assets/icon-maskable-192x192.webp differ diff --git a/front/src/assets/icon-maskable-36x36.png b/front/src/assets/icon-maskable-36x36.png new file mode 100644 index 0000000..b1cdadd Binary files /dev/null and b/front/src/assets/icon-maskable-36x36.png differ diff --git a/front/src/assets/icon-maskable-36x36.webp b/front/src/assets/icon-maskable-36x36.webp new file mode 100644 index 0000000..a58916a Binary files /dev/null and b/front/src/assets/icon-maskable-36x36.webp differ diff --git a/front/src/assets/icon-maskable-48x48.png b/front/src/assets/icon-maskable-48x48.png new file mode 100644 index 0000000..e9e4109 Binary files /dev/null and b/front/src/assets/icon-maskable-48x48.png differ diff --git a/front/src/assets/icon-maskable-48x48.webp b/front/src/assets/icon-maskable-48x48.webp new file mode 100644 index 0000000..f296e10 Binary files /dev/null and b/front/src/assets/icon-maskable-48x48.webp differ diff --git a/front/src/assets/icon-maskable-512x512.png b/front/src/assets/icon-maskable-512x512.png new file mode 100644 index 0000000..55d5ab5 Binary files /dev/null and b/front/src/assets/icon-maskable-512x512.png differ diff --git a/front/src/assets/icon-maskable-512x512.webp b/front/src/assets/icon-maskable-512x512.webp new file mode 100644 index 0000000..a51b674 Binary files /dev/null and b/front/src/assets/icon-maskable-512x512.webp differ diff --git a/front/src/assets/icon-maskable-72x72.png b/front/src/assets/icon-maskable-72x72.png new file mode 100644 index 0000000..87ed570 Binary files /dev/null and b/front/src/assets/icon-maskable-72x72.png differ diff --git a/front/src/assets/icon-maskable-72x72.webp b/front/src/assets/icon-maskable-72x72.webp new file mode 100644 index 0000000..4f0a24e Binary files /dev/null and b/front/src/assets/icon-maskable-72x72.webp differ diff --git a/front/src/assets/icon-maskable-96x96.png b/front/src/assets/icon-maskable-96x96.png new file mode 100644 index 0000000..a9be0c5 Binary files /dev/null and b/front/src/assets/icon-maskable-96x96.png differ diff --git a/front/src/assets/icon-maskable-96x96.webp b/front/src/assets/icon-maskable-96x96.webp new file mode 100644 index 0000000..0ddc7f4 Binary files /dev/null and b/front/src/assets/icon-maskable-96x96.webp differ diff --git a/front/src/browserconfig.xml b/front/src/browserconfig.xml new file mode 100644 index 0000000..ec21d6c --- /dev/null +++ b/front/src/browserconfig.xml @@ -0,0 +1 @@ +#7e57c2 diff --git a/front/src/button.styl b/front/src/button.styl new file mode 100644 index 0000000..ea77391 --- /dev/null +++ b/front/src/button.styl @@ -0,0 +1,11 @@ +.transitioned-button + transition color 0.2s, background 0.2s + + &:not(.mat-button-disabled):hover .mat-button-focus-overlay + opacity 0.04 + + &.strong-hover:not(.mat-button-disabled):hover .mat-button-focus-overlay + opacity 0.1 + +.mat-fab .mat-button-wrapper > .mat-icon + vertical-align bottom diff --git a/front/src/custom-theme.scss b/front/src/custom-theme.scss new file mode 100644 index 0000000..4258f58 --- /dev/null +++ b/front/src/custom-theme.scss @@ -0,0 +1,97 @@ +// Custom Theming for Angular Material +// For more information: https://material.angular.io/guide/theming +@import '~@angular/material/theming'; +// Plus imports for other components in your app. + +$font-sans: "Source Sans Pro", sans-serif; +$font-serif: "Source Serif Pro", serif; +$font-mono: "Fira Code", monospace; + +:root { + --font-sans: "Source Sans Pro", sans-serif; + --font-serif: "Source Serif Pro", serif; + --font-mono: "Fira Code", monospace; +} + +$custom-typography: mat-typography-config( + $font-family: $font-sans, + $display-4: mat-typography-level(112px, 112px, 700, $font-serif, -0.05em), + $display-3: mat-typography-level(56px, 56px, 700, $font-serif, -0.02em), + $display-2: mat-typography-level(45px, 48px, 600, $font-serif, -0.005em), + $display-1: mat-typography-level(34px, 40px, 600, $font-serif), + $headline: mat-typography-level(24px, 32px, 700, $font-serif), + $title: mat-typography-level(20px, 32px, 600, $font-serif), + $subheading-2: mat-typography-level(16px, 28px, 600, $font-serif), + $subheading-1: mat-typography-level(15px, 24px, 600, $font-serif), + $body-2: mat-typography-level(14px, 24px, 600, $font-sans), + $body-1: mat-typography-level(14px, 20px, 400, $font-sans), + $caption: mat-typography-level(12px, 20px, 400, $font-sans), + $button: mat-typography-level(14px, 14px, 600, $font-sans), + // Line-height must be unit-less fraction of the font-size. + $input: mat-typography-level(inherit, 1.125, 400, $font-mono) +); + +// Override typography CSS classes (e.g., mat-h1, mat-display-1, mat-typography, etc.). +@include mat-base-typography($custom-typography); + +// Override typography for a specific Angular Material components. +@include mat-checkbox-typography($custom-typography); + +// Override typography for all Angular Material, including mat-base-typography and all components. +@include angular-material-typography($custom-typography); + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +// Override the typography in the core CSS. +@include mat-core($custom-typography); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$front-primary: mat-palette($mat-deep-purple, 400, 300, 700); +$front-accent: mat-palette($mat-lime, 600, 100, 800); + +// The warn palette is optional (defaults to red). +$front-warn: mat-palette($mat-red); + +// Create the theme object. A theme consists of configurations for individual +// theming systems such as "color" or "typography". +$front-theme: mat-light-theme(( + color: ( + primary: $front-primary, + accent: $front-accent, + warn: $front-warn, + ) +)); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include angular-material-theme($front-theme); + +$-export-color-settings: map_get($front-theme, color); +$-export-primary: map_get($-export-color-settings, primary); +$-export-accent: map_get($-export-color-settings, accent); +$-export-warn: map_get($-export-color-settings, warn); + +:root { + --primary-default: #{map_get($-export-primary, default)}; + --primary-lighter: #{map_get($-export-primary, lighter)}; + --primary-darker: #{map_get($-export-primary, darker)}; + + --accent-default: #{map_get($-export-accent, default)}; + --accent-lighter: #{map_get($-export-accent, lighter)}; + --accent-darker: #{map_get($-export-accent, darker)}; + + --warn-default: #{map_get($-export-warn, default)}; + --warn-lighter: #{map_get($-export-warn, lighter)}; + --warn-darker: #{map_get($-export-warn, darker)}; + + --status-green: #4caf50; +} + +body { + margin: 0; + font-family: #{$font-sans}; +} diff --git a/front/src/dialog-omni.styl b/front/src/dialog-omni.styl new file mode 100644 index 0000000..482b8a3 --- /dev/null +++ b/front/src/dialog-omni.styl @@ -0,0 +1,10 @@ +.dialog-actions + display flex + align-items center + justify-content flex-end + + button:not(:last-child) + margin-right 10px + +.scrollable.dialog-content-scrollable-fix-80vh + max-height calc(80vh - 148px) diff --git a/front/src/environments/environment.prod.ts b/front/src/environments/environment.prod.ts new file mode 100644 index 0000000..f12ced5 --- /dev/null +++ b/front/src/environments/environment.prod.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + recaptchaKey: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI', +}; diff --git a/front/src/environments/environment.ts b/front/src/environments/environment.ts new file mode 100644 index 0000000..060257b --- /dev/null +++ b/front/src/environments/environment.ts @@ -0,0 +1,17 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + recaptchaKey: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI', +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/front/src/favicon.ico b/front/src/favicon.ico new file mode 100644 index 0000000..33a9ba9 Binary files /dev/null and b/front/src/favicon.ico differ diff --git a/front/src/gallery-overlay.styl b/front/src/gallery-overlay.styl new file mode 100644 index 0000000..32a8191 --- /dev/null +++ b/front/src/gallery-overlay.styl @@ -0,0 +1,9 @@ +.gallery-overlay-panel + .mat-dialog-container + padding 0 + border-radius 0 + background transparent + overflow hidden + + &.gallery-overlay-loading .mat-dialog-container + box-shadow none diff --git a/front/src/index.html b/front/src/index.html new file mode 100644 index 0000000..4071c02 --- /dev/null +++ b/front/src/index.html @@ -0,0 +1,35 @@ + + + + + Chromatic + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/src/main.ts b/front/src/main.ts new file mode 100644 index 0000000..c7b673c --- /dev/null +++ b/front/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/front/src/manifest.json b/front/src/manifest.json new file mode 100644 index 0000000..6af5558 --- /dev/null +++ b/front/src/manifest.json @@ -0,0 +1,235 @@ +{ + "lang": "en-US", + "name": "Chromatic", + "short_name": "Chromatic", + "description": "Image host site for web contents.", + "theme_color": "#7e57c2", + "background_color": "#7e57c2", + "display": "standalone", + "orientation": "any", + "start_url": "/", + "icons": [ + { + "src": "/assets/icon-maskable-36x36.webp", + "sizes": "36x36", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-48x48.webp", + "sizes": "48x48", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-72x72.webp", + "sizes": "72x72", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-96x96.webp", + "sizes": "96x96", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-144x144.webp", + "sizes": "144x144", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-192x192.webp", + "sizes": "192x192", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-512x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-36x36.png", + "sizes": "36x36", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-48x48.png", + "sizes": "48x48", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-72x72.png", + "sizes": "72x72", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-96x96.png", + "sizes": "96x96", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-144x144.png", + "sizes": "144x144", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-maskable-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/icon-36x36.webp", + "sizes": "36x36", + "type": "image/webp" + }, + { + "src": "/assets/icon-48x48.webp", + "sizes": "48x48", + "type": "image/webp" + }, + { + "src": "/assets/icon-72x72.webp", + "sizes": "72x72", + "type": "image/webp" + }, + { + "src": "/assets/icon-96x96.webp", + "sizes": "96x96", + "type": "image/webp" + }, + { + "src": "/assets/icon-144x144.webp", + "sizes": "144x144", + "type": "image/webp" + }, + { + "src": "/assets/icon-192x192.webp", + "sizes": "192x192", + "type": "image/webp" + }, + { + "src": "/assets/icon-512x512.webp", + "sizes": "512x512", + "type": "image/webp" + }, + { + "src": "/assets/icon-36x36.png", + "sizes": "36x36", + "type": "image/png" + }, + { + "src": "/assets/icon-48x48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "/assets/icon-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "/assets/icon-96x96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "/assets/icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "/assets/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/assets/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "shortcuts": [ + { + "name": "Upload files", + "url": "/gallery/me(upload:show)", + "icons": [ + { + "src": "/assets/action-upload-36x36.webp", + "sizes": "36x36", + "type": "image/webp" + }, + { + "src": "/assets/action-upload-48x48.webp", + "sizes": "48x48", + "type": "image/webp" + }, + { + "src": "/assets/action-upload-72x72.webp", + "sizes": "72x72", + "type": "image/webp" + }, + { + "src": "/assets/action-upload-96x96.webp", + "sizes": "96x96", + "type": "image/webp" + }, + { + "src": "/assets/action-upload-144x144.webp", + "sizes": "144x144", + "type": "image/webp" + }, + { + "src": "/assets/action-upload-192x192.webp", + "sizes": "192x192", + "type": "image/webp" + }, + { + "src": "/assets/action-upload-36x36.png", + "sizes": "36x36", + "type": "image/png" + }, + { + "src": "/assets/action-upload-48x48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "/assets/action-upload-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "/assets/action-upload-96x96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "/assets/action-upload-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "/assets/action-upload-192x192.png", + "sizes": "192x192", + "type": "image/png" + } + ] + } + ] +} diff --git a/front/src/polyfills.ts b/front/src/polyfills.ts new file mode 100644 index 0000000..03711e5 --- /dev/null +++ b/front/src/polyfills.ts @@ -0,0 +1,63 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/front/src/scrollbar.styl b/front/src/scrollbar.styl new file mode 100644 index 0000000..0c06418 --- /dev/null +++ b/front/src/scrollbar.styl @@ -0,0 +1,72 @@ +html, +.scrollable, +.scrollable-inner > .mat-dialog-container + // somewhat better-than-nothing IE scrollbar prettify + -ms-overflow-style -ms-autohiding-scrollbar + -ms-scrollbar-arrow-color white + -ms-scrollbar-face-color rgba(100, 100, 100, .5) + -ms-scrollbar-shadow-color rgba(100, 100, 100, .5) + -ms-scrollbar-track-color transparent + +// webkit and firefox have media queries +@media (pointer: fine) and (hover: hover) + html, + .scrollable, + .scrollable-inner > .mat-dialog-container + // for firefox, somewhat good + scrollbar-width thin + scrollbar-color rgba(100, 100, 100, .35) transparent + transition scrollbar-color 0.3s // with transition is good + + &:hover + scrollbar-color rgba(100, 100, 100, .75) transparent + + // nice scrollbar for webkit browsers + &::-webkit-scrollbar + width 0.6em + height 0.6em + overflow visible + border-radius 0 + -webkit-border-radius 0 + + &:hover + background-color rgba(100, 100, 100, .05) + + &:active + background-color rgba(100, 100, 100, .17) + + &-thumb:vertical, + &-thumb:horizontal + background rgba(100, 100, 100, .5) + -webkit-border-radius 0 + + &:hover + background rgba(100, 100, 100, .65) + -webkit-border-radius 0 + + &:active + background rgba(100, 100, 100, .8) + -webkit-border-radius 0 + + &-corner + background transparent + + &-track + opacity 0 + + &-thumb:window-inactive + background rgba(100, 100, 100, 0.2) + + .no-bar-scrollable + -ms-overflow-style none + scrollbar-width none + + &::-webkit-scrollbar + width 0 + height 0 + background transparent + +.scrollable-y, +.scrollable-inner-y > .mat-dialog-container + overflow-x hidden + overflow-y auto diff --git a/front/src/styles.styl b/front/src/styles.styl new file mode 100644 index 0000000..64a6a32 --- /dev/null +++ b/front/src/styles.styl @@ -0,0 +1,10 @@ +/* You can add global styles to this file, and also import other style files */ + +html, body + height 100vh + +@import "scrollbar.styl" +@import "button.styl" +@import "gallery-overlay.styl" +@import "action-panel.styl" +@import "dialog-omni.styl" diff --git a/front/src/test.ts b/front/src/test.ts new file mode 100644 index 0000000..50193eb --- /dev/null +++ b/front/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context(path: string, deep?: boolean, filter?: RegExp): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/front/tsconfig.app.json b/front/tsconfig.app.json new file mode 100644 index 0000000..82d91dc --- /dev/null +++ b/front/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/front/tsconfig.json b/front/tsconfig.json new file mode 100644 index 0000000..f69f654 --- /dev/null +++ b/front/tsconfig.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "es2015", + "module": "es2020", + "lib": [ + "es2018", + "dom" + ] + } +} diff --git a/front/tsconfig.spec.json b/front/tsconfig.spec.json new file mode 100644 index 0000000..092345b --- /dev/null +++ b/front/tsconfig.spec.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/front/tslint.json b/front/tslint.json new file mode 100644 index 0000000..67bcdea --- /dev/null +++ b/front/tslint.json @@ -0,0 +1,152 @@ +{ + "extends": "tslint:recommended", + "rules": { + "align": { + "options": [ + "parameters", + "statements" + ] + }, + "array-type": false, + "arrow-return-shorthand": true, + "curly": true, + "deprecation": { + "severity": "warning" + }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ], + "eofline": true, + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "import-spacing": true, + "indent": { + "options": [ + "spaces" + ] + }, + "max-classes-per-file": false, + "max-line-length": [ + true, + 140 + ], + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-empty": false, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-switch-case-fall-through": true, + "no-var-requires": false, + "object-literal-key-quotes": [ + true, + "as-needed" + ], + "quotemark": [ + true, + "single" + ], + "semicolon": { + "options": [ + "always" + ] + }, + "space-before-function-paren": { + "options": { + "anonymous": "never", + "asyncArrow": "always", + "constructor": "never", + "method": "never", + "named": "never" + } + }, + "typedef": [ + true, + "call-signature" + ], + "typedef-whitespace": { + "options": [ + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + }, + { + "call-signature": "onespace", + "index-signature": "onespace", + "parameter": "onespace", + "property-declaration": "onespace", + "variable-declaration": "onespace" + } + ] + }, + "variable-name": { + "options": [ + "ban-keywords", + "check-format", + "allow-pascal-case" + ] + }, + "whitespace": { + "options": [ + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type", + "check-typecast" + ] + }, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": true, + "no-input-rename": true, + "no-inputs-metadata-property": true, + "no-output-native": true, + "no-output-on-prefix": true, + "no-output-rename": true, + "no-outputs-metadata-property": true, + "template-banana-in-box": true, + "template-no-negated-async": true, + "use-lifecycle-interface": true, + "use-pipe-transform-interface": true + }, + "rulesDirectory": [ + "codelyzer" + ] +} \ No newline at end of file diff --git a/fs.go b/fs.go new file mode 100644 index 0000000..910da7a --- /dev/null +++ b/fs.go @@ -0,0 +1,170 @@ +package main + +/** following code is adapted from https://github.com/lpar/gzipped + +Copyright (c) 2016, IBM Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of IBM nor the names of project contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ( + "fmt" + "net/http" + "os" + "path" + "strings" + + "github.com/golang/gddo/httputil/header" +) + +const ( + gzipEncoding = "gzip" + gzipExtension = ".gz" + + brotliEncoding = "br" + brotliExtension = ".br" +) + +type fileHandler struct { + root http.FileSystem +} + +// FileServer is a drop-in replacement for Go's standard http.FileServer +// which adds support for static resources precompressed with gzip, at +// the cost of removing the support for directory browsing. +// +// If file filename.ext has a compressed version filename.ext.gz alongside +// it, if the client indicates that it accepts gzip-compressed data, and +// if the .gz file can be opened, then the compressed version of the file +// will be sent to the client. Otherwise the request is passed on to +// http.ServeContent, and the raw (uncompressed) version is used. +// +// It is up to you to ensure that the compressed and uncompressed versions +// of files match and have sensible timestamps. +// +// Compressed or not, requests are fulfilled using http.ServeContent, and +// details like accept ranges and content-type sniffing are handled by that +// method. +func FileServer(root http.FileSystem) http.Handler { + return &fileHandler{root} +} + +func acceptable(r *http.Request, encoding string) bool { + for _, aspec := range header.ParseAccept(r.Header, "Accept-Encoding") { + if aspec.Value == encoding && aspec.Q == 0.0 { + return false + } + if (aspec.Value == encoding || aspec.Value == "*") && aspec.Q > 0.0 { + return true + } + } + return false +} + +func (f *fileHandler) openAndStat(path string) (http.File, os.FileInfo, error) { + file, err := f.root.Open(path) + var info os.FileInfo + // This slightly weird variable reuse is so we can get 100% test coverage + // without having to come up with a test file that can be opened, yet + // fails to stat. + if err == nil { + info, err = file.Stat() + } + if err != nil { + return file, nil, err + } + if info.IsDir() { + return file, nil, fmt.Errorf("%s is directory", path) + } + return file, info, nil +} + +func (f *fileHandler) findFile(fpath string, w http.ResponseWriter, r *http.Request) (file http.File, info os.FileInfo, err error) { + foundAcceptable := false + + if acceptable(r, brotliEncoding) { + file, info, err = f.openAndStat(fpath + brotliExtension) + if err == nil { + foundAcceptable = true + w.Header().Set("Content-Encoding", brotliEncoding) + } + } + + if !foundAcceptable && acceptable(r, gzipEncoding) { + file, info, err = f.openAndStat(fpath + gzipExtension) + if err == nil { + foundAcceptable = true + w.Header().Set("Content-Encoding", gzipEncoding) + } + } + // If we didn't manage to open a compressed version, try for uncompressed + if !foundAcceptable { + file, info, err = f.openAndStat(fpath) + } + + return +} + +func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + isIndex := false + + upath := r.URL.Path + if !strings.HasPrefix(upath, "/") { + upath = "/" + upath + r.URL.Path = upath + } + fpath := path.Clean(upath) + if strings.HasSuffix(fpath, "/") { + isIndex = true + fpath = "/index.html" + } + // Try for a compressed version if appropriate + var file http.File + var err error + var info os.FileInfo + + file, info, err = f.findFile(fpath, w, r) + if err != nil { + file, info, err = f.findFile("index.html", w, r) + if err != nil { + // Doesn't exist compressed or uncompressed + http.NotFound(w, r) + return + } else { + isIndex = true + } + } + + if isIndex { + w.Header().Set("Content-Type", "text/html") + } else if strings.HasSuffix(fpath, ".js") { + w.Header().Set("Content-Type", "application/javascript") + } + + defer file.Close() + http.ServeContent(w, r, fpath, info.ModTime(), file) +} + diff --git a/gcnotifier/gcnotifier.go b/gcnotifier/gcnotifier.go new file mode 100644 index 0000000..b19fa07 --- /dev/null +++ b/gcnotifier/gcnotifier.go @@ -0,0 +1,127 @@ +/** +The MIT License (MIT) + +Copyright (c) 2016 Carlo Alberto Ferraris + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +// Package gcnotifier provides a way to receive notifications after every +// garbage collection (GC) cycle. This can be useful, in long-running programs, +// to instruct your code to free additional memory resources that you may be +// using. +// +// A common use case for this is when you have custom data structures (e.g. +// buffers, caches, rings, trees, pools, ...): instead of setting a maximum size +// to your data structure you can leave it unbounded and then drop all (or some) +// of the allocated-but-unused slots after every GC run (e.g. sync.Pool drops +// all allocated-but-unused objects in the pool during GC). +// +// To minimize the load on the GC the code that runs after receiving the +// notification should try to avoid allocations as much as possible, or at the +// very least make sure that the amount of new memory allocated is significantly +// smaller than the amount of memory that has been "freed" in response to the +// notification. +// +// GCNotifier guarantees to send a notification after every GC cycle completes. +// Note that the Go runtime does not guarantee that the GC will run: +// specifically there is no guarantee that a GC will run before the program +// terminates. +package gcnotifier + +import "runtime" + +// GCNotifier allows your code to control and receive notifications every time +// the garbage collector runs. +type GCNotifier struct { + n *gcnotifier +} + +type gcnotifier struct { + doneCh chan struct{} + gcCh chan struct{} +} + +type sentinel gcnotifier + +// AfterGC returns the channel that will receive a notification after every GC +// run. No further notifications will be sent until the previous notification +// has been consumed. To stop notifications immediately call the Close() method. +// Otherwise notifications will continue until the GCNotifier object itself is +// garbage collected. Note that the channel returned by AfterGC will be closed +// only when GCNotifier is garbage collected. +// The channel is unique to a single GCNotifier object: use dedicated +// GCNotifiers if you need to listen for GC notifications in multiple receivers +// at the same time. +func (n *GCNotifier) AfterGC() <-chan struct{} { + return n.n.gcCh +} + +// Close will stop and release all resources associated with the GCNotifier. It +// is not required to call Close explicitly: when the GCNotifier object is +// garbage collected Close is called implicitly. +// If you don't call Close explicitly make sure not to accidently maintain the +// GCNotifier object alive. +func (n *GCNotifier) Close() { + autoclose(n.n) +} + +// autoclose is both called explicitely via Close or when the GCNotifier is +// garbage collected +func autoclose(n *gcnotifier) { + select { + case n.doneCh <- struct{}{}: + default: + } +} + +// New creates and arms a new GCNotifier. +func New() *GCNotifier { + n := &gcnotifier{ + gcCh: make(chan struct{}, 1), + doneCh: make(chan struct{}, 1), + } + // sentinel is dead immediately after the call to SetFinalizer + runtime.SetFinalizer(&sentinel{gcCh: n.gcCh, doneCh: n.doneCh}, finalizer) + // n will be dead when the GCNotifier that wraps it (see the return below) is dead + runtime.SetFinalizer(n, autoclose) + // we wrap the internal gcnotifier object in a GCNotifier so that we can + // safely call autoclose when the GCNotifier becomes unreachable + return &GCNotifier{n: n} +} + +func finalizer(s *sentinel) { + // check if we have to shutdown + select { + case <-s.doneCh: + close(s.gcCh) + return + default: + } + + // send the notification + select { + case s.gcCh <- struct{}{}: + default: + // drop it if there's already an unread notification in gcCh + } + + // rearm the finalizer + runtime.SetFinalizer(s, finalizer) +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a5ee20d --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module ImageServer + +go 1.14 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/OneOfOne/xxhash v1.2.2 + github.com/cespare/xxhash v1.1.0 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/gin-gonic/gin v1.6.3 + github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 + github.com/json-iterator/go v1.1.10 + github.com/liut/jpegquality v0.0.0-20200606101153-6dd6284fded6 + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + go.mongodb.org/mongo-driver v1.4.0 + golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..baffd9d --- /dev/null +++ b/go.sum @@ -0,0 +1,294 @@ +cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v3 v3.0.0 h1:1PwO5w5VCtlUUl+KTOBsTGZlhjWkcybsGaAau52tOy8= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aws/aws-sdk-go v1.29.15 h1:0ms/213murpsujhsnxnNKNeVouW60aJqSd992Ks3mxs= +github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 h1:5vu7C+63KTbsSNnLhrgB98Sqy8MNVSW8FdhkcWA/3Rk= +github.com/golang/gddo v0.0.0-20200831202555-721e228c7686/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU= +github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/jade v1.1.3 h1:p7J/50I0cjo0wq/VWVCDFd8taPJbuFC+bq23SniRFX0= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/pongo2 v0.0.1 h1:zGP7pW51oi5eQZMIlGA3I+FHY9/HOQWDB+572yin0to= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1 h1:10g/WnoRR+U+XXHWKBHeNy/+tZmM2kcAVGLOsz+yaDA= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kataras/golog v0.0.10 h1:vRDRUmwacco/pmBAm8geLn8rHEdc+9Z4NAr5Sh7TG/4= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/iris/v12 v12.1.8 h1:O3gJasjm7ZxpxwTH8tApZsvf274scSGQAUpNe47c37U= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/pio v0.0.2 h1:6NAi+uPJ/Zuid6mrAKlgpbI11/zK/lV4B2rxWaJN98Y= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7 h1:hYW1gP94JUmAhBtJ+LNz5My+gBobDxPR1iVuKug26aA= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/liut/jpegquality v0.0.0-20200606101153-6dd6284fded6 h1:sVsHqWyj9PsFBLcj1akUCeMe5XVQdTHtC2FdiWtV/Qg= +github.com/liut/jpegquality v0.0.0-20200606101153-6dd6284fded6/go.mod h1:Xf7+nlHBcCJonDRXypZIL5GiDoODOHqVTC63jsgGcA0= +github.com/lpar/gzipped v1.1.0 h1:FEQnBzF06KTMh8Wnse6wNJvGwe7+vILQIFzuTq6ipGs= +github.com/lpar/gzipped v1.1.0/go.mod h1:JBo67wiCld7AmFYfSNA75NmFG65roJiGwrVohF8uYGE= +github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.mongodb.org/mongo-driver v1.4.0 h1:C8rFn1VF4GVEM/rG+dSoMmlm2pyQ9cs2/oRtUATejRU= +go.mongodb.org/mongo-driver v1.4.0/go.mod h1:llVBH2pkj9HywK0Dtdt6lDikOjFLbceHVu/Rc0iMKLs= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..73e7ff3 --- /dev/null +++ b/handlers.go @@ -0,0 +1,839 @@ +package main + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/json-iterator/go" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "net/url" + "path" + "strconv" + "strings" +) + +var json = jsoniter.ConfigCompatibleWithStandardLibrary + +func jsonResult(c *gin.Context, s SErr) { + c.JSON(http.StatusOK, gin.H{ + "status": s, + }) +} + +func jsonResultD(c *gin.Context, s SErr, d interface{}) { + c.JSON(http.StatusOK, gin.H{ + "status": s, + "data": d, + }) +} + +func abortWithError(c *gin.Context, s SErr) { + jsonResult(c, s) + // discard all pending stuffs + _, _ = io.CopyN(ioutil.Discard, c.Request.Body, int64(config.Site.BodySize+10<<20)) + c.Abort() +} + +// user api + +func userExist(c *gin.Context) { + name := c.Param("name") + + if name == "" { + abortWithError(c, EBadRequest) + return + } + + u, r := UserExist(name) + + jsonResultD(c, r, u) +} + +func register(c *gin.Context) { + var j RegisterQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := Register(&j) + + jsonResult(c, r) +} + +func login(c *gin.Context) { + var j LoginQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + u, r := Login(&j) + + if r == EOk { + if newJWT, err := CreateJWT(u); err == nil { + c.Header("X-Update-Authorization", newJWT) + } else { + log.Printf("[Warn] failed generating new jwt: %s\n", err) + r = EUnknown + } + } + + jsonResultD(c, r, u) +} + +func getUser(c *gin.Context) { + id := c.Param("id") + + if id == "" { + abortWithError(c, EBadRequest) + return + } + + u, r := GetUser(id) + + jsonResultD(c, r, u) +} + +func changePassword(c *gin.Context) { + var j ChangePasswordQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + // u should exist and has type *User. + u, _ := c.Get("user") + r := ChangePassword(&j, u.(*User)) + + jsonResult(c, r) +} + +func readFormFile(fh *multipart.FileHeader) ([]byte, error) { + f, err := fh.Open() + if err != nil { + return nil, err + } + + return ioutil.ReadAll(f) +} + +func setAvatar(c *gin.Context) { + u, _ := c.Get("user") + + a, err := c.FormFile("avatar") + if err != nil { + abortWithError(c, EUnknown) + return + } + + d, err := readFormFile(a) + if err != nil { + abortWithError(c, EUnknown) + return + } + + r := SetAvatar(&SetAvatarQ{ + ID: u.(*User).ID, + Data: d, + }) + + jsonResult(c, r) +} + +func resetAvatar(c *gin.Context) { + u, _ := c.Get("user") + + removeAvatar(u.(*User).ID) + + jsonResult(c, EOk) +} + +func setAvatarP(c *gin.Context) { + id, err := primitive.ObjectIDFromHex(c.PostForm("id")) + if err != nil { + abortWithError(c, EBadRequest) + return + } + + a, err := c.FormFile("avatar") + if err != nil { + abortWithError(c, EUnknown) + return + } + + d, err := readFormFile(a) + if err != nil { + abortWithError(c, EUnknown) + return + } + + r := SetAvatar(&SetAvatarQ{ + ID: id, + Data: d, + }) + + jsonResult(c, r) +} + +func resetAvatarP(c *gin.Context) { + id, err := primitive.ObjectIDFromHex(c.Param("id")) + if err != nil { + abortWithError(c, EBadRequest) + return + } + + removeAvatar(id) + + jsonResult(c, EOk) +} + +func listUser(c *gin.Context) { + var j ListUserQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := ListUser(&j) + + jsonResultD(c, r, p) +} + +func addUser(c *gin.Context) { + var j AddUserQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := AddUser(&j) + + jsonResult(c, r) +} + +func removeUser(c *gin.Context) { + var j RemoveUserQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := RemoveUser(&j) + + jsonResult(c, r) +} + +func setPassword(c *gin.Context) { + var j SetPasswordQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := SetPassword(&j) + + jsonResult(c, r) +} + +func setUserPermission(c *gin.Context) { + var j SetUserPermissionQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := SetUserPermission(&j) + + jsonResult(c, r) +} + +func listInvite(c *gin.Context) { + var j ListInviteQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := ListInvite(&j) + + jsonResultD(c, r, p) +} + +func addInvite(c *gin.Context) { + var j InviteCode + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := AddInvite(&j) + + jsonResult(c, r) +} + +func removeInvite(c *gin.Context) { + code := c.Param("code") + + if code == "" { + abortWithError(c, EBadRequest) + return + } + + r := RemoveInvite(code) + + jsonResult(c, r) +} + +func setInviteTimes(c *gin.Context) { + var j InviteCode + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + r := SetInviteTimes(&j) + + jsonResult(c, r) +} + +// gallery api + +func listImage(c *gin.Context) { + var j ListImageQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := ListImage(&j) + + jsonResultD(c, r, p) +} + +func listImageTags(c *gin.Context) { + strId := c.Param("id") + + id, err := primitive.ObjectIDFromHex(strId) + + if err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := ListImageTags(id) + + jsonResultD(c, r, p) +} + +func listImageWithTag(c *gin.Context) { + var j ListImageWithTagQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := ListImageWithTag(&j) + + jsonResultD(c, r, p) +} + +func listImageContainsTag(c *gin.Context) { + var j ListImageContainsTagQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := ListImageContainsTag(&j) + + jsonResultD(c, r, p) +} + +func setImageInfo(c *gin.Context) { + var j SetImageInfoQ + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + u, _ := c.Get("user_id") + var r SErr + + if u == nil { + r = SetImageInfo(&j, nil) + } else { + r = SetImageInfo(&j, u.(*primitive.ObjectID)) + } + + jsonResult(c, r) +} + +func getImage(c *gin.Context) { + strId := c.Param("id") + + id, err := primitive.ObjectIDFromHex(strId) + + if err != nil { + abortWithError(c, EBadRequest) + return + } + + p, r := GetImage(id) + + jsonResultD(c, r, p) +} + +func removeImage(c *gin.Context) { + var j struct { + Ids []primitive.ObjectID `json:"ids" binding:"required"` + } + + if err := c.ShouldBindJSON(&j); err != nil { + abortWithError(c, EBadRequest) + return + } + + u, _ := c.Get("user_id") + var r SErr + + if u == nil { + r = RemoveImage(j.Ids, nil) + } else { + r = RemoveImage(j.Ids, u.(*primitive.ObjectID)) + } + + jsonResult(c, r) +} + +// image api + +func uploadSimple(c *gin.Context) { + q := struct { + Tag string `form:"tag"` + Origins string `form:"origins"` + Image *multipart.FileHeader `form:"image" binding:"required"` + }{} + + if err := c.ShouldBind(&q); err != nil { + abortWithError(c, EBadRequest) + return + } + + j := UploadSimpleQ{} + + _u, _ := c.Get("user") + u, ok := _u.(*User) + + if !ok { + abortWithError(c, EUnknown) + return + } + + j.User = u.ID + j.Name = u.Name + j.Tag = q.Tag + j.Origins = CleanOrigins(q.Origins) + + filename := q.Image.Filename + if p := strings.LastIndexByte(filename, '.'); p != -1 { + j.GuessedType = filename[p:] + } + + d, err := readFormFile(q.Image) + if err != nil { + abortWithError(c, EUnknown) + return + } + + j.Data = d + + p, r := UploadSimple(&j) + + jsonResultD(c, r, p) +} + +func uploadAdvanced(c *gin.Context) { + q := struct { + Tag string `form:"tag"` + Origins string `form:"origins"` + Images []*multipart.FileHeader `form:"images" binding:"required"` + }{} + + if err := c.ShouldBind(&q); err != nil { + abortWithError(c, EBadRequest) + return + } + + j := UploadAdvancedQ{} + + _u, _ := c.Get("user") + u, ok := _u.(*User) + + if !ok { + abortWithError(c, EUnknown) + return + } + + j.User = u.ID + j.Name = u.Name + j.Tag = q.Tag + j.Origins = CleanOrigins(q.Origins) + j.Files = make([]ImageFile, 0, 4) + + e := make(map[string]struct{}) + + for _, i := range q.Images { + if _, exist := AllowType[i.Filename]; !exist { + continue + } + + if _, exist := e[i.Filename]; exist { + continue + } + + d, err := readFormFile(i) + if err != nil { + abortWithError(c, EUnknown) + return + } + + j.Files = append(j.Files, ImageFile{ + Type: i.Filename, + Data: d, + }) + + e[i.Filename] = struct {}{} + } + + if len(j.Files) == 0 { + abortWithError(c, EBadRequest) + return + } + + p, r := UploadAdvanced(&j) + + jsonResultD(c, r, p) +} + +func updateImage(c *gin.Context) { + q := struct { + Id string `form:"id" binding:"required"` + Images []*multipart.FileHeader `form:"images" binding:"required"` + }{} + + if err := c.ShouldBind(&q); err != nil { + abortWithError(c, EBadRequest) + return + } + + j := UpdateImageQ{} + + id, err := primitive.ObjectIDFromHex(q.Id) + + if err != nil { + abortWithError(c, EBadRequest) + return + } + + j.ID = id + j.Files = make([]ImageFile, 0, 4) + + e := make(map[string]struct{}) + + for _, i := range q.Images { + if _, exist := AllowType[i.Filename]; !exist { + continue + } + + if _, exist := e[i.Filename]; exist { + continue + } + + d, err := readFormFile(i) + if err != nil { + abortWithError(c, EUnknown) + return + } + + j.Files = append(j.Files, ImageFile{ + Type: i.Filename, + Data: d, + }) + + e[i.Filename] = struct {}{} + } + + if len(j.Files) == 0 { + abortWithError(c, EBadRequest) + return + } + + r := UpdateImage(&j) + + jsonResult(c, r) +} + +// image serve api + +func getImageFile(c *gin.Context) { + _id := strings.TrimSuffix(c.Param("id"), ".i") + + id, err := primitive.ObjectIDFromHex(_id) + + if err != nil { + c.Status(http.StatusNotFound) + return + } + + var compatSupport []string + + accept := c.GetHeader("Accept") + ua := c.GetHeader("User-Agent") + + if strings.Contains(accept, "image/avif") { + compatSupport = append(compatSupport, "avif") + } + + // stupid (old) edge doesn't claims itself to support webp, despite it actually do + if strings.Contains(accept, "image/webp") || strings.Contains(ua, "Edge/18") { + compatSupport = append(compatSupport, "webp") + } + + compatSupport = append(compatSupport, "jpeg", "png") + + var rawPref []string + var preference []string + + if pref, err := c.Cookie("Preference"); err != nil { + preference = compatSupport + } else if err := json.UnmarshalFromString(pref, &rawPref); err != nil { + preference = compatSupport + } else { + _map := make(map[string]struct{}) + + for _, t := range rawPref { + if _, exist := AllowType[t]; exist { + _map[t] = struct {}{} + preference = append(preference, t) + } + } + + for _, t := range compatSupport { + if _, exist := _map[t]; !exist { + preference = append(preference, t) + } + } + } + + origin := "" + if org := c.GetHeader("Origin"); org != "" { + u, err := url.Parse(org) + if err == nil { + origin = u.Host + } else if err.Error() == "missing protocol scheme" { + if u, err := url.Parse("http://" + org); err == nil { + origin = u.Host + } + } + } + + if origin == "" { + if ref := c.GetHeader("Referer"); ref != "" { + u, err := url.Parse(ref) + if err == nil { + origin = u.Host + } else if err.Error() == "missing protocol scheme" { + if u, err := url.Parse("http://" + ref); err == nil { + origin = u.Host + } + } + } + } + + im, err := GetImage(id) + + if err == EImageNotExist { + c.Status(http.StatusNotFound) + return + } else if err != EOk { + c.Status(http.StatusInternalServerError) + return + } + + //goland:noinspection GoNilness + if origin != config.Site.Host && !VerifyHostname(origin, im.Origins) { + c.Status(http.StatusForbidden) + return + } + + avail := make(map[string]int64) + hasAvif := false + + //goland:noinspection GoNilness + for _, i := range im.Files { + if i.Hash != 0 { + avail[i.Format] = i.Hash + } + + if i.Format == "avif" { + hasAvif = true + } + } + + var ext string + var tag uint64 + + for _, t := range preference { + if h, exist := avail[t]; exist { + ext = t + tag = uint64(h) + break + } + } + + if ext == "" { + c.Status(http.StatusNotFound) + return + } + + if eTag := c.GetHeader("If-None-Match"); eTag != "" { + if eTag == strconv.FormatUint(tag, 16) { + c.Status(http.StatusNotModified) + return + } + } + + //goland:noinspection GoNilness + hash := im.Storage + + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash, fmt.Sprintf("%s.%s", hash, ext)) + + c.Header("Content-Type", "image/" + ext) + c.Header("ETag", strconv.FormatUint(tag, 16)) + c.File(p) + + //goland:noinspection GoNilness + if origin != config.Site.Host { + if !hasAvif && im.View > config.Site.AvifThreshold - 1 { + _ = PostVisitImage(im.ID, true) + } else { + _ = PostVisitImage(im.ID, false) + } + } + +} + +func getImagePreview(c *gin.Context) { + _id := strings.TrimSuffix(c.Param("id"), ".i") + + id, err := primitive.ObjectIDFromHex(_id) + + if err != nil { + c.Status(http.StatusNotFound) + return + } + + im, err := GetImage(id) + + if err == EImageNotExist { + c.Status(http.StatusNotFound) + return + } else if err != EOk { + c.Status(http.StatusInternalServerError) + return + } + + //goland:noinspection GoNilness + hash := im.Storage + + high, low := fromString(hash) + p := path.Join(config.Site.Storage, "image", high, low, hash, "preview.jpeg") + + c.Header("Content-Type", "image/jpeg") + c.File(p) +} + +func setFormatPreference(c *gin.Context) { + pRaw := c.PostForm("preference") + + var p []string + + for _, f := range strings.Split(pRaw, ",") { + if _, exist := AllowType[f]; exist { + p = append(p, f) + } + } + + if len(p) == 0 { + abortWithError(c, EBadRequest) + return + } + + if s, err := json.Marshal(p); err != nil { + log.Printf("[Warn] failed marshaling preference array: %s\n", err) + abortWithError(c, EUnknown) + } else { + c.SetSameSite(http.SameSiteNoneMode) + c.SetCookie("Preference", string(s), 0, "", "", false, false) + } +} + +func getAvatarFile(c *gin.Context) { + idStr := strings.TrimSuffix(c.Param("id"), ".i") + + id, err := primitive.ObjectIDFromHex(idStr) + + if err != nil { + c.Status(http.StatusNotFound) + return + } + + cu := C("user") + + if n := cu.FindOne(X(), bson.M{"_id": id}); n.Err() == mongo.ErrNoDocuments { + c.Status(http.StatusNotFound) + return + } else if n.Err() != nil { + c.Status(http.StatusInternalServerError) + return + } + + high := idStr[6:8] + low := idStr[10:12] + p := path.Join(config.Site.Storage, "avatar", high, low, idStr+".jpeg") + c.Header("Content-Type", "image/jpeg") + c.File(p) +} + +func ServeStatic(prefix string) gin.HandlerFunc { + fs := FileServer(gin.Dir(config.Site.Static, false)) + if prefix != "" { + fs = http.StripPrefix(prefix, fs) + } + return func(c *gin.Context) { + fs.ServeHTTP(c.Writer, c.Request) + } +} \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..3b33a21 --- /dev/null +++ b/main.go @@ -0,0 +1,148 @@ +package main + +import ( + "ImageServer/gcnotifier" + "ImageServer/native" + "github.com/BurntSushi/toml" + "github.com/gin-gonic/gin" + "io/ioutil" + "log" + "runtime" +) + +var config Config + +func init() { + config.SetDefault() + if c, err := ioutil.ReadFile("./config.toml"); err == nil { + if err = toml.Unmarshal(c, &config); err != nil { + log.Printf("Failed loading config: %s, use default settings.\n", err) + config.SetDefault() + } else { + log.Println("Config loaded.") + } + } else { + log.Println("No config found. use default settings.") + } + + native.SetMaxSize(uint32(config.Site.MaxSize)) + //native.SetMaxThread(1) + + if config.Site.Thread == 0 { + config.Site.Thread = uint64(runtime.NumCPU()) + } + + if runtime.NumCPU() == 1 { + native.SetMaxThread(1) + } else { + native.SetMaxThread(uint32(config.Site.Thread - 1)) + } + + + // Setup database + if err := InitDB(); err != nil { + log.Fatalf("Failed init database: %s", err) + } + + if !config.Site.Debug { + gin.SetMode(gin.ReleaseMode) + } + + InitUser() + InitEncoder() + + if err := InitCleanup(); err != nil { + log.Fatalf("Failed cleanup: %s", err) + } + + // On linux, program will continue holding a huge amount of memory used by encoding codes, + // pressure the memory a lot. We attach malloc_trim call after each GC run to + // reduce that usage. + gcn := gcnotifier.New() + go func() { + for range gcn.AfterGC() { + native.TrimMemory() + } + }() +} + +func RegisterRoute(r *gin.Engine) { + a := r.Group("/api", JWTRetrieve()) + { + user := a.Group("/user") + { + user.GET("/exist/:name", userExist) + user.GET("/get/:id", getUser) + user.POST("/register", register) + user.POST("/login", login) + authed := user.Group("/", JWTAuth()) + { + authed.POST("/changePassword", changePassword) + authed.POST("/setAvatar", setAvatar) + authed.GET("/resetAvatar", resetAvatar) + admin := authed.Group("/admin", AdminOnly()) + { + admin.POST("/setAvatar", setAvatarP) + admin.GET("/resetAvatar/:id", resetAvatarP) + admin.POST("/list", listUser) + admin.POST("/add", addUser) + admin.POST("/remove", removeUser) + admin.POST("/password", setPassword) + admin.POST("/permission", setUserPermission) + } + } + } + invite := a.Group("/invite", JWTAuth(), AdminOnly()) + { + invite.POST("/list", listInvite) + invite.POST("/add", addInvite) + invite.GET("/remove/:code", removeInvite) + invite.POST("/setTimes", setInviteTimes) + } + gallery := a.Group("/gallery") + { + gallery.POST("/list", listImage) + gallery.GET("/listTags/:id", listImageTags) + gallery.POST("/listWithTag", listImageWithTag) + gallery.POST("/listContainsTag", listImageContainsTag) + gallery.POST("/set", JWTAuth(), AdminOrLimited(), setImageInfo) + } + image := a.Group("/image") + { + image.GET("/get/:id", getImage) + image.POST("/remove", JWTAuth(), AdminOrLimited(), removeImage) + upload := a.Group("/upload", JWTAuth(), ActiveUser()) + { + upload.POST("/simple", uploadSimple) + advanced := upload.Group("/", Privileged()) + { + advanced.POST("/advanced", uploadAdvanced) + advanced.POST("/update", updateImage) + } + } + } + } + r.GET("/image/:id", getImageFile) + r.HEAD("/image/:id", getImageFile) + r.GET("/avatar/:id", getAvatarFile) + r.HEAD("/avatar/:id", getAvatarFile) + r.GET("/preview/:id", getImagePreview) + r.HEAD("/preview/:id", getImagePreview) + r.POST("/preference", setFormatPreference) + + r.HandleMethodNotAllowed = true + r.NoRoute(ServeStatic("")) +} + +func main() { + router := gin.Default() + router.MaxMultipartMemory = int64(config.Site.BodySize) // 50MB + + RegisterRoute(router) + + if config.HTTPS.Cert != "" && config.HTTPS.Key != "" { + _ = router.RunTLS(config.Site.Listen, config.HTTPS.Cert, config.HTTPS.Key) + } else { + _ = router.Run(config.Site.Listen) + } +} \ No newline at end of file diff --git a/middleware.go b/middleware.go new file mode 100644 index 0000000..4a88ccd --- /dev/null +++ b/middleware.go @@ -0,0 +1,253 @@ +package main + +import ( + "fmt" + "github.com/dgrijalva/jwt-go" + "github.com/gin-gonic/gin" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "strings" + "time" + + "log" +) + +func CreateJWT(u *User) (string, error) { + claims := UserJWT{ + Name: u.Name, + VersionP: u.VersionP, + VersionC: u.VersionC, + StandardClaims: jwt.StandardClaims{ + Id: u.ID.Hex(), + IssuedAt: time.Now().Unix(), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString([]byte(config.Security.HMAC)) +} + +func JWTRetrieve() gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("jwtError", EOk) + auth := c.Request.Header.Get("Authorization") + if auth == "" { + c.Set("user", nil) + c.Set("jwtError", EMissingAuthorization) + return + } + + if !strings.HasPrefix(auth, "Bearer ") { + c.Set("user", nil) + + c.Set("jwtError", EBadCredential) + return + } + + jwtToken := strings.TrimPrefix(auth, "Bearer ") + + token, err := jwt.ParseWithClaims(jwtToken, &UserJWT{}, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + + return []byte(config.Security.HMAC), nil + }) + + if err != nil { + c.Set("user", nil) + c.Set("jwtError", EBadCredential) + return + } + + claims, ok := token.Claims.(*UserJWT) + + if !ok || !token.Valid { + c.Set("user", nil) + c.Set("jwtError", EBadCredential) + return + } + + cu := C("user") + n := &User{} + + id, err := primitive.ObjectIDFromHex(claims.Id) + + if err != nil { + c.Set("user", nil) + c.Set("jwtError", EBadCredential) + return + } + + if u := cu.FindOne(X(), bson.M{"_id": id}); + u.Err() == mongo.ErrNoDocuments { + c.Set("user", nil) + c.Set("jwtError", EBadCredential) + return + } else if u.Err() != nil { + log.Printf("[Warn] failed finding user data of %s from jwt: %s\n", claims.Id, u.Err()) + c.Set("user", nil) + c.Set("jwtError", EUnknown) + return + } else if err := u.Decode(n); err != nil { + log.Printf("[Warn] failed loading user data of %s from jwt: %s\n", claims.Id, u.Err()) + c.Set("user", nil) + c.Set("jwtError", EUnknown) + return + } + + if n.VersionP != claims.VersionP { + c.Set("user", nil) + c.Set("jwtError", ECredentialExpired) + return + } + + if n.VersionC != claims.VersionC { + if newJWT, err := CreateJWT(n); err == nil { + c.Header("X-Update-Authorization", newJWT) + } else { + log.Printf("[Warn] failed generating new jwt: %s\n", err) + } + } + + c.Set("user", n) + } +} + +func JWTAuth() gin.HandlerFunc { + return func(c *gin.Context) { + ui, exist := c.Get("user") + if !exist { + log.Println("[Warn] JWTAuth appeared without JWTRetrieve.") + abortWithError(c, EUnknown) + return + } else if u, ok := ui.(*User); !ok { + log.Println("[Warn] `user` field set to unknown value.") + erri, exist := c.Get("jwtError") + var err SErr + if !exist { + err = EUnknown + } else { + err = erri.(SErr) + } + abortWithError(c, err) + return + } else { + if u.Frozen { + abortWithError(c, EPermissionDenied) + return + } + } + } +} + +func ActiveUser() gin.HandlerFunc { + return func(c *gin.Context) { + ui, exist := c.Get("user") + if !exist { + log.Println("[Warn] Privileged appeared before jwt.") + abortWithError(c, EUnknown) + return + } else if u, ok := ui.(*User); !ok { + log.Println("[Warn] `user` field set to unknown value.") + abortWithError(c, EUnknown) + return + } else { + if u.Frozen { + abortWithError(c, EPermissionDenied) + return + } + } + } +} + +func Privileged() gin.HandlerFunc { + return func(c *gin.Context) { + ui, exist := c.Get("user") + if !exist { + log.Println("[Warn] Privileged appeared before jwt.") + abortWithError(c, EUnknown) + return + } else if u, ok := ui.(*User); !ok { + log.Println("[Warn] `user` field set to unknown value.") + abortWithError(c, EUnknown) + return + } else { + if !u.Privileged { + abortWithError(c, EPermissionDenied) + return + } + } + } +} + +func AdminOnly() gin.HandlerFunc { + return func(c *gin.Context) { + ui, exist := c.Get("user") + if !exist { + log.Println("[Warn] Privileged appeared before jwt.") + abortWithError(c, EUnknown) + return + } else if u, ok := ui.(*User); !ok { + log.Println("[Warn] `user` field set to unknown value.") + abortWithError(c, EUnknown) + return + } else { + if u.Name != "admin" { + abortWithError(c, EPermissionDenied) + return + } + } + } +} + +func AdminOrLimited() gin.HandlerFunc { + return func(c *gin.Context) { + ui, exist := c.Get("user") + if !exist { + log.Println("[Warn] Privileged appeared before jwt.") + abortWithError(c, EUnknown) + return + } else if u, ok := ui.(*User); !ok { + log.Println("[Warn] `user` field set to unknown value.") + abortWithError(c, EUnknown) + return + } else { + if u.Name == "admin" { + c.Set("user_id", nil) + } else { + c.Set("user_id", &u.ID) + } + } + } +} + +//func fileExists(prefix string, filepath string) bool { +// if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) { +// name := path.Join(config.Site.Static, p) +// stats, err := os.Stat(name) +// if err != nil { +// return false +// } +// if stats.IsDir() { +// return false +// } +// return true +// } +// return false +//} +// +//func ServeStatic(prefix string) gin.HandlerFunc { +// fs := FileServer(gin.Dir(config.Site.Static, false)) +// if prefix != "" { +// fs = http.StripPrefix(prefix, fs) +// } +// return func(c *gin.Context) { +// if fileExists(prefix, c.Request.URL.Path) { +// fs.ServeHTTP(c.Writer, c.Request) +// c.Abort() +// } +// +// } +//} \ No newline at end of file diff --git a/native/cimg.go b/native/cimg.go new file mode 100644 index 0000000..cb86cd6 --- /dev/null +++ b/native/cimg.go @@ -0,0 +1,243 @@ +package native + +// for build with rav1e: #cgo windows LDFLAGS: -lavif_rav1e -lrav1e -lws2_32 -luserenv + +// #cgo CFLAGS: -I./../c/ +// #cgo !windows CFLAGS: -I/opt/mozjpeg/include/ +// #cgo LDFLAGS: -L${SRCDIR}/../c/release/ +// #cgo windows LDFLAGS: -L${SRCDIR}/../c/libs/ +// #cgo !windows LDFLAGS: -L/opt/mozjpeg/lib64/ +// #cgo LDFLAGS: -static -lcImg -lavif -laom -ldav1d -lwebp -lwebpdemux -ljpeg -lpng -lz +// #cgo windows LDFLAGS: -lwinpthread +// #cgo !windows LDFLAGS: -lm -ldl -lpthread -lc +// #include "cimg.h" +import "C" + +import ( + "runtime" + "unsafe" +) +import "github.com/liut/jpegquality" + +type CFrame C.struct_Frame + +type Frame struct { + f CFrame +} + +func (f *Frame) W() uint32 { + return uint32(f.f.width) +} + +func (f *Frame) H() uint32 { + return uint32(f.f.height) +} + +func SetMaxSize(size uint32) { + C.SetMaxSize(C.u32(size)) +} + +func GetMaxSize() uint32 { + return uint32(C.GetMaxSize()) +} + +func SetMaxThread(thread uint32) { + C.SetMaxThread(C.u32(thread)) +} + +func GetMaxThread() uint32 { + return uint32(C.GetMaxThread()) +} + +func ReleaseFrame(f *Frame) bool { + return bool(C.ReleaseFrame((*C.struct_Frame)(&f.f))) +} + +func IsPNG(data []byte) (bool, uint32, uint32) { + var w, h C.u32 + r := C.IsPNG((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), &w, &h) + + if bool(r) { + return true, uint32(w), uint32(h) + } else { + return false, 0, 0 + } +} + +func DecPNG(data []byte) *Frame { + f := &Frame{f: CFrame{}} + r := C.DecPNG((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), (*C.struct_Frame)(&f.f)) + + runtime.SetFinalizer(f, ReleaseFrame) + + if bool(r) { + return f + } else { + return nil + } +} + +func IsJPEG(data []byte) (bool, uint32, uint32) { + var w, h C.u32 + r := C.IsJPEG((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), &w, &h) + + if bool(r) { + return true, uint32(w), uint32(h) + } else { + return false, 0, 0 + } +} + +func DecJPEG(data []byte) *Frame { + f := &Frame{f: CFrame{}} + r := C.DecJPEG((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), (*C.struct_Frame)(&f.f)) + + runtime.SetFinalizer(f, ReleaseFrame) + + if bool(r) { + qF, err := jpegquality.NewWithBytes(data) + if err != nil { + return nil + } + f.f.quality = C.u8(qF.Quality()) + + return f + } else { + return nil + } +} + +func EncJPEG(f *Frame) []byte { + var data *C.u8 = nil + var size C.size_t + + r := C.EncJPEG(&data, &size, (*C.struct_Frame)(&f.f)) + + if data != nil { + defer C.free(unsafe.Pointer(data)) + } + + if !bool(r) { + return nil + } + + e := make([]byte, size) + copy(e, ((*[1 << 30]byte)(unsafe.Pointer(data)))[0:size:size]) + + return e +} + +func IsWEBP(data []byte) (bool, uint32, uint32) { + var w, h C.u32 + r := C.IsWEBP((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), &w, &h) + + if bool(r) { + return true, uint32(w), uint32(h) + } else { + return false, 0, 0 + } +} + +func DecWEBP(data []byte) *Frame { + f := &Frame{f: CFrame{}} + r := C.DecWEBP((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), (*C.struct_Frame)(&f.f)) + + runtime.SetFinalizer(f, ReleaseFrame) + + if bool(r) { + return f + } else { + return nil + } +} + +func EncWEBP(f *Frame) []byte { + var data *C.u8 = nil + var size C.size_t + + r := C.EncWEBP(&data, &size, (*C.struct_Frame)(&f.f)) + + if data != nil { + defer C.free(unsafe.Pointer(data)) + } + + if !bool(r) { + return nil + } + + e := make([]byte, size) + copy(e, ((*[1 << 30]byte)(unsafe.Pointer(data)))[0:size:size]) + + return e +} + +func IsAVIF(data []byte) (bool, uint32, uint32) { + var w, h C.u32 + r := C.IsAVIF((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), &w, &h) + + if bool(r) { + return true, uint32(w), uint32(h) + } else { + return false, 0, 0 + } +} + +func DecAVIF(data []byte) *Frame { + f := &Frame{f: CFrame{}} + r := C.DecAVIF((*C.u8)(unsafe.Pointer(&data[0])), C.size_t(len(data)), (*C.struct_Frame)(&f.f)) + + runtime.SetFinalizer(f, ReleaseFrame) + + if bool(r) { + return f + } else { + return nil + } +} + +func EncAVIF(f *Frame) []byte { + var data *C.u8 = nil + var size C.size_t + + r := C.EncAVIF(&data, &size, (*C.struct_Frame)(&f.f)) + + if data != nil { + defer C.free(unsafe.Pointer(data)) + } + + if !bool(r) { + return nil + } + + e := make([]byte, size) + copy(e, ((*[1 << 30]byte)(unsafe.Pointer(data)))[0:size:size]) + + return e +} + +func RescaleFrame(s *Frame, x, y, ws, hs, wt, ht uint32) *Frame { + f := &Frame{f: CFrame{ + width: C.u32(wt), + height: C.u32(ht), + depth: 8, + format: C.FORMAT_RGB, + quality: 100, + }} + + if !bool(C.AllocateFrame((*C.struct_Frame)(&f.f))) { + return nil + } + + runtime.SetFinalizer(f, ReleaseFrame) + + if !bool(C.RescaleImage((*C.struct_Frame)(&s.f), (*C.struct_Frame)(&f.f), + C.u32(x), C.u32(y), C.u32(ws), C.u32(hs))) { + return nil + } + + return f +} + +func TrimMemory() bool { + return bool(C.TrimMemory()) +} \ No newline at end of file diff --git a/types.go b/types.go new file mode 100644 index 0000000..36718e0 --- /dev/null +++ b/types.go @@ -0,0 +1,47 @@ +package main + +import ( + "github.com/dgrijalva/jwt-go" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type User struct { + ID primitive.ObjectID `bson:"_id" json:"id"` + Name string `bson:"name" json:"name"` + Password []byte `bson:"password" json:"-"` + Privileged bool `bson:"privileged" json:"privileged"` + Frozen bool `bson:"frozen" json:"frozen"` + VersionP uint64 `bson:"version_p" json:"-"` + VersionC uint64 `bson:"version_c" json:"-"` +} + +type ImageFormat struct { + Format string `bson:"format" json:"format"` + Hash int64 `bson:"hash" json:"hash"` +} + +type Image struct { + ID primitive.ObjectID `bson:"_id" json:"id"` + UserID primitive.ObjectID `bson:"user_id" json:"user_id"` + UserName string `bson:"user_name" json:"user_name"` + Storage string `bson:"storage" json:"-"` + Tag string `bson:"tag" json:"tag"` + Upload primitive.DateTime `bson:"upload" json:"upload"` + View uint64 `bson:"view" json:"view"` + Origins []string `bson:"origins" json:"origins"` + Original bool `bson:"original" json:"original"` + Size int `bson:"size" json:"-"` + Files []ImageFormat `bson:"files" json:"files,omitempty"` +} + +type InviteCode struct { + Code string `bson:"code" json:"code" binding:"required"` + Times uint64 `bson:"times" json:"times" binding:"required"` +} + +type UserJWT struct { + Name string `json:"name"` + VersionP uint64 `json:"version_p"` + VersionC uint64 `json:"version_c"` + jwt.StandardClaims +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..52a0780 --- /dev/null +++ b/utils.go @@ -0,0 +1,90 @@ +package main + +import ( + "context" + "golang.org/x/crypto/scrypt" + "io/ioutil" + "net/http" + "net/url" + "time" +) + +func GetSquare(w, h uint32) (x, y, wo, ho uint32) { + if w == h { + return 0, 0, w, h + } else if w > h { + return (w - h) / 2, 0, h, h + } else { + return 0, (h - w) / 2, w, w + } +} + +// X return a context cancel after one second. +// This is an convenient method for mongodb operations +func X() context.Context { + var ctx context.Context + if !config.Site.Debug { + ctx, _ = context.WithTimeout(context.Background(), time.Second) + } else { + // allow more time when debugging + ctx, _ = context.WithTimeout(context.Background(), time.Minute) + } + + return ctx +} + +// Xd return a context cancel after given duration. +// This is an convenient method for mongodb operations +func Xd(duration time.Duration) context.Context { + ctx, _ := context.WithTimeout(context.Background(), time.Second) + return ctx +} + +func PasswordHash(password string) []byte { + h, _ := scrypt.Key([]byte(password), []byte(config.Security.Salt), 32768, 8, 1, 32) + return h +} + +func PasswordVerify(password string) bool { + if len(password) < 8 { + return false + } + + var n, a, A bool + + for _, c := range []byte(password) { + if c >= '0' && c <= '9' { + n = true + } else if c >= 'a' && c <= 'z' { + a = true + } else if c >= 'A' && c <= 'Z' { + A = true + } + } + + return n && a && A +} + +func RecaptchaVerify(response string) SErr { + resp, err := http.PostForm("https://recaptcha.net/recaptcha/api/siteverify", + url.Values{ + "secret": {config.Security.RecaptchaKey}, + "response": {response}}) + if err != nil { + return EUnknown + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return EUnknown + } + var result struct{ Success bool `json:"success"` } + if json.Unmarshal(body, &result) != nil { + return EUnknown + } + if !result.Success { + return EBadRecaptcha + } else { + return EOk + } +} \ No newline at end of file diff --git a/verify.go b/verify.go new file mode 100644 index 0000000..1c33083 --- /dev/null +++ b/verify.go @@ -0,0 +1,194 @@ +package main + +import ( + "net" + "strings" + "unicode/utf8" +) + +// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use +// an explicitly ASCII function to avoid any sharp corners resulting from +// performing Unicode operations on DNS labels. +func toLowerCaseASCII(in string) string { + // If the string is already lower-case then there's nothing to do. + isAlreadyLowerCase := true + for _, c := range in { + if c == utf8.RuneError { + // If we get a UTF-8 error then there might be + // upper-case ASCII bytes in the invalid sequence. + isAlreadyLowerCase = false + break + } + if 'A' <= c && c <= 'Z' { + isAlreadyLowerCase = false + break + } + } + + if isAlreadyLowerCase { + return in + } + + out := []byte(in) + for i, c := range out { + if 'A' <= c && c <= 'Z' { + out[i] += 'a' - 'A' + } + } + return string(out) +} + +func standardizeHostname(host string) (clean string, valid bool) { + if len(host) == 0 || host == "*" || host == " " { + return host, true + } + + host = strings.TrimSuffix(toLowerCaseASCII(host), ".") + + for i, part := range strings.Split(host, ".") { + if part == "" { + // Empty label. + return "", false + } + if i == 0 && part == "*" { + // Only allow full left-most wildcards, as those are the only ones + // we match, and matching literal '*' characters is probably never + // the expected behavior. + continue + } + for j, c := range part { + if 'a' <= c && c <= 'z' { + continue + } + if '0' <= c && c <= '9' { + continue + } + if c == '-' && j != 0 { + continue + } + if c == '_' || c == ':' { + // Not valid characters in hostnames, but commonly + // found in deployments outside the WebPKI. + continue + } + return "", false + } + } + + return host, true +} + +// validHostname reports whether host is a valid hostname that can be matched or +// matched against according to RFC 6125 2.2, with some leniency to accommodate +// legacy values. +func validHostname(host string) bool { + if len(host) == 0 || host == "*" || host == " " { + return true + } + + host = strings.TrimSuffix(host, ".") + + for i, part := range strings.Split(host, ".") { + if part == "" { + // Empty label. + return false + } + if i == 0 && part == "*" { + // Only allow full left-most wildcards, as those are the only ones + // we match, and matching literal '*' characters is probably never + // the expected behavior. + continue + } + for j, c := range part { + if 'a' <= c && c <= 'z' { + continue + } + if '0' <= c && c <= '9' { + continue + } + if 'A' <= c && c <= 'Z' { + continue + } + if c == '-' && j != 0 { + continue + } + if c == '_' || c == ':' { + // Not valid characters in hostnames, but commonly + // found in deployments outside the WebPKI. + continue + } + return false + } + } + + return true +} + +func matchHostnames(pattern, host string) bool { + host = strings.TrimSuffix(host, ".") + pattern = strings.TrimSuffix(pattern, ".") + + if len(pattern) == 0 || len(host) == 0 { + return false + } + + if pattern[0] == '*' { + return strings.HasSuffix(host, pattern[1:]) + } else { + return host == pattern + } +} + +func CleanOrigins(r string) (result []string) { + origins := strings.Split(r, ",") + clean := make(map[string]struct{}) + + for _, o := range origins { + if _, exist := clean[o]; !exist { + clean[o] = struct{}{} + result = append(result, o) + } + + if len(result) == 100 { + return + } + } + return +} + +func verifyHostname(host, pattern string) bool { + if pattern == "" { + return false + } else if pattern == "*" { + return true + } else if host == "" && pattern == " " { + return true + } + + // IP addresses may be written in [ ]. + candidateIP := host + if len(host) >= 3 && host[0] == '[' && host[len(host)-1] == ']' { + candidateIP = host[1 : len(host)-1] + } + if ip := net.ParseIP(candidateIP); ip != nil { + // We only match IP addresses against IP SANs. + // See RFC 6125, Appendix B.2. + if patternIp := net.ParseIP(pattern); patternIp != nil { + return ip.Equal(patternIp) + } else { + return false + } + } + + return matchHostnames(pattern, toLowerCaseASCII(host)) +} + +func VerifyHostname(host string, origins []string) bool { + for _, o := range origins { + if verifyHostname(host, o) { + return true + } + } + + return false +} \ No newline at end of file