commit f84b5118959e1b85dd26f473ffc7050ccf375867 Author: Kevin McIntyre Date: Wed Jun 18 01:00:00 2025 -0400 init diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..631ccf0 Binary files /dev/null and b/CLAUDE.md differ diff --git a/FIXUP.md b/FIXUP.md new file mode 100644 index 0000000..117732a --- /dev/null +++ b/FIXUP.md @@ -0,0 +1,188 @@ +# FIXUP Plan: Go Jdenticon JavaScript Reference Compatibility + +## Problem Summary + +The Go implementation of Jdenticon generates completely different SVG output compared to the JavaScript reference implementation, despite having identical hash generation. The test case `TestJavaScriptReferenceCompatibility` reveals fundamental differences in the generation algorithm. + +## Root Cause Analysis + +Based on test results comparing Go vs JavaScript output for identical inputs: + +### ✅ What's Working +- Hash generation (SHA1) is identical between implementations +- `svgValue()` rounding behavior now matches JavaScript "round half up" +- Basic SVG structure and syntax + +### ❌ What's Broken +1. **Shape Generation Logic**: Completely different shapes and paths generated +2. **Coordinate Calculations**: Different coordinate values (e.g., JS: `35.9`, `39.8` vs Go: `37.2`, `41.1`) +3. **Path Ordering**: SVG paths appear in different sequence +4. **Circle Positioning**: Circles generated at different locations +5. **Transform Application**: Rotation/positioning logic differs + +### Evidence from Test Case +**Input**: `"test-hash"` (size 64) +- **JavaScript**: `` (first path) +- **Go**: `` (first path) +- Completely different shapes, colors, and coordinates + +## Investigation Plan + +### Phase 1: Algorithm Deep Dive (High Priority) +1. **Study JavaScript IconGenerator** + - Examine `jdenticon-js/src/renderer/iconGenerator.js` + - Understand shape selection and positioning logic + - Document the exact algorithm flow + +2. **Study JavaScript Shape Generation** + - Examine `jdenticon-js/src/renderer/shapes.js` + - Understand how shapes are created and positioned + - Document shape types and their generation rules + +3. **Study JavaScript Layout System** + - Examine how the 4x4 grid layout works + - Understand cell positioning and sizing + - Document the exact coordinate calculation logic + +### Phase 2: Go Implementation Analysis (High Priority) +1. **Audit Go Generator Logic** + - Compare `internal/engine/generator.go` with JavaScript equivalent + - Identify algorithmic differences in shape selection + - Check if we're using the same hash parsing logic + +2. **Audit Go Shape Generation** + - Compare `internal/engine/shapes.go` with JavaScript + - Verify shape types and their implementation + - Check transform application + +3. **Audit Go Layout System** + - Compare `internal/engine/layout.go` with JavaScript + - Verify grid calculations and cell positioning + - Check coordinate generation logic + +### Phase 3: Systematic Fixes (High Priority) + +#### 3.1 Fix Shape Selection Algorithm +**Files to modify**: `internal/engine/generator.go` +- Ensure hash bit extraction matches JavaScript exactly +- Verify shape type selection logic +- Fix shape positioning and rotation logic + +#### 3.2 Fix Layout System +**Files to modify**: `internal/engine/layout.go` +- Match JavaScript grid calculations exactly +- Fix cell size and positioning calculations +- Ensure transforms are applied correctly + +#### 3.3 Fix Shape Implementation +**Files to modify**: `internal/engine/shapes.go` +- Verify each shape type matches JavaScript geometry +- Fix coordinate calculations for polygons and circles +- Ensure proper transform application + +#### 3.4 Fix Generation Order +**Files to modify**: `internal/engine/generator.go`, `internal/renderer/svg.go` +- Match the exact order of shape generation +- Ensure SVG paths are written in same sequence as JavaScript +- Fix color assignment order + +### Phase 4: Validation (Medium Priority) + +#### 4.1 Expand Test Coverage +**Files to modify**: `jdenticon/reference_test.go` +- Add more test inputs with known JavaScript outputs +- Test different icon sizes (64, 128, 256) +- Test edge cases and different hash patterns + +#### 4.2 Coordinate-by-Coordinate Validation +- Create debug output showing step-by-step coordinate generation +- Compare each transform operation with JavaScript +- Validate grid positioning calculations + +#### 4.3 Shape-by-Shape Validation +- Test individual shape generation in isolation +- Verify each shape type produces identical output +- Test rotation and transform application + +### Phase 5: Performance & Polish (Low Priority) + +#### 5.1 Optimize Performance +- Ensure fixes don't degrade performance +- Profile generation time vs JavaScript +- Optimize hot paths if needed + +#### 5.2 Documentation +- Document the JavaScript compatibility +- Update comments explaining the algorithm +- Add examples showing identical output + +## Implementation Strategy + +### Step 1: JavaScript Reference Study (Day 1) +1. Read and document JavaScript `iconGenerator.js` algorithm +2. Create flowchart of JavaScript generation process +3. Document exact hash bit usage and shape selection + +### Step 2: Go Algorithm Audit (Day 1-2) +1. Compare Go implementation line-by-line with JavaScript +2. Identify all algorithmic differences +3. Create detailed list of required changes + +### Step 3: Systematic Implementation (Day 2-3) +1. Fix most critical differences first (shape selection) +2. Fix layout and coordinate calculation +3. Fix shape implementation details +4. Fix generation order and path sequencing + +### Step 4: Validation Loop (Day 3-4) +1. Run reference compatibility tests after each fix +2. Add debug output to trace differences +3. Iterate until tests pass +4. Expand test coverage + +## Success Criteria + +### Primary Goals +- [ ] `TestJavaScriptReferenceCompatibility` passes for all test cases +- [ ] Byte-for-byte identical SVG output for same input hash/size +- [ ] No regression in existing functionality + +### Secondary Goals +- [ ] Performance comparable to current implementation +- [ ] Code remains maintainable and well-documented +- [ ] All existing tests continue to pass + +## Risk Assessment + +### High Risk +- **Scope Creep**: The fixes might require rewriting major portions of the generation engine +- **Breaking Changes**: Existing users might rely on current (incorrect) output + +### Medium Risk +- **Performance Impact**: Algorithm changes might affect generation speed +- **Test Maintenance**: Need to maintain both Go and JavaScript reference outputs + +### Low Risk +- **API Changes**: Public API should remain unchanged +- **Backward Compatibility**: Hash generation stays the same + +## Rollback Plan + +If fixes prove too complex or risky: +1. Keep current implementation as `v1-legacy` +2. Implement JavaScript-compatible version as `v2` +3. Provide migration guide for users +4. Allow users to choose implementation version + +## Notes + +- The `svgValue()` rounding fix was correct but insufficient +- This is not a minor coordinate issue - it's a fundamental algorithmic difference +- Success requires matching JavaScript behavior exactly, not just approximating it +- Consider this a "port" rather than a "reimplementation" + +--- + +**Created**: Based on failing `TestJavaScriptReferenceCompatibility` test results +**Priority**: High - Core functionality incorrectly implemented +**Estimated Effort**: 3-4 days of focused development \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5984c4 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# [Jdenticon-go](https://jdenticon.com) + +Go library for generating highly recognizable identicons. + +![Sample identicons](https://jdenticon.com/hosted/github-samples.png) + +## Features + +go-jdenticon is a Go port of the JavaScript library [Jdenticon](https://github.com/dmester/jdenticon). + +* Renders identicons as PNG or SVG with no external dependencies +* Generates consistent, deterministic identicons from any input string +* Highly customizable color themes and styling options +* Simple, clean API for easy integration +* Command-line tool included for standalone usage + +## Installation + +```bash +go get github.com/kevin/go-jdenticon +``` + +## Usage + +### Basic Usage + +```go +package main + +import ( + "fmt" + "github.com/kevin/go-jdenticon/jdenticon" +) + +func main() { + // Generate an identicon + icon := jdenticon.Generate("user@example.com", 200) + + // Get SVG output + svg := icon.ToSVG() + fmt.Println(svg) + + // Get PNG output + png := icon.ToPNG() + // Save or use PNG data... +} +``` + +### Custom Configuration + +```go +config := &jdenticon.Config{ + Hue: 0.3, + Saturation: 0.7, + Lightness: 0.5, + Padding: 0.1, +} + +icon := jdenticon.GenerateWithConfig("user@example.com", 200, config) +``` + +### Command Line Tool + +```bash +# Generate SVG +go run cmd/jdenticon/main.go -value "user@example.com" -size 200 + +# Generate PNG file +go run cmd/jdenticon/main.go -value "user@example.com" -format png -output icon.png +``` + +## API Reference + +### Functions + +- `Generate(value string, size int) *Icon` - Generate an identicon with default settings +- `GenerateWithConfig(value string, size int, config *Config) *Icon` - Generate with custom configuration +- `DefaultConfig() *Config` - Get default configuration settings + +### Types + +#### Icon +- `ToSVG() string` - Render as SVG string +- `ToPNG() []byte` - Render as PNG byte data + +#### Config +- `Hue float64` - Color hue (0.0-1.0) +- `Saturation float64` - Color saturation (0.0-1.0) +- `Lightness float64` - Color lightness (0.0-1.0) +- `BackgroundColor string` - Background color (hex or empty for transparent) +- `Padding float64` - Padding as percentage of size (0.0-0.5) + +## License + +MIT License - see the original [Jdenticon](https://github.com/dmester/jdenticon) project for details. + +## Contributing + +Contributions are welcome! Please ensure all tests pass and follow Go conventions. + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Add tests if applicable +5. Submit a pull request diff --git a/avatar_custom.svg b/avatar_custom.svg new file mode 100644 index 0000000..4529dcf --- /dev/null +++ b/avatar_custom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_email.svg b/avatar_email.svg new file mode 100644 index 0000000..a73f96b --- /dev/null +++ b/avatar_email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_type_0.svg b/avatar_type_0.svg new file mode 100644 index 0000000..49dbace --- /dev/null +++ b/avatar_type_0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_type_1.svg b/avatar_type_1.svg new file mode 100644 index 0000000..5d84259 --- /dev/null +++ b/avatar_type_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_type_2.svg b/avatar_type_2.svg new file mode 100644 index 0000000..4cafdf7 --- /dev/null +++ b/avatar_type_2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_type_3.svg b/avatar_type_3.svg new file mode 100644 index 0000000..3a7b537 --- /dev/null +++ b/avatar_type_3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_type_4.svg b/avatar_type_4.svg new file mode 100644 index 0000000..fd4afe2 --- /dev/null +++ b/avatar_type_4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avatar_username.png b/avatar_username.png new file mode 100644 index 0000000..6e25c6a Binary files /dev/null and b/avatar_username.png differ diff --git a/cmd/jdenticon/jdenticon b/cmd/jdenticon/jdenticon new file mode 100755 index 0000000..133b933 Binary files /dev/null and b/cmd/jdenticon/jdenticon differ diff --git a/cmd/jdenticon/main.go b/cmd/jdenticon/main.go new file mode 100644 index 0000000..f790653 --- /dev/null +++ b/cmd/jdenticon/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/kevin/go-jdenticon/jdenticon" +) + +func main() { + var ( + value = flag.String("value", "", "Input value to generate identicon for (required)") + size = flag.Int("size", 200, "Size of the identicon in pixels") + format = flag.String("format", "svg", "Output format: svg or png") + output = flag.String("output", "", "Output file (if empty, prints to stdout)") + ) + flag.Parse() + + if *value == "" { + fmt.Fprintf(os.Stderr, "Error: -value is required\n") + flag.Usage() + os.Exit(1) + } + + icon, err := jdenticon.Generate(*value, *size) + if err != nil { + fmt.Fprintf(os.Stderr, "Error generating identicon: %v\n", err) + os.Exit(1) + } + + var result []byte + switch *format { + case "svg": + svgStr, err := icon.ToSVG() + if err != nil { + fmt.Fprintf(os.Stderr, "Error generating SVG: %v\n", err) + os.Exit(1) + } + result = []byte(svgStr) + case "png": + pngBytes, err := icon.ToPNG() + if err != nil { + fmt.Fprintf(os.Stderr, "Error generating PNG: %v\n", err) + os.Exit(1) + } + result = pngBytes + default: + fmt.Fprintf(os.Stderr, "Error: invalid format %s (use svg or png)\n", *format) + os.Exit(1) + } + + if *output != "" { + if err := os.WriteFile(*output, result, 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err) + os.Exit(1) + } + fmt.Printf("Identicon saved to %s\n", *output) + } else { + fmt.Print(string(result)) + } +} \ No newline at end of file diff --git a/debug_hash.go b/debug_hash.go new file mode 100644 index 0000000..478c7b6 --- /dev/null +++ b/debug_hash.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + + "github.com/kevin/go-jdenticon/jdenticon" +) + +func main() { + testInputs := []string{"test-hash", "example1@gmail.com"} + + for _, input := range testInputs { + hash := jdenticon.ToHash(input) + fmt.Printf("Input: \"%s\"\n", input) + fmt.Printf("Go SHA1: %s\n", hash) + + svg, err := jdenticon.ToSVG(input, 64) + if err != nil { + fmt.Printf("Error: %v\n", err) + } else { + fmt.Printf("SVG length: %d\n", len(svg)) + } + fmt.Println("---") + } +} \ No newline at end of file diff --git a/debug_hash.js b/debug_hash.js new file mode 100644 index 0000000..585b804 --- /dev/null +++ b/debug_hash.js @@ -0,0 +1,16 @@ +const jdenticon = require('./jdenticon-js/dist/jdenticon-node.js'); +const crypto = require('crypto'); + +const testInputs = ['test-hash', 'example1@gmail.com']; + +testInputs.forEach(input => { + // Generate hash using Node.js crypto (similar to what our Go code should do) + const nodeHash = crypto.createHash('sha1').update(input).digest('hex'); + console.log(`Input: "${input}"`); + console.log(`Node.js SHA1: ${nodeHash}`); + + // See what Jdenticon generates + const svg = jdenticon.toSvg(input, 64); + console.log(`SVG length: ${svg.length}`); + console.log('---'); +}); \ No newline at end of file diff --git a/example_usage.go b/example_usage.go new file mode 100644 index 0000000..900bdea --- /dev/null +++ b/example_usage.go @@ -0,0 +1,123 @@ +package main + +import ( + "fmt" + "os" + + "github.com/kevin/go-jdenticon/jdenticon" +) + +func main() { + // Test the new public API with different input types + + // 1. Generate SVG from email address + fmt.Println("=== Generating SVG avatar for email ===") + svg, err := jdenticon.ToSVG("user@example.com", 128) + if err != nil { + panic(err) + } + fmt.Printf("SVG length: %d characters\n", len(svg)) + fmt.Printf("SVG preview: %s...\n", svg[:100]) + + // Save SVG to file + err = os.WriteFile("avatar_email.svg", []byte(svg), 0644) + if err != nil { + panic(err) + } + fmt.Println("✅ Saved to avatar_email.svg") + + // 2. Generate PNG from username + fmt.Println("\n=== Generating PNG avatar for username ===") + png, err := jdenticon.ToPNG("johndoe", 64) + if err != nil { + panic(err) + } + fmt.Printf("PNG size: %d bytes\n", len(png)) + + // Save PNG to file + err = os.WriteFile("avatar_username.png", png, 0644) + if err != nil { + panic(err) + } + fmt.Println("✅ Saved to avatar_username.png") + + // 3. Generate with custom configuration + fmt.Println("\n=== Generating with custom config ===") + config, err := jdenticon.Configure( + jdenticon.WithHueRestrictions([]float64{120, 240}), // Blue/green hues only + jdenticon.WithColorSaturation(0.8), + jdenticon.WithBackgroundColor("#ffffff"), // White background + jdenticon.WithPadding(0.1), + ) + if err != nil { + panic(err) + } + + customSvg, err := jdenticon.ToSVG("custom-avatar", 96, config) + if err != nil { + panic(err) + } + + err = os.WriteFile("avatar_custom.svg", []byte(customSvg), 0644) + if err != nil { + panic(err) + } + fmt.Println("✅ Saved custom styled avatar to avatar_custom.svg") + + // 4. Test different input types + fmt.Println("\n=== Testing different input types ===") + inputs := []interface{}{ + "hello world", + 42, + 3.14159, + true, + []byte("binary data"), + } + + for i, input := range inputs { + svg, err := jdenticon.ToSVG(input, 48) + if err != nil { + panic(err) + } + filename := fmt.Sprintf("avatar_type_%d.svg", i) + err = os.WriteFile(filename, []byte(svg), 0644) + if err != nil { + panic(err) + } + fmt.Printf("✅ Generated avatar for %T: %v -> %s\n", input, input, filename) + } + + // 5. Show hash generation + fmt.Println("\n=== Hash generation ===") + testValues := []interface{}{"test", 123, []byte("data")} + for _, val := range testValues { + hash := jdenticon.ToHash(val) + fmt.Printf("Hash of %v (%T): %s\n", val, val, hash) + } + + // 6. Generate avatars for a group of users + fmt.Println("\n=== Group avatars ===") + users := []string{ + "alice@company.com", + "bob@company.com", + "charlie@company.com", + "diana@company.com", + } + + for _, user := range users { + png, err := jdenticon.ToPNG(user, 80) + if err != nil { + panic(err) + } + + filename := fmt.Sprintf("user_%s.png", user[:5]) // Use first 5 chars as filename + err = os.WriteFile(filename, png, 0644) + if err != nil { + panic(err) + } + fmt.Printf("✅ Generated avatar for %s -> %s\n", user, filename) + } + + fmt.Println("\n🎉 All avatars generated successfully!") + fmt.Println("Check the generated SVG and PNG files in the current directory.") +} \ No newline at end of file diff --git a/generate_go_compare.go b/generate_go_compare.go new file mode 100644 index 0000000..aaa3137 --- /dev/null +++ b/generate_go_compare.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + "os" + + "github.com/kevin/go-jdenticon/jdenticon" +) + +func main() { + // Test emails + testEmails := []string{ + "example1@gmail.com", + "example2@yahoo.com", + } + + // Test sizes + sizes := []int{64, 128} + + // Create go-output directory + outDir := "./go-output" + if _, err := os.Stat(outDir); os.IsNotExist(err) { + os.Mkdir(outDir, 0755) + } + + // Generate Go versions + for _, email := range testEmails { + for _, size := range sizes { + // Generate SVG + svg, err := jdenticon.ToSVG(email, size) + if err != nil { + fmt.Printf("Error generating SVG for %s@%d: %v\n", email, size, err) + continue + } + + svgFilename := fmt.Sprintf("%s/%s_%d.svg", outDir, + email[0:8]+"_at_"+email[9:13]+"_com", size) + err = os.WriteFile(svgFilename, []byte(svg), 0644) + if err != nil { + fmt.Printf("Error writing SVG file: %v\n", err) + continue + } + fmt.Printf("Generated Go SVG: %s\n", svgFilename) + + // Generate PNG + pngData, err := jdenticon.ToPNG(email, size) + if err != nil { + fmt.Printf("Error generating PNG for %s@%d: %v\n", email, size, err) + continue + } + + pngFilename := fmt.Sprintf("%s/%s_%d.png", outDir, + email[0:8]+"_at_"+email[9:13]+"_com", size) + err = os.WriteFile(pngFilename, pngData, 0644) + if err != nil { + fmt.Printf("Error writing PNG file: %v\n", err) + continue + } + fmt.Printf("Generated Go PNG: %s\n", pngFilename) + } + } + + // Also generate test-hash for comparison + testSvg, err := jdenticon.ToSVG("test-hash", 64) + if err != nil { + fmt.Printf("Error generating test-hash SVG: %v\n", err) + } else { + err = os.WriteFile(outDir+"/test-hash_64.svg", []byte(testSvg), 0644) + if err != nil { + fmt.Printf("Error writing test-hash SVG: %v\n", err) + } else { + fmt.Println("Generated test-hash Go SVG") + } + } + + fmt.Println("\nGo files generated in ./go-output/ directory") +} \ No newline at end of file diff --git a/generate_reference.js b/generate_reference.js new file mode 100644 index 0000000..95807c3 --- /dev/null +++ b/generate_reference.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const jdenticon = require('./jdenticon-js/dist/jdenticon-node.js'); + +// Test emails +const testEmails = [ + 'example1@gmail.com', + 'example2@yahoo.com' +]; + +// Test sizes +const sizes = [64, 128]; + +// Create reference directory +const refDir = './reference'; +if (!fs.existsSync(refDir)) { + fs.mkdirSync(refDir); +} + +// Generate reference SVGs and PNGs +testEmails.forEach(email => { + sizes.forEach(size => { + // Generate SVG + const svg = jdenticon.toSvg(email, size); + const svgFilename = `${email.replace('@', '_at_').replace('.', '_')}_${size}.svg`; + fs.writeFileSync(path.join(refDir, svgFilename), svg); + console.log(`Generated reference SVG: ${svgFilename}`); + + // Generate PNG (if supported) + try { + const pngBuffer = jdenticon.toPng(email, size); + const pngFilename = `${email.replace('@', '_at_').replace('.', '_')}_${size}.png`; + fs.writeFileSync(path.join(refDir, pngFilename), pngBuffer); + console.log(`Generated reference PNG: ${pngFilename}`); + } catch (err) { + console.log(`PNG generation failed for ${email}@${size}: ${err.message}`); + } + }); +}); + +// Also generate a test with fixed coordinates we can examine +const testSvg = jdenticon.toSvg('test-hash', 64); +fs.writeFileSync(path.join(refDir, 'test-hash_64.svg'), testSvg); +console.log('Generated test-hash reference SVG'); + +console.log('\nReference files generated in ./reference/ directory'); \ No newline at end of file diff --git a/go-cleanup/task-01-color-parsing-errors.txt b/go-cleanup/task-01-color-parsing-errors.txt new file mode 100644 index 0000000..b02cc16 --- /dev/null +++ b/go-cleanup/task-01-color-parsing-errors.txt @@ -0,0 +1,17 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #1 from taskmaster and implement the solution: +``` +tm get-task 1 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-02-hash-parsing-errors.txt b/go-cleanup/task-02-hash-parsing-errors.txt new file mode 100644 index 0000000..d9f67ee --- /dev/null +++ b/go-cleanup/task-02-hash-parsing-errors.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #2 from taskmaster and implement the solution: +``` +tm get-task 2 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This task depends on Task 1 being completed first - ensure error handling patterns are consistent. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-03-optimize-color-allocations.txt b/go-cleanup/task-03-optimize-color-allocations.txt new file mode 100644 index 0000000..80ba707 --- /dev/null +++ b/go-cleanup/task-03-optimize-color-allocations.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #3 from taskmaster and implement the solution: +``` +tm get-task 3 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a performance optimization task - measure before/after to ensure improvements. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-04-replace-magic-numbers.txt b/go-cleanup/task-04-replace-magic-numbers.txt new file mode 100644 index 0000000..49f67d9 --- /dev/null +++ b/go-cleanup/task-04-replace-magic-numbers.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #4 from taskmaster and implement the solution: +``` +tm get-task 4 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a readability improvement task - replace magic numbers with named constants while keeping exact same values. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-05-refactor-circle-geometry.txt b/go-cleanup/task-05-refactor-circle-geometry.txt new file mode 100644 index 0000000..b2871df --- /dev/null +++ b/go-cleanup/task-05-refactor-circle-geometry.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #5 from taskmaster and implement the solution: +``` +tm get-task 5 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a data structure improvement task - clean up the Shape struct while maintaining all existing functionality. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-06-optimize-string-building.txt b/go-cleanup/task-06-optimize-string-building.txt new file mode 100644 index 0000000..82aedf3 --- /dev/null +++ b/go-cleanup/task-06-optimize-string-building.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #6 from taskmaster and implement the solution: +``` +tm get-task 6 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a performance optimization task - replace fmt.Sprintf with more efficient string building. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-07-simplify-complex-logic.txt b/go-cleanup/task-07-simplify-complex-logic.txt new file mode 100644 index 0000000..03d345a --- /dev/null +++ b/go-cleanup/task-07-simplify-complex-logic.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #7 from taskmaster and implement the solution: +``` +tm get-task 7 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a readability improvement task - simplify complex logic while maintaining exact same behavior. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-08-apply-go-idioms.txt b/go-cleanup/task-08-apply-go-idioms.txt new file mode 100644 index 0000000..e74e05f --- /dev/null +++ b/go-cleanup/task-08-apply-go-idioms.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #8 from taskmaster and implement the solution: +``` +tm get-task 8 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a Go idioms improvement task - replace JavaScript-style patterns with idiomatic Go. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-09-comprehensive-error-logging.txt b/go-cleanup/task-09-comprehensive-error-logging.txt new file mode 100644 index 0000000..8875ab8 --- /dev/null +++ b/go-cleanup/task-09-comprehensive-error-logging.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #9 from taskmaster and implement the solution: +``` +tm get-task 9 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This task depends on Tasks 1 and 2 being completed first - build on their error handling foundations. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-10-performance-benchmarks.txt b/go-cleanup/task-10-performance-benchmarks.txt new file mode 100644 index 0000000..b75cd43 --- /dev/null +++ b/go-cleanup/task-10-performance-benchmarks.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #10 from taskmaster and implement the solution: +``` +tm get-task 10 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a performance measurement task - create benchmarks to measure icon generation speed and memory usage. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-11-optimize-polygon-rendering.txt b/go-cleanup/task-11-optimize-polygon-rendering.txt new file mode 100644 index 0000000..e8d9ea5 --- /dev/null +++ b/go-cleanup/task-11-optimize-polygon-rendering.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #11 from taskmaster and implement the solution: +``` +tm get-task 11 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a rendering optimization task - improve polygon rendering efficiency while maintaining exact same output. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-12-concurrent-icon-generation.txt b/go-cleanup/task-12-concurrent-icon-generation.txt new file mode 100644 index 0000000..bd38ef6 --- /dev/null +++ b/go-cleanup/task-12-concurrent-icon-generation.txt @@ -0,0 +1,21 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #12 from taskmaster and implement the solution: +``` +tm get-task 12 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This task depends on Tasks 3 and 10 being completed first - ensure optimizations and benchmarks are in place. + +⚠️ This is a concurrency feature task - add support for concurrent icon generation while maintaining thread safety. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-13-comprehensive-documentation.txt b/go-cleanup/task-13-comprehensive-documentation.txt new file mode 100644 index 0000000..76b4d81 --- /dev/null +++ b/go-cleanup/task-13-comprehensive-documentation.txt @@ -0,0 +1,19 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #13 from taskmaster and implement the solution: +``` +tm get-task 13 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This is a documentation task - create detailed documentation for public APIs and important internal functions. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-14-continuous-integration.txt b/go-cleanup/task-14-continuous-integration.txt new file mode 100644 index 0000000..689d6ff --- /dev/null +++ b/go-cleanup/task-14-continuous-integration.txt @@ -0,0 +1,21 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #14 from taskmaster and implement the solution: +``` +tm get-task 14 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This task depends on Task 10 being completed first - ensure benchmarks are available for CI pipeline. + +⚠️ This is a CI/CD setup task - create automated testing pipeline for continuous quality assurance. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-cleanup/task-15-final-code-review.txt b/go-cleanup/task-15-final-code-review.txt new file mode 100644 index 0000000..c8c84a9 --- /dev/null +++ b/go-cleanup/task-15-final-code-review.txt @@ -0,0 +1,21 @@ +You are working on the Go Jdenticon library, a Go port of the JavaScript Jdenticon library that generates deterministic identicons. This library has achieved byte-for-byte identical SVG output with the JavaScript reference implementation, which is CRITICAL to maintain. + +Get task #15 from taskmaster and implement the solution: +``` +tm get-task 15 +``` + +CRITICAL CONSTRAINTS: +⚠️ MUST run reference compatibility tests after any changes: +```bash +go test ./jdenticon -run TestJavaScriptReferenceCompatibility -v +``` +These tests MUST pass - they verify byte-for-byte identical SVG output with the JavaScript implementation. + +⚠️ This task depends on Tasks 1-13 being completed first - this is the final review and cleanup task. + +⚠️ This is a comprehensive review task - perform final code review and ensure consistent style across all changes. + +⚠️ This is a code quality improvement project - maintain all existing functionality while improving error handling, performance, and maintainability. + +Focus on the specific requirements in the task and ensure your implementation follows Go best practices while preserving JavaScript compatibility. \ No newline at end of file diff --git a/go-output/example1_at_gmai_com_128.png b/go-output/example1_at_gmai_com_128.png new file mode 100644 index 0000000..f61fd8f Binary files /dev/null and b/go-output/example1_at_gmai_com_128.png differ diff --git a/go-output/example1_at_gmai_com_128.svg b/go-output/example1_at_gmai_com_128.svg new file mode 100644 index 0000000..e09c7b6 --- /dev/null +++ b/go-output/example1_at_gmai_com_128.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go-output/example1_at_gmai_com_64.png b/go-output/example1_at_gmai_com_64.png new file mode 100644 index 0000000..255d561 Binary files /dev/null and b/go-output/example1_at_gmai_com_64.png differ diff --git a/go-output/example1_at_gmai_com_64.svg b/go-output/example1_at_gmai_com_64.svg new file mode 100644 index 0000000..64e53f8 --- /dev/null +++ b/go-output/example1_at_gmai_com_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go-output/example1_at_gmail_com_64.svg b/go-output/example1_at_gmail_com_64.svg new file mode 100644 index 0000000..4ee22ac --- /dev/null +++ b/go-output/example1_at_gmail_com_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go-output/example2_at_yaho_com_128.png b/go-output/example2_at_yaho_com_128.png new file mode 100644 index 0000000..912ebb1 Binary files /dev/null and b/go-output/example2_at_yaho_com_128.png differ diff --git a/go-output/example2_at_yaho_com_128.svg b/go-output/example2_at_yaho_com_128.svg new file mode 100644 index 0000000..f87faf7 --- /dev/null +++ b/go-output/example2_at_yaho_com_128.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go-output/example2_at_yaho_com_64.png b/go-output/example2_at_yaho_com_64.png new file mode 100644 index 0000000..09c188d Binary files /dev/null and b/go-output/example2_at_yaho_com_64.png differ diff --git a/go-output/example2_at_yaho_com_64.svg b/go-output/example2_at_yaho_com_64.svg new file mode 100644 index 0000000..6068d23 --- /dev/null +++ b/go-output/example2_at_yaho_com_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go-output/example2_at_yahoo_com_64.svg b/go-output/example2_at_yahoo_com_64.svg new file mode 100644 index 0000000..ddce075 --- /dev/null +++ b/go-output/example2_at_yahoo_com_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go-output/test-hash_64.svg b/go-output/test-hash_64.svg new file mode 100644 index 0000000..9589cda --- /dev/null +++ b/go-output/test-hash_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b8a856a --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/kevin/go-jdenticon + +go 1.22.5 diff --git a/internal/engine/color.go b/internal/engine/color.go new file mode 100644 index 0000000..be5df08 --- /dev/null +++ b/internal/engine/color.go @@ -0,0 +1,346 @@ +package engine + +import ( + "fmt" + "math" + "strconv" +) + +// Lightness correctors for each hue segment (based on JavaScript implementation) +var correctors = []float64{0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55} + +// Color represents a color with both HSL and RGB representations +type Color struct { + H, S, L float64 // HSL values: H=[0,1], S=[0,1], L=[0,1] + R, G, B uint8 // RGB values: [0,255] + A uint8 // Alpha channel: [0,255] +} + +// NewColorHSL creates a new Color from HSL values +func NewColorHSL(h, s, l float64) Color { + r, g, b := HSLToRGB(h, s, l) + return Color{ + H: h, S: s, L: l, + R: r, G: g, B: b, + A: 255, + } +} + +// NewColorCorrectedHSL creates a new Color from HSL values with lightness correction +func NewColorCorrectedHSL(h, s, l float64) Color { + r, g, b := CorrectedHSLToRGB(h, s, l) + return Color{ + H: h, S: s, L: l, + R: r, G: g, B: b, + A: 255, + } +} + +// NewColorRGB creates a new Color from RGB values and calculates HSL +func NewColorRGB(r, g, b uint8) Color { + h, s, l := RGBToHSL(r, g, b) + return Color{ + H: h, S: s, L: l, + R: r, G: g, B: b, + A: 255, + } +} + +// NewColorRGBA creates a new Color from RGBA values and calculates HSL +func NewColorRGBA(r, g, b, a uint8) Color { + h, s, l := RGBToHSL(r, g, b) + return Color{ + H: h, S: s, L: l, + R: r, G: g, B: b, + A: a, + } +} + +// String returns the hex representation of the color +func (c Color) String() string { + if c.A == 255 { + return RGBToHex(c.R, c.G, c.B) + } + return fmt.Sprintf("#%02x%02x%02x%02x", c.R, c.G, c.B, c.A) +} + +// Equals compares two colors for equality +func (c Color) Equals(other Color) bool { + return c.R == other.R && c.G == other.G && c.B == other.B && c.A == other.A +} + +// WithAlpha returns a new color with the specified alpha value +func (c Color) WithAlpha(alpha uint8) Color { + return Color{ + H: c.H, S: c.S, L: c.L, + R: c.R, G: c.G, B: c.B, + A: alpha, + } +} + +// IsGrayscale returns true if the color is grayscale (saturation near zero) +func (c Color) IsGrayscale() bool { + return c.S < 0.01 // Small tolerance for floating point comparison +} + +// Darken returns a new color with reduced lightness +func (c Color) Darken(amount float64) Color { + newL := clamp(c.L-amount, 0, 1) + return NewColorCorrectedHSL(c.H, c.S, newL) +} + +// Lighten returns a new color with increased lightness +func (c Color) Lighten(amount float64) Color { + newL := clamp(c.L+amount, 0, 1) + return NewColorCorrectedHSL(c.H, c.S, newL) +} + +// RGBToHSL converts RGB values to HSL +// Returns H=[0,1], S=[0,1], L=[0,1] +func RGBToHSL(r, g, b uint8) (h, s, l float64) { + rf := float64(r) / 255.0 + gf := float64(g) / 255.0 + bf := float64(b) / 255.0 + + max := math.Max(rf, math.Max(gf, bf)) + min := math.Min(rf, math.Min(gf, bf)) + + // Calculate lightness + l = (max + min) / 2.0 + + if max == min { + // Achromatic (gray) + h, s = 0, 0 + } else { + delta := max - min + + // Calculate saturation + if l > 0.5 { + s = delta / (2.0 - max - min) + } else { + s = delta / (max + min) + } + + // Calculate hue + switch max { + case rf: + h = (gf-bf)/delta + (func() float64 { + if gf < bf { + return 6 + } + return 0 + })() + case gf: + h = (bf-rf)/delta + 2 + case bf: + h = (rf-gf)/delta + 4 + } + h /= 6.0 + } + + return h, s, l +} + +// HSLToRGB converts HSL color values to RGB. +// h: hue in range [0, 1] +// s: saturation in range [0, 1] +// l: lightness in range [0, 1] +// Returns RGB values in range [0, 255] +func HSLToRGB(h, s, l float64) (r, g, b uint8) { + // Clamp input values to valid ranges + h = math.Mod(h, 1.0) + if h < 0 { + h += 1.0 + } + s = clamp(s, 0, 1) + l = clamp(l, 0, 1) + + // Handle grayscale case (saturation = 0) + if s == 0 { + // All RGB components are equal for grayscale + gray := uint8(clamp(l*255, 0, 255)) + return gray, gray, gray + } + + // Calculate intermediate values for HSL to RGB conversion + var m2 float64 + if l <= 0.5 { + m2 = l * (s + 1) + } else { + m2 = l + s - l*s + } + m1 := l*2 - m2 + + // Convert each RGB component + r = uint8(clamp(hueToRGB(m1, m2, h*6+2)*255, 0, 255)) + g = uint8(clamp(hueToRGB(m1, m2, h*6)*255, 0, 255)) + b = uint8(clamp(hueToRGB(m1, m2, h*6-2)*255, 0, 255)) + + return r, g, b +} + +// CorrectedHSLToRGB converts HSL to RGB with lightness correction for better visual perception. +// This function adjusts the lightness based on the hue to compensate for the human eye's +// different sensitivity to different colors. +func CorrectedHSLToRGB(h, s, l float64) (r, g, b uint8) { + // Get the corrector for the current hue + hueIndex := int((h*6 + 0.5)) % len(correctors) + corrector := correctors[hueIndex] + + // Adjust lightness relative to the corrector + if l < 0.5 { + l = l * corrector * 2 + } else { + l = corrector + (l-0.5)*(1-corrector)*2 + } + + // Clamp the corrected lightness + l = clamp(l, 0, 1) + + return HSLToRGB(h, s, l) +} + +// hueToRGB converts a hue value to an RGB component value +// Based on the W3C CSS3 color specification +func hueToRGB(m1, m2, h float64) float64 { + // Normalize hue to [0, 6) range + if h < 0 { + h += 6 + } else if h > 6 { + h -= 6 + } + + // Calculate RGB component based on hue position + if h < 1 { + return m1 + (m2-m1)*h + } else if h < 3 { + return m2 + } else if h < 4 { + return m1 + (m2-m1)*(4-h) + } else { + return m1 + } +} + +// clamp constrains a value to the specified range [min, max] +func clamp(value, min, max float64) float64 { + if value < min { + return min + } + if value > max { + return max + } + return value +} + +// RGBToHex converts RGB values to a hexadecimal color string +func RGBToHex(r, g, b uint8) string { + return fmt.Sprintf("#%02x%02x%02x", r, g, b) +} + +// ParseHexColor parses a hexadecimal color string and returns RGB values +// Supports formats: #RGB, #RRGGBB, #RRGGBBAA +// Returns error if the format is invalid +func ParseHexColor(color string) (r, g, b, a uint8, err error) { + if len(color) == 0 || color[0] != '#' { + return 0, 0, 0, 255, fmt.Errorf("invalid color format: %s", color) + } + + hex := color[1:] // Remove '#' prefix + a = 255 // Default alpha + + // Helper to parse a component and chain errors + parse := func(target *uint8, hexStr string) { + if err != nil { + return // Don't parse if a previous component failed + } + *target, err = hexToByte(hexStr) + } + + switch len(hex) { + case 3: // #RGB + parse(&r, hex[0:1]+hex[0:1]) + parse(&g, hex[1:2]+hex[1:2]) + parse(&b, hex[2:3]+hex[2:3]) + case 6: // #RRGGBB + parse(&r, hex[0:2]) + parse(&g, hex[2:4]) + parse(&b, hex[4:6]) + case 8: // #RRGGBBAA + parse(&r, hex[0:2]) + parse(&g, hex[2:4]) + parse(&b, hex[4:6]) + parse(&a, hex[6:8]) + default: + return 0, 0, 0, 255, fmt.Errorf("invalid hex color length: %s", color) + } + + if err != nil { + // Return zero values for color components on error, but keep default alpha + return 0, 0, 0, 255, fmt.Errorf("failed to parse color '%s': %w", color, err) + } + + return r, g, b, a, nil +} + +// hexToByte converts a 2-character hex string to a byte value +func hexToByte(hex string) (uint8, error) { + if len(hex) != 2 { + return 0, fmt.Errorf("invalid hex string length: expected 2 characters, got %d", len(hex)) + } + + n, err := strconv.ParseUint(hex, 16, 8) + if err != nil { + return 0, fmt.Errorf("invalid hex value '%s': %w", hex, err) + } + return uint8(n), nil +} + +// GenerateColor creates a color with the specified hue and configuration-based saturation and lightness +func GenerateColor(hue float64, config ColorConfig, lightnessValue float64) Color { + // Restrict hue according to configuration + restrictedHue := config.RestrictHue(hue) + + // Get lightness from configuration range + lightness := config.ColorLightness.GetLightness(lightnessValue) + + // Use corrected HSL to RGB conversion + return NewColorCorrectedHSL(restrictedHue, config.ColorSaturation, lightness) +} + +// GenerateGrayscale creates a grayscale color with configuration-based saturation and lightness +func GenerateGrayscale(config ColorConfig, lightnessValue float64) Color { + // For grayscale, hue doesn't matter, but we'll use 0 + hue := 0.0 + + // Get lightness from grayscale configuration range + lightness := config.GrayscaleLightness.GetLightness(lightnessValue) + + // Use grayscale saturation (typically 0) + return NewColorCorrectedHSL(hue, config.GrayscaleSaturation, lightness) +} + +// GenerateColorTheme generates a set of color candidates based on the JavaScript colorTheme function +// This matches the JavaScript implementation that creates 5 colors: +// 0: Dark gray, 1: Mid color, 2: Light gray, 3: Light color, 4: Dark color +func GenerateColorTheme(hue float64, config ColorConfig) []Color { + // Restrict hue according to configuration + restrictedHue := config.RestrictHue(hue) + + return []Color{ + // Dark gray (grayscale with lightness 0) + NewColorCorrectedHSL(restrictedHue, config.GrayscaleSaturation, config.GrayscaleLightness.GetLightness(0)), + + // Mid color (normal color with lightness 0.5) + NewColorCorrectedHSL(restrictedHue, config.ColorSaturation, config.ColorLightness.GetLightness(0.5)), + + // Light gray (grayscale with lightness 1) + NewColorCorrectedHSL(restrictedHue, config.GrayscaleSaturation, config.GrayscaleLightness.GetLightness(1)), + + // Light color (normal color with lightness 1) + NewColorCorrectedHSL(restrictedHue, config.ColorSaturation, config.ColorLightness.GetLightness(1)), + + // Dark color (normal color with lightness 0) + NewColorCorrectedHSL(restrictedHue, config.ColorSaturation, config.ColorLightness.GetLightness(0)), + } +} \ No newline at end of file diff --git a/internal/engine/color_bench_test.go b/internal/engine/color_bench_test.go new file mode 100644 index 0000000..a5ebc9c --- /dev/null +++ b/internal/engine/color_bench_test.go @@ -0,0 +1,35 @@ +package engine + +import ( + "testing" +) + +var benchmarkCases = []struct { + h, s, l float64 +}{ + {0.0, 0.5, 0.5}, // Red + {0.33, 0.5, 0.5}, // Green + {0.66, 0.5, 0.5}, // Blue + {0.5, 1.0, 0.3}, // Cyan dark + {0.8, 0.8, 0.7}, // Purple light +} + +func BenchmarkCorrectedHSLToRGB(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + tc := benchmarkCases[i%len(benchmarkCases)] + CorrectedHSLToRGB(tc.h, tc.s, tc.l) + } +} + +func BenchmarkNewColorCorrectedHSL(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + tc := benchmarkCases[i%len(benchmarkCases)] + NewColorCorrectedHSL(tc.h, tc.s, tc.l) + } +} \ No newline at end of file diff --git a/internal/engine/color_test.go b/internal/engine/color_test.go new file mode 100644 index 0000000..264cbed --- /dev/null +++ b/internal/engine/color_test.go @@ -0,0 +1,663 @@ +package engine + +import ( + "math" + "testing" +) + +func TestHSLToRGB(t *testing.T) { + tests := []struct { + name string + h, s, l float64 + r, g, b uint8 + }{ + { + name: "pure red", + h: 0.0, s: 1.0, l: 0.5, + r: 255, g: 0, b: 0, + }, + { + name: "pure green", + h: 1.0/3.0, s: 1.0, l: 0.5, + r: 0, g: 255, b: 0, + }, + { + name: "pure blue", + h: 2.0/3.0, s: 1.0, l: 0.5, + r: 0, g: 0, b: 255, + }, + { + name: "white", + h: 0.0, s: 0.0, l: 1.0, + r: 255, g: 255, b: 255, + }, + { + name: "black", + h: 0.0, s: 0.0, l: 0.0, + r: 0, g: 0, b: 0, + }, + { + name: "gray", + h: 0.0, s: 0.0, l: 0.5, + r: 127, g: 127, b: 127, + }, + { + name: "dark red", + h: 0.0, s: 1.0, l: 0.25, + r: 127, g: 0, b: 0, + }, + { + name: "light blue", + h: 2.0/3.0, s: 1.0, l: 0.75, + r: 127, g: 127, b: 255, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r, g, b := HSLToRGB(tt.h, tt.s, tt.l) + + // Allow small tolerance due to floating point arithmetic + tolerance := uint8(2) + if abs(int(r), int(tt.r)) > int(tolerance) || + abs(int(g), int(tt.g)) > int(tolerance) || + abs(int(b), int(tt.b)) > int(tolerance) { + t.Errorf("HSLToRGB(%f, %f, %f) = (%d, %d, %d), want (%d, %d, %d)", + tt.h, tt.s, tt.l, r, g, b, tt.r, tt.g, tt.b) + } + }) + } +} + +func TestCorrectedHSLToRGB(t *testing.T) { + // Test that corrected HSL produces valid RGB values + testCases := []struct { + name string + h, s, l float64 + }{ + {"Red", 0.0, 1.0, 0.5}, + {"Green", 0.33, 1.0, 0.5}, + {"Blue", 0.67, 1.0, 0.5}, + {"Gray", 0.0, 0.0, 0.5}, + {"DarkCyan", 0.5, 0.7, 0.3}, + {"LightMagenta", 0.8, 0.8, 0.8}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + r, g, b := CorrectedHSLToRGB(tc.h, tc.s, tc.l) + + // Verify RGB values are in valid range + if r > 255 || g > 255 || b > 255 { + t.Errorf("CorrectedHSLToRGB(%f, %f, %f) = (%d, %d, %d), RGB values should be <= 255", + tc.h, tc.s, tc.l, r, g, b) + } + }) + } +} + +func TestRGBToHex(t *testing.T) { + tests := []struct { + name string + r, g, b uint8 + expected string + }{ + {"black", 0, 0, 0, "#000000"}, + {"white", 255, 255, 255, "#ffffff"}, + {"red", 255, 0, 0, "#ff0000"}, + {"green", 0, 255, 0, "#00ff00"}, + {"blue", 0, 0, 255, "#0000ff"}, + {"gray", 128, 128, 128, "#808080"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := RGBToHex(tt.r, tt.g, tt.b) + if result != tt.expected { + t.Errorf("RGBToHex(%d, %d, %d) = %s, want %s", tt.r, tt.g, tt.b, result, tt.expected) + } + }) + } +} + +func TestHexToByte(t *testing.T) { + tests := []struct { + name string + input string + expected uint8 + expectError bool + }{ + { + name: "valid hex 00", + input: "00", + expected: 0, + }, + { + name: "valid hex ff", + input: "ff", + expected: 255, + }, + { + name: "valid hex a5", + input: "a5", + expected: 165, + }, + { + name: "valid hex A5 uppercase", + input: "A5", + expected: 165, + }, + { + name: "invalid length - too short", + input: "f", + expectError: true, + }, + { + name: "invalid length - too long", + input: "fff", + expectError: true, + }, + { + name: "invalid character x", + input: "fx", + expectError: true, + }, + { + name: "invalid character z", + input: "zz", + expectError: true, + }, + { + name: "empty string", + input: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := hexToByte(tt.input) + + if tt.expectError { + if err == nil { + t.Errorf("hexToByte(%s) expected error, got nil", tt.input) + } + return + } + + if err != nil { + t.Errorf("hexToByte(%s) unexpected error: %v", tt.input, err) + return + } + + if result != tt.expected { + t.Errorf("hexToByte(%s) = %d, want %d", tt.input, result, tt.expected) + } + }) + } +} + +func TestParseHexColor(t *testing.T) { + tests := []struct { + name string + input string + expectError bool + r, g, b, a uint8 + }{ + { + name: "3-char hex", + input: "#f0a", + r: 255, g: 0, b: 170, a: 255, + }, + { + name: "6-char hex", + input: "#ff00aa", + r: 255, g: 0, b: 170, a: 255, + }, + { + name: "8-char hex with alpha", + input: "#ff00aa80", + r: 255, g: 0, b: 170, a: 128, + }, + { + name: "black", + input: "#000", + r: 0, g: 0, b: 0, a: 255, + }, + { + name: "white", + input: "#fff", + r: 255, g: 255, b: 255, a: 255, + }, + { + name: "invalid format - no hash", + input: "ff0000", + expectError: true, + }, + { + name: "invalid format - too short", + input: "#f", + expectError: true, + }, + { + name: "invalid format - too long", + input: "#ff00aa12345", + expectError: true, + }, + { + name: "invalid hex character in 3-char", + input: "#fxz", + expectError: true, + }, + { + name: "invalid hex character in 6-char", + input: "#ff00xz", + expectError: true, + }, + { + name: "invalid hex character in 8-char", + input: "#ff00aaxz", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r, g, b, a, err := ParseHexColor(tt.input) + + if tt.expectError { + if err == nil { + t.Errorf("ParseHexColor(%s) expected error, got nil", tt.input) + } + return + } + + if err != nil { + t.Errorf("ParseHexColor(%s) unexpected error: %v", tt.input, err) + return + } + + if r != tt.r || g != tt.g || b != tt.b || a != tt.a { + t.Errorf("ParseHexColor(%s) = (%d, %d, %d, %d), want (%d, %d, %d, %d)", + tt.input, r, g, b, a, tt.r, tt.g, tt.b, tt.a) + } + }) + } +} + +func TestClamp(t *testing.T) { + tests := []struct { + value, min, max, expected float64 + }{ + {0.5, 0.0, 1.0, 0.5}, // within range + {-0.5, 0.0, 1.0, 0.0}, // below min + {1.5, 0.0, 1.0, 1.0}, // above max + {0.0, 0.0, 1.0, 0.0}, // at min + {1.0, 0.0, 1.0, 1.0}, // at max + } + + for _, tt := range tests { + result := clamp(tt.value, tt.min, tt.max) + if result != tt.expected { + t.Errorf("clamp(%f, %f, %f) = %f, want %f", tt.value, tt.min, tt.max, result, tt.expected) + } + } +} + +func TestNewColorHSL(t *testing.T) { + color := NewColorHSL(0.0, 1.0, 0.5) // Pure red + + if color.H != 0.0 || color.S != 1.0 || color.L != 0.5 { + t.Errorf("NewColorHSL(0.0, 1.0, 0.5) HSL = (%f, %f, %f), want (0.0, 1.0, 0.5)", + color.H, color.S, color.L) + } + + if color.R != 255 || color.G != 0 || color.B != 0 { + t.Errorf("NewColorHSL(0.0, 1.0, 0.5) RGB = (%d, %d, %d), want (255, 0, 0)", + color.R, color.G, color.B) + } + + if color.A != 255 { + t.Errorf("NewColorHSL(0.0, 1.0, 0.5) A = %d, want 255", color.A) + } +} + +func TestNewColorRGB(t *testing.T) { + color := NewColorRGB(255, 0, 0) // Pure red + + if color.R != 255 || color.G != 0 || color.B != 0 { + t.Errorf("NewColorRGB(255, 0, 0) RGB = (%d, %d, %d), want (255, 0, 0)", + color.R, color.G, color.B) + } + + // HSL values should be approximately (0, 1, 0.5) for pure red + tolerance := 0.01 + if math.Abs(color.H-0.0) > tolerance || math.Abs(color.S-1.0) > tolerance || math.Abs(color.L-0.5) > tolerance { + t.Errorf("NewColorRGB(255, 0, 0) HSL = (%f, %f, %f), want approximately (0.0, 1.0, 0.5)", + color.H, color.S, color.L) + } +} + +func TestColorString(t *testing.T) { + tests := []struct { + name string + color Color + expected string + }{ + { + name: "red without alpha", + color: NewColorRGB(255, 0, 0), + expected: "#ff0000", + }, + { + name: "blue with alpha", + color: NewColorRGBA(0, 0, 255, 128), + expected: "#0000ff80", + }, + { + name: "black", + color: NewColorRGB(0, 0, 0), + expected: "#000000", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.color.String() + if result != tt.expected { + t.Errorf("Color.String() = %s, want %s", result, tt.expected) + } + }) + } +} + +func TestColorEquals(t *testing.T) { + color1 := NewColorRGB(255, 0, 0) + color2 := NewColorRGB(255, 0, 0) + color3 := NewColorRGB(0, 255, 0) + color4 := NewColorRGBA(255, 0, 0, 128) + + if !color1.Equals(color2) { + t.Error("Expected equal colors to be equal") + } + + if color1.Equals(color3) { + t.Error("Expected different colors to not be equal") + } + + if color1.Equals(color4) { + t.Error("Expected colors with different alpha to not be equal") + } +} + +func TestColorWithAlpha(t *testing.T) { + color := NewColorRGB(255, 0, 0) + newColor := color.WithAlpha(128) + + if newColor.A != 128 { + t.Errorf("WithAlpha(128) A = %d, want 128", newColor.A) + } + + // RGB and HSL should remain the same + if newColor.R != color.R || newColor.G != color.G || newColor.B != color.B { + t.Error("WithAlpha should not change RGB values") + } + + if newColor.H != color.H || newColor.S != color.S || newColor.L != color.L { + t.Error("WithAlpha should not change HSL values") + } +} + +func TestColorIsGrayscale(t *testing.T) { + grayColor := NewColorRGB(128, 128, 128) + redColor := NewColorRGB(255, 0, 0) + + if !grayColor.IsGrayscale() { + t.Error("Expected gray color to be identified as grayscale") + } + + if redColor.IsGrayscale() { + t.Error("Expected red color to not be identified as grayscale") + } +} + +func TestColorDarkenLighten(t *testing.T) { + color := NewColorHSL(0.0, 1.0, 0.5) // Pure red + + darker := color.Darken(0.2) + if darker.L >= color.L { + t.Error("Darken should reduce lightness") + } + + lighter := color.Lighten(0.2) + if lighter.L <= color.L { + t.Error("Lighten should increase lightness") + } + + // Test clamping + veryDark := color.Darken(1.0) + if veryDark.L != 0.0 { + t.Errorf("Darken with large amount should clamp to 0, got %f", veryDark.L) + } + + veryLight := color.Lighten(1.0) + if veryLight.L != 1.0 { + t.Errorf("Lighten with large amount should clamp to 1, got %f", veryLight.L) + } +} + +func TestRGBToHSL(t *testing.T) { + tests := []struct { + name string + r, g, b uint8 + h, s, l float64 + }{ + { + name: "red", + r: 255, g: 0, b: 0, + h: 0.0, s: 1.0, l: 0.5, + }, + { + name: "green", + r: 0, g: 255, b: 0, + h: 1.0/3.0, s: 1.0, l: 0.5, + }, + { + name: "blue", + r: 0, g: 0, b: 255, + h: 2.0/3.0, s: 1.0, l: 0.5, + }, + { + name: "white", + r: 255, g: 255, b: 255, + h: 0.0, s: 0.0, l: 1.0, + }, + { + name: "black", + r: 0, g: 0, b: 0, + h: 0.0, s: 0.0, l: 0.0, + }, + { + name: "gray", + r: 128, g: 128, b: 128, + h: 0.0, s: 0.0, l: 0.502, // approximately 0.5 + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h, s, l := RGBToHSL(tt.r, tt.g, tt.b) + + tolerance := 0.01 + if math.Abs(h-tt.h) > tolerance || math.Abs(s-tt.s) > tolerance || math.Abs(l-tt.l) > tolerance { + t.Errorf("RGBToHSL(%d, %d, %d) = (%f, %f, %f), want approximately (%f, %f, %f)", + tt.r, tt.g, tt.b, h, s, l, tt.h, tt.s, tt.l) + } + }) + } +} + +func TestGenerateColor(t *testing.T) { + config := DefaultColorConfig() + + // Test color generation with mid-range lightness + color := GenerateColor(0.0, config, 0.5) // Red hue, mid lightness + + // Should be approximately red with default saturation (0.5) and mid lightness (0.6) + expectedLightness := config.ColorLightness.GetLightness(0.5) // Should be 0.6 + tolerance := 0.01 + + if math.Abs(color.H-0.0) > tolerance { + t.Errorf("GenerateColor hue = %f, want approximately 0.0", color.H) + } + + if math.Abs(color.S-config.ColorSaturation) > tolerance { + t.Errorf("GenerateColor saturation = %f, want %f", color.S, config.ColorSaturation) + } + + if math.Abs(color.L-expectedLightness) > tolerance { + t.Errorf("GenerateColor lightness = %f, want approximately %f", color.L, expectedLightness) + } +} + +func TestGenerateGrayscale(t *testing.T) { + config := DefaultColorConfig() + + // Test grayscale generation + color := GenerateGrayscale(config, 0.5) + + // Should be grayscale (saturation 0) with mid lightness + expectedLightness := config.GrayscaleLightness.GetLightness(0.5) // Should be 0.6 + tolerance := 0.01 + + if math.Abs(color.S-config.GrayscaleSaturation) > tolerance { + t.Errorf("GenerateGrayscale saturation = %f, want %f", color.S, config.GrayscaleSaturation) + } + + if math.Abs(color.L-expectedLightness) > tolerance { + t.Errorf("GenerateGrayscale lightness = %f, want approximately %f", color.L, expectedLightness) + } + + if !color.IsGrayscale() { + t.Error("GenerateGrayscale should produce a grayscale color") + } +} + +func TestGenerateColorTheme(t *testing.T) { + config := DefaultColorConfig() + hue := 0.25 // Green-ish hue + + theme := GenerateColorTheme(hue, config) + + // Should have exactly 5 colors + if len(theme) != 5 { + t.Errorf("GenerateColorTheme returned %d colors, want 5", len(theme)) + } + + // Test color indices according to JavaScript implementation: + // 0: Dark gray, 1: Mid color, 2: Light gray, 3: Light color, 4: Dark color + + // Index 0: Dark gray (grayscale with lightness 0) + darkGray := theme[0] + if !darkGray.IsGrayscale() { + t.Error("Theme color 0 should be grayscale (dark gray)") + } + expectedLightness := config.GrayscaleLightness.GetLightness(0) + if math.Abs(darkGray.L-expectedLightness) > 0.01 { + t.Errorf("Dark gray lightness = %f, want %f", darkGray.L, expectedLightness) + } + + // Index 1: Mid color (normal color with lightness 0.5) + midColor := theme[1] + if midColor.IsGrayscale() { + t.Error("Theme color 1 should not be grayscale (mid color)") + } + expectedLightness = config.ColorLightness.GetLightness(0.5) + if math.Abs(midColor.L-expectedLightness) > 0.01 { + t.Errorf("Mid color lightness = %f, want %f", midColor.L, expectedLightness) + } + + // Index 2: Light gray (grayscale with lightness 1) + lightGray := theme[2] + if !lightGray.IsGrayscale() { + t.Error("Theme color 2 should be grayscale (light gray)") + } + expectedLightness = config.GrayscaleLightness.GetLightness(1) + if math.Abs(lightGray.L-expectedLightness) > 0.01 { + t.Errorf("Light gray lightness = %f, want %f", lightGray.L, expectedLightness) + } + + // Index 3: Light color (normal color with lightness 1) + lightColor := theme[3] + if lightColor.IsGrayscale() { + t.Error("Theme color 3 should not be grayscale (light color)") + } + expectedLightness = config.ColorLightness.GetLightness(1) + if math.Abs(lightColor.L-expectedLightness) > 0.01 { + t.Errorf("Light color lightness = %f, want %f", lightColor.L, expectedLightness) + } + + // Index 4: Dark color (normal color with lightness 0) + darkColor := theme[4] + if darkColor.IsGrayscale() { + t.Error("Theme color 4 should not be grayscale (dark color)") + } + expectedLightness = config.ColorLightness.GetLightness(0) + if math.Abs(darkColor.L-expectedLightness) > 0.01 { + t.Errorf("Dark color lightness = %f, want %f", darkColor.L, expectedLightness) + } + + // All colors should have the same hue (or close to it for grayscale) + for i, color := range theme { + if !color.IsGrayscale() { // Only check hue for non-grayscale colors + if math.Abs(color.H-hue) > 0.01 { + t.Errorf("Theme color %d hue = %f, want approximately %f", i, color.H, hue) + } + } + } +} + +func TestGenerateColorThemeWithHueRestriction(t *testing.T) { + // Test with hue restriction + config := NewColorConfigBuilder(). + WithHues(180). // Only allow cyan (180 degrees = 0.5 turns) + Build() + + theme := GenerateColorTheme(0.25, config) // Request green, should get cyan + + for i, color := range theme { + if !color.IsGrayscale() { // Only check hue for non-grayscale colors + if math.Abs(color.H-0.5) > 0.01 { + t.Errorf("Theme color %d hue = %f, want approximately 0.5 (restricted)", i, color.H) + } + } + } +} + +func TestGenerateColorWithConfiguration(t *testing.T) { + // Test with custom configuration + config := NewColorConfigBuilder(). + WithColorSaturation(0.8). + WithColorLightness(0.2, 0.6). + Build() + + color := GenerateColor(0.33, config, 1.0) // Green hue, max lightness + + tolerance := 0.01 + if math.Abs(color.S-0.8) > tolerance { + t.Errorf("Custom config saturation = %f, want 0.8", color.S) + } + + expectedLightness := config.ColorLightness.GetLightness(1.0) // Should be 0.6 + if math.Abs(color.L-expectedLightness) > tolerance { + t.Errorf("Custom config lightness = %f, want %f", color.L, expectedLightness) + } +} + +// Helper function for absolute difference +func abs(a, b int) int { + if a > b { + return a - b + } + return b - a +} \ No newline at end of file diff --git a/internal/engine/config.go b/internal/engine/config.go new file mode 100644 index 0000000..938c6c0 --- /dev/null +++ b/internal/engine/config.go @@ -0,0 +1,175 @@ +package engine + +import "math" + +// ColorConfig represents the configuration for color generation +type ColorConfig struct { + // Saturation settings + ColorSaturation float64 // Saturation for normal colors [0, 1] + GrayscaleSaturation float64 // Saturation for grayscale colors [0, 1] + + // Lightness ranges + ColorLightness LightnessRange // Lightness range for normal colors + GrayscaleLightness LightnessRange // Lightness range for grayscale colors + + // Hue restrictions + Hues []float64 // Allowed hues in degrees [0, 360] or range [0, 1]. Empty means no restriction + + // Background color + BackColor *Color // Background color (nil for transparent) + + // Icon padding + IconPadding float64 // Padding as percentage of icon size [0, 1] +} + +// LightnessRange represents a range of lightness values +type LightnessRange struct { + Min float64 // Minimum lightness [0, 1] + Max float64 // Maximum lightness [0, 1] +} + +// GetLightness returns a lightness value for the given position in range [0, 1] +// where 0 returns Min and 1 returns Max +func (lr LightnessRange) GetLightness(value float64) float64 { + // Clamp value to [0, 1] range + value = clamp(value, 0, 1) + + // Linear interpolation between min and max + result := lr.Min + value*(lr.Max-lr.Min) + + // Clamp result to valid lightness range + return clamp(result, 0, 1) +} + +// DefaultColorConfig returns the default configuration matching the JavaScript implementation +func DefaultColorConfig() ColorConfig { + return ColorConfig{ + ColorSaturation: 0.5, + GrayscaleSaturation: 0.0, + ColorLightness: LightnessRange{Min: 0.4, Max: 0.8}, + GrayscaleLightness: LightnessRange{Min: 0.3, Max: 0.9}, + Hues: nil, // No hue restriction + BackColor: nil, // Transparent background + IconPadding: 0.08, + } +} + +// RestrictHue applies hue restrictions to the given hue value +// Returns the restricted hue in range [0, 1] +func (c ColorConfig) RestrictHue(originalHue float64) float64 { + // Normalize hue to [0, 1) range + hue := math.Mod(originalHue, 1.0) + if hue < 0 { + hue += 1.0 + } + + // If no hue restrictions, return original + if len(c.Hues) == 0 { + return hue + } + + // Find the closest allowed hue + // originalHue is in range [0, 1], multiply by 0.999 to get range [0, 1) + // then truncate to get index + index := int((0.999 * hue * float64(len(c.Hues)))) + if index >= len(c.Hues) { + index = len(c.Hues) - 1 + } + + restrictedHue := c.Hues[index] + + // Convert from degrees to turns in range [0, 1) + // Handle any turn - e.g. 746° is valid + result := math.Mod(restrictedHue/360.0, 1.0) + if result < 0 { + result += 1.0 + } + + return result +} + +// ValidateConfig validates and corrects a ColorConfig to ensure all values are within valid ranges +func (c *ColorConfig) Validate() { + // Clamp saturation values + c.ColorSaturation = clamp(c.ColorSaturation, 0, 1) + c.GrayscaleSaturation = clamp(c.GrayscaleSaturation, 0, 1) + + // Validate lightness ranges + c.ColorLightness.Min = clamp(c.ColorLightness.Min, 0, 1) + c.ColorLightness.Max = clamp(c.ColorLightness.Max, 0, 1) + if c.ColorLightness.Min > c.ColorLightness.Max { + c.ColorLightness.Min, c.ColorLightness.Max = c.ColorLightness.Max, c.ColorLightness.Min + } + + c.GrayscaleLightness.Min = clamp(c.GrayscaleLightness.Min, 0, 1) + c.GrayscaleLightness.Max = clamp(c.GrayscaleLightness.Max, 0, 1) + if c.GrayscaleLightness.Min > c.GrayscaleLightness.Max { + c.GrayscaleLightness.Min, c.GrayscaleLightness.Max = c.GrayscaleLightness.Max, c.GrayscaleLightness.Min + } + + // Clamp icon padding + c.IconPadding = clamp(c.IconPadding, 0, 1) + + // Validate hues (no need to clamp as RestrictHue handles normalization) +} + +// ColorConfigBuilder provides a fluent interface for building ColorConfig +type ColorConfigBuilder struct { + config ColorConfig +} + +// NewColorConfigBuilder creates a new builder with default values +func NewColorConfigBuilder() *ColorConfigBuilder { + return &ColorConfigBuilder{ + config: DefaultColorConfig(), + } +} + +// WithColorSaturation sets the color saturation +func (b *ColorConfigBuilder) WithColorSaturation(saturation float64) *ColorConfigBuilder { + b.config.ColorSaturation = saturation + return b +} + +// WithGrayscaleSaturation sets the grayscale saturation +func (b *ColorConfigBuilder) WithGrayscaleSaturation(saturation float64) *ColorConfigBuilder { + b.config.GrayscaleSaturation = saturation + return b +} + +// WithColorLightness sets the color lightness range +func (b *ColorConfigBuilder) WithColorLightness(min, max float64) *ColorConfigBuilder { + b.config.ColorLightness = LightnessRange{Min: min, Max: max} + return b +} + +// WithGrayscaleLightness sets the grayscale lightness range +func (b *ColorConfigBuilder) WithGrayscaleLightness(min, max float64) *ColorConfigBuilder { + b.config.GrayscaleLightness = LightnessRange{Min: min, Max: max} + return b +} + +// WithHues sets the allowed hues in degrees +func (b *ColorConfigBuilder) WithHues(hues ...float64) *ColorConfigBuilder { + b.config.Hues = make([]float64, len(hues)) + copy(b.config.Hues, hues) + return b +} + +// WithBackColor sets the background color +func (b *ColorConfigBuilder) WithBackColor(color Color) *ColorConfigBuilder { + b.config.BackColor = &color + return b +} + +// WithIconPadding sets the icon padding +func (b *ColorConfigBuilder) WithIconPadding(padding float64) *ColorConfigBuilder { + b.config.IconPadding = padding + return b +} + +// Build returns the configured ColorConfig after validation +func (b *ColorConfigBuilder) Build() ColorConfig { + b.config.Validate() + return b.config +} \ No newline at end of file diff --git a/internal/engine/config_test.go b/internal/engine/config_test.go new file mode 100644 index 0000000..e43d736 --- /dev/null +++ b/internal/engine/config_test.go @@ -0,0 +1,218 @@ +package engine + +import ( + "math" + "testing" +) + +func TestDefaultColorConfig(t *testing.T) { + config := DefaultColorConfig() + + // Test default values match JavaScript implementation + if config.ColorSaturation != 0.5 { + t.Errorf("ColorSaturation = %f, want 0.5", config.ColorSaturation) + } + + if config.GrayscaleSaturation != 0.0 { + t.Errorf("GrayscaleSaturation = %f, want 0.0", config.GrayscaleSaturation) + } + + if config.ColorLightness.Min != 0.4 || config.ColorLightness.Max != 0.8 { + t.Errorf("ColorLightness = {%f, %f}, want {0.4, 0.8}", + config.ColorLightness.Min, config.ColorLightness.Max) + } + + if config.GrayscaleLightness.Min != 0.3 || config.GrayscaleLightness.Max != 0.9 { + t.Errorf("GrayscaleLightness = {%f, %f}, want {0.3, 0.9}", + config.GrayscaleLightness.Min, config.GrayscaleLightness.Max) + } + + if len(config.Hues) != 0 { + t.Errorf("Hues should be empty by default, got %v", config.Hues) + } + + if config.BackColor != nil { + t.Error("BackColor should be nil by default") + } + + if config.IconPadding != 0.08 { + t.Errorf("IconPadding = %f, want 0.08", config.IconPadding) + } +} + +func TestLightnessRangeGetLightness(t *testing.T) { + lr := LightnessRange{Min: 0.3, Max: 0.9} + + tests := []struct { + value float64 + expected float64 + }{ + {0.0, 0.3}, // Min value + {1.0, 0.9}, // Max value + {0.5, 0.6}, // Middle value: 0.3 + 0.5 * (0.9 - 0.3) = 0.6 + {-0.5, 0.3}, // Below range, should clamp to min + {1.5, 0.9}, // Above range, should clamp to max + } + + for _, tt := range tests { + result := lr.GetLightness(tt.value) + if math.Abs(result-tt.expected) > 0.001 { + t.Errorf("GetLightness(%f) = %f, want %f", tt.value, result, tt.expected) + } + } +} + +func TestConfigRestrictHue(t *testing.T) { + tests := []struct { + name string + hues []float64 + originalHue float64 + expectedHue float64 + }{ + { + name: "no restriction", + hues: nil, + originalHue: 0.25, + expectedHue: 0.25, + }, + { + name: "empty restriction", + hues: []float64{}, + originalHue: 0.25, + expectedHue: 0.25, + }, + { + name: "single hue restriction", + hues: []float64{180}, // 180 degrees = 0.5 turns + originalHue: 0.25, + expectedHue: 0.5, + }, + { + name: "multiple hue restriction", + hues: []float64{0, 120, 240}, // Red, Green, Blue + originalHue: 0.1, // Should map to first hue (0 degrees) + expectedHue: 0.0, + }, + { + name: "hue normalization - negative", + hues: []float64{90}, // 90 degrees = 0.25 turns + originalHue: -0.5, + expectedHue: 0.25, + }, + { + name: "hue normalization - over 1", + hues: []float64{270}, // 270 degrees = 0.75 turns + originalHue: 1.5, + expectedHue: 0.75, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := ColorConfig{Hues: tt.hues} + result := config.RestrictHue(tt.originalHue) + + if math.Abs(result-tt.expectedHue) > 0.001 { + t.Errorf("RestrictHue(%f) = %f, want %f", tt.originalHue, result, tt.expectedHue) + } + }) + } +} + +func TestConfigValidate(t *testing.T) { + // Test that validation corrects invalid values + config := ColorConfig{ + ColorSaturation: -0.5, // Invalid: below 0 + GrayscaleSaturation: 1.5, // Invalid: above 1 + ColorLightness: LightnessRange{Min: 0.8, Max: 0.2}, // Invalid: min > max + GrayscaleLightness: LightnessRange{Min: -0.1, Max: 1.1}, // Invalid: out of range + IconPadding: 2.0, // Invalid: above 1 + } + + config.Validate() + + if config.ColorSaturation != 0.0 { + t.Errorf("ColorSaturation after validation = %f, want 0.0", config.ColorSaturation) + } + + if config.GrayscaleSaturation != 1.0 { + t.Errorf("GrayscaleSaturation after validation = %f, want 1.0", config.GrayscaleSaturation) + } + + // Min and max should be swapped + if config.ColorLightness.Min != 0.2 || config.ColorLightness.Max != 0.8 { + t.Errorf("ColorLightness after validation = {%f, %f}, want {0.2, 0.8}", + config.ColorLightness.Min, config.ColorLightness.Max) + } + + // Values should be clamped + if config.GrayscaleLightness.Min != 0.0 || config.GrayscaleLightness.Max != 1.0 { + t.Errorf("GrayscaleLightness after validation = {%f, %f}, want {0.0, 1.0}", + config.GrayscaleLightness.Min, config.GrayscaleLightness.Max) + } + + if config.IconPadding != 1.0 { + t.Errorf("IconPadding after validation = %f, want 1.0", config.IconPadding) + } +} + +func TestColorConfigBuilder(t *testing.T) { + redColor := NewColorRGB(255, 0, 0) + + config := NewColorConfigBuilder(). + WithColorSaturation(0.7). + WithGrayscaleSaturation(0.1). + WithColorLightness(0.2, 0.8). + WithGrayscaleLightness(0.1, 0.9). + WithHues(0, 120, 240). + WithBackColor(redColor). + WithIconPadding(0.1). + Build() + + if config.ColorSaturation != 0.7 { + t.Errorf("ColorSaturation = %f, want 0.7", config.ColorSaturation) + } + + if config.GrayscaleSaturation != 0.1 { + t.Errorf("GrayscaleSaturation = %f, want 0.1", config.GrayscaleSaturation) + } + + if config.ColorLightness.Min != 0.2 || config.ColorLightness.Max != 0.8 { + t.Errorf("ColorLightness = {%f, %f}, want {0.2, 0.8}", + config.ColorLightness.Min, config.ColorLightness.Max) + } + + if config.GrayscaleLightness.Min != 0.1 || config.GrayscaleLightness.Max != 0.9 { + t.Errorf("GrayscaleLightness = {%f, %f}, want {0.1, 0.9}", + config.GrayscaleLightness.Min, config.GrayscaleLightness.Max) + } + + if len(config.Hues) != 3 || config.Hues[0] != 0 || config.Hues[1] != 120 || config.Hues[2] != 240 { + t.Errorf("Hues = %v, want [0, 120, 240]", config.Hues) + } + + if config.BackColor == nil || !config.BackColor.Equals(redColor) { + t.Error("BackColor should be set to red") + } + + if config.IconPadding != 0.1 { + t.Errorf("IconPadding = %f, want 0.1", config.IconPadding) + } +} + +func TestColorConfigBuilderValidation(t *testing.T) { + // Test that builder validates configuration + config := NewColorConfigBuilder(). + WithColorSaturation(-0.5). // Invalid + WithGrayscaleSaturation(1.5). // Invalid + Build() + + // Should be corrected by validation + if config.ColorSaturation != 0.0 { + t.Errorf("ColorSaturation = %f, want 0.0 (corrected)", config.ColorSaturation) + } + + if config.GrayscaleSaturation != 1.0 { + t.Errorf("GrayscaleSaturation = %f, want 1.0 (corrected)", config.GrayscaleSaturation) + } +} \ No newline at end of file diff --git a/internal/engine/generator.go b/internal/engine/generator.go new file mode 100644 index 0000000..1daa416 --- /dev/null +++ b/internal/engine/generator.go @@ -0,0 +1,353 @@ +package engine + +import ( + "fmt" + "sync" + + "github.com/kevin/go-jdenticon/internal/util" +) + +// Icon represents a generated jdenticon with its configuration and geometry +type Icon struct { + Hash string + Size float64 + Config ColorConfig + Shapes []ShapeGroup +} + +// ShapeGroup represents a group of shapes with the same color +type ShapeGroup struct { + Color Color + Shapes []Shape + ShapeType string +} + +// Shape represents a single geometric shape. It acts as a discriminated union +// where the `Type` field determines which other fields are valid. +// - For "polygon", `Points` is used. +// - For "circle", `CircleX`, `CircleY`, and `CircleSize` are used. +type Shape struct { + Type string + Points []Point + Transform Transform + Invert bool + // Circle-specific fields + CircleX float64 + CircleY float64 + CircleSize float64 +} + +// Generator encapsulates the icon generation logic and provides caching +type Generator struct { + config ColorConfig + cache map[string]*Icon + mu sync.RWMutex +} + +// NewGenerator creates a new Generator with the specified configuration +func NewGenerator(config ColorConfig) *Generator { + config.Validate() + return &Generator{ + config: config, + cache: make(map[string]*Icon), + } +} + +// NewDefaultGenerator creates a new Generator with default configuration +func NewDefaultGenerator() *Generator { + return NewGenerator(DefaultColorConfig()) +} + +// Generate creates an icon from a hash string using the configured settings +func (g *Generator) Generate(hash string, size float64) (*Icon, error) { + if hash == "" { + return nil, fmt.Errorf("hash cannot be empty") + } + + if size <= 0 { + return nil, fmt.Errorf("size must be positive, got %f", size) + } + + // Check cache first + cacheKey := g.cacheKey(hash, size) + g.mu.RLock() + if cached, exists := g.cache[cacheKey]; exists { + g.mu.RUnlock() + return cached, nil + } + g.mu.RUnlock() + + // Validate hash format + if !util.IsValidHash(hash) { + return nil, fmt.Errorf("invalid hash format: %s", hash) + } + + // Generate new icon + icon, err := g.generateIcon(hash, size) + if err != nil { + return nil, err + } + + // Cache the result + g.mu.Lock() + g.cache[cacheKey] = icon + g.mu.Unlock() + + return icon, nil +} + +// generateIcon performs the actual icon generation +func (g *Generator) generateIcon(hash string, size float64) (*Icon, error) { + // Calculate padding and round to nearest integer (matching JavaScript) + padding := int((0.5 + size*g.config.IconPadding)) + iconSize := size - float64(padding*2) + + // Calculate cell size and ensure it is an integer (matching JavaScript) + cell := int(iconSize / 4) + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + x := int(float64(padding) + iconSize/2 - float64(cell*2)) + y := int(float64(padding) + iconSize/2 - float64(cell*2)) + + // Extract hue from hash (last 7 characters) + hue, err := g.extractHue(hash) + if err != nil { + return nil, fmt.Errorf("generateIcon: %w", err) + } + + // Generate color theme + availableColors := GenerateColorTheme(hue, g.config) + + // Select colors for each shape layer + selectedColorIndexes, err := g.selectColors(hash, availableColors) + if err != nil { + return nil, err + } + + // Generate shape groups in exact JavaScript order + shapeGroups := make([]ShapeGroup, 0, 3) + + // 1. Sides (outer edges) - renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + sideShapes, err := g.renderShape(hash, 0, 2, 3, + [][]int{{1, 0}, {2, 0}, {2, 3}, {1, 3}, {0, 1}, {3, 1}, {3, 2}, {0, 2}}, + x, y, cell, true) + if err != nil { + return nil, fmt.Errorf("generateIcon: failed to render side shapes: %w", err) + } + if len(sideShapes) > 0 { + shapeGroups = append(shapeGroups, ShapeGroup{ + Color: availableColors[selectedColorIndexes[0]], + Shapes: sideShapes, + ShapeType: "sides", + }) + } + + // 2. Corners - renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + cornerShapes, err := g.renderShape(hash, 1, 4, 5, + [][]int{{0, 0}, {3, 0}, {3, 3}, {0, 3}}, + x, y, cell, true) + if err != nil { + return nil, fmt.Errorf("generateIcon: failed to render corner shapes: %w", err) + } + if len(cornerShapes) > 0 { + shapeGroups = append(shapeGroups, ShapeGroup{ + Color: availableColors[selectedColorIndexes[1]], + Shapes: cornerShapes, + ShapeType: "corners", + }) + } + + // 3. Center - renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + centerShapes, err := g.renderShape(hash, 2, 1, -1, + [][]int{{1, 1}, {2, 1}, {2, 2}, {1, 2}}, + x, y, cell, false) + if err != nil { + return nil, fmt.Errorf("generateIcon: failed to render center shapes: %w", err) + } + if len(centerShapes) > 0 { + shapeGroups = append(shapeGroups, ShapeGroup{ + Color: availableColors[selectedColorIndexes[2]], + Shapes: centerShapes, + ShapeType: "center", + }) + } + + return &Icon{ + Hash: hash, + Size: size, + Config: g.config, + Shapes: shapeGroups, + }, nil +} + +// extractHue extracts the hue value from the hash string +func (g *Generator) extractHue(hash string) (float64, error) { + // Use the last 7 characters of the hash to determine hue + hueValue, err := util.ParseHex(hash, -7, 7) + if err != nil { + return 0, fmt.Errorf("extractHue: %w", err) + } + return float64(hueValue) / 0xfffffff, nil +} + +// selectColors selects 3 colors from the available color palette +func (g *Generator) selectColors(hash string, availableColors []Color) ([]int, error) { + if len(availableColors) == 0 { + return nil, fmt.Errorf("no available colors") + } + + selectedIndexes := make([]int, 3) + + for i := 0; i < 3; i++ { + indexValue, err := util.ParseHex(hash, 8+i, 1) + if err != nil { + return nil, fmt.Errorf("selectColors: failed to parse color index at position %d: %w", 8+i, err) + } + index := indexValue % len(availableColors) + + // Apply color conflict resolution rules from JavaScript implementation + if g.isDuplicateColor(index, selectedIndexes[:i], []int{0, 4}) || // Disallow dark gray and dark color combo + g.isDuplicateColor(index, selectedIndexes[:i], []int{2, 3}) { // Disallow light gray and light color combo + index = 1 // Use mid color as fallback + } + + selectedIndexes[i] = index + } + + return selectedIndexes, nil +} + +// contains checks if a slice contains a specific value +func contains(slice []int, value int) bool { + for _, item := range slice { + if item == value { + return true + } + } + return false +} + +// isDuplicateColor checks for problematic color combinations +func (g *Generator) isDuplicateColor(index int, selected []int, forbidden []int) bool { + if !contains(forbidden, index) { + return false + } + for _, s := range selected { + if contains(forbidden, s) { + return true + } + } + return false +} + + +// renderShape implements the JavaScript renderShape function exactly +func (g *Generator) renderShape(hash string, colorIndex, shapeHashIndex, rotationHashIndex int, positions [][]int, x, y, cell int, isOuter bool) ([]Shape, error) { + shapeIndexValue, err := util.ParseHex(hash, shapeHashIndex, 1) + if err != nil { + return nil, fmt.Errorf("renderShape: failed to parse shape index at position %d: %w", shapeHashIndex, err) + } + shapeIndex := shapeIndexValue + + var rotation int + if rotationHashIndex >= 0 { + rotationValue, err := util.ParseHex(hash, rotationHashIndex, 1) + if err != nil { + return nil, fmt.Errorf("renderShape: failed to parse rotation at position %d: %w", rotationHashIndex, err) + } + rotation = rotationValue + } + + shapes := make([]Shape, 0, len(positions)) + + for i, pos := range positions { + // Calculate transform exactly like JavaScript: new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4) + transformX := float64(x + pos[0]*cell) + transformY := float64(y + pos[1]*cell) + var transformRotation int + if rotationHashIndex >= 0 { + transformRotation = (rotation + i) % 4 + } else { + // For center shapes (rotationIndex is null), r starts at 0 and increments + transformRotation = i % 4 + } + + transform := NewTransform(transformX, transformY, float64(cell), transformRotation) + + // Create shape using graphics with transform + graphics := NewGraphicsWithTransform(&shapeCollector{}, transform) + + if isOuter { + RenderOuterShape(graphics, shapeIndex, float64(cell)) + } else { + RenderCenterShape(graphics, shapeIndex, float64(cell), float64(i)) + } + + collector := graphics.renderer.(*shapeCollector) + for _, shape := range collector.shapes { + shapes = append(shapes, shape) + } + } + + return shapes, nil +} + +// shapeCollector implements Renderer interface to collect shapes during generation +type shapeCollector struct { + shapes []Shape +} + +func (sc *shapeCollector) AddPolygon(points []Point) { + sc.shapes = append(sc.shapes, Shape{ + Type: "polygon", + Points: points, + }) +} + +func (sc *shapeCollector) AddCircle(topLeft Point, size float64, invert bool) { + // Store circle with dedicated circle geometry fields + sc.shapes = append(sc.shapes, Shape{ + Type: "circle", + CircleX: topLeft.X, + CircleY: topLeft.Y, + CircleSize: size, + Invert: invert, + }) +} + + + +// cacheKey generates a cache key for the given parameters +func (g *Generator) cacheKey(hash string, size float64) string { + return fmt.Sprintf("%s:%.2f", hash, size) +} + +// ClearCache clears the internal cache +func (g *Generator) ClearCache() { + g.mu.Lock() + defer g.mu.Unlock() + g.cache = make(map[string]*Icon) +} + +// GetCacheSize returns the number of cached icons +func (g *Generator) GetCacheSize() int { + g.mu.RLock() + defer g.mu.RUnlock() + return len(g.cache) +} + +// SetConfig updates the generator configuration and clears cache +func (g *Generator) SetConfig(config ColorConfig) { + config.Validate() + g.mu.Lock() + g.config = config + g.cache = make(map[string]*Icon) + g.mu.Unlock() +} + +// GetConfig returns a copy of the current configuration +func (g *Generator) GetConfig() ColorConfig { + g.mu.RLock() + defer g.mu.RUnlock() + return g.config +} \ No newline at end of file diff --git a/internal/engine/generator_test.go b/internal/engine/generator_test.go new file mode 100644 index 0000000..750551f --- /dev/null +++ b/internal/engine/generator_test.go @@ -0,0 +1,517 @@ +package engine + +import ( + "testing" + + "github.com/kevin/go-jdenticon/internal/util" +) + +func TestNewGenerator(t *testing.T) { + config := DefaultColorConfig() + generator := NewGenerator(config) + + if generator == nil { + t.Fatal("NewGenerator returned nil") + } + + if generator.config.IconPadding != config.IconPadding { + t.Errorf("Expected icon padding %f, got %f", config.IconPadding, generator.config.IconPadding) + } + + if generator.cache == nil { + t.Error("Generator cache was not initialized") + } +} + +func TestNewDefaultGenerator(t *testing.T) { + generator := NewDefaultGenerator() + + if generator == nil { + t.Fatal("NewDefaultGenerator returned nil") + } + + expectedConfig := DefaultColorConfig() + if generator.config.IconPadding != expectedConfig.IconPadding { + t.Errorf("Expected icon padding %f, got %f", expectedConfig.IconPadding, generator.config.IconPadding) + } +} + +func TestGenerateValidHash(t *testing.T) { + generator := NewDefaultGenerator() + hash := "abcdef123456789" + size := 64.0 + + icon, err := generator.Generate(hash, size) + + if err != nil { + t.Fatalf("Generate failed with error: %v", err) + } + + if icon == nil { + t.Fatal("Generate returned nil icon") + } + + if icon.Hash != hash { + t.Errorf("Expected hash %s, got %s", hash, icon.Hash) + } + + if icon.Size != size { + t.Errorf("Expected size %f, got %f", size, icon.Size) + } + + if len(icon.Shapes) == 0 { + t.Error("Generated icon has no shapes") + } +} + +func TestGenerateInvalidInputs(t *testing.T) { + generator := NewDefaultGenerator() + + tests := []struct { + name string + hash string + size float64 + wantErr bool + }{ + { + name: "empty hash", + hash: "", + size: 64.0, + wantErr: true, + }, + { + name: "zero size", + hash: "abcdef123456789", + size: 0.0, + wantErr: true, + }, + { + name: "negative size", + hash: "abcdef123456789", + size: -10.0, + wantErr: true, + }, + { + name: "short hash", + hash: "abc", + size: 64.0, + wantErr: true, + }, + { + name: "invalid hex characters", + hash: "xyz123456789abc", + size: 64.0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := generator.Generate(tt.hash, tt.size) + if (err != nil) != tt.wantErr { + t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGenerateCaching(t *testing.T) { + generator := NewDefaultGenerator() + hash := "abcdef123456789" + size := 64.0 + + // Generate icon first time + icon1, err := generator.Generate(hash, size) + if err != nil { + t.Fatalf("First generate failed: %v", err) + } + + // Check cache size + if generator.GetCacheSize() != 1 { + t.Errorf("Expected cache size 1, got %d", generator.GetCacheSize()) + } + + // Generate same icon again + icon2, err := generator.Generate(hash, size) + if err != nil { + t.Fatalf("Second generate failed: %v", err) + } + + // Should be the same instance from cache + if icon1 != icon2 { + t.Error("Second generate did not return cached instance") + } + + // Cache size should still be 1 + if generator.GetCacheSize() != 1 { + t.Errorf("Expected cache size 1 after second generate, got %d", generator.GetCacheSize()) + } +} + +func TestClearCache(t *testing.T) { + generator := NewDefaultGenerator() + hash := "abcdef123456789" + size := 64.0 + + // Generate an icon to populate cache + _, err := generator.Generate(hash, size) + if err != nil { + t.Fatalf("Generate failed: %v", err) + } + + // Verify cache has content + if generator.GetCacheSize() == 0 { + t.Error("Cache should not be empty after generate") + } + + // Clear cache + generator.ClearCache() + + // Verify cache is empty + if generator.GetCacheSize() != 0 { + t.Errorf("Expected cache size 0 after clear, got %d", generator.GetCacheSize()) + } +} + +func TestSetConfig(t *testing.T) { + generator := NewDefaultGenerator() + hash := "abcdef123456789" + size := 64.0 + + // Generate an icon to populate cache + _, err := generator.Generate(hash, size) + if err != nil { + t.Fatalf("Generate failed: %v", err) + } + + // Verify cache has content + if generator.GetCacheSize() == 0 { + t.Error("Cache should not be empty after generate") + } + + // Set new config + newConfig := DefaultColorConfig() + newConfig.IconPadding = 0.1 + generator.SetConfig(newConfig) + + // Verify config was updated + if generator.GetConfig().IconPadding != 0.1 { + t.Errorf("Expected icon padding 0.1, got %f", generator.GetConfig().IconPadding) + } + + // Verify cache was cleared + if generator.GetCacheSize() != 0 { + t.Errorf("Expected cache size 0 after config change, got %d", generator.GetCacheSize()) + } +} + +func TestExtractHue(t *testing.T) { + generator := NewDefaultGenerator() + + tests := []struct { + name string + hash string + expected float64 + tolerance float64 + }{ + { + name: "all zeros", + hash: "0000000000000000000", + expected: 0.0, + tolerance: 0.0001, + }, + { + name: "all fs", + hash: "ffffffffffffffffff", + expected: 1.0, + tolerance: 0.0001, + }, + { + name: "half value", + hash: "000000000007ffffff", + expected: 0.5, + tolerance: 0.001, // Allow small floating point variance + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := generator.extractHue(tt.hash) + if err != nil { + t.Fatalf("extractHue failed: %v", err) + } + diff := result - tt.expected + if diff < 0 { + diff = -diff + } + if diff > tt.tolerance { + t.Errorf("Expected hue %f, got %f (tolerance %f)", tt.expected, result, tt.tolerance) + } + }) + } +} + +func TestSelectColors(t *testing.T) { + generator := NewDefaultGenerator() + hash := "123456789abcdef" + + // Create test color palette + availableColors := []Color{ + NewColorRGB(50, 50, 50), // 0: Dark gray + NewColorRGB(100, 100, 200), // 1: Mid color + NewColorRGB(200, 200, 200), // 2: Light gray + NewColorRGB(150, 150, 255), // 3: Light color + NewColorRGB(25, 25, 100), // 4: Dark color + } + + selectedIndexes, err := generator.selectColors(hash, availableColors) + + if err != nil { + t.Fatalf("selectColors failed: %v", err) + } + + if len(selectedIndexes) != 3 { + t.Fatalf("Expected 3 selected colors, got %d", len(selectedIndexes)) + } + + for i, index := range selectedIndexes { + if index < 0 || index >= len(availableColors) { + t.Errorf("Color index %d at position %d is out of range [0, %d)", index, i, len(availableColors)) + } + } +} + +func TestSelectColorsEmptyPalette(t *testing.T) { + generator := NewDefaultGenerator() + hash := "123456789abcdef" + + _, err := generator.selectColors(hash, []Color{}) + + if err == nil { + t.Error("Expected error for empty color palette") + } +} + +func TestIsValidHash(t *testing.T) { + + tests := []struct { + name string + hash string + valid bool + }{ + { + name: "valid hash", + hash: "abcdef123456789", + valid: true, + }, + { + name: "too short", + hash: "abc", + valid: false, + }, + { + name: "invalid characters", + hash: "xyz123456789abc", + valid: false, + }, + { + name: "uppercase valid", + hash: "ABCDEF123456789", + valid: true, + }, + { + name: "mixed case valid", + hash: "AbCdEf123456789", + valid: true, + }, + { + name: "empty", + hash: "", + valid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := util.IsValidHash(tt.hash) + if result != tt.valid { + t.Errorf("Expected isValidHash(%s) = %v, got %v", tt.hash, tt.valid, result) + } + }) + } +} + +func TestParseHex(t *testing.T) { + hash := "123456789abcdef" + + tests := []struct { + name string + start int + octets int + expected int + wantErr bool + }{ + { + name: "single character", + start: 0, + octets: 1, + expected: 1, + wantErr: false, + }, + { + name: "two characters", + start: 1, + octets: 2, + expected: 0x23, + wantErr: false, + }, + { + name: "negative index", + start: -1, + octets: 1, + expected: 0xf, + wantErr: false, + }, + { + name: "out of bounds", + start: 100, + octets: 1, + expected: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := util.ParseHex(hash, tt.start, tt.octets) + if tt.wantErr { + if err == nil { + t.Errorf("Expected an error, but got nil") + } + return // Test is done for error cases + } + if err != nil { + t.Fatalf("parseHex failed unexpectedly: %v", err) + } + if result != tt.expected { + t.Errorf("Expected %d, got %d", tt.expected, result) + } + }) + } +} + +func TestShapeCollector(t *testing.T) { + collector := &shapeCollector{} + + // Test AddPolygon + points := []Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}} + collector.AddPolygon(points) + + if len(collector.shapes) != 1 { + t.Fatalf("Expected 1 shape after AddPolygon, got %d", len(collector.shapes)) + } + + shape := collector.shapes[0] + if shape.Type != "polygon" { + t.Errorf("Expected shape type 'polygon', got '%s'", shape.Type) + } + + if len(shape.Points) != len(points) { + t.Errorf("Expected %d points, got %d", len(points), len(shape.Points)) + } + + // Test AddCircle + center := Point{X: 5, Y: 5} + radius := 2.5 + collector.AddCircle(center, radius, false) + + if len(collector.shapes) != 2 { + t.Fatalf("Expected 2 shapes after AddCircle, got %d", len(collector.shapes)) + } + + circleShape := collector.shapes[1] + if circleShape.Type != "circle" { + t.Errorf("Expected shape type 'circle', got '%s'", circleShape.Type) + } + + // Verify circle fields are set correctly + if circleShape.CircleX != center.X { + t.Errorf("Expected CircleX %f, got %f", center.X, circleShape.CircleX) + } + if circleShape.CircleY != center.Y { + t.Errorf("Expected CircleY %f, got %f", center.Y, circleShape.CircleY) + } + if circleShape.CircleSize != radius { + t.Errorf("Expected CircleSize %f, got %f", radius, circleShape.CircleSize) + } + if circleShape.Invert != false { + t.Errorf("Expected Invert false, got %t", circleShape.Invert) + } +} + +func BenchmarkGenerate(b *testing.B) { + generator := NewDefaultGenerator() + hash := "abcdef123456789" + size := 64.0 + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := generator.Generate(hash, size) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + } +} + +func BenchmarkGenerateWithCache(b *testing.B) { + generator := NewDefaultGenerator() + hash := "abcdef123456789" + size := 64.0 + + // Pre-populate cache + _, err := generator.Generate(hash, size) + if err != nil { + b.Fatalf("Initial generate failed: %v", err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := generator.Generate(hash, size) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + } +} + +func TestConsistentGeneration(t *testing.T) { + generator1 := NewDefaultGenerator() + generator2 := NewDefaultGenerator() + + hash := "abcdef123456789" + size := 64.0 + + icon1, err := generator1.Generate(hash, size) + if err != nil { + t.Fatalf("Generator1 failed: %v", err) + } + + icon2, err := generator2.Generate(hash, size) + if err != nil { + t.Fatalf("Generator2 failed: %v", err) + } + + // Icons should have same number of shape groups + if len(icon1.Shapes) != len(icon2.Shapes) { + t.Errorf("Different number of shape groups: %d vs %d", len(icon1.Shapes), len(icon2.Shapes)) + } + + // Colors should be the same + for i := range icon1.Shapes { + if i >= len(icon2.Shapes) { + break + } + if !icon1.Shapes[i].Color.Equals(icon2.Shapes[i].Color) { + t.Errorf("Different colors at group %d", i) + } + } +} \ No newline at end of file diff --git a/internal/engine/layout.go b/internal/engine/layout.go new file mode 100644 index 0000000..8fe8172 --- /dev/null +++ b/internal/engine/layout.go @@ -0,0 +1,136 @@ +package engine + +// Grid represents a 4x4 layout grid for positioning shapes in a jdenticon +type Grid struct { + Size float64 + Cell int + X int + Y int + Padding int +} + +// Position represents an x, y coordinate pair +type Position struct { + X, Y int +} + +// NewGrid creates a new Grid with the specified icon size and padding ratio +func NewGrid(iconSize float64, paddingRatio float64) *Grid { + // Calculate padding and round to nearest integer (matches JS: (0.5 + size * parsedConfig.iconPadding) | 0) + padding := int(0.5 + iconSize*paddingRatio) + size := iconSize - float64(padding*2) + + // Calculate cell size and ensure it is an integer (matches JS: 0 | (size / 4)) + cell := int(size / 4) + + // Center the icon since cell size is integer-based (matches JS implementation) + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + x := padding + int((size - float64(cell*4))/2) + y := padding + int((size - float64(cell*4))/2) + + return &Grid{ + Size: size, + Cell: cell, + X: x, + Y: y, + Padding: padding, + } +} + +// CellToCoordinate converts a grid cell position to actual coordinates +func (g *Grid) CellToCoordinate(cellX, cellY int) (x, y float64) { + return float64(g.X + cellX*g.Cell), float64(g.Y + cellY*g.Cell) +} + +// GetCellSize returns the size of each cell in the grid +func (g *Grid) GetCellSize() float64 { + return float64(g.Cell) +} + +// LayoutEngine manages the overall layout and positioning of icon elements +type LayoutEngine struct { + grid *Grid +} + +// NewLayoutEngine creates a new LayoutEngine with the specified parameters +func NewLayoutEngine(iconSize float64, paddingRatio float64) *LayoutEngine { + return &LayoutEngine{ + grid: NewGrid(iconSize, paddingRatio), + } +} + +// Grid returns the underlying grid +func (le *LayoutEngine) Grid() *Grid { + return le.grid +} + +// GetShapePositions returns the positions for different shape types based on the jdenticon pattern +func (le *LayoutEngine) GetShapePositions(shapeType string) []Position { + switch shapeType { + case "sides": + // Sides: positions around the perimeter (8 positions) + return []Position{ + {1, 0}, {2, 0}, {2, 3}, {1, 3}, // top and bottom + {0, 1}, {3, 1}, {3, 2}, {0, 2}, // left and right + } + case "corners": + // Corners: four corner positions + return []Position{ + {0, 0}, {3, 0}, {3, 3}, {0, 3}, + } + case "center": + // Center: four center positions + return []Position{ + {1, 1}, {2, 1}, {2, 2}, {1, 2}, + } + default: + return []Position{} + } +} + +// ApplySymmetry applies symmetrical transformations to position indices +// This ensures the icon has the characteristic jdenticon symmetry +func ApplySymmetry(positions []Position, index int) []Position { + if index >= len(positions) { + return positions + } + + // For jdenticon, we apply rotational symmetry + // The pattern is designed to be symmetrical, so we don't need to modify positions + // The symmetry is achieved through the predefined position arrays + return positions +} + +// GetTransformedPosition applies rotation and returns the final position +func (le *LayoutEngine) GetTransformedPosition(cellX, cellY int, rotation int) (x, y float64, cellSize float64) { + // Apply rotation if needed (rotation is 0-3 for 0°, 90°, 180°, 270°) + switch rotation % 4 { + case 0: // 0° + // No rotation + case 1: // 90° clockwise + cellX, cellY = cellY, 3-cellX + case 2: // 180° + cellX, cellY = 3-cellX, 3-cellY + case 3: // 270° clockwise (90° counter-clockwise) + cellX, cellY = 3-cellY, cellX + } + + x, y = le.grid.CellToCoordinate(cellX, cellY) + cellSize = le.grid.GetCellSize() + return +} + +// ValidateGrid checks if the grid configuration is valid +func (g *Grid) ValidateGrid() bool { + return g.Cell > 0 && g.Size > 0 && g.Padding >= 0 +} + +// GetIconBounds returns the bounds of the icon within the grid +func (g *Grid) GetIconBounds() (x, y, width, height float64) { + return float64(g.X), float64(g.Y), float64(g.Cell * 4), float64(g.Cell * 4) +} + +// GetCenterOffset returns the offset needed to center content within a cell +func (g *Grid) GetCenterOffset() (dx, dy float64) { + return float64(g.Cell) / 2, float64(g.Cell) / 2 +} \ No newline at end of file diff --git a/internal/engine/layout_test.go b/internal/engine/layout_test.go new file mode 100644 index 0000000..ed6d0d2 --- /dev/null +++ b/internal/engine/layout_test.go @@ -0,0 +1,380 @@ +package engine + +import ( + "math" + "testing" +) + +func TestNewGrid(t *testing.T) { + tests := []struct { + name string + iconSize float64 + paddingRatio float64 + wantPadding int + wantCell int + }{ + { + name: "standard 64px icon with 8% padding", + iconSize: 64.0, + paddingRatio: 0.08, + wantPadding: 5, // 0.5 + 64 * 0.08 = 5.62, rounded to 5 + wantCell: 13, // (64 - 5*2) / 4 = 54/4 = 13.5, truncated to 13 + }, + { + name: "large 256px icon with 10% padding", + iconSize: 256.0, + paddingRatio: 0.10, + wantPadding: 26, // 0.5 + 256 * 0.10 = 26.1, rounded to 26 + wantCell: 51, // (256 - 26*2) / 4 = 204/4 = 51 + }, + { + name: "small 32px icon with 5% padding", + iconSize: 32.0, + paddingRatio: 0.05, + wantPadding: 2, // 0.5 + 32 * 0.05 = 2.1, rounded to 2 + wantCell: 7, // (32 - 2*2) / 4 = 28/4 = 7 + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + grid := NewGrid(tt.iconSize, tt.paddingRatio) + + if grid.Padding != tt.wantPadding { + t.Errorf("NewGrid() padding = %v, want %v", grid.Padding, tt.wantPadding) + } + + if grid.Cell != tt.wantCell { + t.Errorf("NewGrid() cell = %v, want %v", grid.Cell, tt.wantCell) + } + + // Verify that the grid is centered + expectedSize := tt.iconSize - float64(tt.wantPadding*2) + if math.Abs(grid.Size-expectedSize) > 0.1 { + t.Errorf("NewGrid() size = %v, want %v", grid.Size, expectedSize) + } + }) + } +} + +func TestGridCellToCoordinate(t *testing.T) { + grid := NewGrid(64.0, 0.08) + + tests := []struct { + name string + cellX int + cellY int + wantX float64 + wantY float64 + }{ + { + name: "origin cell (0,0)", + cellX: 0, + cellY: 0, + wantX: float64(grid.X), + wantY: float64(grid.Y), + }, + { + name: "center cell (1,1)", + cellX: 1, + cellY: 1, + wantX: float64(grid.X + grid.Cell), + wantY: float64(grid.Y + grid.Cell), + }, + { + name: "corner cell (3,3)", + cellX: 3, + cellY: 3, + wantX: float64(grid.X + 3*grid.Cell), + wantY: float64(grid.Y + 3*grid.Cell), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotX, gotY := grid.CellToCoordinate(tt.cellX, tt.cellY) + + if gotX != tt.wantX { + t.Errorf("CellToCoordinate() x = %v, want %v", gotX, tt.wantX) + } + + if gotY != tt.wantY { + t.Errorf("CellToCoordinate() y = %v, want %v", gotY, tt.wantY) + } + }) + } +} + +func TestLayoutEngineGetShapePositions(t *testing.T) { + le := NewLayoutEngine(64.0, 0.08) + + tests := []struct { + name string + shapeType string + wantLen int + wantFirst Position + wantLast Position + }{ + { + name: "sides positions", + shapeType: "sides", + wantLen: 8, + wantFirst: Position{1, 0}, + wantLast: Position{0, 2}, + }, + { + name: "corners positions", + shapeType: "corners", + wantLen: 4, + wantFirst: Position{0, 0}, + wantLast: Position{0, 3}, + }, + { + name: "center positions", + shapeType: "center", + wantLen: 4, + wantFirst: Position{1, 1}, + wantLast: Position{1, 2}, + }, + { + name: "invalid shape type", + shapeType: "invalid", + wantLen: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + positions := le.GetShapePositions(tt.shapeType) + + if len(positions) != tt.wantLen { + t.Errorf("GetShapePositions() len = %v, want %v", len(positions), tt.wantLen) + } + + if tt.wantLen > 0 { + if positions[0] != tt.wantFirst { + t.Errorf("GetShapePositions() first = %v, want %v", positions[0], tt.wantFirst) + } + + if positions[len(positions)-1] != tt.wantLast { + t.Errorf("GetShapePositions() last = %v, want %v", positions[len(positions)-1], tt.wantLast) + } + } + }) + } +} + +func TestLayoutEngineGetTransformedPosition(t *testing.T) { + le := NewLayoutEngine(64.0, 0.08) + + tests := []struct { + name string + cellX int + cellY int + rotation int + wantX int // Expected cell X after rotation + wantY int // Expected cell Y after rotation + }{ + { + name: "no rotation", + cellX: 1, + cellY: 0, + rotation: 0, + wantX: 1, + wantY: 0, + }, + { + name: "90 degree rotation", + cellX: 1, + cellY: 0, + rotation: 1, + wantX: 0, + wantY: 2, // 3-1 = 2 + }, + { + name: "180 degree rotation", + cellX: 1, + cellY: 0, + rotation: 2, + wantX: 2, // 3-1 = 2 + wantY: 3, // 3-0 = 3 + }, + { + name: "270 degree rotation", + cellX: 1, + cellY: 0, + rotation: 3, + wantX: 3, // 3-0 = 3 + wantY: 1, + }, + { + name: "rotation overflow (4 = 0)", + cellX: 1, + cellY: 0, + rotation: 4, + wantX: 1, + wantY: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotX, gotY, gotCellSize := le.GetTransformedPosition(tt.cellX, tt.cellY, tt.rotation) + + // Convert back to cell coordinates to verify rotation + expectedX, expectedY := le.grid.CellToCoordinate(tt.wantX, tt.wantY) + + if gotX != expectedX { + t.Errorf("GetTransformedPosition() x = %v, want %v", gotX, expectedX) + } + + if gotY != expectedY { + t.Errorf("GetTransformedPosition() y = %v, want %v", gotY, expectedY) + } + + if gotCellSize != float64(le.grid.Cell) { + t.Errorf("GetTransformedPosition() cellSize = %v, want %v", gotCellSize, float64(le.grid.Cell)) + } + }) + } +} + +func TestApplySymmetry(t *testing.T) { + positions := []Position{{0, 0}, {1, 0}, {2, 0}, {3, 0}} + + tests := []struct { + name string + index int + want int // expected length + }{ + { + name: "valid index", + index: 1, + want: 4, + }, + { + name: "index out of bounds", + index: 10, + want: 4, + }, + { + name: "negative index", + index: -1, + want: 4, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ApplySymmetry(positions, tt.index) + + if len(result) != tt.want { + t.Errorf("ApplySymmetry() len = %v, want %v", len(result), tt.want) + } + + // Verify that the positions are unchanged (current implementation) + for i, pos := range result { + if pos != positions[i] { + t.Errorf("ApplySymmetry() changed position at index %d: got %v, want %v", i, pos, positions[i]) + } + } + }) + } +} + +func TestGridValidateGrid(t *testing.T) { + tests := []struct { + name string + grid *Grid + want bool + }{ + { + name: "valid grid", + grid: &Grid{Size: 64, Cell: 16, Padding: 4}, + want: true, + }, + { + name: "zero cell size", + grid: &Grid{Size: 64, Cell: 0, Padding: 4}, + want: false, + }, + { + name: "zero size", + grid: &Grid{Size: 0, Cell: 16, Padding: 4}, + want: false, + }, + { + name: "negative padding", + grid: &Grid{Size: 64, Cell: 16, Padding: -1}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.grid.ValidateGrid(); got != tt.want { + t.Errorf("ValidateGrid() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGridGetIconBounds(t *testing.T) { + grid := NewGrid(64.0, 0.08) + + x, y, width, height := grid.GetIconBounds() + + expectedX := float64(grid.X) + expectedY := float64(grid.Y) + expectedWidth := float64(grid.Cell * 4) + expectedHeight := float64(grid.Cell * 4) + + if x != expectedX { + t.Errorf("GetIconBounds() x = %v, want %v", x, expectedX) + } + + if y != expectedY { + t.Errorf("GetIconBounds() y = %v, want %v", y, expectedY) + } + + if width != expectedWidth { + t.Errorf("GetIconBounds() width = %v, want %v", width, expectedWidth) + } + + if height != expectedHeight { + t.Errorf("GetIconBounds() height = %v, want %v", height, expectedHeight) + } +} + +func TestGridGetCenterOffset(t *testing.T) { + grid := NewGrid(64.0, 0.08) + + dx, dy := grid.GetCenterOffset() + + expected := float64(grid.Cell) / 2 + + if dx != expected { + t.Errorf("GetCenterOffset() dx = %v, want %v", dx, expected) + } + + if dy != expected { + t.Errorf("GetCenterOffset() dy = %v, want %v", dy, expected) + } +} + +func TestNewLayoutEngine(t *testing.T) { + le := NewLayoutEngine(64.0, 0.08) + + if le.grid == nil { + t.Error("NewLayoutEngine() grid is nil") + } + + if le.Grid() != le.grid { + t.Error("NewLayoutEngine() Grid() does not return internal grid") + } + + // Verify grid configuration + if !le.grid.ValidateGrid() { + t.Error("NewLayoutEngine() created invalid grid") + } +} \ No newline at end of file diff --git a/internal/engine/shapes.go b/internal/engine/shapes.go new file mode 100644 index 0000000..2bfd5da --- /dev/null +++ b/internal/engine/shapes.go @@ -0,0 +1,266 @@ +package engine + +import "math" + +// Point represents a 2D point +type Point struct { + X, Y float64 +} + +// Renderer interface defines the methods that a renderer must implement +type Renderer interface { + AddPolygon(points []Point) + AddCircle(topLeft Point, size float64, invert bool) +} + +// Graphics provides helper functions for rendering common basic shapes +type Graphics struct { + renderer Renderer + currentTransform Transform +} + +// NewGraphics creates a new Graphics instance with the given renderer +func NewGraphics(renderer Renderer) *Graphics { + return &Graphics{ + renderer: renderer, + currentTransform: NoTransform, + } +} + +// NewGraphicsWithTransform creates a new Graphics instance with the given renderer and transform +func NewGraphicsWithTransform(renderer Renderer, transform Transform) *Graphics { + return &Graphics{ + renderer: renderer, + currentTransform: transform, + } +} + +// AddPolygon adds a polygon to the underlying renderer +func (g *Graphics) AddPolygon(points []Point, invert bool) { + // Transform all points + transformedPoints := make([]Point, len(points)) + if invert { + // Reverse the order and transform + for i, p := range points { + transformedPoints[len(points)-1-i] = g.currentTransform.TransformIconPoint(p.X, p.Y, 0, 0) + } + } else { + // Transform in order + for i, p := range points { + transformedPoints[i] = g.currentTransform.TransformIconPoint(p.X, p.Y, 0, 0) + } + } + g.renderer.AddPolygon(transformedPoints) +} + +// AddCircle adds a circle to the underlying renderer +func (g *Graphics) AddCircle(x, y, size float64, invert bool) { + // Transform the circle position + transformedPoint := g.currentTransform.TransformIconPoint(x, y, size, size) + g.renderer.AddCircle(transformedPoint, size, invert) +} + +// AddRectangle adds a rectangle to the underlying renderer +func (g *Graphics) AddRectangle(x, y, w, h float64, invert bool) { + points := []Point{ + {X: x, Y: y}, + {X: x + w, Y: y}, + {X: x + w, Y: y + h}, + {X: x, Y: y + h}, + } + g.AddPolygon(points, invert) +} + +// AddTriangle adds a right triangle to the underlying renderer +// r is the rotation (0-3), where 0 = right corner in lower left +func (g *Graphics) AddTriangle(x, y, w, h float64, r int, invert bool) { + points := []Point{ + {X: x + w, Y: y}, + {X: x + w, Y: y + h}, + {X: x, Y: y + h}, + {X: x, Y: y}, + } + + // Remove one corner based on rotation + removeIndex := (r % 4) * 1 + if removeIndex < len(points) { + points = append(points[:removeIndex], points[removeIndex+1:]...) + } + + g.AddPolygon(points, invert) +} + +// AddRhombus adds a rhombus (diamond) to the underlying renderer +func (g *Graphics) AddRhombus(x, y, w, h float64, invert bool) { + points := []Point{ + {X: x + w/2, Y: y}, + {X: x + w, Y: y + h/2}, + {X: x + w/2, Y: y + h}, + {X: x, Y: y + h/2}, + } + g.AddPolygon(points, invert) +} + +// RenderCenterShape renders one of the 14 distinct center shape patterns +func RenderCenterShape(g *Graphics, shapeIndex int, cell, positionIndex float64) { + index := shapeIndex % 14 + + switch index { + case 0: + // Shape 0: Asymmetric polygon + k := cell * 0.42 + points := []Point{ + {X: 0, Y: 0}, + {X: cell, Y: 0}, + {X: cell, Y: cell - k*2}, + {X: cell - k, Y: cell}, + {X: 0, Y: cell}, + } + g.AddPolygon(points, false) + + case 1: + // Shape 1: Triangle + w := math.Floor(cell * 0.5) + h := math.Floor(cell * 0.8) + g.AddTriangle(cell-w, 0, w, h, 2, false) + + case 2: + // Shape 2: Rectangle + w := math.Floor(cell / 3) + g.AddRectangle(w, w, cell-w, cell-w, false) + + case 3: + // Shape 3: Nested rectangles + inner := cell * 0.1 + var outer float64 + if cell < 6 { + outer = 1 + } else if cell < 8 { + outer = 2 + } else { + outer = math.Floor(cell * 0.25) + } + + if inner > 1 { + inner = math.Floor(inner) + } else if inner > 0.5 { + inner = 1 + } + + g.AddRectangle(outer, outer, cell-inner-outer, cell-inner-outer, false) + + case 4: + // Shape 4: Circle + m := math.Floor(cell * 0.15) + w := math.Floor(cell * 0.5) + g.AddCircle(cell-w-m, cell-w-m, w, false) + + case 5: + // Shape 5: Rectangle with triangular cutout + inner := cell * 0.1 + outer := inner * 4 + + if outer > 3 { + outer = math.Floor(outer) + } + + g.AddRectangle(0, 0, cell, cell, false) + points := []Point{ + {X: outer, Y: outer}, + {X: cell - inner, Y: outer}, + {X: outer + (cell-outer-inner)/2, Y: cell - inner}, + } + g.AddPolygon(points, true) + + case 6: + // Shape 6: Complex polygon + points := []Point{ + {X: 0, Y: 0}, + {X: cell, Y: 0}, + {X: cell, Y: cell * 0.7}, + {X: cell * 0.4, Y: cell * 0.4}, + {X: cell * 0.7, Y: cell}, + {X: 0, Y: cell}, + } + g.AddPolygon(points, false) + + case 7: + // Shape 7: Small triangle + g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 3, false) + + case 8: + // Shape 8: Composite shape + g.AddRectangle(0, 0, cell, cell/2, false) + g.AddRectangle(0, cell/2, cell/2, cell/2, false) + g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 1, false) + + case 9: + // Shape 9: Rectangle with rectangular cutout + inner := cell * 0.14 + var outer float64 + if cell < 4 { + outer = 1 + } else if cell < 6 { + outer = 2 + } else { + outer = math.Floor(cell * 0.35) + } + + if cell >= 8 { + inner = math.Floor(inner) + } + + g.AddRectangle(0, 0, cell, cell, false) + g.AddRectangle(outer, outer, cell-outer-inner, cell-outer-inner, true) + + case 10: + // Shape 10: Rectangle with circular cutout + inner := cell * 0.12 + outer := inner * 3 + + g.AddRectangle(0, 0, cell, cell, false) + g.AddCircle(outer, outer, cell-inner-outer, true) + + case 11: + // Shape 11: Small triangle (same as 7) + g.AddTriangle(cell/2, cell/2, cell/2, cell/2, 3, false) + + case 12: + // Shape 12: Rectangle with rhombus cutout + m := cell * 0.25 + g.AddRectangle(0, 0, cell, cell, false) + g.AddRhombus(m, m, cell-m, cell-m, true) + + case 13: + // Shape 13: Large circle (only for position 0) + if positionIndex == 0 { + m := cell * 0.4 + w := cell * 1.2 + g.AddCircle(m, m, w, false) + } + } +} + +// RenderOuterShape renders one of the 4 distinct outer shape patterns +func RenderOuterShape(g *Graphics, shapeIndex int, cell float64) { + index := shapeIndex % 4 + + switch index { + case 0: + // Shape 0: Triangle + g.AddTriangle(0, 0, cell, cell, 0, false) + + case 1: + // Shape 1: Triangle (different orientation) + g.AddTriangle(0, cell/2, cell, cell/2, 0, false) + + case 2: + // Shape 2: Rhombus + g.AddRhombus(0, 0, cell, cell, false) + + case 3: + // Shape 3: Circle + m := cell / 6 + g.AddCircle(m, m, cell-2*m, false) + } +} \ No newline at end of file diff --git a/internal/engine/shapes_test.go b/internal/engine/shapes_test.go new file mode 100644 index 0000000..2612a85 --- /dev/null +++ b/internal/engine/shapes_test.go @@ -0,0 +1,257 @@ +package engine + +import ( + "testing" +) + +// MockRenderer implements the Renderer interface for testing +type MockRenderer struct { + Polygons [][]Point + Circles []MockCircle +} + +type MockCircle struct { + TopLeft Point + Size float64 + Invert bool +} + +func (m *MockRenderer) AddPolygon(points []Point) { + m.Polygons = append(m.Polygons, points) +} + +func (m *MockRenderer) AddCircle(topLeft Point, size float64, invert bool) { + m.Circles = append(m.Circles, MockCircle{ + TopLeft: topLeft, + Size: size, + Invert: invert, + }) +} + +func (m *MockRenderer) Reset() { + m.Polygons = nil + m.Circles = nil +} + +func TestGraphicsAddRectangle(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + + g.AddRectangle(10, 20, 30, 40, false) + + if len(mock.Polygons) != 1 { + t.Errorf("Expected 1 polygon, got %d", len(mock.Polygons)) + return + } + + expected := []Point{ + {X: 10, Y: 20}, + {X: 40, Y: 20}, + {X: 40, Y: 60}, + {X: 10, Y: 60}, + } + + polygon := mock.Polygons[0] + if len(polygon) != len(expected) { + t.Errorf("Expected %d points, got %d", len(expected), len(polygon)) + return + } + + for i, point := range expected { + if polygon[i].X != point.X || polygon[i].Y != point.Y { + t.Errorf("Point %d: expected (%f, %f), got (%f, %f)", + i, point.X, point.Y, polygon[i].X, polygon[i].Y) + } + } +} + +func TestGraphicsAddCircle(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + + g.AddCircle(10, 20, 30, false) + + if len(mock.Circles) != 1 { + t.Errorf("Expected 1 circle, got %d", len(mock.Circles)) + return + } + + circle := mock.Circles[0] + expectedTopLeft := Point{X: 10, Y: 20} + expectedSize := float64(30) + + if circle.TopLeft.X != expectedTopLeft.X || circle.TopLeft.Y != expectedTopLeft.Y { + t.Errorf("Expected top-left (%f, %f), got (%f, %f)", + expectedTopLeft.X, expectedTopLeft.Y, circle.TopLeft.X, circle.TopLeft.Y) + } + + if circle.Size != expectedSize { + t.Errorf("Expected size %f, got %f", expectedSize, circle.Size) + } + + if circle.Invert != false { + t.Errorf("Expected invert false, got %t", circle.Invert) + } +} + +func TestGraphicsAddRhombus(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + + g.AddRhombus(0, 0, 20, 30, false) + + if len(mock.Polygons) != 1 { + t.Errorf("Expected 1 polygon, got %d", len(mock.Polygons)) + return + } + + expected := []Point{ + {X: 10, Y: 0}, // top + {X: 20, Y: 15}, // right + {X: 10, Y: 30}, // bottom + {X: 0, Y: 15}, // left + } + + polygon := mock.Polygons[0] + if len(polygon) != len(expected) { + t.Errorf("Expected %d points, got %d", len(expected), len(polygon)) + return + } + + for i, point := range expected { + if polygon[i].X != point.X || polygon[i].Y != point.Y { + t.Errorf("Point %d: expected (%f, %f), got (%f, %f)", + i, point.X, point.Y, polygon[i].X, polygon[i].Y) + } + } +} + +func TestRenderCenterShape(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + cell := float64(60) + + // Test each center shape + for i := 0; i < 14; i++ { + mock.Reset() + RenderCenterShape(g, i, cell, 0) + + // Verify that some drawing commands were issued + if len(mock.Polygons) == 0 && len(mock.Circles) == 0 { + // Shape 13 at position != 0 doesn't draw anything, which is expected + if i == 13 { + continue + } + t.Errorf("Shape %d: expected some drawing commands, got none", i) + } + } +} + +func TestRenderCenterShapeSpecific(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + cell := float64(60) + + // Test shape 2 (rectangle) + mock.Reset() + RenderCenterShape(g, 2, cell, 0) + + if len(mock.Polygons) != 1 { + t.Errorf("Shape 2: expected 1 polygon, got %d", len(mock.Polygons)) + } + + // Test shape 4 (circle) + mock.Reset() + RenderCenterShape(g, 4, cell, 0) + + if len(mock.Circles) != 1 { + t.Errorf("Shape 4: expected 1 circle, got %d", len(mock.Circles)) + } + + // Test shape 13 at position 0 (should draw) + mock.Reset() + RenderCenterShape(g, 13, cell, 0) + + if len(mock.Circles) != 1 { + t.Errorf("Shape 13 at position 0: expected 1 circle, got %d", len(mock.Circles)) + } + + // Test shape 13 at position 1 (should not draw) + mock.Reset() + RenderCenterShape(g, 13, cell, 1) + + if len(mock.Circles) != 0 { + t.Errorf("Shape 13 at position 1: expected 0 circles, got %d", len(mock.Circles)) + } +} + +func TestRenderOuterShape(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + cell := float64(60) + + // Test each outer shape + for i := 0; i < 4; i++ { + mock.Reset() + RenderOuterShape(g, i, cell) + + // Verify that some drawing commands were issued + if len(mock.Polygons) == 0 && len(mock.Circles) == 0 { + t.Errorf("Outer shape %d: expected some drawing commands, got none", i) + } + } +} + +func TestRenderOuterShapeSpecific(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + cell := float64(60) + + // Test outer shape 2 (rhombus) + mock.Reset() + RenderOuterShape(g, 2, cell) + + if len(mock.Polygons) != 1 { + t.Errorf("Outer shape 2: expected 1 polygon, got %d", len(mock.Polygons)) + } + + // Test outer shape 3 (circle) + mock.Reset() + RenderOuterShape(g, 3, cell) + + if len(mock.Circles) != 1 { + t.Errorf("Outer shape 3: expected 1 circle, got %d", len(mock.Circles)) + } +} + +func TestShapeIndexModulo(t *testing.T) { + mock := &MockRenderer{} + g := NewGraphics(mock) + cell := float64(60) + + // Test that shape indices wrap around correctly + mock.Reset() + RenderCenterShape(g, 0, cell, 0) + polygonsShape0 := len(mock.Polygons) + circlesShape0 := len(mock.Circles) + + mock.Reset() + RenderCenterShape(g, 14, cell, 0) // Should be same as shape 0 + + if len(mock.Polygons) != polygonsShape0 || len(mock.Circles) != circlesShape0 { + t.Errorf("Shape 14 should be equivalent to shape 0") + } + + // Test outer shapes + mock.Reset() + RenderOuterShape(g, 0, cell) + polygonsOuter0 := len(mock.Polygons) + circlesOuter0 := len(mock.Circles) + + mock.Reset() + RenderOuterShape(g, 4, cell) // Should be same as outer shape 0 + + if len(mock.Polygons) != polygonsOuter0 || len(mock.Circles) != circlesOuter0 { + t.Errorf("Outer shape 4 should be equivalent to outer shape 0") + } +} \ No newline at end of file diff --git a/internal/engine/transform.go b/internal/engine/transform.go new file mode 100644 index 0000000..ee89c4e --- /dev/null +++ b/internal/engine/transform.go @@ -0,0 +1,103 @@ +package engine + +import "math" + +// Matrix represents a 2D transformation matrix in the form: +// | A C E | +// | B D F | +// | 0 0 1 | +type Matrix struct { + A, B, C, D, E, F float64 +} + +// NewIdentityMatrix creates an identity matrix +func NewIdentityMatrix() Matrix { + return Matrix{ + A: 1, B: 0, C: 0, + D: 1, E: 0, F: 0, + } +} + +// Translate creates a translation matrix +func Translate(x, y float64) Matrix { + return Matrix{ + A: 1, B: 0, C: 0, + D: 1, E: x, F: y, + } +} + +// Rotate creates a rotation matrix for the given angle in radians +func Rotate(angle float64) Matrix { + cos := math.Cos(angle) + sin := math.Sin(angle) + return Matrix{ + A: cos, B: sin, C: -sin, + D: cos, E: 0, F: 0, + } +} + +// Scale creates a scaling matrix +func Scale(sx, sy float64) Matrix { + return Matrix{ + A: sx, B: 0, C: 0, + D: sy, E: 0, F: 0, + } +} + +// Multiply multiplies two matrices +func (m Matrix) Multiply(other Matrix) Matrix { + return Matrix{ + A: m.A*other.A + m.C*other.B, + B: m.B*other.A + m.D*other.B, + C: m.A*other.C + m.C*other.D, + D: m.B*other.C + m.D*other.D, + E: m.A*other.E + m.C*other.F + m.E, + F: m.B*other.E + m.D*other.F + m.F, + } +} + +// Transform represents a geometric transformation +type Transform struct { + x, y, size float64 + rotation int // 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad +} + +// NewTransform creates a new Transform +func NewTransform(x, y, size float64, rotation int) Transform { + return Transform{ + x: x, + y: y, + size: size, + rotation: rotation, + } +} + +// TransformIconPoint transforms a point based on the translation and rotation specification +// w and h represent the width and height of the transformed rectangle for proper corner positioning +func (t Transform) TransformIconPoint(x, y, w, h float64) Point { + right := t.x + t.size + bottom := t.y + t.size + rotation := t.rotation % 4 + + switch rotation { + case 1: // 90 degrees + return Point{X: right - y - h, Y: t.y + x} + case 2: // 180 degrees + return Point{X: right - x - w, Y: bottom - y - h} + case 3: // 270 degrees + return Point{X: t.x + y, Y: bottom - x - w} + default: // 0 degrees + return Point{X: t.x + x, Y: t.y + y} + } +} + +// ApplyTransform applies a transformation matrix to a point +func ApplyTransform(point Point, matrix Matrix) Point { + return Point{ + X: matrix.A*point.X + matrix.C*point.Y + matrix.E, + Y: matrix.B*point.X + matrix.D*point.Y + matrix.F, + } +} + +// NoTransform represents an identity transformation +var NoTransform = NewTransform(0, 0, 0, 0) \ No newline at end of file diff --git a/internal/engine/transform_test.go b/internal/engine/transform_test.go new file mode 100644 index 0000000..5a36397 --- /dev/null +++ b/internal/engine/transform_test.go @@ -0,0 +1,182 @@ +package engine + +import ( + "math" + "testing" +) + +func TestNewIdentityMatrix(t *testing.T) { + m := NewIdentityMatrix() + expected := Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0} + if m != expected { + t.Errorf("NewIdentityMatrix() = %v, want %v", m, expected) + } +} + +func TestTranslate(t *testing.T) { + tests := []struct { + x, y float64 + expected Matrix + }{ + {10, 20, Matrix{A: 1, B: 0, C: 0, D: 1, E: 10, F: 20}}, + {0, 0, Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0}}, + {-5, 15, Matrix{A: 1, B: 0, C: 0, D: 1, E: -5, F: 15}}, + } + + for _, tt := range tests { + result := Translate(tt.x, tt.y) + if result != tt.expected { + t.Errorf("Translate(%v, %v) = %v, want %v", tt.x, tt.y, result, tt.expected) + } + } +} + +func TestRotate(t *testing.T) { + tests := []struct { + angle float64 + expected Matrix + }{ + {0, Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0}}, + {math.Pi / 2, Matrix{A: 0, B: 1, C: -1, D: 0, E: 0, F: 0}}, + {math.Pi, Matrix{A: -1, B: 0, C: 0, D: -1, E: 0, F: 0}}, + {3 * math.Pi / 2, Matrix{A: 0, B: -1, C: 1, D: 0, E: 0, F: 0}}, + } + + for _, tt := range tests { + result := Rotate(tt.angle) + // Use approximate equality for floating point comparison + if !approximatelyEqual(result.A, tt.expected.A) || + !approximatelyEqual(result.B, tt.expected.B) || + !approximatelyEqual(result.C, tt.expected.C) || + !approximatelyEqual(result.D, tt.expected.D) || + !approximatelyEqual(result.E, tt.expected.E) || + !approximatelyEqual(result.F, tt.expected.F) { + t.Errorf("Rotate(%v) = %v, want %v", tt.angle, result, tt.expected) + } + } +} + +func TestScale(t *testing.T) { + tests := []struct { + sx, sy float64 + expected Matrix + }{ + {1, 1, Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0}}, + {2, 3, Matrix{A: 2, B: 0, C: 0, D: 3, E: 0, F: 0}}, + {0.5, 2, Matrix{A: 0.5, B: 0, C: 0, D: 2, E: 0, F: 0}}, + } + + for _, tt := range tests { + result := Scale(tt.sx, tt.sy) + if result != tt.expected { + t.Errorf("Scale(%v, %v) = %v, want %v", tt.sx, tt.sy, result, tt.expected) + } + } +} + +func TestMatrixMultiply(t *testing.T) { + // Test identity multiplication + identity := NewIdentityMatrix() + translate := Translate(10, 20) + result := identity.Multiply(translate) + if result != translate { + t.Errorf("Identity * Translate = %v, want %v", result, translate) + } + + // Test translation composition + t1 := Translate(10, 20) + t2 := Translate(5, 10) + result = t1.Multiply(t2) + expected := Translate(15, 30) + if result != expected { + t.Errorf("Translate(10,20) * Translate(5,10) = %v, want %v", result, expected) + } + + // Test scale composition + s1 := Scale(2, 3) + s2 := Scale(0.5, 0.5) + result = s1.Multiply(s2) + expected = Scale(1, 1.5) + if result != expected { + t.Errorf("Scale(2,3) * Scale(0.5,0.5) = %v, want %v", result, expected) + } +} + +func TestApplyTransform(t *testing.T) { + tests := []struct { + point Point + matrix Matrix + expected Point + }{ + {Point{X: 0, Y: 0}, NewIdentityMatrix(), Point{X: 0, Y: 0}}, + {Point{X: 10, Y: 20}, Translate(5, 10), Point{X: 15, Y: 30}}, + {Point{X: 1, Y: 0}, Scale(3, 2), Point{X: 3, Y: 0}}, + {Point{X: 0, Y: 1}, Scale(3, 2), Point{X: 0, Y: 2}}, + } + + for _, tt := range tests { + result := ApplyTransform(tt.point, tt.matrix) + if !approximatelyEqual(result.X, tt.expected.X) || !approximatelyEqual(result.Y, tt.expected.Y) { + t.Errorf("ApplyTransform(%v, %v) = %v, want %v", tt.point, tt.matrix, result, tt.expected) + } + } +} + +func TestNewTransform(t *testing.T) { + transform := NewTransform(10, 20, 100, 1) + if transform.x != 10 || transform.y != 20 || transform.size != 100 || transform.rotation != 1 { + t.Errorf("NewTransform(10, 20, 100, 1) = %v, want {x:10, y:20, size:100, rotation:1}", transform) + } +} + +func TestTransformIconPoint(t *testing.T) { + tests := []struct { + transform Transform + x, y, w, h float64 + expected Point + }{ + // No rotation (0 degrees) + {NewTransform(0, 0, 100, 0), 10, 20, 5, 5, Point{X: 10, Y: 20}}, + {NewTransform(10, 20, 100, 0), 5, 10, 0, 0, Point{X: 15, Y: 30}}, + + // 90 degrees rotation + {NewTransform(0, 0, 100, 1), 10, 20, 5, 5, Point{X: 75, Y: 10}}, + + // 180 degrees rotation + {NewTransform(0, 0, 100, 2), 10, 20, 5, 5, Point{X: 85, Y: 75}}, + + // 270 degrees rotation + {NewTransform(0, 0, 100, 3), 10, 20, 5, 5, Point{X: 20, Y: 85}}, + + // Test rotation normalization (rotation > 3) + {NewTransform(0, 0, 100, 4), 10, 20, 0, 0, Point{X: 10, Y: 20}}, // Same as rotation 0 + {NewTransform(0, 0, 100, 5), 10, 20, 5, 5, Point{X: 75, Y: 10}}, // Same as rotation 1 + } + + for _, tt := range tests { + result := tt.transform.TransformIconPoint(tt.x, tt.y, tt.w, tt.h) + if !approximatelyEqual(result.X, tt.expected.X) || !approximatelyEqual(result.Y, tt.expected.Y) { + t.Errorf("Transform(%v).TransformIconPoint(%v, %v, %v, %v) = %v, want %v", + tt.transform, tt.x, tt.y, tt.w, tt.h, result, tt.expected) + } + } +} + +func TestNoTransform(t *testing.T) { + if NoTransform.x != 0 || NoTransform.y != 0 || NoTransform.size != 0 || NoTransform.rotation != 0 { + t.Errorf("NoTransform should be {x:0, y:0, size:0, rotation:0}, got %v", NoTransform) + } + + // Test that NoTransform doesn't change points + point := Point{X: 10, Y: 20} + result := NoTransform.TransformIconPoint(point.X, point.Y, 0, 0) + if result != point { + t.Errorf("NoTransform should not change point %v, got %v", point, result) + } +} + +// approximatelyEqual checks if two float64 values are approximately equal +func approximatelyEqual(a, b float64) bool { + const epsilon = 1e-9 + return math.Abs(a-b) < epsilon +} \ No newline at end of file diff --git a/internal/renderer/integration_test.go b/internal/renderer/integration_test.go new file mode 100644 index 0000000..8afc5c7 --- /dev/null +++ b/internal/renderer/integration_test.go @@ -0,0 +1,566 @@ +package renderer + +import ( + "bytes" + "crypto/sha1" + "fmt" + "image/png" + "testing" + + "github.com/kevin/go-jdenticon/internal/engine" +) + +// TestPNGRenderer_VisualRegression tests that PNG output matches expected characteristics +func TestPNGRenderer_VisualRegression(t *testing.T) { + testCases := []struct { + name string + size int + bg string + bgOp float64 + shapes []testShape + checksum string // Expected checksum of PNG data + }{ + { + name: "simple_red_square", + size: 50, + bg: "#ffffff", + bgOp: 1.0, + shapes: []testShape{ + { + color: "#ff0000", + polygons: [][]engine.Point{ + { + {X: 10, Y: 10}, + {X: 40, Y: 10}, + {X: 40, Y: 40}, + {X: 10, Y: 40}, + }, + }, + }, + }, + }, + { + name: "blue_circle", + size: 60, + bg: "#f0f0f0", + bgOp: 1.0, + shapes: []testShape{ + { + color: "#0000ff", + circles: []testCircle{ + {center: engine.Point{X: 30, Y: 30}, radius: 20, invert: false}, + }, + }, + }, + }, + { + name: "transparent_background", + size: 40, + bg: "#000000", + bgOp: 0.0, + shapes: []testShape{ + { + color: "#00ff00", + polygons: [][]engine.Point{ + { + {X: 5, Y: 5}, + {X: 35, Y: 5}, + {X: 20, Y: 35}, + }, + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + renderer := NewPNGRenderer(tc.size) + + if tc.bgOp > 0 { + renderer.SetBackground(tc.bg, tc.bgOp) + } + + for _, shape := range tc.shapes { + renderer.BeginShape(shape.color) + + for _, points := range shape.polygons { + renderer.AddPolygon(points) + } + + for _, circle := range shape.circles { + renderer.AddCircle(circle.center, circle.radius, circle.invert) + } + + renderer.EndShape() + } + + pngData := renderer.ToPNG() + + // Verify PNG is valid + reader := bytes.NewReader(pngData) + img, err := png.Decode(reader) + if err != nil { + t.Fatalf("Failed to decode PNG: %v", err) + } + + bounds := img.Bounds() + if bounds.Max.X != tc.size || bounds.Max.Y != tc.size { + t.Errorf("Image size = %dx%d, want %dx%d", + bounds.Max.X, bounds.Max.Y, tc.size, tc.size) + } + + // Calculate checksum for regression testing + checksum := fmt.Sprintf("%x", sha1.Sum(pngData)) + t.Logf("PNG checksum for %s: %s", tc.name, checksum) + + // Basic size validation + if len(pngData) < 100 { + t.Errorf("PNG data too small: %d bytes", len(pngData)) + } + }) + } +} + +// testShape represents a shape to be drawn for testing +type testShape struct { + color string + polygons [][]engine.Point + circles []testCircle +} + +type testCircle struct { + center engine.Point + radius float64 + invert bool +} + +// TestPNGRenderer_ComplexIcon tests rendering a more complex icon pattern +func TestPNGRenderer_ComplexIcon(t *testing.T) { + renderer := NewPNGRenderer(100) + renderer.SetBackground("#f8f8f8", 1.0) + + // Simulate a complex icon with multiple shapes and colors + // This mimics the patterns that would be generated by the actual jdenticon algorithm + + // Outer shapes (corners) + renderer.BeginShape("#3f7cac") + // Top-left triangle + renderer.AddPolygon([]engine.Point{ + {X: 0, Y: 0}, {X: 25, Y: 0}, {X: 0, Y: 25}, + }) + // Top-right triangle + renderer.AddPolygon([]engine.Point{ + {X: 75, Y: 0}, {X: 100, Y: 0}, {X: 100, Y: 25}, + }) + // Bottom-left triangle + renderer.AddPolygon([]engine.Point{ + {X: 0, Y: 75}, {X: 0, Y: 100}, {X: 25, Y: 100}, + }) + // Bottom-right triangle + renderer.AddPolygon([]engine.Point{ + {X: 75, Y: 100}, {X: 100, Y: 100}, {X: 100, Y: 75}, + }) + renderer.EndShape() + + // Middle shapes + renderer.BeginShape("#95b3d0") + // Left rhombus + renderer.AddPolygon([]engine.Point{ + {X: 12.5, Y: 37.5}, {X: 25, Y: 50}, {X: 12.5, Y: 62.5}, {X: 0, Y: 50}, + }) + // Right rhombus + renderer.AddPolygon([]engine.Point{ + {X: 87.5, Y: 37.5}, {X: 100, Y: 50}, {X: 87.5, Y: 62.5}, {X: 75, Y: 50}, + }) + // Top rhombus + renderer.AddPolygon([]engine.Point{ + {X: 37.5, Y: 12.5}, {X: 50, Y: 0}, {X: 62.5, Y: 12.5}, {X: 50, Y: 25}, + }) + // Bottom rhombus + renderer.AddPolygon([]engine.Point{ + {X: 37.5, Y: 87.5}, {X: 50, Y: 75}, {X: 62.5, Y: 87.5}, {X: 50, Y: 100}, + }) + renderer.EndShape() + + // Center shape + renderer.BeginShape("#2f5f8f") + renderer.AddCircle(engine.Point{X: 50, Y: 50}, 15, false) + renderer.EndShape() + + pngData := renderer.ToPNG() + + // Verify the complex icon renders successfully + reader := bytes.NewReader(pngData) + img, err := png.Decode(reader) + if err != nil { + t.Fatalf("Failed to decode complex PNG: %v", err) + } + + bounds := img.Bounds() + if bounds.Max.X != 100 || bounds.Max.Y != 100 { + t.Errorf("Complex icon size = %dx%d, want 100x100", bounds.Max.X, bounds.Max.Y) + } + + // Ensure PNG is reasonable size (not too large, not too small) + if len(pngData) < 500 || len(pngData) > 50000 { + t.Errorf("Complex PNG size %d bytes seems unreasonable", len(pngData)) + } + + t.Logf("Complex icon PNG size: %d bytes", len(pngData)) +} + +// TestRendererInterface_Consistency tests that both SVG and PNG renderers +// implement the Renderer interface consistently +func TestRendererInterface_Consistency(t *testing.T) { + testCases := []struct { + name string + size int + bg string + bgOp float64 + testFunc func(Renderer) + }{ + { + name: "basic_shapes", + size: 100, + bg: "#ffffff", + bgOp: 1.0, + testFunc: func(r Renderer) { + r.BeginShape("#ff0000") + r.AddRectangle(10, 10, 30, 30) + r.EndShape() + + r.BeginShape("#00ff00") + r.AddCircle(engine.Point{X: 70, Y: 70}, 15, false) + r.EndShape() + + r.BeginShape("#0000ff") + r.AddTriangle( + engine.Point{X: 20, Y: 80}, + engine.Point{X: 40, Y: 80}, + engine.Point{X: 30, Y: 60}, + ) + r.EndShape() + }, + }, + { + name: "complex_polygon", + size: 80, + bg: "#f8f8f8", + bgOp: 0.8, + testFunc: func(r Renderer) { + r.BeginShape("#8B4513") + // Star shape + points := []engine.Point{ + {X: 40, Y: 10}, + {X: 45, Y: 25}, + {X: 60, Y: 25}, + {X: 50, Y: 35}, + {X: 55, Y: 50}, + {X: 40, Y: 40}, + {X: 25, Y: 50}, + {X: 30, Y: 35}, + {X: 20, Y: 25}, + {X: 35, Y: 25}, + } + r.AddPolygon(points) + r.EndShape() + }, + }, + { + name: "primitive_drawing", + size: 60, + bg: "", + bgOp: 0, + testFunc: func(r Renderer) { + r.BeginShape("#FF6B35") + r.MoveTo(10, 10) + r.LineTo(50, 10) + r.LineTo(50, 50) + r.CurveTo(45, 55, 35, 55, 30, 50) + r.LineTo(10, 50) + r.ClosePath() + r.Fill("#FF6B35") + r.EndShape() + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Test with PNG renderer + t.Run("png", func(t *testing.T) { + renderer := NewPNGRenderer(tc.size) + if tc.bgOp > 0 { + renderer.SetBackground(tc.bg, tc.bgOp) + } + + tc.testFunc(renderer) + + // Verify PNG output + pngData := renderer.ToPNG() + if len(pngData) == 0 { + t.Error("PNG renderer produced no data") + } + + reader := bytes.NewReader(pngData) + img, err := png.Decode(reader) + if err != nil { + t.Fatalf("PNG decode failed: %v", err) + } + + bounds := img.Bounds() + if bounds.Max.X != tc.size || bounds.Max.Y != tc.size { + t.Errorf("PNG size = %dx%d, want %dx%d", + bounds.Max.X, bounds.Max.Y, tc.size, tc.size) + } + }) + + // Test with SVG renderer + t.Run("svg", func(t *testing.T) { + renderer := NewSVGRenderer(tc.size) + if tc.bgOp > 0 { + renderer.SetBackground(tc.bg, tc.bgOp) + } + + tc.testFunc(renderer) + + // Verify SVG output + svgData := renderer.ToSVG() + if len(svgData) == 0 { + t.Error("SVG renderer produced no data") + } + + // Basic SVG validation + if !bytes.Contains([]byte(svgData), []byte("")) { + t.Error("SVG output missing closing tag") + } + + // Check size attributes + expectedWidth := fmt.Sprintf(`width="%d"`, tc.size) + expectedHeight := fmt.Sprintf(`height="%d"`, tc.size) + if !bytes.Contains([]byte(svgData), []byte(expectedWidth)) { + t.Errorf("SVG missing width attribute: %s", expectedWidth) + } + if !bytes.Contains([]byte(svgData), []byte(expectedHeight)) { + t.Errorf("SVG missing height attribute: %s", expectedHeight) + } + }) + }) + } +} + +// TestRendererInterface_BaseRendererMethods tests that renderers properly use BaseRenderer methods +func TestRendererInterface_BaseRendererMethods(t *testing.T) { + renderers := []struct { + name string + renderer Renderer + }{ + {"svg", NewSVGRenderer(50)}, + {"png", NewPNGRenderer(50)}, + } + + for _, r := range renderers { + t.Run(r.name, func(t *testing.T) { + renderer := r.renderer + + // Test size getter + if renderer.GetSize() != 50 { + t.Errorf("GetSize() = %d, want 50", renderer.GetSize()) + } + + // Test background setting + renderer.SetBackground("#123456", 0.75) + if svgRenderer, ok := renderer.(*SVGRenderer); ok { + if bg, op := svgRenderer.GetBackground(); bg != "#123456" || op != 0.75 { + t.Errorf("SVG GetBackground() = %s, %f, want #123456, 0.75", bg, op) + } + } + if pngRenderer, ok := renderer.(*PNGRenderer); ok { + if bg, op := pngRenderer.GetBackground(); bg != "#123456" || op != 0.75 { + t.Errorf("PNG GetBackground() = %s, %f, want #123456, 0.75", bg, op) + } + } + + // Test shape management + renderer.BeginShape("#ff0000") + if svgRenderer, ok := renderer.(*SVGRenderer); ok { + if color := svgRenderer.GetCurrentColor(); color != "#ff0000" { + t.Errorf("SVG GetCurrentColor() = %s, want #ff0000", color) + } + } + if pngRenderer, ok := renderer.(*PNGRenderer); ok { + if color := pngRenderer.GetCurrentColor(); color != "#ff0000" { + t.Errorf("PNG GetCurrentColor() = %s, want #ff0000", color) + } + } + + // Test clearing + renderer.Clear() + if svgRenderer, ok := renderer.(*SVGRenderer); ok { + if color := svgRenderer.GetCurrentColor(); color != "" { + t.Errorf("SVG GetCurrentColor() after Clear() = %s, want empty", color) + } + } + if pngRenderer, ok := renderer.(*PNGRenderer); ok { + if color := pngRenderer.GetCurrentColor(); color != "" { + t.Errorf("PNG GetCurrentColor() after Clear() = %s, want empty", color) + } + } + }) + } +} + +// TestRendererInterface_CompatibilityWithJavaScript tests patterns from JavaScript reference +func TestRendererInterface_CompatibilityWithJavaScript(t *testing.T) { + // This test replicates patterns that would be used by the JavaScript jdenticon library + // to ensure our Go implementation is compatible + + testJavaScriptPattern := func(r Renderer) { + // Simulate the JavaScript renderer usage pattern + r.SetBackground("#f0f0f0", 1.0) + + // Pattern similar to what iconGenerator.js would create + shapes := []struct { + color string + actions func() + }{ + { + color: "#4a90e2", + actions: func() { + // Corner triangles (like JavaScript implementation) + r.AddPolygon([]engine.Point{ + {X: 0, Y: 0}, {X: 20, Y: 0}, {X: 0, Y: 20}, + }) + r.AddPolygon([]engine.Point{ + {X: 80, Y: 0}, {X: 100, Y: 0}, {X: 100, Y: 20}, + }) + r.AddPolygon([]engine.Point{ + {X: 0, Y: 80}, {X: 0, Y: 100}, {X: 20, Y: 100}, + }) + r.AddPolygon([]engine.Point{ + {X: 80, Y: 100}, {X: 100, Y: 100}, {X: 100, Y: 80}, + }) + }, + }, + { + color: "#7fc383", + actions: func() { + // Center circle + r.AddCircle(engine.Point{X: 50, Y: 50}, 25, false) + }, + }, + { + color: "#e94b3c", + actions: func() { + // Side rhombs + r.AddPolygon([]engine.Point{ + {X: 25, Y: 37.5}, {X: 37.5, Y: 50}, {X: 25, Y: 62.5}, {X: 12.5, Y: 50}, + }) + r.AddPolygon([]engine.Point{ + {X: 75, Y: 37.5}, {X: 87.5, Y: 50}, {X: 75, Y: 62.5}, {X: 62.5, Y: 50}, + }) + }, + }, + } + + for _, shape := range shapes { + r.BeginShape(shape.color) + shape.actions() + r.EndShape() + } + } + + t.Run("svg_javascript_pattern", func(t *testing.T) { + renderer := NewSVGRenderer(100) + testJavaScriptPattern(renderer) + + svgData := renderer.ToSVG() + + // Should contain multiple paths with different colors + for _, color := range []string{"#4a90e2", "#7fc383", "#e94b3c"} { + expected := fmt.Sprintf(`fill="%s"`, color) + if !bytes.Contains([]byte(svgData), []byte(expected)) { + t.Errorf("SVG missing expected color: %s", color) + } + } + + // Should contain background + if !bytes.Contains([]byte(svgData), []byte("#f0f0f0")) { + t.Error("SVG missing background color") + } + }) + + t.Run("png_javascript_pattern", func(t *testing.T) { + renderer := NewPNGRenderer(100) + testJavaScriptPattern(renderer) + + pngData := renderer.ToPNG() + + // Verify valid PNG + reader := bytes.NewReader(pngData) + img, err := png.Decode(reader) + if err != nil { + t.Fatalf("PNG decode failed: %v", err) + } + + bounds := img.Bounds() + if bounds.Max.X != 100 || bounds.Max.Y != 100 { + t.Errorf("PNG size = %dx%d, want 100x100", bounds.Max.X, bounds.Max.Y) + } + }) +} + +// TestPNGRenderer_EdgeCases tests various edge cases +func TestPNGRenderer_EdgeCases(t *testing.T) { + t.Run("very_small_icon", func(t *testing.T) { + renderer := NewPNGRenderer(1) + renderer.BeginShape("#ff0000") + renderer.AddPolygon([]engine.Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}}) + renderer.EndShape() + + pngData := renderer.ToPNG() + if len(pngData) == 0 { + t.Error("1x1 PNG should generate data") + } + }) + + t.Run("large_icon", func(t *testing.T) { + renderer := NewPNGRenderer(512) + renderer.SetBackground("#ffffff", 1.0) + renderer.BeginShape("#000000") + renderer.AddCircle(engine.Point{X: 256, Y: 256}, 200, false) + renderer.EndShape() + + pngData := renderer.ToPNG() + if len(pngData) == 0 { + t.Error("512x512 PNG should generate data") + } + + // Large images should compress well due to simple content + t.Logf("512x512 PNG size: %d bytes", len(pngData)) + }) + + t.Run("shapes_outside_bounds", func(t *testing.T) { + renderer := NewPNGRenderer(50) + renderer.BeginShape("#ff0000") + + // Add shapes that extend outside the image bounds + renderer.AddPolygon([]engine.Point{ + {X: -10, Y: -10}, {X: 60, Y: -10}, {X: 60, Y: 60}, {X: -10, Y: 60}, + }) + renderer.AddCircle(engine.Point{X: 25, Y: 25}, 50, false) + renderer.EndShape() + + // Should not panic and should produce valid PNG + pngData := renderer.ToPNG() + reader := bytes.NewReader(pngData) + _, err := png.Decode(reader) + if err != nil { + t.Errorf("Failed to decode PNG with out-of-bounds shapes: %v", err) + } + }) +} diff --git a/internal/renderer/png.go b/internal/renderer/png.go new file mode 100644 index 0000000..1deb573 --- /dev/null +++ b/internal/renderer/png.go @@ -0,0 +1,292 @@ +package renderer + +import ( + "bytes" + "image" + "image/color" + "image/draw" + "image/png" + "math" + "strconv" + "strings" + "sync" + + "github.com/kevin/go-jdenticon/internal/engine" +) + +// PNGRenderer implements the Renderer interface for PNG output +type PNGRenderer struct { + *BaseRenderer + img *image.RGBA + currentColor color.RGBA + background color.RGBA + hasBackground bool + mu sync.RWMutex // For thread safety in concurrent generation +} + +// bufferPool provides buffer pooling for efficient PNG generation +var bufferPool = sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, +} + +// NewPNGRenderer creates a new PNG renderer with the specified icon size +func NewPNGRenderer(iconSize int) *PNGRenderer { + return &PNGRenderer{ + BaseRenderer: NewBaseRenderer(iconSize), + img: image.NewRGBA(image.Rect(0, 0, iconSize, iconSize)), + } +} + +// SetBackground sets the background color and opacity +func (r *PNGRenderer) SetBackground(fillColor string, opacity float64) { + r.mu.Lock() + defer r.mu.Unlock() + + r.BaseRenderer.SetBackground(fillColor, opacity) + r.background = parseColor(fillColor, opacity) + r.hasBackground = opacity > 0 + + if r.hasBackground { + // Fill the entire image with background color + draw.Draw(r.img, r.img.Bounds(), &image.Uniform{r.background}, image.Point{}, draw.Src) + } +} + +// BeginShape marks the beginning of a new shape with the specified color +func (r *PNGRenderer) BeginShape(fillColor string) { + r.mu.Lock() + defer r.mu.Unlock() + + r.BaseRenderer.BeginShape(fillColor) + r.currentColor = parseColor(fillColor, 1.0) +} + +// EndShape marks the end of the currently drawn shape +func (r *PNGRenderer) EndShape() { + // No action needed for PNG - shapes are drawn immediately +} + +// AddPolygon adds a polygon with the current fill color to the image +func (r *PNGRenderer) AddPolygon(points []engine.Point) { + if len(points) == 0 { + return + } + + r.mu.Lock() + defer r.mu.Unlock() + + // Convert engine.Point to image coordinates + imagePoints := make([]image.Point, len(points)) + for i, p := range points { + imagePoints[i] = image.Point{ + X: int(math.Round(p.X)), + Y: int(math.Round(p.Y)), + } + } + + // Fill polygon using scanline algorithm + r.fillPolygon(imagePoints) +} + +// AddCircle adds a circle with the current fill color to the image +func (r *PNGRenderer) AddCircle(topLeft engine.Point, size float64, invert bool) { + r.mu.Lock() + defer r.mu.Unlock() + + radius := size / 2 + centerX := int(math.Round(topLeft.X + radius)) + centerY := int(math.Round(topLeft.Y + radius)) + radiusInt := int(math.Round(radius)) + + // Use Bresenham's circle algorithm for anti-aliased circle drawing + r.drawCircle(centerX, centerY, radiusInt, invert) +} + +// ToPNG generates the final PNG image data +func (r *PNGRenderer) ToPNG() []byte { + r.mu.RLock() + defer r.mu.RUnlock() + + buf := bufferPool.Get().(*bytes.Buffer) + buf.Reset() + defer bufferPool.Put(buf) + + // Encode to PNG with compression + encoder := &png.Encoder{ + CompressionLevel: png.BestCompression, + } + + if err := encoder.Encode(buf, r.img); err != nil { + return nil + } + + // Return a copy of the buffer data + result := make([]byte, buf.Len()) + copy(result, buf.Bytes()) + return result +} + +// parseColor converts a hex color string to RGBA color +func parseColor(hexColor string, opacity float64) color.RGBA { + // Remove # prefix if present + hexColor = strings.TrimPrefix(hexColor, "#") + + // Default to black if parsing fails + var r, g, b uint8 = 0, 0, 0 + + switch len(hexColor) { + case 3: + // Short form: #RGB -> #RRGGBB + if val, err := strconv.ParseUint(hexColor, 16, 12); err == nil { + r = uint8((val >> 8 & 0xF) * 17) + g = uint8((val >> 4 & 0xF) * 17) + b = uint8((val & 0xF) * 17) + } + case 6: + // Full form: #RRGGBB + if val, err := strconv.ParseUint(hexColor, 16, 24); err == nil { + r = uint8(val >> 16) + g = uint8(val >> 8) + b = uint8(val) + } + case 8: + // With alpha: #RRGGBBAA + if val, err := strconv.ParseUint(hexColor, 16, 32); err == nil { + r = uint8(val >> 24) + g = uint8(val >> 16) + b = uint8(val >> 8) + // Override opacity with alpha from color + opacity = float64(uint8(val)) / 255.0 + } + } + + alpha := uint8(math.Round(opacity * 255)) + return color.RGBA{R: r, G: g, B: b, A: alpha} +} + +// fillPolygon fills a polygon using a scanline algorithm +func (r *PNGRenderer) fillPolygon(points []image.Point) { + if len(points) < 3 { + return + } + + // Find bounding box + minY, maxY := points[0].Y, points[0].Y + for _, p := range points[1:] { + if p.Y < minY { + minY = p.Y + } + if p.Y > maxY { + maxY = p.Y + } + } + + // Ensure bounds are within image + bounds := r.img.Bounds() + if minY < bounds.Min.Y { + minY = bounds.Min.Y + } + if maxY >= bounds.Max.Y { + maxY = bounds.Max.Y - 1 + } + + // For each scanline, find intersections and fill + for y := minY; y <= maxY; y++ { + intersections := r.getIntersections(points, y) + if len(intersections) < 2 { + continue + } + + // Sort intersections and fill between pairs + for i := 0; i < len(intersections); i += 2 { + if i+1 < len(intersections) { + x1, x2 := intersections[i], intersections[i+1] + if x1 > x2 { + x1, x2 = x2, x1 + } + + // Clamp to image bounds + if x1 < bounds.Min.X { + x1 = bounds.Min.X + } + if x2 >= bounds.Max.X { + x2 = bounds.Max.X - 1 + } + + // Fill the horizontal line + for x := x1; x <= x2; x++ { + r.img.SetRGBA(x, y, r.currentColor) + } + } + } + } +} + +// getIntersections finds x-coordinates where a horizontal line intersects polygon edges +func (r *PNGRenderer) getIntersections(points []image.Point, y int) []int { + var intersections []int + n := len(points) + + for i := 0; i < n; i++ { + j := (i + 1) % n + p1, p2 := points[i], points[j] + + // Check if the edge crosses the scanline + if (p1.Y <= y && p2.Y > y) || (p2.Y <= y && p1.Y > y) { + // Calculate intersection x-coordinate + x := p1.X + (y-p1.Y)*(p2.X-p1.X)/(p2.Y-p1.Y) + intersections = append(intersections, x) + } + } + + // Sort intersections + for i := 0; i < len(intersections)-1; i++ { + for j := i + 1; j < len(intersections); j++ { + if intersections[i] > intersections[j] { + intersections[i], intersections[j] = intersections[j], intersections[i] + } + } + } + + return intersections +} + +// drawCircle draws a filled circle using Bresenham's algorithm +func (r *PNGRenderer) drawCircle(centerX, centerY, radius int, invert bool) { + bounds := r.img.Bounds() + + // For filled circle, we'll draw it by filling horizontal lines + for y := -radius; y <= radius; y++ { + actualY := centerY + y + if actualY < bounds.Min.Y || actualY >= bounds.Max.Y { + continue + } + + // Calculate x extent for this y + x := int(math.Sqrt(float64(radius*radius - y*y))) + + x1, x2 := centerX-x, centerX+x + + // Clamp to image bounds + if x1 < bounds.Min.X { + x1 = bounds.Min.X + } + if x2 >= bounds.Max.X { + x2 = bounds.Max.X - 1 + } + + // Fill the horizontal line + for x := x1; x <= x2; x++ { + if invert { + // For inverted circles, we need to punch a hole + // This would typically be handled by a compositing mode + // For now, we'll set to transparent + r.img.SetRGBA(x, actualY, color.RGBA{0, 0, 0, 0}) + } else { + r.img.SetRGBA(x, actualY, r.currentColor) + } + } + } +} diff --git a/internal/renderer/png_test.go b/internal/renderer/png_test.go new file mode 100644 index 0000000..4869426 --- /dev/null +++ b/internal/renderer/png_test.go @@ -0,0 +1,290 @@ +package renderer + +import ( + "bytes" + "image/color" + "image/png" + "testing" + + "github.com/kevin/go-jdenticon/internal/engine" +) + +func TestNewPNGRenderer(t *testing.T) { + renderer := NewPNGRenderer(100) + + if renderer.iconSize != 100 { + t.Errorf("NewPNGRenderer(100).iconSize = %v, want 100", renderer.iconSize) + } + if renderer.img == nil { + t.Error("img should be initialized") + } + if renderer.img.Bounds().Max.X != 100 || renderer.img.Bounds().Max.Y != 100 { + t.Errorf("image bounds = %v, want 100x100", renderer.img.Bounds()) + } +} + +func TestPNGRenderer_SetBackground(t *testing.T) { + renderer := NewPNGRenderer(50) + + renderer.SetBackground("#ff0000", 1.0) + + if !renderer.hasBackground { + t.Error("hasBackground should be true") + } + if renderer.backgroundOp != 1.0 { + t.Errorf("backgroundOp = %v, want 1.0", renderer.backgroundOp) + } + + // Check that background was actually set + expectedColor := color.RGBA{R: 255, G: 0, B: 0, A: 255} + if renderer.background != expectedColor { + t.Errorf("background color = %v, want %v", renderer.background, expectedColor) + } + + // Check that image was filled with background + actualColor := renderer.img.RGBAAt(25, 25) + if actualColor != expectedColor { + t.Errorf("image pixel color = %v, want %v", actualColor, expectedColor) + } +} + +func TestPNGRenderer_SetBackgroundWithOpacity(t *testing.T) { + renderer := NewPNGRenderer(50) + + renderer.SetBackground("#00ff00", 0.5) + + expectedColor := color.RGBA{R: 0, G: 255, B: 0, A: 128} + if renderer.background != expectedColor { + t.Errorf("background color = %v, want %v", renderer.background, expectedColor) + } +} + +func TestPNGRenderer_BeginEndShape(t *testing.T) { + renderer := NewPNGRenderer(100) + + renderer.BeginShape("#0000ff") + expectedColor := color.RGBA{R: 0, G: 0, B: 255, A: 255} + if renderer.currentColor != expectedColor { + t.Errorf("currentColor = %v, want %v", renderer.currentColor, expectedColor) + } + + renderer.EndShape() + // EndShape is a no-op for PNG, just verify it doesn't panic +} + +func TestPNGRenderer_AddPolygon(t *testing.T) { + renderer := NewPNGRenderer(100) + renderer.BeginShape("#ff0000") + + // Create a simple triangle + points := []engine.Point{ + {X: 10, Y: 10}, + {X: 30, Y: 10}, + {X: 20, Y: 30}, + } + + renderer.AddPolygon(points) + + // Check that some pixels in the triangle are red + redColor := color.RGBA{R: 255, G: 0, B: 0, A: 255} + centerPixel := renderer.img.RGBAAt(20, 15) // Should be inside triangle + if centerPixel != redColor { + t.Errorf("triangle center pixel = %v, want %v", centerPixel, redColor) + } + + // Check that pixels outside triangle are not red (should be transparent) + outsidePixel := renderer.img.RGBAAt(5, 5) + if outsidePixel == redColor { + t.Error("pixel outside triangle should not be red") + } +} + +func TestPNGRenderer_AddPolygonEmpty(t *testing.T) { + renderer := NewPNGRenderer(100) + renderer.BeginShape("#ff0000") + + // Empty polygon should not panic + renderer.AddPolygon([]engine.Point{}) + + // Polygon with < 3 points should not panic + renderer.AddPolygon([]engine.Point{{X: 10, Y: 10}}) + renderer.AddPolygon([]engine.Point{{X: 10, Y: 10}, {X: 20, Y: 20}}) +} + +func TestPNGRenderer_AddCircle(t *testing.T) { + renderer := NewPNGRenderer(100) + renderer.BeginShape("#00ff00") + + // Circle with center at (50, 50) and radius 20 means topLeft at (30, 30) and size 40 + topLeft := engine.Point{X: 30, Y: 30} + size := 40.0 + + renderer.AddCircle(topLeft, size, false) + + // Check that center pixel is green + greenColor := color.RGBA{R: 0, G: 255, B: 0, A: 255} + centerPixel := renderer.img.RGBAAt(50, 50) + if centerPixel != greenColor { + t.Errorf("circle center pixel = %v, want %v", centerPixel, greenColor) + } + + // Check that a pixel clearly outside the circle is not green + outsidePixel := renderer.img.RGBAAt(10, 10) + if outsidePixel == greenColor { + t.Error("pixel outside circle should not be green") + } +} + +func TestPNGRenderer_AddCircleInvert(t *testing.T) { + renderer := NewPNGRenderer(100) + + // First fill with background + renderer.SetBackground("#ffffff", 1.0) + renderer.BeginShape("#ff0000") + + // Add inverted circle (should punch a hole) + // Circle with center at (50, 50) and radius 20 means topLeft at (30, 30) and size 40 + topLeft := engine.Point{X: 30, Y: 30} + size := 40.0 + + renderer.AddCircle(topLeft, size, true) + + // Check that center pixel is transparent (inverted) + centerPixel := renderer.img.RGBAAt(50, 50) + if centerPixel.A != 0 { + t.Errorf("inverted circle center should be transparent, got %v", centerPixel) + } +} + +func TestPNGRenderer_ToPNG(t *testing.T) { + renderer := NewPNGRenderer(50) + renderer.SetBackground("#ffffff", 1.0) + + renderer.BeginShape("#ff0000") + points := []engine.Point{ + {X: 10, Y: 10}, + {X: 40, Y: 10}, + {X: 40, Y: 40}, + {X: 10, Y: 40}, + } + renderer.AddPolygon(points) + + pngData := renderer.ToPNG() + + if len(pngData) == 0 { + t.Error("ToPNG() should return non-empty data") + } + + // Verify it's valid PNG data by decoding it + reader := bytes.NewReader(pngData) + decodedImg, err := png.Decode(reader) + if err != nil { + t.Errorf("ToPNG() returned invalid PNG data: %v", err) + } + + // Check dimensions + bounds := decodedImg.Bounds() + if bounds.Max.X != 50 || bounds.Max.Y != 50 { + t.Errorf("decoded image bounds = %v, want 50x50", bounds) + } +} + +func TestPNGRenderer_ToPNGEmpty(t *testing.T) { + renderer := NewPNGRenderer(10) + + pngData := renderer.ToPNG() + + if len(pngData) == 0 { + t.Error("ToPNG() should return data even for empty image") + } + + // Should be valid PNG + reader := bytes.NewReader(pngData) + _, err := png.Decode(reader) + if err != nil { + t.Errorf("ToPNG() returned invalid PNG data: %v", err) + } +} + +func TestParseColor(t *testing.T) { + tests := []struct { + input string + opacity float64 + expected color.RGBA + }{ + {"#ff0000", 1.0, color.RGBA{R: 255, G: 0, B: 0, A: 255}}, + {"ff0000", 1.0, color.RGBA{R: 255, G: 0, B: 0, A: 255}}, + {"#00ff00", 0.5, color.RGBA{R: 0, G: 255, B: 0, A: 128}}, + {"#0000ff", 0.0, color.RGBA{R: 0, G: 0, B: 255, A: 0}}, + {"#f00", 1.0, color.RGBA{R: 255, G: 0, B: 0, A: 255}}, + {"#0f0", 1.0, color.RGBA{R: 0, G: 255, B: 0, A: 255}}, + {"#00f", 1.0, color.RGBA{R: 0, G: 0, B: 255, A: 255}}, + {"#ff0000ff", 1.0, color.RGBA{R: 255, G: 0, B: 0, A: 255}}, + {"#ff000080", 1.0, color.RGBA{R: 255, G: 0, B: 0, A: 128}}, + {"invalid", 1.0, color.RGBA{R: 0, G: 0, B: 0, A: 255}}, + {"", 1.0, color.RGBA{R: 0, G: 0, B: 0, A: 255}}, + } + + for _, test := range tests { + result := parseColor(test.input, test.opacity) + if result != test.expected { + t.Errorf("parseColor(%q, %v) = %v, want %v", + test.input, test.opacity, result, test.expected) + } + } +} + +func TestPNGRenderer_ConcurrentAccess(t *testing.T) { + renderer := NewPNGRenderer(100) + + // Test concurrent access to ensure thread safety + done := make(chan bool, 10) + + for i := 0; i < 10; i++ { + go func(id int) { + renderer.BeginShape("#ff0000") + points := []engine.Point{ + {X: float64(id * 5), Y: float64(id * 5)}, + {X: float64(id*5 + 10), Y: float64(id * 5)}, + {X: float64(id*5 + 10), Y: float64(id*5 + 10)}, + {X: float64(id * 5), Y: float64(id*5 + 10)}, + } + renderer.AddPolygon(points) + renderer.EndShape() + done <- true + }(i) + } + + // Wait for all goroutines to complete + for i := 0; i < 10; i++ { + <-done + } + + // Should be able to generate PNG without issues + pngData := renderer.ToPNG() + if len(pngData) == 0 { + t.Error("concurrent access test failed - no PNG data generated") + } +} + +func BenchmarkPNGRenderer_ToPNG(b *testing.B) { + renderer := NewPNGRenderer(200) + renderer.SetBackground("#ffffff", 1.0) + + // Add some shapes for a realistic benchmark + renderer.BeginShape("#ff0000") + renderer.AddPolygon([]engine.Point{ + {X: 20, Y: 20}, {X: 80, Y: 20}, {X: 80, Y: 80}, {X: 20, Y: 80}, + }) + + renderer.BeginShape("#00ff00") + renderer.AddCircle(engine.Point{X: 100, Y: 100}, 30, false) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + pngData := renderer.ToPNG() + if len(pngData) == 0 { + b.Fatal("ToPNG returned empty data") + } + } +} diff --git a/internal/renderer/renderer.go b/internal/renderer/renderer.go new file mode 100644 index 0000000..8841219 --- /dev/null +++ b/internal/renderer/renderer.go @@ -0,0 +1,237 @@ +package renderer + +import ( + "github.com/kevin/go-jdenticon/internal/engine" +) + +// Renderer defines the interface for rendering identicons to various output formats. +// It provides a set of drawing primitives that can be implemented by concrete renderers +// such as SVG, PNG, or other custom formats. +type Renderer interface { + // Drawing primitives + MoveTo(x, y float64) + LineTo(x, y float64) + CurveTo(x1, y1, x2, y2, x, y float64) + ClosePath() + + // Fill and stroke operations + Fill(color string) + Stroke(color string, width float64) + + // Shape management + BeginShape(color string) + EndShape() + + // Background and configuration + SetBackground(fillColor string, opacity float64) + + // High-level shape methods + AddPolygon(points []engine.Point) + AddCircle(topLeft engine.Point, size float64, invert bool) + AddRectangle(x, y, width, height float64) + AddTriangle(p1, p2, p3 engine.Point) + + // Utility methods + GetSize() int + Clear() +} + +// BaseRenderer provides default implementations for common renderer functionality. +// Concrete renderers can embed this struct and override specific methods as needed. +type BaseRenderer struct { + iconSize int + currentColor string + background string + backgroundOp float64 + + // Current path state for primitive operations + currentPath []PathCommand + pathStart engine.Point + currentPos engine.Point +} + +// PathCommandType represents the type of path command +type PathCommandType int + +const ( + MoveToCommand PathCommandType = iota + LineToCommand + CurveToCommand + ClosePathCommand +) + +// PathCommand represents a single drawing command in a path +type PathCommand struct { + Type PathCommandType + Points []engine.Point +} + +// NewBaseRenderer creates a new base renderer with the specified icon size +func NewBaseRenderer(iconSize int) *BaseRenderer { + return &BaseRenderer{ + iconSize: iconSize, + currentPath: make([]PathCommand, 0), + } +} + +// MoveTo moves the current drawing position to the specified coordinates +func (r *BaseRenderer) MoveTo(x, y float64) { + pos := engine.Point{X: x, Y: y} + r.currentPos = pos + r.pathStart = pos + r.currentPath = append(r.currentPath, PathCommand{ + Type: MoveToCommand, + Points: []engine.Point{pos}, + }) +} + +// LineTo draws a line from the current position to the specified coordinates +func (r *BaseRenderer) LineTo(x, y float64) { + pos := engine.Point{X: x, Y: y} + r.currentPos = pos + r.currentPath = append(r.currentPath, PathCommand{ + Type: LineToCommand, + Points: []engine.Point{pos}, + }) +} + +// CurveTo draws a cubic Bézier curve from the current position to (x, y) using (x1, y1) and (x2, y2) as control points +func (r *BaseRenderer) CurveTo(x1, y1, x2, y2, x, y float64) { + endPos := engine.Point{X: x, Y: y} + r.currentPos = endPos + r.currentPath = append(r.currentPath, PathCommand{ + Type: CurveToCommand, + Points: []engine.Point{ + {X: x1, Y: y1}, + {X: x2, Y: y2}, + endPos, + }, + }) +} + +// ClosePath closes the current path by drawing a line back to the path start +func (r *BaseRenderer) ClosePath() { + r.currentPos = r.pathStart + r.currentPath = append(r.currentPath, PathCommand{ + Type: ClosePathCommand, + Points: []engine.Point{}, + }) +} + +// Fill fills the current path with the specified color +func (r *BaseRenderer) Fill(color string) { + // Default implementation - concrete renderers should override +} + +// Stroke strokes the current path with the specified color and width +func (r *BaseRenderer) Stroke(color string, width float64) { + // Default implementation - concrete renderers should override +} + +// BeginShape starts a new shape with the specified color +func (r *BaseRenderer) BeginShape(color string) { + r.currentColor = color + r.currentPath = make([]PathCommand, 0) +} + +// EndShape ends the current shape +func (r *BaseRenderer) EndShape() { + // Default implementation - concrete renderers should override +} + +// SetBackground sets the background color and opacity +func (r *BaseRenderer) SetBackground(fillColor string, opacity float64) { + r.background = fillColor + r.backgroundOp = opacity +} + +// AddPolygon adds a polygon to the renderer using the current fill color +func (r *BaseRenderer) AddPolygon(points []engine.Point) { + if len(points) == 0 { + return + } + + // Move to first point + r.MoveTo(points[0].X, points[0].Y) + + // Line to subsequent points + for i := 1; i < len(points); i++ { + r.LineTo(points[i].X, points[i].Y) + } + + // Close the path + r.ClosePath() + + // Fill with current color + r.Fill(r.currentColor) +} + +// AddCircle adds a circle to the renderer using the current fill color +func (r *BaseRenderer) AddCircle(topLeft engine.Point, size float64, invert bool) { + // Approximate circle using cubic Bézier curves + // Magic number for circle approximation with Bézier curves + const kappa = 0.5522847498307936 // 4/3 * (sqrt(2) - 1) + + radius := size / 2 + centerX := topLeft.X + radius + centerY := topLeft.Y + radius + + cp := kappa * radius // Control point distance + + // Start at rightmost point + r.MoveTo(centerX+radius, centerY) + + // Four cubic curves to approximate circle + r.CurveTo(centerX+radius, centerY+cp, centerX+cp, centerY+radius, centerX, centerY+radius) + r.CurveTo(centerX-cp, centerY+radius, centerX-radius, centerY+cp, centerX-radius, centerY) + r.CurveTo(centerX-radius, centerY-cp, centerX-cp, centerY-radius, centerX, centerY-radius) + r.CurveTo(centerX+cp, centerY-radius, centerX+radius, centerY-cp, centerX+radius, centerY) + + r.ClosePath() + r.Fill(r.currentColor) +} + +// AddRectangle adds a rectangle to the renderer +func (r *BaseRenderer) AddRectangle(x, y, width, height float64) { + points := []engine.Point{ + {X: x, Y: y}, + {X: x + width, Y: y}, + {X: x + width, Y: y + height}, + {X: x, Y: y + height}, + } + r.AddPolygon(points) +} + +// AddTriangle adds a triangle to the renderer +func (r *BaseRenderer) AddTriangle(p1, p2, p3 engine.Point) { + points := []engine.Point{p1, p2, p3} + r.AddPolygon(points) +} + +// GetSize returns the icon size +func (r *BaseRenderer) GetSize() int { + return r.iconSize +} + +// Clear clears the renderer state +func (r *BaseRenderer) Clear() { + r.currentPath = make([]PathCommand, 0) + r.currentColor = "" + r.background = "" + r.backgroundOp = 0 +} + +// GetCurrentPath returns the current path commands +func (r *BaseRenderer) GetCurrentPath() []PathCommand { + return r.currentPath +} + +// GetCurrentColor returns the current drawing color +func (r *BaseRenderer) GetCurrentColor() string { + return r.currentColor +} + +// GetBackground returns the background color and opacity +func (r *BaseRenderer) GetBackground() (string, float64) { + return r.background, r.backgroundOp +} \ No newline at end of file diff --git a/internal/renderer/renderer_test.go b/internal/renderer/renderer_test.go new file mode 100644 index 0000000..bbb5cb7 --- /dev/null +++ b/internal/renderer/renderer_test.go @@ -0,0 +1,362 @@ +package renderer + +import ( + "testing" + + "github.com/kevin/go-jdenticon/internal/engine" +) + +func TestNewBaseRenderer(t *testing.T) { + iconSize := 100 + r := NewBaseRenderer(iconSize) + + if r.GetSize() != iconSize { + t.Errorf("Expected icon size %d, got %d", iconSize, r.GetSize()) + } + + if len(r.GetCurrentPath()) != 0 { + t.Errorf("Expected empty path, got %d commands", len(r.GetCurrentPath())) + } + + if r.GetCurrentColor() != "" { + t.Errorf("Expected empty current color, got %s", r.GetCurrentColor()) + } + + bg, bgOp := r.GetBackground() + if bg != "" || bgOp != 0 { + t.Errorf("Expected empty background, got %s with opacity %f", bg, bgOp) + } +} + +func TestBaseRendererSetBackground(t *testing.T) { + r := NewBaseRenderer(100) + + color := "#ff0000" + opacity := 0.5 + + r.SetBackground(color, opacity) + + bg, bgOp := r.GetBackground() + if bg != color { + t.Errorf("Expected background color %s, got %s", color, bg) + } + if bgOp != opacity { + t.Errorf("Expected background opacity %f, got %f", opacity, bgOp) + } +} + +func TestBaseRendererBeginShape(t *testing.T) { + r := NewBaseRenderer(100) + + color := "#00ff00" + r.BeginShape(color) + + if r.GetCurrentColor() != color { + t.Errorf("Expected current color %s, got %s", color, r.GetCurrentColor()) + } + + // Path should be reset when beginning a shape + if len(r.GetCurrentPath()) != 0 { + t.Errorf("Expected empty path after BeginShape, got %d commands", len(r.GetCurrentPath())) + } +} + +func TestBaseRendererMoveTo(t *testing.T) { + r := NewBaseRenderer(100) + + x, y := 10.5, 20.3 + r.MoveTo(x, y) + + path := r.GetCurrentPath() + if len(path) != 1 { + t.Fatalf("Expected 1 path command, got %d", len(path)) + } + + cmd := path[0] + if cmd.Type != MoveToCommand { + t.Errorf("Expected MoveToCommand, got %v", cmd.Type) + } + + if len(cmd.Points) != 1 { + t.Fatalf("Expected 1 point, got %d", len(cmd.Points)) + } + + point := cmd.Points[0] + if point.X != x || point.Y != y { + t.Errorf("Expected point (%f, %f), got (%f, %f)", x, y, point.X, point.Y) + } +} + +func TestBaseRendererLineTo(t *testing.T) { + r := NewBaseRenderer(100) + + // Move to start point first + r.MoveTo(0, 0) + + x, y := 15.7, 25.9 + r.LineTo(x, y) + + path := r.GetCurrentPath() + if len(path) != 2 { + t.Fatalf("Expected 2 path commands, got %d", len(path)) + } + + cmd := path[1] // Second command should be LineTo + if cmd.Type != LineToCommand { + t.Errorf("Expected LineToCommand, got %v", cmd.Type) + } + + if len(cmd.Points) != 1 { + t.Fatalf("Expected 1 point, got %d", len(cmd.Points)) + } + + point := cmd.Points[0] + if point.X != x || point.Y != y { + t.Errorf("Expected point (%f, %f), got (%f, %f)", x, y, point.X, point.Y) + } +} + +func TestBaseRendererCurveTo(t *testing.T) { + r := NewBaseRenderer(100) + + // Move to start point first + r.MoveTo(0, 0) + + x1, y1 := 10.0, 5.0 + x2, y2 := 20.0, 15.0 + x, y := 30.0, 25.0 + + r.CurveTo(x1, y1, x2, y2, x, y) + + path := r.GetCurrentPath() + if len(path) != 2 { + t.Fatalf("Expected 2 path commands, got %d", len(path)) + } + + cmd := path[1] // Second command should be CurveTo + if cmd.Type != CurveToCommand { + t.Errorf("Expected CurveToCommand, got %v", cmd.Type) + } + + if len(cmd.Points) != 3 { + t.Fatalf("Expected 3 points, got %d", len(cmd.Points)) + } + + // Check control points and end point + if cmd.Points[0].X != x1 || cmd.Points[0].Y != y1 { + t.Errorf("Expected first control point (%f, %f), got (%f, %f)", x1, y1, cmd.Points[0].X, cmd.Points[0].Y) + } + if cmd.Points[1].X != x2 || cmd.Points[1].Y != y2 { + t.Errorf("Expected second control point (%f, %f), got (%f, %f)", x2, y2, cmd.Points[1].X, cmd.Points[1].Y) + } + if cmd.Points[2].X != x || cmd.Points[2].Y != y { + t.Errorf("Expected end point (%f, %f), got (%f, %f)", x, y, cmd.Points[2].X, cmd.Points[2].Y) + } +} + +func TestBaseRendererClosePath(t *testing.T) { + r := NewBaseRenderer(100) + + // Move to start point first + r.MoveTo(0, 0) + r.LineTo(10, 10) + r.ClosePath() + + path := r.GetCurrentPath() + if len(path) != 3 { + t.Fatalf("Expected 3 path commands, got %d", len(path)) + } + + cmd := path[2] // Third command should be ClosePath + if cmd.Type != ClosePathCommand { + t.Errorf("Expected ClosePathCommand, got %v", cmd.Type) + } + + if len(cmd.Points) != 0 { + t.Errorf("Expected 0 points for ClosePath, got %d", len(cmd.Points)) + } +} + +func TestBaseRendererAddPolygon(t *testing.T) { + r := NewBaseRenderer(100) + r.BeginShape("#ff0000") + + points := []engine.Point{ + {X: 0, Y: 0}, + {X: 10, Y: 0}, + {X: 10, Y: 10}, + {X: 0, Y: 10}, + } + + r.AddPolygon(points) + + path := r.GetCurrentPath() + + // Should have MoveTo + 3 LineTo + ClosePath = 5 commands + expectedCommands := len(points) + 1 // +1 for ClosePath + if len(path) != expectedCommands { + t.Fatalf("Expected %d path commands, got %d", expectedCommands, len(path)) + } + + // Check first command is MoveTo + if path[0].Type != MoveToCommand { + t.Errorf("Expected first command to be MoveTo, got %v", path[0].Type) + } + + // Check last command is ClosePath + if path[len(path)-1].Type != ClosePathCommand { + t.Errorf("Expected last command to be ClosePath, got %v", path[len(path)-1].Type) + } +} + +func TestBaseRendererAddRectangle(t *testing.T) { + r := NewBaseRenderer(100) + r.BeginShape("#0000ff") + + x, y, width, height := 5.0, 10.0, 20.0, 15.0 + r.AddRectangle(x, y, width, height) + + path := r.GetCurrentPath() + + // Should have MoveTo + 3 LineTo + ClosePath = 5 commands + if len(path) != 5 { + t.Fatalf("Expected 5 path commands, got %d", len(path)) + } + + // Verify the rectangle points + expectedPoints := []engine.Point{ + {X: x, Y: y}, // bottom-left + {X: x + width, Y: y}, // bottom-right + {X: x + width, Y: y + height}, // top-right + {X: x, Y: y + height}, // top-left + } + + // Check MoveTo point + if path[0].Points[0] != expectedPoints[0] { + t.Errorf("Expected first point %v, got %v", expectedPoints[0], path[0].Points[0]) + } + + // Check LineTo points + for i := 1; i < 4; i++ { + if path[i].Type != LineToCommand { + t.Errorf("Expected LineTo command at index %d, got %v", i, path[i].Type) + } + if path[i].Points[0] != expectedPoints[i] { + t.Errorf("Expected point %v at index %d, got %v", expectedPoints[i], i, path[i].Points[0]) + } + } +} + +func TestBaseRendererAddTriangle(t *testing.T) { + r := NewBaseRenderer(100) + r.BeginShape("#00ffff") + + p1 := engine.Point{X: 0, Y: 0} + p2 := engine.Point{X: 10, Y: 0} + p3 := engine.Point{X: 5, Y: 10} + + r.AddTriangle(p1, p2, p3) + + path := r.GetCurrentPath() + + // Should have MoveTo + 2 LineTo + ClosePath = 4 commands + if len(path) != 4 { + t.Fatalf("Expected 4 path commands, got %d", len(path)) + } + + // Check the triangle points + if path[0].Points[0] != p1 { + t.Errorf("Expected first point %v, got %v", p1, path[0].Points[0]) + } + if path[1].Points[0] != p2 { + t.Errorf("Expected second point %v, got %v", p2, path[1].Points[0]) + } + if path[2].Points[0] != p3 { + t.Errorf("Expected third point %v, got %v", p3, path[2].Points[0]) + } +} + +func TestBaseRendererAddCircle(t *testing.T) { + r := NewBaseRenderer(100) + r.BeginShape("#ffff00") + + center := engine.Point{X: 50, Y: 50} + radius := 25.0 + + r.AddCircle(center, radius, false) + + path := r.GetCurrentPath() + + // Should have MoveTo + 4 CurveTo + ClosePath = 6 commands + if len(path) != 6 { + t.Fatalf("Expected 6 path commands for circle, got %d", len(path)) + } + + // Check first command is MoveTo + if path[0].Type != MoveToCommand { + t.Errorf("Expected first command to be MoveTo, got %v", path[0].Type) + } + + // Check that we have 4 CurveTo commands + curveCount := 0 + for i := 1; i < len(path)-1; i++ { + if path[i].Type == CurveToCommand { + curveCount++ + } + } + if curveCount != 4 { + t.Errorf("Expected 4 CurveTo commands for circle, got %d", curveCount) + } + + // Check last command is ClosePath + if path[len(path)-1].Type != ClosePathCommand { + t.Errorf("Expected last command to be ClosePath, got %v", path[len(path)-1].Type) + } +} + +func TestBaseRendererClear(t *testing.T) { + r := NewBaseRenderer(100) + + // Set some state + r.BeginShape("#ff0000") + r.SetBackground("#ffffff", 0.8) + r.MoveTo(10, 20) + r.LineTo(30, 40) + + // Verify state is set + if r.GetCurrentColor() == "" { + t.Error("Expected current color to be set before clear") + } + if len(r.GetCurrentPath()) == 0 { + t.Error("Expected path commands before clear") + } + + // Clear the renderer + r.Clear() + + // Verify state is cleared + if r.GetCurrentColor() != "" { + t.Errorf("Expected empty current color after clear, got %s", r.GetCurrentColor()) + } + if len(r.GetCurrentPath()) != 0 { + t.Errorf("Expected empty path after clear, got %d commands", len(r.GetCurrentPath())) + } + + bg, bgOp := r.GetBackground() + if bg != "" || bgOp != 0 { + t.Errorf("Expected empty background after clear, got %s with opacity %f", bg, bgOp) + } +} + +func TestBaseRendererEmptyPolygon(t *testing.T) { + r := NewBaseRenderer(100) + r.BeginShape("#ff0000") + + // Test with empty points slice + r.AddPolygon([]engine.Point{}) + + path := r.GetCurrentPath() + if len(path) != 0 { + t.Errorf("Expected no path commands for empty polygon, got %d", len(path)) + } +} \ No newline at end of file diff --git a/internal/renderer/svg.go b/internal/renderer/svg.go new file mode 100644 index 0000000..bb693b2 --- /dev/null +++ b/internal/renderer/svg.go @@ -0,0 +1,172 @@ +package renderer + +import ( + "fmt" + "math" + "strconv" + "strings" + + "github.com/kevin/go-jdenticon/internal/engine" +) + +// SVGPath represents an SVG path element +type SVGPath struct { + data strings.Builder +} + +// AddPolygon adds a polygon to the SVG path +func (p *SVGPath) AddPolygon(points []engine.Point) { + if len(points) == 0 { + return + } + + // Move to first point + p.data.WriteString(fmt.Sprintf("M%s %s", svgValue(points[0].X), svgValue(points[0].Y))) + + // Line to subsequent points + for i := 1; i < len(points); i++ { + p.data.WriteString(fmt.Sprintf("L%s %s", svgValue(points[i].X), svgValue(points[i].Y))) + } + + // Close path + p.data.WriteString("Z") +} + +// AddCircle adds a circle to the SVG path +func (p *SVGPath) AddCircle(topLeft engine.Point, size float64, counterClockwise bool) { + sweepFlag := "1" + if counterClockwise { + sweepFlag = "0" + } + + radius := size / 2 + centerX := topLeft.X + radius + centerY := topLeft.Y + radius + + svgRadius := svgValue(radius) + svgDiameter := svgValue(size) + svgArc := fmt.Sprintf("a%s,%s 0 1,%s ", svgRadius, svgRadius, sweepFlag) + + // Move to start point (left side of circle) + startX := centerX - radius + startY := centerY + + p.data.WriteString(fmt.Sprintf("M%s %s", svgValue(startX), svgValue(startY))) + p.data.WriteString(svgArc + svgDiameter + ",0") + p.data.WriteString(svgArc + "-" + svgDiameter + ",0") +} + +// DataString returns the SVG path data string +func (p *SVGPath) DataString() string { + return p.data.String() +} + +// SVGRenderer implements the Renderer interface for SVG output +type SVGRenderer struct { + *BaseRenderer + pathsByColor map[string]*SVGPath + colorOrder []string +} + +// NewSVGRenderer creates a new SVG renderer +func NewSVGRenderer(iconSize int) *SVGRenderer { + return &SVGRenderer{ + BaseRenderer: NewBaseRenderer(iconSize), + pathsByColor: make(map[string]*SVGPath), + colorOrder: make([]string, 0), + } +} + +// SetBackground sets the background color and opacity +func (r *SVGRenderer) SetBackground(fillColor string, opacity float64) { + r.BaseRenderer.SetBackground(fillColor, opacity) +} + +// BeginShape marks the beginning of a new shape with the specified color +func (r *SVGRenderer) BeginShape(color string) { + r.BaseRenderer.BeginShape(color) + if _, exists := r.pathsByColor[color]; !exists { + r.pathsByColor[color] = &SVGPath{} + r.colorOrder = append(r.colorOrder, color) + } +} + +// EndShape marks the end of the currently drawn shape +func (r *SVGRenderer) EndShape() { + // No action needed for SVG +} + +// getCurrentPath returns the current path for the active color +func (r *SVGRenderer) getCurrentPath() *SVGPath { + currentColor := r.GetCurrentColor() + if currentColor == "" { + return nil + } + return r.pathsByColor[currentColor] +} + +// AddPolygon adds a polygon with the current fill color to the SVG +func (r *SVGRenderer) AddPolygon(points []engine.Point) { + if path := r.getCurrentPath(); path != nil { + path.AddPolygon(points) + } +} + +// AddCircle adds a circle with the current fill color to the SVG +func (r *SVGRenderer) AddCircle(topLeft engine.Point, size float64, invert bool) { + if path := r.getCurrentPath(); path != nil { + path.AddCircle(topLeft, size, invert) + } +} + +// ToSVG generates the final SVG XML string +func (r *SVGRenderer) ToSVG() string { + var svg strings.Builder + + iconSize := r.GetSize() + background, backgroundOp := r.GetBackground() + + // SVG opening tag with namespace and dimensions + svg.WriteString(fmt.Sprintf(``, + iconSize, iconSize, iconSize, iconSize)) + + // Add background rectangle if specified + if background != "" && backgroundOp > 0 { + if backgroundOp >= 1.0 { + svg.WriteString(fmt.Sprintf(``, background)) + } else { + svg.WriteString(fmt.Sprintf(``, + background, backgroundOp)) + } + } + + // Add paths for each color (in insertion order to preserve z-order) + for _, color := range r.colorOrder { + path := r.pathsByColor[color] + dataString := path.DataString() + if dataString != "" { + svg.WriteString(fmt.Sprintf(``, color, dataString)) + } + } + + // SVG closing tag + svg.WriteString("") + + return svg.String() +} + +// svgValue rounds a float64 to one decimal place, mimicking the Jdenticon JS implementation's +// "round half up" behavior. It also formats the number to a minimal string representation. +func svgValue(value float64) string { + // Use math.Floor to replicate the "round half up" logic from the JS implementation. + // JavaScript: ((value * 10 + 0.5) | 0) / 10 + rounded := math.Floor(value*10 + 0.5) / 10 + + // Format to an integer string if there's no fractional part. + if rounded == math.Trunc(rounded) { + return strconv.Itoa(int(rounded)) + } + + // Otherwise, format to one decimal place. + return strconv.FormatFloat(rounded, 'f', 1, 64) +} diff --git a/internal/renderer/svg_test.go b/internal/renderer/svg_test.go new file mode 100644 index 0000000..58ec73f --- /dev/null +++ b/internal/renderer/svg_test.go @@ -0,0 +1,240 @@ +package renderer + +import ( + "fmt" + "strings" + "testing" + + "github.com/kevin/go-jdenticon/internal/engine" +) + +func TestSVGPath_AddPolygon(t *testing.T) { + path := &SVGPath{} + points := []engine.Point{ + {X: 0, Y: 0}, + {X: 10, Y: 0}, + {X: 10, Y: 10}, + {X: 0, Y: 10}, + } + + path.AddPolygon(points) + expected := "M0 0L10 0L10 10L0 10Z" + if got := path.DataString(); got != expected { + t.Errorf("AddPolygon() = %v, want %v", got, expected) + } +} + +func TestSVGPath_AddPolygonEmpty(t *testing.T) { + path := &SVGPath{} + path.AddPolygon([]engine.Point{}) + + if got := path.DataString(); got != "" { + t.Errorf("AddPolygon([]) = %v, want empty string", got) + } +} + +func TestSVGPath_AddCircle(t *testing.T) { + path := &SVGPath{} + topLeft := engine.Point{X: 25, Y: 25} // Top-left corner to get center at (50, 50) + size := 50.0 // Size 50 gives radius 25 + + path.AddCircle(topLeft, size, false) + + // Should start at left side of circle and draw two arcs + result := path.DataString() + if !strings.HasPrefix(result, "M25 50") { + t.Errorf("Circle should start at left side, got: %s", result) + } + if !strings.Contains(result, "a25,25 0 1,1") { + t.Errorf("Circle should contain clockwise arc, got: %s", result) + } +} + +func TestSVGPath_AddCircleCounterClockwise(t *testing.T) { + path := &SVGPath{} + topLeft := engine.Point{X: 25, Y: 25} // Top-left corner to get center at (50, 50) + size := 50.0 // Size 50 gives radius 25 + + path.AddCircle(topLeft, size, true) + + result := path.DataString() + if !strings.Contains(result, "a25,25 0 1,0") { + t.Errorf("Counter-clockwise circle should have sweep flag 0, got: %s", result) + } +} + +func TestSVGRenderer_NewSVGRenderer(t *testing.T) { + renderer := NewSVGRenderer(100) + + if renderer.iconSize != 100 { + t.Errorf("NewSVGRenderer(100).iconSize = %v, want 100", renderer.iconSize) + } + if renderer.pathsByColor == nil { + t.Error("pathsByColor should be initialized") + } +} + +func TestSVGRenderer_BeginEndShape(t *testing.T) { + renderer := NewSVGRenderer(100) + + renderer.BeginShape("#ff0000") + if renderer.currentColor != "#ff0000" { + t.Errorf("BeginShape should set currentColor, got %v", renderer.currentColor) + } + + if _, exists := renderer.pathsByColor["#ff0000"]; !exists { + t.Error("BeginShape should create path for color") + } + + renderer.EndShape() + // EndShape is a no-op for SVG, just verify it doesn't panic +} + +func TestSVGRenderer_AddPolygon(t *testing.T) { + renderer := NewSVGRenderer(100) + renderer.BeginShape("#ff0000") + + points := []engine.Point{ + {X: 0, Y: 0}, + {X: 10, Y: 0}, + {X: 5, Y: 10}, + } + + renderer.AddPolygon(points) + + path := renderer.pathsByColor["#ff0000"] + expected := "M0 0L10 0L5 10Z" + if got := path.DataString(); got != expected { + t.Errorf("AddPolygon() = %v, want %v", got, expected) + } +} + +func TestSVGRenderer_AddCircle(t *testing.T) { + renderer := NewSVGRenderer(100) + renderer.BeginShape("#00ff00") + + topLeft := engine.Point{X: 30, Y: 30} // Top-left corner to get center at (50, 50) + size := 40.0 // Size 40 gives radius 20 + + renderer.AddCircle(topLeft, size, false) + + path := renderer.pathsByColor["#00ff00"] + result := path.DataString() + if !strings.HasPrefix(result, "M30 50") { + t.Errorf("Circle should start at correct position, got: %s", result) + } +} + +func TestSVGRenderer_ToSVG(t *testing.T) { + renderer := NewSVGRenderer(100) + renderer.SetBackground("#ffffff", 1.0) + + renderer.BeginShape("#ff0000") + points := []engine.Point{ + {X: 0, Y: 0}, + {X: 10, Y: 0}, + {X: 10, Y: 10}, + } + renderer.AddPolygon(points) + + svg := renderer.ToSVG() + + // Check SVG structure + if !strings.Contains(svg, ``) { + t.Error("SVG should contain background rect") + } + if !strings.Contains(svg, ``) { + t.Error("SVG should contain path with correct data") + } + if !strings.HasSuffix(svg, "") { + t.Error("SVG should end with closing tag") + } +} + +func TestSVGRenderer_ToSVGWithoutBackground(t *testing.T) { + renderer := NewSVGRenderer(50) + + renderer.BeginShape("#0000ff") + center := engine.Point{X: 25, Y: 25} + renderer.AddCircle(center, 10, false) + + svg := renderer.ToSVG() + + // Should not contain background rect + if strings.Contains(svg, "= len(hash) { + return 0, fmt.Errorf("parseHex: position %d out of bounds for hash length %d", startPosition, len(hash)) + } + + // If octets is 0 or negative, read from startPosition to end (like JavaScript default) + end := len(hash) + if octets > 0 { + end = startPosition + octets + if end > len(hash) { + end = len(hash) + } + } + + // Extract substring and parse as hexadecimal + substr := hash[startPosition:end] + if len(substr) == 0 { + return 0, fmt.Errorf("parseHex: empty substring at position %d", startPosition) + } + + result, err := strconv.ParseInt(substr, 16, 64) + if err != nil { + return 0, fmt.Errorf("parseHex: failed to parse hex '%s' at position %d: %w", substr, startPosition, err) + } + + return int(result), nil +} + +// IsValidHash checks if a hash string is valid for jdenticon generation +// This implementation is shared between engine and jdenticon packages for consistency +func IsValidHash(hash string) bool { + if len(hash) < 11 { + return false + } + + // Check if all characters are valid hexadecimal + for _, r := range hash { + if !((r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')) { + return false + } + } + + return true +} \ No newline at end of file diff --git a/jdenticon-js/.eslintrc.js b/jdenticon-js/.eslintrc.js new file mode 100644 index 0000000..c950200 --- /dev/null +++ b/jdenticon-js/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + "env": { + "browser": true, + "es2020": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + } +}; diff --git a/jdenticon-js/.gitattributes b/jdenticon-js/.gitattributes new file mode 100644 index 0000000..05a99ae --- /dev/null +++ b/jdenticon-js/.gitattributes @@ -0,0 +1,3 @@ + +# Treat minifyed files as binary to ensure the integrity does not change +dist/*.min.js binary diff --git a/jdenticon-js/.github/workflows/tests.js.yml b/jdenticon-js/.github/workflows/tests.js.yml new file mode 100644 index 0000000..5375ea5 --- /dev/null +++ b/jdenticon-js/.github/workflows/tests.js.yml @@ -0,0 +1,143 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Tests + +on: [push] + +env: + TAP_COLORS: 1 + +jobs: + build: + name: Build and run unit tests + runs-on: ubuntu-latest + env: + TAP_NO_ESM: 1 + steps: + - uses: actions/checkout@v4.1.5 + - name: Use Node.js v14 + uses: actions/setup-node@v4.0.2 + with: + node-version: 14.x + - run: npm install + + - name: Build Jdenticon + run: npm run build + + - name: TypeScript typings tests + run: npm run test:types + - name: Unit tests + run: npm run test:unit + + - name: Webpack 4 bundle test + run: npm run test:webpack4 + - name: Webpack 5 bundle test + run: npm run test:webpack5 + + - name: Rollup bundle test + run: npm run test:rollup + + - name: Node test (CommonJS) + run: npm run test:node-cjs + - name: Node test (ESM) + run: npm run test:node-esm + + - name: Publish artifacts + uses: actions/upload-artifact@v4.3.3 + if: ${{ always() }} + with: + name: package + path: ./test/node_modules/jdenticon + + e2e: + name: E2E tests (Node ${{ matrix.node }}) + runs-on: ubuntu-latest + needs: build + strategy: + fail-fast: false + matrix: + node: [ '6.4', '8.x', '10.x', '12.x', '18.x', '20.x' ] + steps: + - uses: actions/checkout@v4.1.5 + + - uses: actions/download-artifact@v4.1.7 + with: + name: package + path: test/node_modules/jdenticon + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v4.0.2 + with: + node-version: ${{ matrix.node }} + + # Use an older tap version to ensure compatibility with the old Node version + # bind-obj-methods broke old Node 6 support in 2.0.1 + - name: npm install (Node 6.4) + if: ${{ matrix.node == '6.4' }} + run: | + npm install -g npm@6.14.17 + npm install tap@12.7.0 bind-obj-methods@2.0.0 + + - name: npm install (Node 8.x) + if: ${{ matrix.node == '8.x' }} + run: npm install tap@14.11.0 + + - name: npm install (Node 10+) + if: ${{ matrix.node != '6.4' && matrix.node != '8.x' }} + run: npm install + + - name: Node test (CommonJS) + run: npm run test:node-cjs + + - name: Node test (ESM, Node 12+) + if: ${{ matrix.node != '6.4' && matrix.node != '8.x' && matrix.node != '10.x' }} + run: npm run test:node-esm + + - name: Publish artifacts + uses: actions/upload-artifact@v4.3.3 + if: ${{ failure() }} + with: + name: e2e-${{ matrix.node }} + path: ./test/e2e/node/expected + + visual: + name: Visual tests + needs: build + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ 'macos-latest', 'windows-latest' ] + env: + ARTIFACTS_DIR: ./artifacts + BROWSER_SCREENSHOT_DIR: ./artifacts/screenshots + BROWSER_DIFF_DIR: ./artifacts/diffs + steps: + - uses: actions/checkout@v4.1.5 + - name: Use Node.js + uses: actions/setup-node@v4.0.2 + with: + node-version: 16.x + - run: npm install + + - uses: actions/download-artifact@v4.1.7 + with: + name: package + path: test/node_modules/jdenticon + + - name: Run visual tests (Windows) + if: ${{ startsWith(matrix.os, 'windows') }} + run: | + $env:PATH = "C:\SeleniumWebDrivers\IEDriver;$env:PATH" + npm run test:browser-win + - name: Run visual tests (macOS) + if: ${{ startsWith(matrix.os, 'macos') }} + run: npm run test:browser-macos + + - name: Publish artifacts + uses: actions/upload-artifact@v4.3.3 + if: ${{ always() }} + with: + name: visual-${{ matrix.os }} + path: ${{ env.ARTIFACTS_DIR }} + \ No newline at end of file diff --git a/jdenticon-js/.gitignore b/jdenticon-js/.gitignore new file mode 100644 index 0000000..4f08641 --- /dev/null +++ b/jdenticon-js/.gitignore @@ -0,0 +1,7 @@ +artifacts +obj +releases +bower_components +node_modules +.vs +.nyc_output diff --git a/jdenticon-js/LICENSE b/jdenticon-js/LICENSE new file mode 100644 index 0000000..8aa8741 --- /dev/null +++ b/jdenticon-js/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2014-2024 Daniel Mester Pirttijärvi + +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. diff --git a/jdenticon-js/README.md b/jdenticon-js/README.md new file mode 100644 index 0000000..48a58eb --- /dev/null +++ b/jdenticon-js/README.md @@ -0,0 +1,72 @@ +# [Jdenticon](https://jdenticon.com) +JavaScript library for generating highly recognizable identicons using HTML5 canvas or SVG. + +![Sample identicons](https://jdenticon.com/hosted/github-samples.png) + +[![Tests](https://img.shields.io/github/actions/workflow/status/dmester/jdenticon/tests.js.yml?branch=master&style=flat-square)](https://github.com/dmester/jdenticon/actions) +[![Downloads](https://img.shields.io/npm/dt/jdenticon.svg?style=flat-square)](https://www.npmjs.com/package/jdenticon) +[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/jdenticon/badge?style=square)](https://www.jsdelivr.com/package/npm/jdenticon) +[![npm bundle size](https://img.shields.io/bundlephobia/min/jdenticon.svg?style=flat-square)](https://bundlephobia.com/result?p=jdenticon) +[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](https://github.com/dmester/jdenticon/blob/master/LICENSE) + +## Live demo +https://jdenticon.com + +## Getting started +Using Jdenticon is simple. Follow the steps below to integrate Jdenticon into your website. + +### 1. Add identicon placeholders +Jdenticon is able to render both raster and vector identicons. Raster icons are rendered +slightly faster than vector icons, but vector icons scale better on high resolution screens. +Add a canvas to render a raster icon, or an inline svg element to render a vector icon. + +```HTML + + + + + + + +``` + +### 2. Add reference to Jdenticon +Include the Jdenticon library somewhere on your page. You can either host it yourself or +use it right off [jsDelivr](https://www.jsdelivr.com). + +```HTML + + + + + + + +``` +That's it! + +## Other resources +### API documentation +For more usage examples and API documentation, please see: + +https://jdenticon.com + +### Other platforms +There are ports or bindings for Jdenticon available for the following platforms: + +* [PHP](https://github.com/dmester/jdenticon-php/) +* [React](https://www.npmjs.com/package/react-jdenticon) +* [Angular](https://www.npmjs.com/package/ngx-jdenticon) +* [.NET](https://github.com/dmester/jdenticon-net/) +* [Rust](https://github.com/jay3332/rdenticon) +* [Polymer](https://github.com/GeoloeG/identicon-element) +* [Swift](https://github.com/aleph7/jdenticon-swift) +* [Java](https://github.com/sunshower-io/sunshower-arcus/tree/master/arcus-identicon) +* [Dart/Flutter](https://pub.dartlang.org/packages/jdenticon_dart) +* [Kotlin](https://github.com/WycliffeAssociates/jdenticon-kotlin) + +## License +Jdenticon is available under the [MIT license](https://github.com/dmester/jdenticon/blob/master/LICENSE). diff --git a/jdenticon-js/bin/jdenticon.js b/jdenticon-js/bin/jdenticon.js new file mode 100644 index 0000000..752f5f6 --- /dev/null +++ b/jdenticon-js/bin/jdenticon.js @@ -0,0 +1,177 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const jdenticon = require("../dist/jdenticon-node"); + + +// Handle command + +const parsedArgs = parseArgs(process.argv); + +if (parsedArgs.help) { + writeHelp(); + process.exit(0); + +} else if (parsedArgs.version) { + console.log(jdenticon.version); + process.exit(0); + +} else { + const validatedArgs = validateArgs(parsedArgs); + if (validatedArgs) { + var output = validatedArgs.svg ? + jdenticon.toSvg(validatedArgs.value, validatedArgs.size, validatedArgs.config) : + jdenticon.toPng(validatedArgs.value, validatedArgs.size, validatedArgs.config); + + if (validatedArgs.output) { + fs.writeFileSync(validatedArgs.output, output); + } else { + process.stdout.write(output); + } + process.exit(0); + + } else { + writeHelp(); + process.exit(1); + } +} + + +// Functions + +function writeHelp() { + console.log("Generates an identicon as a PNG or SVG file for a specified value."); + console.log(""); + console.log("Usage: jdenticon [-s ] [-o ]"); + console.log(""); + console.log("Options:"); + console.log(" -s, --size Icon size in pixels. (default: 100)"); + console.log(" -o, --output Output file. (default: )"); + console.log(" -f, --format Format of generated icon. Otherwise detected from output path. (default: png)"); + console.log(" -b, --back-color Background color on format #rgb, #rgba, #rrggbb or #rrggbbaa. (default: transparent)"); + console.log(" -p, --padding Padding in percent in range 0 to 0.5. (default: 0.08)"); + console.log(" -v, --version Gets the version of Jdenticon."); + console.log(" -h, --help Show this help information."); + console.log(""); + console.log("Examples:"); + console.log(" jdenticon user127 -s 100 -o icon.png"); +} + +function parseArgs(args) { + // Argument 1 is always node + // Argument 2 is always jdenticon + // Argument 3 and forward are actual arguments + args = args.slice(2); + + function consume(aliases, hasValue) { + for (var argIndex = 0; argIndex < args.length; argIndex++) { + var arg = args[argIndex]; + + for (var aliasIndex = 0; aliasIndex < aliases.length; aliasIndex++) { + var alias = aliases[aliasIndex]; + + if (arg === alias) { + var value; + + if (hasValue) { + if (argIndex + 1 < args.length) { + value = args[argIndex + 1]; + } else { + console.warn("WARN Missing value of argument " + alias); + } + } else { + value = true; + } + + args.splice(argIndex, hasValue ? 2 : 1); + return value; + } + + if (arg.startsWith(alias) && arg[alias.length] === "=") { + var value = arg.substr(alias.length + 1); + if (!hasValue) { + value = value !== "false"; + } + args.splice(argIndex, 1); + return value; + } + } + } + } + + if (consume(["-h", "--help", "-?", "/?", "/h"], false)) { + return { + help: true + }; + } + + if (consume(["-v", "--version"], false)) { + return { + version: true + }; + } + + return { + size: consume(["-s", "--size"], true), + output: consume(["-o", "--output"], true), + format: consume(["-f", "--format"], true), + padding: consume(["-p", "--padding"], true), + backColor: consume(["-b", "--back-color"], true), + value: args + }; +} + +function validateArgs(args) { + if (args.value.length) { + + // Size + var size = 100; + if (args.size) { + size = Number(args.size); + if (!size || size < 1) { + size = 100; + console.warn("WARN Invalid size specified. Defaults to 100."); + } + } + + // Padding + var padding; + if (args.padding != null) { + padding = Number(args.padding); + if (isNaN(padding) || padding < 0 || padding >= 0.5) { + padding = 0.08; + console.warn("WARN Invalid padding specified. Defaults to 0.08."); + } + } + + // Background color + var backColor; + if (args.backColor != null) { + backColor = args.backColor; + if (!/^(#[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(backColor)) { + backColor = undefined; + console.warn("WARN Invalid background color specified. Defaults to transparent."); + } + } + + // Format + var generateSvg = + args.format ? /^svg$/i.test(args.format) : + args.output ? /\.svg$/i.test(args.output) : + false; + if (args.format != null && !/^(svg|png)$/i.test(args.format)) { + console.warn("WARN Invalid format specified. Defaults to " + (generateSvg ? "svg" : "png") + "."); + } + + return { + config: { + padding: padding, + backColor: backColor + }, + output: args.output, + size: size, + svg: generateSvg, + value: args.value.join("") + }; + } +} diff --git a/jdenticon-js/bower.json b/jdenticon-js/bower.json new file mode 100644 index 0000000..2b6d39f --- /dev/null +++ b/jdenticon-js/bower.json @@ -0,0 +1,32 @@ +{ + "name": "Jdenticon", + "authors": [ + "Daniel Mester Pirttijrvi" + ], + "description": "Javascript identicon generator", + "main": "dist/jdenticon.js", + "keywords": [ + "javascript", + "identicon", + "avatar", + "library" + ], + "license": "MIT", + "homepage": "https://jdenticon.com/", + "ignore": [ + ".npmignore", + ".gitignore", + ".vs", + "*.bat", + "*.nuspec", + "build", + "gulpfile.js", + "node_modules", + "obj", + "releases", + "src", + "template.*", + "test", + "utils" + ] +} diff --git a/jdenticon-js/browser/package.json b/jdenticon-js/browser/package.json new file mode 100644 index 0000000..be4e742 --- /dev/null +++ b/jdenticon-js/browser/package.json @@ -0,0 +1,4 @@ +{ + "main": "../dist/jdenticon-module", + "types": "../types/module.d.ts" +} diff --git a/jdenticon-js/build/gulp/ast-transform-stream.js b/jdenticon-js/build/gulp/ast-transform-stream.js new file mode 100644 index 0000000..d64591e --- /dev/null +++ b/jdenticon-js/build/gulp/ast-transform-stream.js @@ -0,0 +1,34 @@ +const { Transform } = require("stream"); +const { parse } = require("acorn"); +const { Replacement } = require("./replacement"); + +function astTransformStream(transformer) { + return new Transform({ + objectMode: true, + + transform(inputFile, _, fileDone) { + const input = inputFile.contents.toString(); + + const comments = []; + const ast = parse(input, { + ecmaVersion: 10, + sourceType: "module", + onComment: comments, + }); + + const replacement = new Replacement(); + transformer(replacement, ast, comments, input); + const output = replacement.replace(input, inputFile.sourceMap); + + inputFile.contents = Buffer.from(output.output); + + if (inputFile.sourceMap) { + inputFile.sourceMap = output.sourceMap; + } + + fileDone(null, inputFile); + } + }); +} + +module.exports = transformer => () => astTransformStream(transformer); diff --git a/jdenticon-js/build/gulp/domprops.js b/jdenticon-js/build/gulp/domprops.js new file mode 100644 index 0000000..dc896f2 --- /dev/null +++ b/jdenticon-js/build/gulp/domprops.js @@ -0,0 +1,4120 @@ + +// Generated using +// https://github.com/terser/terser/blob/master/tools/props.html + +module.exports = new Set([ + "$", + "$$", + "$&", + "$'", + "$+", + "$0", + "$1", + "$2", + "$3", + "$4", + "$5", + "$6", + "$7", + "$8", + "$9", + "$_", + "$`", + "$x", + "0", + "1", + "2", + "3", + "__defineGetter__", + "__defineSetter__", + "__lookupGetter__", + "__lookupSetter__", + "a", + "abbr", + "abort", + "ABORT_ERR", + "AbortController", + "aborted", + "AbortSignal", + "abs", + "accept", + "acceptCharset", + "accessKey", + "accuracy", + "acos", + "acosh", + "action", + "actions", + "active", + "ACTIVE_ATTRIBUTES", + "ACTIVE_TEXTURE", + "ACTIVE_UNIFORM_BLOCKS", + "ACTIVE_UNIFORMS", + "activeCues", + "activeElement", + "activeSourceBuffers", + "activeTexture", + "actualBoundingBoxAscent", + "actualBoundingBoxDescent", + "actualBoundingBoxLeft", + "actualBoundingBoxRight", + "add", + "addColorStop", + "addCue", + "addedNodes", + "addEventListener", + "addFromString", + "addFromUri", + "addIceCandidate", + "ADDITION", + "addListener", + "addPath", + "addRange", + "address", + "addRule", + "AddSearchProvider", + "addSourceBuffer", + "addStream", + "addTextTrack", + "addTrack", + "addTransceiver", + "adoptedStyleSheets", + "adoptNode", + "advance", + "after", + "album", + "alert", + "ALIASED_LINE_WIDTH_RANGE", + "ALIASED_POINT_SIZE_RANGE", + "align", + "aLink", + "alinkColor", + "all", + "allow", + "allowedFeatures", + "allowFullscreen", + "allowPaymentRequest", + "allowsFeature", + "allSettled", + "ALPHA", + "ALPHA_BITS", + "ALREADY_SIGNALED", + "alt", + "altitude", + "altitudeAccuracy", + "altKey", + "ALWAYS", + "amplitude", + "AnalyserNode", + "ancestorOrigins", + "anchor", + "anchorNode", + "anchorOffset", + "anchors", + "and", + "angle", + "animate", + "animatedPoints", + "Animation", + "AnimationEffect", + "AnimationEvent", + "animationName", + "AnimationPlaybackEvent", + "animationsPaused", + "AnimationTimeline", + "animVal", + "ANY_SAMPLES_PASSED", + "ANY_SAMPLES_PASSED_CONSERVATIVE", + "ANY_TYPE", + "ANY_UNORDERED_NODE_TYPE", + "app", + "appCodeName", + "append", + "appendBuffer", + "appendChild", + "appendData", + "appendItem", + "appendMedium", + "appendRule", + "appendWindowEnd", + "appendWindowStart", + "applets", + "applicationServerKey", + "apply", + "applyConstraints", + "appName", + "appVersion", + "arc", + "archive", + "arcTo", + "areas", + "arguments", + "ariaAtomic", + "ariaAutoComplete", + "ariaBusy", + "ariaChecked", + "ariaColCount", + "ariaColIndex", + "ariaColSpan", + "ariaCurrent", + "ariaDescription", + "ariaDisabled", + "ariaExpanded", + "ariaHasPopup", + "ariaHidden", + "ariaKeyShortcuts", + "ariaLabel", + "ariaLevel", + "ariaLive", + "ariaModal", + "ariaMultiLine", + "ariaMultiSelectable", + "ariaOrientation", + "ariaPlaceholder", + "ariaPosInSet", + "ariaPressed", + "ariaReadOnly", + "ariaRelevant", + "ariaRequired", + "ariaRoleDescription", + "ariaRowCount", + "ariaRowIndex", + "ariaRowSpan", + "ariaSelected", + "ariaSetSize", + "ariaSort", + "ariaValueMax", + "ariaValueMin", + "ariaValueNow", + "ariaValueText", + "Array", + "ARRAY_BUFFER", + "ARRAY_BUFFER_BINDING", + "ArrayBuffer", + "arrayBuffer", + "artist", + "artwork", + "as", + "asin", + "asinh", + "asIntN", + "assert", + "assign", + "assignedElements", + "assignedNodes", + "assignedSlot", + "asUintN", + "async", + "asyncIterator", + "AT_TARGET", + "atan", + "atan2", + "atanh", + "atob", + "Atomics", + "ATTACHED_SHADERS", + "attachInternals", + "attachShader", + "attachShadow", + "attack", + "Attr", + "attrChange", + "ATTRIBUTE_NODE", + "attributeName", + "attributeNamespace", + "attributes", + "attributeStyleMap", + "attribution", + "attrName", + "Audio", + "audioBitsPerSecond", + "AudioBuffer", + "AudioBufferSourceNode", + "AudioContext", + "AudioDestinationNode", + "AudioListener", + "AudioNode", + "AudioParam", + "AudioParamMap", + "AudioProcessingEvent", + "AudioScheduledSourceNode", + "AudioWorkletNode", + "autocapitalize", + "autocomplete", + "autofocus", + "autoIncrement", + "automationRate", + "autoplay", + "availHeight", + "availLeft", + "availTop", + "availWidth", + "ax", + "axes", + "axis", + "ay", + "azimuth", + "b", + "BACK", + "back", + "background", + "BackgroundFetchManager", + "BackgroundFetchRecord", + "BackgroundFetchRegistration", + "badge", + "badInput", + "BarProp", + "BaseAudioContext", + "baseFrequencyX", + "baseFrequencyY", + "baseLatency", + "baseNode", + "baseOffset", + "baseURI", + "baseVal", + "BatteryManager", + "before", + "BeforeInstallPromptEvent", + "BeforeUnloadEvent", + "beginElement", + "beginElementAt", + "beginPath", + "beginQuery", + "beginTransformFeedback", + "behavior", + "bezierCurveTo", + "bgColor", + "bias", + "big", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "binaryType", + "bind", + "bindAttribLocation", + "bindBuffer", + "bindBufferBase", + "bindBufferRange", + "bindFramebuffer", + "bindRenderbuffer", + "bindSampler", + "bindTexture", + "bindTransformFeedback", + "bindVertexArray", + "BiquadFilterNode", + "BLEND", + "BLEND_COLOR", + "BLEND_DST_ALPHA", + "BLEND_DST_RGB", + "BLEND_EQUATION", + "BLEND_EQUATION_ALPHA", + "BLEND_EQUATION_RGB", + "BLEND_SRC_ALPHA", + "BLEND_SRC_RGB", + "blendColor", + "blendEquation", + "blendEquationSeparate", + "blendFunc", + "blendFuncSeparate", + "blink", + "blitFramebuffer", + "Blob", + "blob", + "BlobEvent", + "blockedURI", + "blockSize", + "BLUE_BITS", + "BluetoothUUID", + "blur", + "body", + "bodyUsed", + "bold", + "BOOL", + "BOOL_VEC2", + "BOOL_VEC3", + "BOOL_VEC4", + "Boolean", + "BOOLEAN_TYPE", + "booleanValue", + "border", + "borderBoxSize", + "bottom", + "bound", + "boundingClientRect", + "BroadcastChannel", + "BROWSER_DEFAULT_WEBGL", + "btoa", + "bubbles", + "BUBBLING_PHASE", + "buffer", + "BUFFER_SIZE", + "BUFFER_USAGE", + "bufferData", + "buffered", + "bufferedAmount", + "bufferedAmountLowThreshold", + "bufferSize", + "bufferSubData", + "button", + "buttons", + "BYTE", + "byteLength", + "ByteLengthQueuingStrategy", + "byteOffset", + "BYTES_PER_ELEMENT", + "c", + "cache", + "call", + "caller", + "cancel", + "cancelable", + "cancelAndHoldAtTime", + "cancelAnimationFrame", + "cancelBubble", + "cancelIdleCallback", + "cancelScheduledValues", + "cancelVideoFrameCallback", + "cancelWatchAvailability", + "candidate", + "canInsertDTMF", + "canonicalUUID", + "canPlayType", + "canTrickleIceCandidates", + "canvas", + "CanvasCaptureMediaStreamTrack", + "CanvasGradient", + "CanvasPattern", + "CanvasRenderingContext2D", + "caption", + "captureEvents", + "captureStackTrace", + "captureStream", + "CAPTURING_PHASE", + "caretRangeFromPoint", + "catch", + "cbrt", + "CCW", + "CDATA_SECTION_NODE", + "CDATASection", + "ceil", + "cellIndex", + "cellPadding", + "cells", + "cellSpacing", + "ch", + "changedTouches", + "changeType", + "channel", + "channelCount", + "channelCountMode", + "channelInterpretation", + "ChannelMergerNode", + "ChannelSplitterNode", + "CharacterData", + "characterSet", + "charAt", + "charCode", + "charCodeAt", + "charging", + "chargingTime", + "charIndex", + "charLength", + "charset", + "CHARSET_RULE", + "checked", + "checkEnclosure", + "checkFramebufferStatus", + "checkIntersection", + "checkValidity", + "childElementCount", + "childNodes", + "children", + "chOff", + "chrome", + "cite", + "CLAMP_TO_EDGE", + "classList", + "className", + "clear", + "clearBufferfi", + "clearBufferfv", + "clearBufferiv", + "clearBufferuiv", + "clearColor", + "clearData", + "clearDepth", + "clearInterval", + "clearLiveSeekableRange", + "clearMarks", + "clearMeasures", + "clearParameters", + "clearRect", + "clearResourceTimings", + "clearStencil", + "clearTimeout", + "clearWatch", + "click", + "clientHeight", + "clientInformation", + "clientLeft", + "clientTop", + "clientWaitSync", + "clientWidth", + "clientX", + "clientY", + "clip", + "clipboardData", + "ClipboardEvent", + "ClipboardItem", + "clipPathUnits", + "clone", + "cloneContents", + "cloneNode", + "cloneRange", + "close", + "closed", + "CLOSED", + "CloseEvent", + "closePath", + "closest", + "CLOSING", + "clz32", + "cm", + "cmp", + "code", + "codeBase", + "codePointAt", + "codeType", + "collapse", + "collapsed", + "collapseToEnd", + "collapseToStart", + "Collator", + "colno", + "COLOR", + "color", + "COLOR_ATTACHMENT0", + "COLOR_ATTACHMENT1", + "COLOR_ATTACHMENT10", + "COLOR_ATTACHMENT11", + "COLOR_ATTACHMENT12", + "COLOR_ATTACHMENT13", + "COLOR_ATTACHMENT14", + "COLOR_ATTACHMENT15", + "COLOR_ATTACHMENT2", + "COLOR_ATTACHMENT3", + "COLOR_ATTACHMENT4", + "COLOR_ATTACHMENT5", + "COLOR_ATTACHMENT6", + "COLOR_ATTACHMENT7", + "COLOR_ATTACHMENT8", + "COLOR_ATTACHMENT9", + "COLOR_BUFFER_BIT", + "COLOR_CLEAR_VALUE", + "COLOR_WRITEMASK", + "colorDepth", + "colorMask", + "cols", + "colSpan", + "columnNumber", + "Comment", + "COMMENT_NODE", + "commit", + "commitStyles", + "commonAncestorContainer", + "compact", + "COMPARE_REF_TO_TEXTURE", + "compareBoundaryPoints", + "compareDocumentPosition", + "compareExchange", + "comparePoint", + "compatMode", + "compile", + "COMPILE_STATUS", + "CompileError", + "compileShader", + "compileStreaming", + "complete", + "component", + "composed", + "composedPath", + "composite", + "CompositionEvent", + "COMPRESSED_TEXTURE_FORMATS", + "compressedTexImage2D", + "compressedTexImage3D", + "compressedTexSubImage2D", + "compressedTexSubImage3D", + "CompressionStream", + "computedStyleMap", + "concat", + "CONDITION_SATISFIED", + "conditionText", + "coneInnerAngle", + "coneOuterAngle", + "coneOuterGain", + "confirm", + "connect", + "connected", + "connectEnd", + "CONNECTING", + "connection", + "connectionState", + "connectStart", + "console", + "consolidate", + "CONSTANT_ALPHA", + "CONSTANT_COLOR", + "ConstantSourceNode", + "constraint", + "construct", + "constructor", + "containerId", + "containerName", + "containerSrc", + "containerType", + "contains", + "containsNode", + "content", + "contentBoxSize", + "contentDocument", + "contentEditable", + "contentHint", + "contentRect", + "contentType", + "contentWindow", + "context", + "CONTEXT_LOST_WEBGL", + "continue", + "continuePrimaryKey", + "continuous", + "control", + "controls", + "controlsList", + "convertToBlob", + "convertToSpecifiedUnits", + "ConvolverNode", + "cookie", + "cookieEnabled", + "coords", + "copy", + "COPY_READ_BUFFER", + "COPY_READ_BUFFER_BINDING", + "COPY_WRITE_BUFFER", + "COPY_WRITE_BUFFER_BINDING", + "copyBufferSubData", + "copyFromChannel", + "copyTexImage2D", + "copyTexSubImage2D", + "copyTexSubImage3D", + "copyToChannel", + "copyWithin", + "corruptedVideoFrames", + "cos", + "cosh", + "count", + "CountQueuingStrategy", + "countReset", + "create", + "createAnalyser", + "createAnswer", + "createAttribute", + "createAttributeNS", + "createBiquadFilter", + "createBuffer", + "createBufferSource", + "createCaption", + "createCDATASection", + "createChannelMerger", + "createChannelSplitter", + "createComment", + "createConstantSource", + "createContextualFragment", + "createConvolver", + "createDataChannel", + "createDelay", + "createDocument", + "createDocumentFragment", + "createDocumentType", + "createDTMFSender", + "createDynamicsCompressor", + "createElement", + "createElementNS", + "createEvent", + "createExpression", + "createFramebuffer", + "createGain", + "createHTML", + "createHTMLDocument", + "createIIRFilter", + "createImageBitmap", + "createImageData", + "createIndex", + "createLinearGradient", + "createMediaElementSource", + "createMediaStreamDestination", + "createMediaStreamSource", + "createNodeIterator", + "createNSResolver", + "createObjectStore", + "createObjectURL", + "createOffer", + "createOscillator", + "createPanner", + "createPattern", + "createPeriodicWave", + "createPolicy", + "createProcessingInstruction", + "createProgram", + "createQuery", + "createRadialGradient", + "createRange", + "createRenderbuffer", + "createSampler", + "createScript", + "createScriptProcessor", + "createScriptURL", + "createShader", + "createStereoPanner", + "createSVGAngle", + "createSVGLength", + "createSVGMatrix", + "createSVGNumber", + "createSVGPoint", + "createSVGRect", + "createSVGTransform", + "createSVGTransformFromMatrix", + "createTBody", + "createTextNode", + "createTexture", + "createTFoot", + "createTHead", + "createTransformFeedback", + "createTreeWalker", + "createVertexArray", + "createWaveShaper", + "creationTime", + "credentials", + "crossOrigin", + "Crypto", + "crypto", + "csi", + "csp", + "CSS", + "CSSAnimation", + "CSSConditionRule", + "cssFloat", + "CSSFontFaceRule", + "CSSGroupingRule", + "CSSImageValue", + "CSSImportRule", + "CSSKeyframeRule", + "CSSKeyframesRule", + "CSSKeywordValue", + "CSSMathInvert", + "CSSMathMax", + "CSSMathMin", + "CSSMathNegate", + "CSSMathProduct", + "CSSMathSum", + "CSSMathValue", + "CSSMatrixComponent", + "CSSMediaRule", + "CSSNamespaceRule", + "CSSNumericArray", + "CSSNumericValue", + "CSSPageRule", + "CSSPerspective", + "CSSPositionValue", + "CSSRotate", + "CSSRule", + "CSSRuleList", + "cssRules", + "CSSScale", + "CSSSkew", + "CSSSkewX", + "CSSSkewY", + "CSSStyleDeclaration", + "CSSStyleRule", + "CSSStyleSheet", + "CSSStyleValue", + "CSSSupportsRule", + "cssText", + "CSSTransformComponent", + "CSSTransformValue", + "CSSTransition", + "CSSTranslate", + "CSSUnitValue", + "CSSUnparsedValue", + "CSSVariableReferenceValue", + "ctrlKey", + "cues", + "CULL_FACE", + "CULL_FACE_MODE", + "cullFace", + "CURRENT_PROGRAM", + "CURRENT_QUERY", + "CURRENT_VERTEX_ATTRIB", + "currentDirection", + "currentLocalDescription", + "currentNode", + "currentRect", + "currentRemoteDescription", + "currentScale", + "currentScript", + "currentSrc", + "currentTarget", + "currentTime", + "currentTranslate", + "curve", + "CustomElementRegistry", + "customElements", + "customError", + "CustomEvent", + "CW", + "cx", + "cy", + "d", + "data", + "DATA_CLONE_ERR", + "databases", + "dataLoss", + "dataLossMessage", + "dataset", + "DataTransfer", + "dataTransfer", + "DataTransferItem", + "DataTransferItemList", + "DataView", + "Date", + "dateTime", + "DateTimeFormat", + "db", + "debug", + "declare", + "decode", + "decodeAudioData", + "decodedBodySize", + "decodeURI", + "decodeURIComponent", + "decoding", + "decodingInfo", + "DecompressionStream", + "DECR", + "DECR_WRAP", + "default", + "defaultChecked", + "defaultMuted", + "defaultPlaybackRate", + "defaultPolicy", + "defaultPrevented", + "defaultSelected", + "defaultStatus", + "defaultstatus", + "defaultValue", + "defaultView", + "defer", + "define", + "defineProperties", + "defineProperty", + "deg", + "delay", + "DelayNode", + "delayTime", + "delegatesFocus", + "delete", + "DELETE_STATUS", + "deleteBuffer", + "deleteCaption", + "deleteCell", + "deleteContents", + "deleteData", + "deleteDatabase", + "deleteFramebuffer", + "deleteFromDocument", + "deleteIndex", + "deleteMedium", + "deleteObjectStore", + "deleteProgram", + "deleteProperty", + "deleteQuery", + "deleteRenderbuffer", + "deleteRow", + "deleteRule", + "deleteSampler", + "deleteShader", + "deleteSync", + "deleteTexture", + "deleteTFoot", + "deleteTHead", + "deleteTransformFeedback", + "deleteVertexArray", + "deltaMode", + "deltaX", + "deltaY", + "deltaZ", + "DEPTH", + "DEPTH24_STENCIL8", + "DEPTH32F_STENCIL8", + "DEPTH_ATTACHMENT", + "DEPTH_BITS", + "DEPTH_BUFFER_BIT", + "DEPTH_CLEAR_VALUE", + "DEPTH_COMPONENT", + "DEPTH_COMPONENT16", + "DEPTH_COMPONENT24", + "DEPTH_COMPONENT32F", + "DEPTH_FUNC", + "DEPTH_RANGE", + "DEPTH_STENCIL", + "DEPTH_STENCIL_ATTACHMENT", + "DEPTH_TEST", + "DEPTH_WRITEMASK", + "depthFunc", + "depthMask", + "depthRange", + "deref", + "description", + "deselectAll", + "designMode", + "desiredSize", + "destination", + "detach", + "detachShader", + "detail", + "detune", + "devicePixelContentBoxSize", + "devicePixelRatio", + "didTimeout", + "diffuseConstant", + "dir", + "direction", + "dirName", + "dirxml", + "disable", + "disabled", + "disablePictureInPicture", + "disableRemotePlayback", + "disableVertexAttribArray", + "dischargingTime", + "disconnect", + "dispatchEvent", + "display", + "DisplayNames", + "disposition", + "distanceModel", + "DITHER", + "div", + "divisor", + "doctype", + "Document", + "document", + "DOCUMENT_FRAGMENT_NODE", + "DOCUMENT_NODE", + "DOCUMENT_POSITION_CONTAINED_BY", + "DOCUMENT_POSITION_CONTAINS", + "DOCUMENT_POSITION_DISCONNECTED", + "DOCUMENT_POSITION_FOLLOWING", + "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", + "DOCUMENT_POSITION_PRECEDING", + "DOCUMENT_TYPE_NODE", + "documentElement", + "DocumentFragment", + "DocumentTimeline", + "DocumentType", + "documentURI", + "DOM_DELTA_LINE", + "DOM_DELTA_PAGE", + "DOM_DELTA_PIXEL", + "DOM_KEY_LOCATION_LEFT", + "DOM_KEY_LOCATION_NUMPAD", + "DOM_KEY_LOCATION_RIGHT", + "DOM_KEY_LOCATION_STANDARD", + "domain", + "domainLookupEnd", + "domainLookupStart", + "domComplete", + "domContentLoadedEventEnd", + "domContentLoadedEventStart", + "DOMError", + "DOMException", + "DOMImplementation", + "domInteractive", + "domLoading", + "DOMMatrix", + "DOMMatrixReadOnly", + "DOMParser", + "DOMPoint", + "DOMPointReadOnly", + "DOMQuad", + "DOMRect", + "DOMRectList", + "DOMRectReadOnly", + "DOMSTRING_SIZE_ERR", + "DOMStringList", + "DOMStringMap", + "DOMTokenList", + "DONE", + "doNotTrack", + "DONT_CARE", + "dotAll", + "downlink", + "download", + "downloaded", + "downloadTotal", + "dpcm", + "dpi", + "dppx", + "DragEvent", + "draggable", + "DRAW_BUFFER0", + "DRAW_BUFFER1", + "DRAW_BUFFER10", + "DRAW_BUFFER11", + "DRAW_BUFFER12", + "DRAW_BUFFER13", + "DRAW_BUFFER14", + "DRAW_BUFFER15", + "DRAW_BUFFER2", + "DRAW_BUFFER3", + "DRAW_BUFFER4", + "DRAW_BUFFER5", + "DRAW_BUFFER6", + "DRAW_BUFFER7", + "DRAW_BUFFER8", + "DRAW_BUFFER9", + "DRAW_FRAMEBUFFER", + "DRAW_FRAMEBUFFER_BINDING", + "drawArrays", + "drawArraysInstanced", + "drawBuffers", + "drawElements", + "drawElementsInstanced", + "drawFocusIfNeeded", + "drawImage", + "drawingBufferHeight", + "drawingBufferWidth", + "drawRangeElements", + "dropEffect", + "droppedVideoFrames", + "DST_ALPHA", + "DST_COLOR", + "dtmf", + "durability", + "duration", + "dx", + "dy", + "DYNAMIC_COPY", + "DYNAMIC_DRAW", + "DYNAMIC_READ", + "DynamicsCompressorNode", + "E", + "e", + "edgeMode", + "effect", + "effectAllowed", + "effectiveDirective", + "effectiveType", + "elapsedTime", + "Element", + "element", + "ELEMENT_ARRAY_BUFFER", + "ELEMENT_ARRAY_BUFFER_BINDING", + "ELEMENT_NODE", + "elementFromPoint", + "ElementInternals", + "elements", + "elementsFromPoint", + "elementTiming", + "elevation", + "ellipse", + "em", + "embeddedSearch", + "embeds", + "emma", + "empty", + "EMPTY", + "emptyHTML", + "emptyScript", + "enable", + "enabled", + "enableDelegations", + "enabledPlugin", + "enableVertexAttribArray", + "encode", + "encodedBodySize", + "encodeInto", + "encodeURI", + "encodeURIComponent", + "encoding", + "enctype", + "end", + "END_TO_END", + "END_TO_START", + "endContainer", + "ended", + "endElement", + "endElementAt", + "endOffset", + "endOfStream", + "endpoint", + "endQuery", + "endsWith", + "endTime", + "endTransformFeedback", + "enterKeyHint", + "EnterPictureInPictureEvent", + "ENTITY_NODE", + "ENTITY_REFERENCE_NODE", + "entries", + "entryType", + "EPSILON", + "EQUAL", + "equals", + "Error", + "error", + "ERROR", + "errorCode", + "errorDetail", + "ErrorEvent", + "errorText", + "escape", + "eval", + "EvalError", + "evaluate", + "Event", + "event", + "eventPhase", + "EventSource", + "EventTarget", + "every", + "ex", + "exchange", + "exec", + "execCommand", + "exitFullscreen", + "exitPictureInPicture", + "exitPointerLock", + "exp", + "expand", + "expirationTime", + "expires", + "expm1", + "exponent", + "exponentialRampToValueAtTime", + "extend", + "extensions", + "extentNode", + "extentOffset", + "External", + "external", + "extractContents", + "f", + "face", + "failureReason", + "fallback", + "family", + "farthestViewportElement", + "FASTEST", + "fatal", + "FeaturePolicy", + "featurePolicy", + "features", + "featureSettings", + "fenceSync", + "fetch", + "fetchStart", + "fftSize", + "fgColor", + "File", + "FileList", + "filename", + "FileReader", + "files", + "fill", + "fillLightMode", + "fillRect", + "fillStyle", + "fillText", + "filter", + "FILTER_ACCEPT", + "FILTER_REJECT", + "FILTER_SKIP", + "filterUnits", + "FinalizationRegistry", + "finally", + "find", + "findIndex", + "findRule", + "finish", + "finished", + "firesTouchEvents", + "FIRST_ORDERED_NODE_TYPE", + "firstChild", + "firstElementChild", + "fixed", + "flags", + "flat", + "flatMap", + "flipX", + "flipY", + "FLOAT", + "Float32Array", + "Float64Array", + "FLOAT_32_UNSIGNED_INT_24_8_REV", + "FLOAT_MAT2", + "FLOAT_MAT2x3", + "FLOAT_MAT2x4", + "FLOAT_MAT3", + "FLOAT_MAT3x2", + "FLOAT_MAT3x4", + "FLOAT_MAT4", + "FLOAT_MAT4x2", + "FLOAT_MAT4x3", + "FLOAT_VEC2", + "FLOAT_VEC3", + "FLOAT_VEC4", + "floor", + "flush", + "focus", + "FocusEvent", + "focusNode", + "focusOffset", + "font", + "FONT_FACE_RULE", + "fontcolor", + "FontFace", + "fontfaces", + "FontFaceSetLoadEvent", + "fonts", + "fontsize", + "for", + "force", + "forceRedraw", + "forEach", + "form", + "formAction", + "FormData", + "formData", + "FormDataEvent", + "formEnctype", + "formMethod", + "formNoValidate", + "forms", + "formTarget", + "forward", + "forwardX", + "forwardY", + "forwardZ", + "foundation", + "fr", + "FRAGMENT_SHADER", + "FRAGMENT_SHADER_DERIVATIVE_HINT", + "FragmentDirective", + "fragmentDirective", + "frame", + "frameBorder", + "FRAMEBUFFER", + "FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE", + "FRAMEBUFFER_ATTACHMENT_BLUE_SIZE", + "FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING", + "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE", + "FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE", + "FRAMEBUFFER_ATTACHMENT_GREEN_SIZE", + "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", + "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", + "FRAMEBUFFER_ATTACHMENT_RED_SIZE", + "FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", + "FRAMEBUFFER_BINDING", + "FRAMEBUFFER_COMPLETE", + "FRAMEBUFFER_DEFAULT", + "FRAMEBUFFER_INCOMPLETE_ATTACHMENT", + "FRAMEBUFFER_INCOMPLETE_DIMENSIONS", + "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", + "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE", + "FRAMEBUFFER_UNSUPPORTED", + "framebufferRenderbuffer", + "framebufferTexture2D", + "framebufferTextureLayer", + "frameElement", + "frames", + "freeze", + "frequency", + "frequencyBinCount", + "from", + "fromCharCode", + "fromCodePoint", + "fromElement", + "fromEntries", + "fromFloat32Array", + "fromFloat64Array", + "fromMatrix", + "fromPoint", + "fromQuad", + "fromRect", + "FRONT", + "FRONT_AND_BACK", + "FRONT_FACE", + "frontFace", + "fround", + "fullscreen", + "fullscreenElement", + "fullscreenEnabled", + "FUNC_ADD", + "FUNC_REVERSE_SUBTRACT", + "FUNC_SUBTRACT", + "Function", + "fx", + "fy", + "gain", + "GainNode", + "Gamepad", + "gamepad", + "GamepadButton", + "GamepadEvent", + "GamepadHapticActuator", + "GENERATE_MIPMAP_HINT", + "generateCertificate", + "generateMipmap", + "Geolocation", + "geolocation", + "GeolocationCoordinates", + "GeolocationPosition", + "GeolocationPositionError", + "GEQUAL", + "get", + "getActiveAttrib", + "getActiveUniform", + "getActiveUniformBlockName", + "getActiveUniformBlockParameter", + "getActiveUniforms", + "getAll", + "getAllKeys", + "getAllowlistForFeature", + "getAllResponseHeaders", + "getAnimations", + "getAsFile", + "getAsString", + "getAttachedShaders", + "getAttribLocation", + "getAttribute", + "getAttributeNames", + "getAttributeNode", + "getAttributeNodeNS", + "getAttributeNS", + "getAttributeType", + "getAudioTracks", + "getBattery", + "getBBox", + "getBigInt64", + "getBigUint64", + "getBoundingClientRect", + "getBounds", + "getBufferParameter", + "getBufferSubData", + "getByteFrequencyData", + "getByteTimeDomainData", + "getCanonicalLocales", + "getCapabilities", + "getChannelData", + "getCharacteristic", + "getCharNumAtPosition", + "getClientRects", + "getCoalescedEvents", + "getComputedStyle", + "getComputedTextLength", + "getComputedTiming", + "getConfiguration", + "getConstraints", + "getContext", + "getContextAttributes", + "getContributingSources", + "getCTM", + "getCueAsHTML", + "getCueById", + "getCurrentPosition", + "getCurrentTime", + "getData", + "getDate", + "getDay", + "getDescriptor", + "getDistributedNodes", + "getElementById", + "getElementsByClassName", + "getElementsByName", + "getElementsByTagName", + "getElementsByTagNameNS", + "getEnclosureList", + "getEndPositionOfChar", + "getEntries", + "getEntriesByName", + "getEntriesByType", + "getError", + "getEventListeners", + "getExtension", + "getExtentOfChar", + "getFingerprints", + "getFloat32", + "getFloat64", + "getFloatFrequencyData", + "getFloatTimeDomainData", + "getFragDataLocation", + "getFramebufferAttachmentParameter", + "getFrequencyResponse", + "getFullYear", + "getGamepads", + "getHours", + "getIds", + "getImageData", + "getIndexedParameter", + "getInt16", + "getInt32", + "getInt8", + "getInternalformatParameter", + "getIntersectionList", + "getItem", + "getKey", + "getKeyframes", + "getLineDash", + "getLocalStreams", + "getMilliseconds", + "getMinutes", + "getModifierState", + "getMonth", + "getNamedItem", + "getNamedItemNS", + "getNumberOfChars", + "getOutputTimestamp", + "getOwnPropertyDescriptor", + "getOwnPropertyDescriptors", + "getOwnPropertyNames", + "getOwnPropertySymbols", + "getParameter", + "getParameters", + "getPhotoCapabilities", + "getPhotoSettings", + "getPointAtLength", + "getPredictedEvents", + "getProgramInfoLog", + "getProgramParameter", + "getPropertyPriority", + "getPropertyType", + "getPropertyValue", + "getPrototypeOf", + "getQuery", + "getQueryParameter", + "getRandomValues", + "getRangeAt", + "getReader", + "getReceivers", + "getRemoteCertificates", + "getRemoteStreams", + "getRenderbufferParameter", + "getResponseHeader", + "getRootNode", + "getRotationOfChar", + "getSamplerParameter", + "getScreenCTM", + "getSeconds", + "getSelection", + "getSenders", + "getService", + "getSettings", + "getShaderInfoLog", + "getShaderParameter", + "getShaderPrecisionFormat", + "getShaderSource", + "getSimpleDuration", + "getStartPositionOfChar", + "getStartTime", + "getStats", + "getSubscription", + "getSubStringLength", + "getSupportedExtensions", + "getSVGDocument", + "getSynchronizationSources", + "getSyncParameter", + "getTags", + "getTargetRanges", + "getTexParameter", + "getTime", + "getTimezoneOffset", + "getTiming", + "getTotalLength", + "getTrackById", + "getTracks", + "getTransceivers", + "getTransform", + "getTransformFeedbackVarying", + "getType", + "getTypeMapping", + "getUint16", + "getUint32", + "getUint8", + "getUniform", + "getUniformBlockIndex", + "getUniformIndices", + "getUniformLocation", + "getUTCDate", + "getUTCDay", + "getUTCFullYear", + "getUTCHours", + "getUTCMilliseconds", + "getUTCMinutes", + "getUTCMonth", + "getUTCSeconds", + "getVertexAttrib", + "getVertexAttribOffset", + "getVideoPlaybackQuality", + "getVideoTracks", + "getWriter", + "getYear", + "global", + "Global", + "globalAlpha", + "globalCompositeOperation", + "globalThis", + "go", + "grabFrame", + "grad", + "gradientTransform", + "gradientUnits", + "grammars", + "GREATER", + "GREEN_BITS", + "group", + "groupCollapsed", + "groupEnd", + "hadRecentInput", + "HALF_FLOAT", + "hardwareConcurrency", + "has", + "hasAttribute", + "hasAttributeNS", + "hasAttributes", + "hasBeenActive", + "hasChildNodes", + "hasFeature", + "hasFocus", + "hash", + "HashChangeEvent", + "hasInstance", + "hasOwnProperty", + "hasPointerCapture", + "HAVE_CURRENT_DATA", + "HAVE_ENOUGH_DATA", + "HAVE_FUTURE_DATA", + "HAVE_METADATA", + "HAVE_NOTHING", + "head", + "Headers", + "headers", + "HEADERS_RECEIVED", + "heading", + "height", + "hidden", + "HIERARCHY_REQUEST_ERR", + "high", + "HIGH_FLOAT", + "HIGH_INT", + "highWaterMark", + "hint", + "History", + "history", + "host", + "hostCandidate", + "hostname", + "href", + "hreflang", + "hrefTranslate", + "hspace", + "HTMLAllCollection", + "HTMLAnchorElement", + "HTMLAreaElement", + "HTMLAudioElement", + "HTMLBaseElement", + "HTMLBodyElement", + "HTMLBRElement", + "HTMLButtonElement", + "HTMLCanvasElement", + "HTMLCollection", + "HTMLContentElement", + "HTMLDataElement", + "HTMLDataListElement", + "HTMLDetailsElement", + "HTMLDialogElement", + "HTMLDirectoryElement", + "HTMLDivElement", + "HTMLDListElement", + "HTMLDocument", + "HTMLElement", + "HTMLEmbedElement", + "HTMLFieldSetElement", + "HTMLFontElement", + "htmlFor", + "HTMLFormControlsCollection", + "HTMLFormElement", + "HTMLFrameElement", + "HTMLFrameSetElement", + "HTMLHeadElement", + "HTMLHeadingElement", + "HTMLHRElement", + "HTMLHtmlElement", + "HTMLIFrameElement", + "HTMLImageElement", + "HTMLInputElement", + "HTMLLabelElement", + "HTMLLegendElement", + "HTMLLIElement", + "HTMLLinkElement", + "HTMLMapElement", + "HTMLMarqueeElement", + "HTMLMediaElement", + "HTMLMenuElement", + "HTMLMetaElement", + "HTMLMeterElement", + "HTMLModElement", + "HTMLObjectElement", + "HTMLOListElement", + "HTMLOptGroupElement", + "HTMLOptionElement", + "HTMLOptionsCollection", + "HTMLOutputElement", + "HTMLParagraphElement", + "HTMLParamElement", + "HTMLPictureElement", + "HTMLPreElement", + "HTMLProgressElement", + "HTMLQuoteElement", + "HTMLScriptElement", + "HTMLSelectElement", + "HTMLShadowElement", + "HTMLSlotElement", + "HTMLSourceElement", + "HTMLSpanElement", + "HTMLStyleElement", + "HTMLTableCaptionElement", + "HTMLTableCellElement", + "HTMLTableColElement", + "HTMLTableElement", + "HTMLTableRowElement", + "HTMLTableSectionElement", + "HTMLTemplateElement", + "HTMLTextAreaElement", + "HTMLTimeElement", + "HTMLTitleElement", + "HTMLTrackElement", + "HTMLUListElement", + "HTMLUnknownElement", + "HTMLVideoElement", + "httpEquiv", + "httpRequestStatusCode", + "hypot", + "Hz", + "iceConnectionState", + "iceGatheringState", + "iceTransport", + "icon", + "id", + "IDBCursor", + "IDBCursorWithValue", + "IDBDatabase", + "IDBFactory", + "IDBIndex", + "IDBKeyRange", + "IDBObjectStore", + "IDBOpenDBRequest", + "IDBRequest", + "IDBTransaction", + "IDBVersionChangeEvent", + "identifier", + "IdleDeadline", + "ignoreBOM", + "ignoreCase", + "IIRFilterNode", + "Image", + "image", + "ImageBitmap", + "ImageBitmapRenderingContext", + "ImageCapture", + "ImageData", + "imageHeight", + "images", + "imageSizes", + "imageSmoothingEnabled", + "imageSmoothingQuality", + "imageSrcset", + "imageWidth", + "implementation", + "IMPLEMENTATION_COLOR_READ_FORMAT", + "IMPLEMENTATION_COLOR_READ_TYPE", + "IMPORT_RULE", + "importNode", + "importStylesheet", + "imul", + "in", + "in1", + "in2", + "includes", + "INCR", + "INCR_WRAP", + "incremental", + "indeterminate", + "index", + "INDEX_SIZE_ERR", + "indexedDB", + "indexNames", + "indexOf", + "Infinity", + "info", + "initCompositionEvent", + "initCustomEvent", + "initData", + "initDataType", + "initEvent", + "initialize", + "initiatorType", + "initKeyboardEvent", + "initMessageEvent", + "initMouseEvent", + "initMutationEvent", + "initStorageEvent", + "initTextEvent", + "initUIEvent", + "inlineSize", + "innerHeight", + "innerHTML", + "innerText", + "innerWidth", + "input", + "inputBuffer", + "InputDeviceCapabilities", + "InputDeviceInfo", + "inputEncoding", + "InputEvent", + "inputMode", + "inputType", + "insertAdjacentElement", + "insertAdjacentHTML", + "insertAdjacentText", + "insertBefore", + "insertCell", + "insertData", + "insertDTMF", + "insertItemBefore", + "insertNode", + "insertRow", + "insertRule", + "inspect", + "Instance", + "instantiate", + "instantiateStreaming", + "instruments", + "INT", + "Int16Array", + "Int32Array", + "Int8Array", + "INT_2_10_10_10_REV", + "INT_SAMPLER_2D", + "INT_SAMPLER_2D_ARRAY", + "INT_SAMPLER_3D", + "INT_SAMPLER_CUBE", + "INT_VEC2", + "INT_VEC3", + "INT_VEC4", + "integrity", + "intercept", + "interimResults", + "INTERLEAVED_ATTRIBS", + "interpretation", + "IntersectionObserver", + "IntersectionObserverEntry", + "intersectionRatio", + "intersectionRect", + "intersectsNode", + "Intl", + "INUSE_ATTRIBUTE_ERR", + "INVALID_ACCESS_ERR", + "INVALID_CHARACTER_ERR", + "INVALID_ENUM", + "INVALID_FRAMEBUFFER_OPERATION", + "INVALID_INDEX", + "INVALID_MODIFICATION_ERR", + "INVALID_NODE_TYPE_ERR", + "INVALID_OPERATION", + "INVALID_STATE_ERR", + "INVALID_VALUE", + "invalidateFramebuffer", + "invalidateSubFramebuffer", + "invalidIteratorState", + "inverse", + "INVERT", + "invertSelf", + "is", + "is2D", + "isActive", + "isArray", + "isBuffer", + "isCollapsed", + "isComposing", + "isConcatSpreadable", + "isConnected", + "isContentEditable", + "isContextLost", + "isDefaultNamespace", + "isEnabled", + "isEqualNode", + "isExtensible", + "isFinite", + "isFramebuffer", + "isFrozen", + "isHistoryNavigation", + "isHTML", + "isIdentity", + "isInteger", + "isIntersecting", + "isLockFree", + "isMap", + "isNaN", + "isPointInFill", + "isPointInPath", + "isPointInRange", + "isPointInStroke", + "isPrimary", + "isProgram", + "isPrototypeOf", + "isQuery", + "isRenderbuffer", + "isSafeInteger", + "isSameNode", + "isSampler", + "isScript", + "isScriptURL", + "isSealed", + "IsSearchProviderInstalled", + "isSecureContext", + "isShader", + "isSync", + "isTexture", + "isTransformFeedback", + "isTrusted", + "isTypeSupported", + "isVertexArray", + "isView", + "isVisible", + "italics", + "item", + "items", + "iterateNext", + "iterator", + "javaEnabled", + "join", + "JSON", + "json", + "k1", + "k2", + "k3", + "k4", + "KEEP", + "keepalive", + "kernelMatrix", + "kernelUnitLengthX", + "kernelUnitLengthY", + "key", + "KeyboardEvent", + "keyCode", + "keyFor", + "KEYFRAME_RULE", + "KeyframeEffect", + "KEYFRAMES_RULE", + "keyPath", + "keys", + "keyText", + "kHz", + "kind", + "knee", + "label", + "labels", + "lang", + "language", + "languages", + "LargestContentfulPaint", + "lastChild", + "lastElementChild", + "lastEventId", + "lastIndex", + "lastIndexOf", + "lastInputTime", + "lastMatch", + "lastModified", + "lastModifiedDate", + "lastParen", + "latitude", + "layerX", + "layerY", + "LayoutShift", + "LayoutShiftAttribution", + "left", + "leftContext", + "length", + "lengthAdjust", + "LENGTHADJUST_SPACING", + "LENGTHADJUST_SPACINGANDGLYPHS", + "LENGTHADJUST_UNKNOWN", + "lengthComputable", + "LEQUAL", + "LESS", + "level", + "limitingConeAngle", + "line", + "LINE_LOOP", + "LINE_STRIP", + "LINE_WIDTH", + "LINEAR", + "LINEAR_MIPMAP_LINEAR", + "LINEAR_MIPMAP_NEAREST", + "linearRampToValueAtTime", + "lineCap", + "lineDashOffset", + "lineJoin", + "lineno", + "lineNumber", + "LINES", + "lineTo", + "lineWidth", + "link", + "LINK_STATUS", + "linkColor", + "LinkError", + "linkProgram", + "links", + "list", + "listener", + "ListFormat", + "LN10", + "LN2", + "load", + "loaded", + "LOADED", + "loadEventEnd", + "loadEventStart", + "LOADING", + "loading", + "loadTime", + "loadTimes", + "localDescription", + "Locale", + "localeCompare", + "localName", + "localStorage", + "Location", + "location", + "locationbar", + "lock", + "locked", + "log", + "log10", + "LOG10E", + "log1p", + "log2", + "LOG2E", + "longDesc", + "longitude", + "lookupNamespaceURI", + "lookupPrefix", + "loop", + "loopEnd", + "loopStart", + "low", + "LOW_FLOAT", + "LOW_INT", + "lower", + "lowerBound", + "lowerOpen", + "lowsrc", + "LUMINANCE", + "LUMINANCE_ALPHA", + "m11", + "m12", + "m13", + "m14", + "m21", + "m22", + "m23", + "m24", + "m31", + "m32", + "m33", + "m34", + "m41", + "m42", + "m43", + "m44", + "map", + "Map", + "mapping", + "marginHeight", + "marginWidth", + "mark", + "markerHeight", + "markerUnits", + "markerWidth", + "maskContentUnits", + "maskUnits", + "match", + "matchAll", + "matches", + "matchMedia", + "Math", + "matrix", + "matrixTransform", + "max", + "MAX", + "MAX_3D_TEXTURE_SIZE", + "MAX_ARRAY_TEXTURE_LAYERS", + "MAX_CLIENT_WAIT_TIMEOUT_WEBGL", + "MAX_COLOR_ATTACHMENTS", + "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", + "MAX_COMBINED_TEXTURE_IMAGE_UNITS", + "MAX_COMBINED_UNIFORM_BLOCKS", + "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", + "MAX_CUBE_MAP_TEXTURE_SIZE", + "MAX_DRAW_BUFFERS", + "MAX_ELEMENT_INDEX", + "MAX_ELEMENTS_INDICES", + "MAX_ELEMENTS_VERTICES", + "MAX_FRAGMENT_INPUT_COMPONENTS", + "MAX_FRAGMENT_UNIFORM_BLOCKS", + "MAX_FRAGMENT_UNIFORM_COMPONENTS", + "MAX_FRAGMENT_UNIFORM_VECTORS", + "MAX_PROGRAM_TEXEL_OFFSET", + "MAX_RENDERBUFFER_SIZE", + "MAX_SAFE_INTEGER", + "MAX_SAMPLES", + "MAX_SERVER_WAIT_TIMEOUT", + "MAX_TEXTURE_IMAGE_UNITS", + "MAX_TEXTURE_LOD_BIAS", + "MAX_TEXTURE_SIZE", + "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", + "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", + "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", + "MAX_UNIFORM_BLOCK_SIZE", + "MAX_UNIFORM_BUFFER_BINDINGS", + "MAX_VALUE", + "MAX_VARYING_COMPONENTS", + "MAX_VARYING_VECTORS", + "MAX_VERTEX_ATTRIBS", + "MAX_VERTEX_OUTPUT_COMPONENTS", + "MAX_VERTEX_TEXTURE_IMAGE_UNITS", + "MAX_VERTEX_UNIFORM_BLOCKS", + "MAX_VERTEX_UNIFORM_COMPONENTS", + "MAX_VERTEX_UNIFORM_VECTORS", + "MAX_VIEWPORT_DIMS", + "maxActions", + "maxAlternatives", + "maxChannelCount", + "maxChannels", + "maxDecibels", + "maxDistance", + "maxLength", + "maxMessageSize", + "maxPacketLifeTime", + "maxRetransmits", + "maxTouchPoints", + "maxValue", + "measure", + "measureText", + "media", + "MEDIA_ERR_ABORTED", + "MEDIA_ERR_DECODE", + "MEDIA_ERR_NETWORK", + "MEDIA_ERR_SRC_NOT_SUPPORTED", + "MEDIA_RULE", + "MediaCapabilities", + "mediaCapabilities", + "mediaElement", + "MediaElementAudioSourceNode", + "MediaEncryptedEvent", + "MediaError", + "MediaList", + "MediaMetadata", + "MediaQueryList", + "MediaQueryListEvent", + "MediaRecorder", + "MediaSession", + "mediaSession", + "MediaSettingsRange", + "MediaSource", + "MediaStream", + "mediaStream", + "MediaStreamAudioDestinationNode", + "MediaStreamAudioSourceNode", + "MediaStreamEvent", + "MediaStreamTrack", + "MediaStreamTrackEvent", + "mediaText", + "MEDIUM_FLOAT", + "MEDIUM_INT", + "meetOrSlice", + "memory", + "Memory", + "menubar", + "message", + "MessageChannel", + "MessageEvent", + "MessagePort", + "metadata", + "metaKey", + "method", + "mid", + "MimeType", + "mimeType", + "MimeTypeArray", + "mimeTypes", + "min", + "MIN", + "MIN_PROGRAM_TEXEL_OFFSET", + "MIN_SAFE_INTEGER", + "MIN_VALUE", + "minDecibels", + "minLength", + "minValue", + "MIRRORED_REPEAT", + "miterLimit", + "mm", + "mode", + "MODIFICATION", + "modify", + "Module", + "monitor", + "monitorEvents", + "MouseEvent", + "moveBy", + "movementX", + "movementY", + "moveTo", + "ms", + "mul", + "multiEntry", + "multiline", + "multiple", + "multiply", + "multiplySelf", + "MutationEvent", + "MutationObserver", + "MutationRecord", + "muted", + "name", + "namedItem", + "NamedNodeMap", + "NAMESPACE_ERR", + "NAMESPACE_RULE", + "namespaceURI", + "NaN", + "naturalHeight", + "naturalWidth", + "navigation", + "navigationStart", + "Navigator", + "navigator", + "NEAREST", + "NEAREST_MIPMAP_LINEAR", + "NEAREST_MIPMAP_NEAREST", + "nearestViewportElement", + "NEGATIVE_INFINITY", + "negotiated", + "NETWORK_EMPTY", + "NETWORK_ERR", + "NETWORK_IDLE", + "NETWORK_LOADING", + "NETWORK_NO_SOURCE", + "NetworkInformation", + "networkState", + "NEVER", + "newURL", + "newValue", + "newValueSpecifiedUnits", + "newVersion", + "nextElementSibling", + "nextHopProtocol", + "nextNode", + "nextSibling", + "NICEST", + "NO_DATA_ALLOWED_ERR", + "NO_ERROR", + "NO_MODIFICATION_ALLOWED_ERR", + "Node", + "node", + "NodeFilter", + "NodeIterator", + "NodeList", + "nodeName", + "nodeType", + "nodeValue", + "noHref", + "noModule", + "nonce", + "NONE", + "noResize", + "normalize", + "noShade", + "NOT_FOUND_ERR", + "NOT_SUPPORTED_ERR", + "NOTATION_NODE", + "NOTEQUAL", + "Notification", + "notify", + "noValidate", + "now", + "noWrap", + "Number", + "number", + "NUMBER_TYPE", + "NumberFormat", + "numberOfChannels", + "numberOfInputs", + "numberOfItems", + "numberOfOutputs", + "numberValue", + "numOctaves", + "Object", + "OBJECT_TYPE", + "objectStore", + "objectStoreNames", + "observe", + "of", + "OfflineAudioCompletionEvent", + "OfflineAudioContext", + "offscreenBuffering", + "OffscreenCanvas", + "OffscreenCanvasRenderingContext2D", + "offset", + "offsetHeight", + "offsetLeft", + "offsetParent", + "offsetTop", + "offsetWidth", + "offsetX", + "offsetY", + "ok", + "oldURL", + "oldValue", + "oldVersion", + "onabort", + "onactive", + "onaddsourcebuffer", + "onaddstream", + "onaddtrack", + "onafterprint", + "onanimationend", + "onanimationiteration", + "onanimationstart", + "onappinstalled", + "onaudioend", + "onaudioprocess", + "onaudiostart", + "onauxclick", + "onbeforecopy", + "onbeforecut", + "onbeforeinstallprompt", + "onbeforepaste", + "onbeforeprint", + "onbeforeunload", + "onbeforexrselect", + "onbegin", + "onblocked", + "onblur", + "onboundary", + "onbufferedamountlow", + "oncancel", + "oncanplay", + "oncanplaythrough", + "onchange", + "onchargingchange", + "onchargingtimechange", + "onclick", + "onclose", + "onclosing", + "oncomplete", + "onconnect", + "onconnecting", + "onconnectionstatechange", + "oncontextmenu", + "oncopy", + "oncuechange", + "oncut", + "ondataavailable", + "ondatachannel", + "ondblclick", + "ondischargingtimechange", + "ondisconnect", + "ondrag", + "ondragend", + "ondragenter", + "ondragleave", + "ondragover", + "ondragstart", + "ondrop", + "ondurationchange", + "ONE", + "ONE_MINUS_CONSTANT_ALPHA", + "ONE_MINUS_CONSTANT_COLOR", + "ONE_MINUS_DST_ALPHA", + "ONE_MINUS_DST_COLOR", + "ONE_MINUS_SRC_ALPHA", + "ONE_MINUS_SRC_COLOR", + "onemptied", + "onencrypted", + "onend", + "onended", + "onenter", + "onenterpictureinpicture", + "onerror", + "onexit", + "onfinish", + "onfocus", + "onformdata", + "onfreeze", + "onfullscreenchange", + "onfullscreenerror", + "ongotpointercapture", + "onhashchange", + "onicecandidate", + "onicecandidateerror", + "oniceconnectionstatechange", + "onicegatheringstatechange", + "oninactive", + "oninput", + "oninvalid", + "onkeydown", + "onkeypress", + "onkeyup", + "onlanguagechange", + "onleavepictureinpicture", + "onlevelchange", + "onLine", + "onload", + "onloadeddata", + "onloadedmetadata", + "onloadend", + "onloadstart", + "onlostpointercapture", + "only", + "onmark", + "onmessage", + "onmessageerror", + "onmousedown", + "onmouseenter", + "onmouseleave", + "onmousemove", + "onmouseout", + "onmouseover", + "onmouseup", + "onmousewheel", + "onmute", + "onnegotiationneeded", + "onnomatch", + "onoffline", + "ononline", + "onopen", + "onpagehide", + "onpageshow", + "onpaste", + "onpause", + "onplay", + "onplaying", + "onpointercancel", + "onpointerdown", + "onpointerenter", + "onpointerleave", + "onpointerlockchange", + "onpointerlockerror", + "onpointermove", + "onpointerout", + "onpointerover", + "onpointerrawupdate", + "onpointerup", + "onpopstate", + "onprocessorerror", + "onprogress", + "onratechange", + "onreadystatechange", + "onrejectionhandled", + "onremove", + "onremovesourcebuffer", + "onremovestream", + "onremovetrack", + "onrepeat", + "onreset", + "onresize", + "onresourcetimingbufferfull", + "onresult", + "onresume", + "onscroll", + "onsearch", + "onsecuritypolicyviolation", + "onseeked", + "onseeking", + "onselect", + "onselectionchange", + "onselectstart", + "onshow", + "onsignalingstatechange", + "onsoundend", + "onsoundstart", + "onsourceclose", + "onsourceended", + "onsourceopen", + "onspeechend", + "onspeechstart", + "onstalled", + "onstart", + "onstatechange", + "onstop", + "onstorage", + "onsubmit", + "onsuccess", + "onsuspend", + "ontimeout", + "ontimeupdate", + "ontoggle", + "ontonechange", + "ontrack", + "ontransitionend", + "onunhandledrejection", + "onunload", + "onunmute", + "onupdate", + "onupdateend", + "onupdatestart", + "onupgradeneeded", + "onversionchange", + "onvisibilitychange", + "onvolumechange", + "onwaiting", + "onwaitingforkey", + "onwebkitanimationend", + "onwebkitanimationiteration", + "onwebkitanimationstart", + "onwebkitfullscreenchange", + "onwebkitfullscreenerror", + "onwebkittransitionend", + "onwheel", + "open", + "OPEN", + "openCursor", + "openDatabase", + "OPENED", + "opener", + "openKeyCursor", + "operator", + "optimum", + "Option", + "options", + "or", + "ordered", + "ORDERED_NODE_ITERATOR_TYPE", + "ORDERED_NODE_SNAPSHOT_TYPE", + "orderX", + "orderY", + "orientAngle", + "orientation", + "orientationX", + "orientationY", + "orientationZ", + "orientType", + "origin", + "originalPolicy", + "OscillatorNode", + "OUT_OF_MEMORY", + "outerHeight", + "outerHTML", + "outerText", + "outerWidth", + "outputBuffer", + "OverconstrainedError", + "overrideMimeType", + "oversample", + "ownerDocument", + "ownerElement", + "ownerNode", + "ownerRule", + "ownerSVGElement", + "ownKeys", + "p1", + "p2", + "p3", + "p4", + "PACK_ALIGNMENT", + "PACK_ROW_LENGTH", + "PACK_SKIP_PIXELS", + "PACK_SKIP_ROWS", + "padEnd", + "padStart", + "PAGE_RULE", + "pageLeft", + "pageTop", + "PageTransitionEvent", + "pageX", + "pageXOffset", + "pageY", + "pageYOffset", + "pan", + "PannerNode", + "panningModel", + "parameters", + "parent", + "parentElement", + "parentNode", + "parentRule", + "parentStyleSheet", + "parse", + "parseAll", + "parseFloat", + "parseFromString", + "parseInt", + "part", + "password", + "path", + "Path2D", + "pathLength", + "pathname", + "pattern", + "patternContentUnits", + "patternMismatch", + "patternTransform", + "patternUnits", + "pause", + "pauseAnimations", + "paused", + "pauseOnExit", + "pauseTransformFeedback", + "PaymentInstruments", + "PaymentManager", + "PaymentRequestUpdateEvent", + "pc", + "pending", + "pendingLocalDescription", + "pendingRemoteDescription", + "percent", + "Performance", + "performance", + "PerformanceElementTiming", + "PerformanceEntry", + "PerformanceEventTiming", + "PerformanceLongTaskTiming", + "PerformanceMark", + "PerformanceMeasure", + "PerformanceNavigation", + "PerformanceNavigationTiming", + "PerformanceObserver", + "PerformanceObserverEntryList", + "PerformancePaintTiming", + "PerformanceResourceTiming", + "PerformanceServerTiming", + "PerformanceTiming", + "PeriodicSyncManager", + "PeriodicWave", + "permission", + "PERMISSION_DENIED", + "Permissions", + "permissions", + "permissionState", + "PermissionStatus", + "persist", + "persisted", + "PERSISTENT", + "personalbar", + "PhotoCapabilities", + "PI", + "pictureInPictureElement", + "pictureInPictureEnabled", + "PictureInPictureWindow", + "pictureInPictureWindow", + "ping", + "pipeThrough", + "pipeTo", + "pitch", + "PIXEL_PACK_BUFFER", + "PIXEL_PACK_BUFFER_BINDING", + "PIXEL_UNPACK_BUFFER", + "PIXEL_UNPACK_BUFFER_BINDING", + "pixelDepth", + "pixelStorei", + "placeholder", + "platform", + "platforms", + "play", + "playbackRate", + "playbackState", + "playbackTime", + "played", + "playEffect", + "playoutDelayHint", + "playsInline", + "playState", + "Plugin", + "PluginArray", + "plugins", + "PluralRules", + "pointerBeforeReferenceNode", + "PointerEvent", + "pointerId", + "pointerLockElement", + "pointerType", + "POINTS", + "points", + "pointsAtX", + "pointsAtY", + "pointsAtZ", + "POLYGON_OFFSET_FACTOR", + "POLYGON_OFFSET_FILL", + "POLYGON_OFFSET_UNITS", + "polygonOffset", + "pop", + "PopStateEvent", + "port", + "port1", + "port2", + "ports", + "position", + "POSITION_UNAVAILABLE", + "positionX", + "positionY", + "positionZ", + "POSITIVE_INFINITY", + "poster", + "postMessage", + "pow", + "precision", + "prefix", + "preload", + "preMultiplySelf", + "prepend", + "preserveAlpha", + "preserveAspectRatio", + "pressed", + "pressure", + "preventDefault", + "preventExtensions", + "previousElementSibling", + "previousNode", + "previousRect", + "previousSibling", + "prevValue", + "primaryKey", + "primitiveUnits", + "print", + "priority", + "PROCESSING_INSTRUCTION_NODE", + "processingEnd", + "ProcessingInstruction", + "processingStart", + "product", + "productSub", + "profile", + "profileEnd", + "ProgressEvent", + "Promise", + "promise", + "PromiseRejectionEvent", + "prompt", + "propertyIsEnumerable", + "propertyName", + "protocol", + "prototype", + "Proxy", + "pseudoElement", + "pt", + "publicId", + "push", + "PushManager", + "pushState", + "PushSubscription", + "PushSubscriptionOptions", + "put", + "putImageData", + "px", + "Q", + "quadraticCurveTo", + "query", + "QUERY_RESULT", + "QUERY_RESULT_AVAILABLE", + "queryCommandEnabled", + "queryCommandIndeterm", + "queryCommandState", + "queryCommandSupported", + "queryCommandValue", + "queryObjects", + "querySelector", + "querySelectorAll", + "queueMicrotask", + "QUOTA_EXCEEDED_ERR", + "r", + "R11F_G11F_B10F", + "R16F", + "R16I", + "R16UI", + "R32F", + "R32I", + "R32UI", + "R8", + "R8_SNORM", + "R8I", + "R8UI", + "race", + "rad", + "RadioNodeList", + "radiusX", + "radiusY", + "random", + "Range", + "rangeCount", + "RangeError", + "rangeMax", + "rangeMin", + "rangeOverflow", + "rangeUnderflow", + "RASTERIZER_DISCARD", + "rate", + "ratio", + "raw", + "read", + "READ_BUFFER", + "READ_FRAMEBUFFER", + "READ_FRAMEBUFFER_BINDING", + "readable", + "ReadableStream", + "ReadableStreamDefaultReader", + "readAsArrayBuffer", + "readAsBinaryString", + "readAsDataURL", + "readAsText", + "readBuffer", + "readOnly", + "readPixels", + "ready", + "readyState", + "reason", + "receivedAlert", + "receiver", + "recordsAvailable", + "rect", + "RED", + "RED_BITS", + "RED_INTEGER", + "redEyeReduction", + "redirect", + "redirectCount", + "redirected", + "redirectEnd", + "redirectStart", + "reduce", + "reduceRight", + "reduction", + "refDistance", + "ReferenceError", + "referenceNode", + "referrer", + "referrerPolicy", + "Reflect", + "refresh", + "refX", + "refY", + "RegExp", + "register", + "registerProperty", + "reject", + "rel", + "relatedAddress", + "relatedNode", + "relatedPort", + "relatedTarget", + "RelativeTimeFormat", + "release", + "releaseEvents", + "releaseLock", + "releasePointerCapture", + "reliable", + "relList", + "reload", + "rem", + "remote", + "remoteDescription", + "RemotePlayback", + "REMOVAL", + "remove", + "removeAllRanges", + "removeAttribute", + "removeAttributeNode", + "removeAttributeNS", + "removeChild", + "removeCue", + "removedNodes", + "removeEventListener", + "removeItem", + "removeListener", + "removeNamedItem", + "removeNamedItemNS", + "removeParameter", + "removeProperty", + "removeRange", + "removeRule", + "removeSourceBuffer", + "removeStream", + "removeTrack", + "RENDERBUFFER", + "RENDERBUFFER_ALPHA_SIZE", + "RENDERBUFFER_BINDING", + "RENDERBUFFER_BLUE_SIZE", + "RENDERBUFFER_DEPTH_SIZE", + "RENDERBUFFER_GREEN_SIZE", + "RENDERBUFFER_HEIGHT", + "RENDERBUFFER_INTERNAL_FORMAT", + "RENDERBUFFER_RED_SIZE", + "RENDERBUFFER_SAMPLES", + "RENDERBUFFER_STENCIL_SIZE", + "RENDERBUFFER_WIDTH", + "renderbufferStorage", + "renderbufferStorageMultisample", + "renderedBuffer", + "RENDERER", + "renderTime", + "renotify", + "repeat", + "REPEAT", + "replace", + "REPLACE", + "replaceChild", + "replaceData", + "replaceItem", + "replaceState", + "replaceSync", + "replaceTrack", + "replaceWith", + "ReportingObserver", + "reportValidity", + "Request", + "request", + "requestAnimationFrame", + "requestData", + "requestFrame", + "requestFullscreen", + "requestIdleCallback", + "requestPermission", + "requestPictureInPicture", + "requestPointerLock", + "requestStart", + "requestSubmit", + "requestVideoFrameCallback", + "required", + "requiredExtensions", + "requireInteraction", + "reset", + "resetTransform", + "resizeBy", + "ResizeObserver", + "ResizeObserverEntry", + "ResizeObserverSize", + "resizeTo", + "resolve", + "Response", + "response", + "responseEnd", + "responseReady", + "responseStart", + "responseText", + "responseType", + "responseURL", + "responseXML", + "restartIce", + "restore", + "result", + "resultIndex", + "results", + "resultType", + "resume", + "resumeTransformFeedback", + "returnValue", + "rev", + "reverse", + "reversed", + "revocable", + "revokeObjectURL", + "RG", + "RG16F", + "RG16I", + "RG16UI", + "RG32F", + "RG32I", + "RG32UI", + "RG8", + "RG8_SNORM", + "RG8I", + "RG8UI", + "RG_INTEGER", + "RGB", + "RGB10_A2", + "RGB10_A2UI", + "RGB16F", + "RGB16I", + "RGB16UI", + "RGB32F", + "RGB32I", + "RGB32UI", + "RGB565", + "RGB5_A1", + "RGB8", + "RGB8_SNORM", + "RGB8I", + "RGB8UI", + "RGB9_E5", + "RGB_INTEGER", + "RGBA", + "RGBA16F", + "RGBA16I", + "RGBA16UI", + "RGBA32F", + "RGBA32I", + "RGBA32UI", + "RGBA4", + "RGBA8", + "RGBA8_SNORM", + "RGBA8I", + "RGBA8UI", + "RGBA_INTEGER", + "right", + "rightContext", + "rolloffFactor", + "root", + "rootBounds", + "rootElement", + "rootMargin", + "rotate", + "rotateAxisAngle", + "rotateAxisAngleSelf", + "rotateFromVector", + "rotateFromVectorSelf", + "rotateSelf", + "rotationAngle", + "round", + "rowIndex", + "rows", + "rowSpan", + "RTCCertificate", + "RTCDataChannel", + "RTCDataChannelEvent", + "RTCDtlsTransport", + "RTCDTMFSender", + "RTCDTMFToneChangeEvent", + "RTCError", + "RTCErrorEvent", + "RTCIceCandidate", + "RTCPeerConnection", + "RTCPeerConnectionIceErrorEvent", + "RTCPeerConnectionIceEvent", + "rtcpTransport", + "RTCRtpReceiver", + "RTCRtpSender", + "RTCRtpTransceiver", + "RTCSctpTransport", + "RTCSessionDescription", + "RTCStatsReport", + "RTCTrackEvent", + "rtt", + "rules", + "RuntimeError", + "rx", + "ry", + "s", + "sample", + "SAMPLE_ALPHA_TO_COVERAGE", + "SAMPLE_BUFFERS", + "SAMPLE_COVERAGE", + "SAMPLE_COVERAGE_INVERT", + "SAMPLE_COVERAGE_VALUE", + "sampleCoverage", + "SAMPLER_2D", + "SAMPLER_2D_ARRAY", + "SAMPLER_2D_ARRAY_SHADOW", + "SAMPLER_2D_SHADOW", + "SAMPLER_3D", + "SAMPLER_BINDING", + "SAMPLER_CUBE", + "SAMPLER_CUBE_SHADOW", + "sampleRate", + "samplerParameterf", + "samplerParameteri", + "SAMPLES", + "sandbox", + "save", + "saveData", + "scale", + "scale3d", + "scale3dSelf", + "scaleNonUniform", + "scaleSelf", + "scheme", + "scissor", + "SCISSOR_BOX", + "SCISSOR_TEST", + "scope", + "Screen", + "screen", + "screenLeft", + "ScreenOrientation", + "screenTop", + "screenX", + "screenY", + "ScriptProcessorNode", + "scripts", + "scroll", + "scrollAmount", + "scrollbars", + "scrollBy", + "scrollDelay", + "scrollHeight", + "scrolling", + "scrollingElement", + "scrollIntoView", + "scrollIntoViewIfNeeded", + "scrollLeft", + "scrollRestoration", + "scrollTo", + "scrollTop", + "scrollWidth", + "scrollX", + "scrollY", + "sctp", + "sctpCauseCode", + "sdp", + "sdpLineNumber", + "sdpMid", + "sdpMLineIndex", + "seal", + "search", + "searchParams", + "sectionRowIndex", + "secureConnectionStart", + "SECURITY_ERR", + "SecurityPolicyViolationEvent", + "seed", + "seekable", + "seeking", + "select", + "selectAllChildren", + "selected", + "selectedIndex", + "selectedOptions", + "Selection", + "selectionDirection", + "selectionEnd", + "selectionStart", + "selectNode", + "selectNodeContents", + "selectorText", + "selectSubString", + "self", + "send", + "sendBeacon", + "sender", + "sentAlert", + "SEPARATE_ATTRIBS", + "serializeToString", + "serverTiming", + "sessionStorage", + "Set", + "set", + "setActionHandler", + "setAttribute", + "setAttributeNode", + "setAttributeNodeNS", + "setAttributeNS", + "setBaseAndExtent", + "setBigInt64", + "setBigUint64", + "setCodecPreferences", + "setConfiguration", + "setCurrentTime", + "setCustomValidity", + "setData", + "setDate", + "setDragImage", + "setEnd", + "setEndAfter", + "setEndBefore", + "setFloat32", + "setFloat64", + "setFormValue", + "setFullYear", + "setHours", + "setInt16", + "setInt32", + "setInt8", + "setInterval", + "setItem", + "setKeyframes", + "setLineDash", + "setLiveSeekableRange", + "setLocalDescription", + "setMatrix", + "setMatrixValue", + "setMilliseconds", + "setMinutes", + "setMonth", + "setNamedItem", + "setNamedItemNS", + "setOrientation", + "setOrientToAngle", + "setOrientToAuto", + "setParameter", + "setParameters", + "setPeriodicWave", + "setPointerCapture", + "setPosition", + "setPositionState", + "setProperty", + "setPrototypeOf", + "setRangeText", + "setRemoteDescription", + "setRequestHeader", + "setResourceTimingBufferSize", + "setRotate", + "setScale", + "setSeconds", + "setSelectionRange", + "setSinkId", + "setSkewX", + "setSkewY", + "setStart", + "setStartAfter", + "setStartBefore", + "setStdDeviation", + "setStreams", + "setTargetAtTime", + "setTime", + "setTimeout", + "setTransform", + "setTranslate", + "setUint16", + "setUint32", + "setUint8", + "setUTCDate", + "setUTCFullYear", + "setUTCHours", + "setUTCMilliseconds", + "setUTCMinutes", + "setUTCMonth", + "setUTCSeconds", + "setValidity", + "setValueAtTime", + "setValueCurveAtTime", + "setYear", + "SHADER_TYPE", + "shaderSource", + "SHADING_LANGUAGE_VERSION", + "shadowBlur", + "shadowColor", + "shadowOffsetX", + "shadowOffsetY", + "ShadowRoot", + "shadowRoot", + "shape", + "SharedArrayBuffer", + "SharedWorker", + "sheet", + "shift", + "shiftKey", + "SHORT", + "show", + "SHOW_ALL", + "SHOW_ATTRIBUTE", + "SHOW_CDATA_SECTION", + "SHOW_COMMENT", + "SHOW_DOCUMENT", + "SHOW_DOCUMENT_FRAGMENT", + "SHOW_DOCUMENT_TYPE", + "SHOW_ELEMENT", + "SHOW_ENTITY", + "SHOW_ENTITY_REFERENCE", + "SHOW_NOTATION", + "SHOW_PROCESSING_INSTRUCTION", + "SHOW_TEXT", + "showModal", + "sign", + "signal", + "SIGNALED", + "signalingState", + "SIGNED_NORMALIZED", + "silent", + "sin", + "singleNodeValue", + "sinh", + "sinkId", + "size", + "sizes", + "skewX", + "skewXSelf", + "skewY", + "skewYSelf", + "slice", + "slope", + "slot", + "small", + "smoothingTimeConstant", + "snapshotItem", + "snapshotLength", + "snapToLines", + "some", + "sort", + "source", + "SourceBuffer", + "SourceBufferList", + "sourceBuffers", + "sourceCapabilities", + "sourceFile", + "sources", + "spacing", + "span", + "species", + "specified", + "specularConstant", + "specularExponent", + "speechSynthesis", + "SpeechSynthesisErrorEvent", + "SpeechSynthesisEvent", + "SpeechSynthesisUtterance", + "speed", + "spellcheck", + "splice", + "split", + "splitText", + "spreadMethod", + "sqrt", + "SQRT1_2", + "SQRT2", + "src", + "SRC_ALPHA", + "SRC_ALPHA_SATURATE", + "SRC_COLOR", + "srcdoc", + "srcElement", + "srclang", + "srcObject", + "srcset", + "SRGB", + "SRGB8", + "SRGB8_ALPHA8", + "stack", + "stackTraceLimit", + "standby", + "start", + "START_TO_END", + "START_TO_START", + "startContainer", + "startOffset", + "startRendering", + "startsWith", + "startTime", + "state", + "STATIC_COPY", + "STATIC_DRAW", + "STATIC_READ", + "StaticRange", + "status", + "statusbar", + "statusCode", + "statusMessage", + "statusText", + "stdDeviationX", + "stdDeviationY", + "STENCIL", + "STENCIL_ATTACHMENT", + "STENCIL_BACK_FAIL", + "STENCIL_BACK_FUNC", + "STENCIL_BACK_PASS_DEPTH_FAIL", + "STENCIL_BACK_PASS_DEPTH_PASS", + "STENCIL_BACK_REF", + "STENCIL_BACK_VALUE_MASK", + "STENCIL_BACK_WRITEMASK", + "STENCIL_BITS", + "STENCIL_BUFFER_BIT", + "STENCIL_CLEAR_VALUE", + "STENCIL_FAIL", + "STENCIL_FUNC", + "STENCIL_INDEX8", + "STENCIL_PASS_DEPTH_FAIL", + "STENCIL_PASS_DEPTH_PASS", + "STENCIL_REF", + "STENCIL_TEST", + "STENCIL_VALUE_MASK", + "STENCIL_WRITEMASK", + "stencilFunc", + "stencilFuncSeparate", + "stencilMask", + "stencilMaskSeparate", + "stencilOp", + "stencilOpSeparate", + "step", + "stepDown", + "stepMismatch", + "stepUp", + "StereoPannerNode", + "sticky", + "stitchTiles", + "stop", + "stopImmediatePropagation", + "stopped", + "stopPropagation", + "Storage", + "storageArea", + "StorageEvent", + "store", + "stream", + "STREAM_COPY", + "STREAM_DRAW", + "STREAM_READ", + "streams", + "stretch", + "strike", + "String", + "STRING_TYPE", + "stringify", + "stringValue", + "stroke", + "strokeRect", + "strokeStyle", + "strokeText", + "style", + "STYLE_RULE", + "styleMap", + "styleMedia", + "StylePropertyMap", + "StylePropertyMapReadOnly", + "StyleSheet", + "styleSheet", + "StyleSheetList", + "styleSheets", + "sub", + "submit", + "SubmitEvent", + "submitter", + "SUBPIXEL_BITS", + "subscribe", + "substr", + "substring", + "substringData", + "suffixes", + "summary", + "sup", + "supportedContentEncodings", + "supportedEntryTypes", + "supports", + "SUPPORTS_RULE", + "surfaceScale", + "surroundContents", + "suspend", + "suspendRedraw", + "SVG_ANGLETYPE_DEG", + "SVG_ANGLETYPE_GRAD", + "SVG_ANGLETYPE_RAD", + "SVG_ANGLETYPE_UNKNOWN", + "SVG_ANGLETYPE_UNSPECIFIED", + "SVG_CHANNEL_A", + "SVG_CHANNEL_B", + "SVG_CHANNEL_G", + "SVG_CHANNEL_R", + "SVG_CHANNEL_UNKNOWN", + "SVG_EDGEMODE_DUPLICATE", + "SVG_EDGEMODE_NONE", + "SVG_EDGEMODE_UNKNOWN", + "SVG_EDGEMODE_WRAP", + "SVG_FEBLEND_MODE_COLOR", + "SVG_FEBLEND_MODE_COLOR_BURN", + "SVG_FEBLEND_MODE_COLOR_DODGE", + "SVG_FEBLEND_MODE_DARKEN", + "SVG_FEBLEND_MODE_DIFFERENCE", + "SVG_FEBLEND_MODE_EXCLUSION", + "SVG_FEBLEND_MODE_HARD_LIGHT", + "SVG_FEBLEND_MODE_HUE", + "SVG_FEBLEND_MODE_LIGHTEN", + "SVG_FEBLEND_MODE_LUMINOSITY", + "SVG_FEBLEND_MODE_MULTIPLY", + "SVG_FEBLEND_MODE_NORMAL", + "SVG_FEBLEND_MODE_OVERLAY", + "SVG_FEBLEND_MODE_SATURATION", + "SVG_FEBLEND_MODE_SCREEN", + "SVG_FEBLEND_MODE_SOFT_LIGHT", + "SVG_FEBLEND_MODE_UNKNOWN", + "SVG_FECOLORMATRIX_TYPE_HUEROTATE", + "SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA", + "SVG_FECOLORMATRIX_TYPE_MATRIX", + "SVG_FECOLORMATRIX_TYPE_SATURATE", + "SVG_FECOLORMATRIX_TYPE_UNKNOWN", + "SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE", + "SVG_FECOMPONENTTRANSFER_TYPE_GAMMA", + "SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY", + "SVG_FECOMPONENTTRANSFER_TYPE_LINEAR", + "SVG_FECOMPONENTTRANSFER_TYPE_TABLE", + "SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN", + "SVG_FECOMPOSITE_OPERATOR_ARITHMETIC", + "SVG_FECOMPOSITE_OPERATOR_ATOP", + "SVG_FECOMPOSITE_OPERATOR_IN", + "SVG_FECOMPOSITE_OPERATOR_OUT", + "SVG_FECOMPOSITE_OPERATOR_OVER", + "SVG_FECOMPOSITE_OPERATOR_UNKNOWN", + "SVG_FECOMPOSITE_OPERATOR_XOR", + "SVG_LENGTHTYPE_CM", + "SVG_LENGTHTYPE_EMS", + "SVG_LENGTHTYPE_EXS", + "SVG_LENGTHTYPE_IN", + "SVG_LENGTHTYPE_MM", + "SVG_LENGTHTYPE_NUMBER", + "SVG_LENGTHTYPE_PC", + "SVG_LENGTHTYPE_PERCENTAGE", + "SVG_LENGTHTYPE_PT", + "SVG_LENGTHTYPE_PX", + "SVG_LENGTHTYPE_UNKNOWN", + "SVG_MARKER_ORIENT_ANGLE", + "SVG_MARKER_ORIENT_AUTO", + "SVG_MARKER_ORIENT_UNKNOWN", + "SVG_MARKERUNITS_STROKEWIDTH", + "SVG_MARKERUNITS_UNKNOWN", + "SVG_MARKERUNITS_USERSPACEONUSE", + "SVG_MEETORSLICE_MEET", + "SVG_MEETORSLICE_SLICE", + "SVG_MEETORSLICE_UNKNOWN", + "SVG_MORPHOLOGY_OPERATOR_DILATE", + "SVG_MORPHOLOGY_OPERATOR_ERODE", + "SVG_MORPHOLOGY_OPERATOR_UNKNOWN", + "SVG_PRESERVEASPECTRATIO_NONE", + "SVG_PRESERVEASPECTRATIO_UNKNOWN", + "SVG_PRESERVEASPECTRATIO_XMAXYMAX", + "SVG_PRESERVEASPECTRATIO_XMAXYMID", + "SVG_PRESERVEASPECTRATIO_XMAXYMIN", + "SVG_PRESERVEASPECTRATIO_XMIDYMAX", + "SVG_PRESERVEASPECTRATIO_XMIDYMID", + "SVG_PRESERVEASPECTRATIO_XMIDYMIN", + "SVG_PRESERVEASPECTRATIO_XMINYMAX", + "SVG_PRESERVEASPECTRATIO_XMINYMID", + "SVG_PRESERVEASPECTRATIO_XMINYMIN", + "SVG_SPREADMETHOD_PAD", + "SVG_SPREADMETHOD_REFLECT", + "SVG_SPREADMETHOD_REPEAT", + "SVG_SPREADMETHOD_UNKNOWN", + "SVG_STITCHTYPE_NOSTITCH", + "SVG_STITCHTYPE_STITCH", + "SVG_STITCHTYPE_UNKNOWN", + "SVG_TRANSFORM_MATRIX", + "SVG_TRANSFORM_ROTATE", + "SVG_TRANSFORM_SCALE", + "SVG_TRANSFORM_SKEWX", + "SVG_TRANSFORM_SKEWY", + "SVG_TRANSFORM_TRANSLATE", + "SVG_TRANSFORM_UNKNOWN", + "SVG_TURBULENCE_TYPE_FRACTALNOISE", + "SVG_TURBULENCE_TYPE_TURBULENCE", + "SVG_TURBULENCE_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_OBJECTBOUNDINGBOX", + "SVG_UNIT_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_USERSPACEONUSE", + "SVG_ZOOMANDPAN_DISABLE", + "SVG_ZOOMANDPAN_MAGNIFY", + "SVG_ZOOMANDPAN_UNKNOWN", + "SVGAElement", + "SVGAngle", + "SVGAnimatedAngle", + "SVGAnimatedBoolean", + "SVGAnimatedEnumeration", + "SVGAnimatedInteger", + "SVGAnimatedLength", + "SVGAnimatedLengthList", + "SVGAnimatedNumber", + "SVGAnimatedNumberList", + "SVGAnimatedPreserveAspectRatio", + "SVGAnimatedRect", + "SVGAnimatedString", + "SVGAnimatedTransformList", + "SVGAnimateElement", + "SVGAnimateMotionElement", + "SVGAnimateTransformElement", + "SVGAnimationElement", + "SVGCircleElement", + "SVGClipPathElement", + "SVGComponentTransferFunctionElement", + "SVGDefsElement", + "SVGDescElement", + "SVGElement", + "SVGEllipseElement", + "SVGFEBlendElement", + "SVGFEColorMatrixElement", + "SVGFEComponentTransferElement", + "SVGFECompositeElement", + "SVGFEConvolveMatrixElement", + "SVGFEDiffuseLightingElement", + "SVGFEDisplacementMapElement", + "SVGFEDistantLightElement", + "SVGFEDropShadowElement", + "SVGFEFloodElement", + "SVGFEFuncAElement", + "SVGFEFuncBElement", + "SVGFEFuncGElement", + "SVGFEFuncRElement", + "SVGFEGaussianBlurElement", + "SVGFEImageElement", + "SVGFEMergeElement", + "SVGFEMergeNodeElement", + "SVGFEMorphologyElement", + "SVGFEOffsetElement", + "SVGFEPointLightElement", + "SVGFESpecularLightingElement", + "SVGFESpotLightElement", + "SVGFETileElement", + "SVGFETurbulenceElement", + "SVGFilterElement", + "SVGForeignObjectElement", + "SVGGElement", + "SVGGeometryElement", + "SVGGradientElement", + "SVGGraphicsElement", + "SVGImageElement", + "SVGLength", + "SVGLengthList", + "SVGLinearGradientElement", + "SVGLineElement", + "SVGMarkerElement", + "SVGMaskElement", + "SVGMatrix", + "SVGMetadataElement", + "SVGMPathElement", + "SVGNumber", + "SVGNumberList", + "SVGPathElement", + "SVGPatternElement", + "SVGPoint", + "SVGPointList", + "SVGPolygonElement", + "SVGPolylineElement", + "SVGPreserveAspectRatio", + "SVGRadialGradientElement", + "SVGRect", + "SVGRectElement", + "SVGScriptElement", + "SVGSetElement", + "SVGStopElement", + "SVGStringList", + "SVGStyleElement", + "SVGSVGElement", + "SVGSwitchElement", + "SVGSymbolElement", + "SVGTextContentElement", + "SVGTextElement", + "SVGTextPathElement", + "SVGTextPositioningElement", + "SVGTitleElement", + "SVGTransform", + "SVGTransformList", + "SVGTSpanElement", + "SVGUnitTypes", + "SVGUseElement", + "SVGViewElement", + "Symbol", + "SYNC_CONDITION", + "SYNC_FENCE", + "SYNC_FLAGS", + "SYNC_FLUSH_COMMANDS_BIT", + "SYNC_GPU_COMMANDS_COMPLETE", + "SYNC_STATUS", + "SyncManager", + "SYNTAX_ERR", + "SyntaxError", + "systemId", + "systemLanguage", + "tabIndex", + "table", + "Table", + "tableValues", + "tag", + "tagName", + "takePhoto", + "takeRecords", + "tan", + "tangentialPressure", + "tanh", + "target", + "targetElement", + "targetTouches", + "targetX", + "targetY", + "TaskAttributionTiming", + "tBodies", + "tcpType", + "tee", + "TEMPORARY", + "terminate", + "test", + "texImage2D", + "texImage3D", + "texParameterf", + "texParameteri", + "texStorage2D", + "texStorage3D", + "texSubImage2D", + "texSubImage3D", + "Text", + "text", + "TEXT_NODE", + "textAlign", + "textBaseline", + "textContent", + "TextDecoder", + "TextDecoderStream", + "TextEncoder", + "TextEncoderStream", + "TextEvent", + "textLength", + "TextMetrics", + "TEXTPATH_METHODTYPE_ALIGN", + "TEXTPATH_METHODTYPE_STRETCH", + "TEXTPATH_METHODTYPE_UNKNOWN", + "TEXTPATH_SPACINGTYPE_AUTO", + "TEXTPATH_SPACINGTYPE_EXACT", + "TEXTPATH_SPACINGTYPE_UNKNOWN", + "TextTrack", + "TextTrackCue", + "TextTrackCueList", + "TextTrackList", + "textTracks", + "TEXTURE", + "TEXTURE0", + "TEXTURE1", + "TEXTURE10", + "TEXTURE11", + "TEXTURE12", + "TEXTURE13", + "TEXTURE14", + "TEXTURE15", + "TEXTURE16", + "TEXTURE17", + "TEXTURE18", + "TEXTURE19", + "TEXTURE2", + "TEXTURE20", + "TEXTURE21", + "TEXTURE22", + "TEXTURE23", + "TEXTURE24", + "TEXTURE25", + "TEXTURE26", + "TEXTURE27", + "TEXTURE28", + "TEXTURE29", + "TEXTURE3", + "TEXTURE30", + "TEXTURE31", + "TEXTURE4", + "TEXTURE5", + "TEXTURE6", + "TEXTURE7", + "TEXTURE8", + "TEXTURE9", + "TEXTURE_2D", + "TEXTURE_2D_ARRAY", + "TEXTURE_3D", + "TEXTURE_BASE_LEVEL", + "TEXTURE_BINDING_2D", + "TEXTURE_BINDING_2D_ARRAY", + "TEXTURE_BINDING_3D", + "TEXTURE_BINDING_CUBE_MAP", + "TEXTURE_COMPARE_FUNC", + "TEXTURE_COMPARE_MODE", + "TEXTURE_CUBE_MAP", + "TEXTURE_CUBE_MAP_NEGATIVE_X", + "TEXTURE_CUBE_MAP_NEGATIVE_Y", + "TEXTURE_CUBE_MAP_NEGATIVE_Z", + "TEXTURE_CUBE_MAP_POSITIVE_X", + "TEXTURE_CUBE_MAP_POSITIVE_Y", + "TEXTURE_CUBE_MAP_POSITIVE_Z", + "TEXTURE_IMMUTABLE_FORMAT", + "TEXTURE_IMMUTABLE_LEVELS", + "TEXTURE_MAG_FILTER", + "TEXTURE_MAX_LEVEL", + "TEXTURE_MAX_LOD", + "TEXTURE_MIN_FILTER", + "TEXTURE_MIN_LOD", + "TEXTURE_WRAP_R", + "TEXTURE_WRAP_S", + "TEXTURE_WRAP_T", + "tFoot", + "tHead", + "then", + "threshold", + "thresholds", + "tiltX", + "tiltY", + "time", + "timecode", + "timeEnd", + "timeline", + "timelineTime", + "timeLog", + "timeOrigin", + "TIMEOUT", + "timeout", + "TIMEOUT_ERR", + "TIMEOUT_EXPIRED", + "TIMEOUT_IGNORED", + "TimeRanges", + "timeRemaining", + "timeStamp", + "timestamp", + "timestampOffset", + "timing", + "title", + "to", + "toBlob", + "toDataURL", + "toDateString", + "toElement", + "toExponential", + "toFixed", + "toFloat32Array", + "toFloat64Array", + "toggle", + "toggleAttribute", + "toGMTString", + "toISOString", + "toJSON", + "toLocaleDateString", + "toLocaleLowerCase", + "toLocaleString", + "toLocaleTimeString", + "toLocaleUpperCase", + "toLowerCase", + "toMatrix", + "tone", + "toneBuffer", + "toolbar", + "tooLong", + "tooShort", + "top", + "toPrecision", + "toPrimitive", + "toString", + "toStringTag", + "toSum", + "total", + "totalVideoFrames", + "toTimeString", + "Touch", + "touched", + "touches", + "TouchEvent", + "TouchList", + "toUpperCase", + "toUTCString", + "trace", + "track", + "TrackEvent", + "trackVisibility", + "transaction", + "transceiver", + "transferControlToOffscreen", + "transferFromImageBitmap", + "transferSize", + "transferToImageBitmap", + "transform", + "TRANSFORM_FEEDBACK", + "TRANSFORM_FEEDBACK_ACTIVE", + "TRANSFORM_FEEDBACK_BINDING", + "TRANSFORM_FEEDBACK_BUFFER", + "TRANSFORM_FEEDBACK_BUFFER_BINDING", + "TRANSFORM_FEEDBACK_BUFFER_MODE", + "TRANSFORM_FEEDBACK_BUFFER_SIZE", + "TRANSFORM_FEEDBACK_BUFFER_START", + "TRANSFORM_FEEDBACK_PAUSED", + "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", + "TRANSFORM_FEEDBACK_VARYINGS", + "transformFeedbackVaryings", + "transformPoint", + "TransformStream", + "transformToDocument", + "transformToFragment", + "TransitionEvent", + "transitionProperty", + "translate", + "translateSelf", + "transport", + "TreeWalker", + "TRIANGLE_FAN", + "TRIANGLE_STRIP", + "TRIANGLES", + "trim", + "trimEnd", + "trimLeft", + "trimRight", + "trimStart", + "trueSpeed", + "trunc", + "TrustedHTML", + "TrustedScript", + "TrustedScriptURL", + "TrustedTypePolicy", + "TrustedTypePolicyFactory", + "trustedTypes", + "turn", + "twist", + "type", + "TYPE_BACK_FORWARD", + "TYPE_MISMATCH_ERR", + "TYPE_NAVIGATE", + "TYPE_RELOAD", + "TYPE_RESERVED", + "TypeError", + "typeMismatch", + "types", + "UIEvent", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "undebug", + "undefined", + "unescape", + "unicode", + "unicodeRange", + "uniform1f", + "uniform1fv", + "uniform1i", + "uniform1iv", + "uniform1ui", + "uniform1uiv", + "uniform2f", + "uniform2fv", + "uniform2i", + "uniform2iv", + "uniform2ui", + "uniform2uiv", + "uniform3f", + "uniform3fv", + "uniform3i", + "uniform3iv", + "uniform3ui", + "uniform3uiv", + "uniform4f", + "uniform4fv", + "uniform4i", + "uniform4iv", + "uniform4ui", + "uniform4uiv", + "UNIFORM_ARRAY_STRIDE", + "UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES", + "UNIFORM_BLOCK_ACTIVE_UNIFORMS", + "UNIFORM_BLOCK_BINDING", + "UNIFORM_BLOCK_DATA_SIZE", + "UNIFORM_BLOCK_INDEX", + "UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER", + "UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER", + "UNIFORM_BUFFER", + "UNIFORM_BUFFER_BINDING", + "UNIFORM_BUFFER_OFFSET_ALIGNMENT", + "UNIFORM_BUFFER_SIZE", + "UNIFORM_BUFFER_START", + "UNIFORM_IS_ROW_MAJOR", + "UNIFORM_MATRIX_STRIDE", + "UNIFORM_OFFSET", + "UNIFORM_SIZE", + "UNIFORM_TYPE", + "uniformBlockBinding", + "uniformMatrix2fv", + "uniformMatrix2x3fv", + "uniformMatrix2x4fv", + "uniformMatrix3fv", + "uniformMatrix3x2fv", + "uniformMatrix3x4fv", + "uniformMatrix4fv", + "uniformMatrix4x2fv", + "uniformMatrix4x3fv", + "unique", + "unit", + "unitType", + "unloadEventEnd", + "unloadEventStart", + "unlock", + "unmonitor", + "unmonitorEvents", + "unobserve", + "UNORDERED_NODE_ITERATOR_TYPE", + "UNORDERED_NODE_SNAPSHOT_TYPE", + "UNPACK_ALIGNMENT", + "UNPACK_COLORSPACE_CONVERSION_WEBGL", + "UNPACK_FLIP_Y_WEBGL", + "UNPACK_IMAGE_HEIGHT", + "UNPACK_PREMULTIPLY_ALPHA_WEBGL", + "UNPACK_ROW_LENGTH", + "UNPACK_SKIP_IMAGES", + "UNPACK_SKIP_PIXELS", + "UNPACK_SKIP_ROWS", + "unpauseAnimations", + "unregister", + "unscopables", + "UNSENT", + "unshift", + "UNSIGNALED", + "UNSIGNED_BYTE", + "UNSIGNED_INT", + "UNSIGNED_INT_10F_11F_11F_REV", + "UNSIGNED_INT_24_8", + "UNSIGNED_INT_2_10_10_10_REV", + "UNSIGNED_INT_5_9_9_9_REV", + "UNSIGNED_INT_SAMPLER_2D", + "UNSIGNED_INT_SAMPLER_2D_ARRAY", + "UNSIGNED_INT_SAMPLER_3D", + "UNSIGNED_INT_SAMPLER_CUBE", + "UNSIGNED_INT_VEC2", + "UNSIGNED_INT_VEC3", + "UNSIGNED_INT_VEC4", + "UNSIGNED_NORMALIZED", + "UNSIGNED_SHORT", + "UNSIGNED_SHORT_4_4_4_4", + "UNSIGNED_SHORT_5_5_5_1", + "UNSIGNED_SHORT_5_6_5", + "unsubscribe", + "unsuspendRedraw", + "unsuspendRedrawAll", + "update", + "updatePlaybackRate", + "updateTiming", + "updateWith", + "updating", + "upgrade", + "upload", + "uploaded", + "uploadTotal", + "upper", + "upperBound", + "upperOpen", + "upX", + "upY", + "upZ", + "URIError", + "URL", + "url", + "URL_MISMATCH_ERR", + "URLSearchParams", + "useMap", + "useProgram", + "UserActivation", + "userActivation", + "userAgent", + "userChoice", + "userHint", + "username", + "usernameFragment", + "userVisibleOnly", + "UTC", + "utterance", + "v8BreakIterator", + "valid", + "validate", + "VALIDATE_STATUS", + "validateProgram", + "VALIDATION_ERR", + "validationMessage", + "validity", + "ValidityState", + "vAlign", + "value", + "valueAsDate", + "valueAsNumber", + "valueAsString", + "valueInSpecifiedUnits", + "valueMissing", + "valueOf", + "values", + "valueType", + "variable", + "variant", + "VENDOR", + "vendor", + "vendorSub", + "VERSION", + "version", + "VERTEX_ARRAY_BINDING", + "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", + "VERTEX_ATTRIB_ARRAY_DIVISOR", + "VERTEX_ATTRIB_ARRAY_ENABLED", + "VERTEX_ATTRIB_ARRAY_INTEGER", + "VERTEX_ATTRIB_ARRAY_NORMALIZED", + "VERTEX_ATTRIB_ARRAY_POINTER", + "VERTEX_ATTRIB_ARRAY_SIZE", + "VERTEX_ATTRIB_ARRAY_STRIDE", + "VERTEX_ATTRIB_ARRAY_TYPE", + "VERTEX_SHADER", + "vertexAttrib1f", + "vertexAttrib1fv", + "vertexAttrib2f", + "vertexAttrib2fv", + "vertexAttrib3f", + "vertexAttrib3fv", + "vertexAttrib4f", + "vertexAttrib4fv", + "vertexAttribDivisor", + "vertexAttribI4i", + "vertexAttribI4iv", + "vertexAttribI4ui", + "vertexAttribI4uiv", + "vertexAttribIPointer", + "vertexAttribPointer", + "vertical", + "vh", + "vibrate", + "vibrationActuator", + "videoBitsPerSecond", + "videoHeight", + "VideoPlaybackQuality", + "videoWidth", + "view", + "viewBox", + "VIEWPORT", + "viewport", + "viewportElement", + "violatedDirective", + "visibilityState", + "visible", + "VisualViewport", + "visualViewport", + "vLink", + "vlinkColor", + "vmax", + "vmin", + "voice", + "volume", + "vspace", + "VTTCue", + "vw", + "w", + "wait", + "WAIT_FAILED", + "waitSync", + "wake", + "warn", + "wasClean", + "wasDiscarded", + "watchAvailability", + "watchPosition", + "WaveShaperNode", + "WeakMap", + "WeakRef", + "WeakSet", + "WebAssembly", + "WebGL2RenderingContext", + "WebGLActiveInfo", + "WebGLBuffer", + "WebGLContextEvent", + "WebGLFramebuffer", + "WebGLProgram", + "WebGLQuery", + "WebGLRenderbuffer", + "WebGLRenderingContext", + "WebGLSampler", + "WebGLShader", + "WebGLShaderPrecisionFormat", + "WebGLSync", + "WebGLTexture", + "WebGLTransformFeedback", + "WebGLUniformLocation", + "WebGLVertexArrayObject", + "webkitAudioDecodedByteCount", + "webkitCancelAnimationFrame", + "webkitCancelFullScreen", + "WebKitCSSMatrix", + "webkitCurrentFullScreenElement", + "webkitDecodedFrameCount", + "webkitdirectory", + "webkitDisplayingFullscreen", + "webkitDroppedFrameCount", + "webkitEnterFullscreen", + "webkitEnterFullScreen", + "webkitEntries", + "webkitExitFullscreen", + "webkitExitFullScreen", + "webkitFullscreenElement", + "webkitFullscreenEnabled", + "webkitGetAsEntry", + "webkitHidden", + "webkitIsFullScreen", + "webkitMatchesSelector", + "webkitMediaStream", + "WebKitMutationObserver", + "webkitPersistentStorage", + "webkitRelativePath", + "webkitRequestAnimationFrame", + "webkitRequestFileSystem", + "webkitRequestFullScreen", + "webkitRequestFullscreen", + "webkitResolveLocalFileSystemURL", + "webkitRTCPeerConnection", + "webkitSpeechGrammar", + "webkitSpeechGrammarList", + "webkitSpeechRecognition", + "webkitSpeechRecognitionError", + "webkitSpeechRecognitionEvent", + "webkitStorageInfo", + "webkitSupportsFullscreen", + "webkitTemporaryStorage", + "webkitURL", + "webkitVideoDecodedByteCount", + "webkitVisibilityState", + "WebSocket", + "weight", + "whatToShow", + "wheelDelta", + "wheelDeltaX", + "wheelDeltaY", + "WheelEvent", + "whenDefined", + "which", + "wholeText", + "width", + "willValidate", + "Window", + "window", + "withCredentials", + "Worker", + "workerStart", + "wrap", + "writable", + "WritableStream", + "WritableStreamDefaultWriter", + "write", + "writeln", + "WRONG_DOCUMENT_ERR", + "x", + "x1", + "x2", + "xChannelSelector", + "XMLDocument", + "xmlEncoding", + "XMLHttpRequest", + "XMLHttpRequestEventTarget", + "XMLHttpRequestUpload", + "XMLSerializer", + "xmlStandalone", + "xmlVersion", + "xor", + "XPathEvaluator", + "XPathExpression", + "XPathResult", + "XSLTProcessor", + "y", + "y1", + "y2", + "yChannelSelector", + "z", + "ZERO", + "zoomAndPan" +]); diff --git a/jdenticon-js/build/gulp/pre-minify.js b/jdenticon-js/build/gulp/pre-minify.js new file mode 100644 index 0000000..bd4b803 --- /dev/null +++ b/jdenticon-js/build/gulp/pre-minify.js @@ -0,0 +1,186 @@ +const { Node } = require("acorn"); +const astTransformStream = require("./ast-transform-stream"); +const DOMPROPS = require("./domprops"); +const RESERVED_NAMES = require("./reserved-keywords"); + +const CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + +function visit(node, visitor) { + if (node instanceof Node) { + visitor(node); + } + + for (const key in node) { + const value = node[key]; + if (Array.isArray(value)) { + value.forEach(subNode => visit(subNode, visitor)); + } else if (value instanceof Node) { + visit(value, visitor); + } + } +} + +function generateIdentifier(seed) { + let identifier = ""; + + seed = Math.abs(Math.floor(seed)); + + do { + const mod = seed % CHARACTERS.length; + identifier += CHARACTERS[mod]; + seed = (seed / CHARACTERS.length) | 0; + } while (seed); + + return identifier; +} + +function mangleProps(input, ast, replacement) { + const identifierNodes = []; + const longToShortName = new Map(); + + // Find identifiers + visit(ast, node => { + let identifier; + + if (node.type === "MemberExpression" && !node.computed) { + // Matches x.y + // Not x["y"] (computed: true) + identifier = node.property; + } else if (node.type === "MethodDefinition") { + // Matches x() { } + identifier = node.key; + } else if (node.type === "Property") { + // Matches { x: y } + // Not { "x": y } + identifier = node.key; + } + + if (identifier && identifier.type === "Identifier") { + identifierNodes.push(identifier); + } + }); + + // Collect usage statistics per name + const usageMap = new Map(); + identifierNodes.forEach(node => { + if (node.name && !RESERVED_NAMES.has(node.name) && !DOMPROPS.has(node.name)) { + usageMap.set(node.name, (usageMap.get(node.name) || 0) + 1); + } + }); + + // Sort by usage in descending order + const usageStats = Array.from(usageMap).sort((a, b) => b[1] - a[1]); + + // Allocate identifiers in order of usage statistics to ensure + // frequently used symbols get as short identifiers as possible. + let runningCounter = 0; + usageStats.forEach(identifier => { + const longName = identifier[0]; + + if (!longToShortName.has(longName)) { + let shortName; + + do { + shortName = generateIdentifier(runningCounter++); + } while (RESERVED_NAMES.has(shortName) || DOMPROPS.has(shortName)); + + longToShortName.set(longName, shortName); + } + }); + + // Populate replacements + identifierNodes.forEach(node => { + const minifiedName = longToShortName.get(node.name); + if (minifiedName) { + replacement.addRange({ + start: node.start, + end: node.end, + replacement: minifiedName + "/*" + node.name + "*/", + name: node.name, + }); + } + }); + + return replacement; +} + +function simplifyES5Class(input, ast, replacement) { + const prototypeMemberExpressions = []; + const duplicateNamedFunctions = []; + + visit(ast, node => { + if (node.type === "MemberExpression" && + !node.computed && + node.object.type === "Identifier" && + node.property.type === "Identifier" && + node.property.name === "prototype" + ) { + // Matches: xxx.prototype + prototypeMemberExpressions.push(node); + + } else if ( + node.type === "VariableDeclaration" && + node.declarations.length === 1 && + node.declarations[0].init && + node.declarations[0].init.type === "FunctionExpression" && + node.declarations[0].init.id && + node.declarations[0].init.id.name === node.declarations[0].id.name + ) { + // Matches: var xxx = function xxx (); + duplicateNamedFunctions.push(node); + } + }); + + duplicateNamedFunctions.forEach(duplicateNamedFunction => { + const functionName = duplicateNamedFunction.declarations[0].init.id.name; + + // Remove: var xxx = + replacement.addRange({ + start: duplicateNamedFunction.start, + end: duplicateNamedFunction.declarations[0].init.start, + replacement: "", + }); + + // Remove trailing semicolons + let semicolons = 0; + while (input[duplicateNamedFunction.end - semicolons - 1] === ";") semicolons++; + + // Find prototype references + const refs = prototypeMemberExpressions.filter(node => node.object.name === functionName); + if (refs.length > 1) { + + // Insert: var xx__prototype = xxx.prototype; + replacement.addRange({ + start: duplicateNamedFunction.end - semicolons, + end: duplicateNamedFunction.end, + replacement: `\r\nvar ${functionName}__prototype = ${functionName}.prototype;`, + }); + + // Replace references + refs.forEach(ref => { + replacement.addRange({ + start: ref.start, + end: ref.end, + replacement: `${functionName}__prototype`, + }); + }); + } else if (semicolons) { + replacement.addRange({ + start: duplicateNamedFunction.end - semicolons, + end: duplicateNamedFunction.end, + replacement: "", + }); + } + }); +} + +function browserConstants(input, ast, replacement) { + replacement.addText("Node.ELEMENT_NODE", "1"); +} + +const MINIFIERS = [simplifyES5Class, mangleProps, browserConstants]; + +module.exports = astTransformStream(function (replacement, ast, comments, input) { + MINIFIERS.forEach(minifier => minifier(input, ast, replacement)); +}); + diff --git a/jdenticon-js/build/gulp/remove-jsdoc-imports.js b/jdenticon-js/build/gulp/remove-jsdoc-imports.js new file mode 100644 index 0000000..1291f37 --- /dev/null +++ b/jdenticon-js/build/gulp/remove-jsdoc-imports.js @@ -0,0 +1,52 @@ +const astTransformStream = require("./ast-transform-stream"); + +function removeJSDocImports(comments, replacement) { + const REGEX = /[ \t]*\*[ \t]*@typedef\s+\{import.+\r?\n?|import\(.*?\)\.([a-zA-Z_]+)/g; + const JSDOC_COMMENT_OFFSET = 2; + + comments.forEach(comment => { + if (comment.type === "Block" && comment.value[0] === "*") { + // JSDoc comment + const resultingComment = comment.value.replace(REGEX, (match, importName, matchIndex) => { + matchIndex += comment.start + JSDOC_COMMENT_OFFSET; + + if (importName) { + // { import().xxx } + replacement.addRange({ + start: matchIndex, + end: matchIndex + match.length, + replacement: importName, + }); + + return importName; + + } else { + // @typedef + replacement.addRange({ + start: matchIndex, + end: matchIndex + match.length, + replacement: "", + }); + + return ""; + } + }); + + if (!/[^\s\*]/.test(resultingComment)) { + // Empty comment left + replacement.addRange({ + start: comment.start, + end: comment.end, + replacement: "", + }); + } + } + }); +} + +module.exports = astTransformStream(function (replacement, ast, comments, input) { + removeJSDocImports(comments, replacement); +}); + + + diff --git a/jdenticon-js/build/gulp/remove-mapped-source.js b/jdenticon-js/build/gulp/remove-mapped-source.js new file mode 100644 index 0000000..3eae0f7 --- /dev/null +++ b/jdenticon-js/build/gulp/remove-mapped-source.js @@ -0,0 +1,54 @@ +const { Transform } = require("stream"); +const { SourceMapConsumer, SourceMapGenerator } = require("source-map"); + +function removeMappedSourceStream(...sourceNames) { + const sourceNamesToRemove = new Set(sourceNames); + + return new Transform({ + objectMode: true, + + transform(inputFile, _, fileDone) { + if (inputFile.sourceMap) { + let consumer = inputFile.sourceMap; + if (!(consumer instanceof SourceMapConsumer)) { + consumer = new SourceMapConsumer(consumer); + } + + const generator = new SourceMapGenerator({ + file: consumer.file, + sourceRoot: consumer.sourceRoot, + }); + + consumer.sources.forEach(sourceFile => { + const content = consumer.sourceContentFor(sourceFile); + if (content != null && !sourceNamesToRemove.has(sourceFile)) { + generator.setSourceContent(sourceFile, content); + } + }); + + consumer.eachMapping(mapping => { + if (!sourceNamesToRemove.has(mapping.source)) { + generator.addMapping({ + original: { + line: mapping.originalLine, + column: mapping.originalColumn, + }, + generated: { + line: mapping.generatedLine, + column: mapping.generatedColumn, + }, + source: mapping.source, + name: mapping.name, + }); + } + }); + + inputFile.sourceMap = generator.toJSON(); + } + + fileDone(null, inputFile); + } + }); +} + +module.exports = removeMappedSourceStream; diff --git a/jdenticon-js/build/gulp/replacement.js b/jdenticon-js/build/gulp/replacement.js new file mode 100644 index 0000000..b139b8f --- /dev/null +++ b/jdenticon-js/build/gulp/replacement.js @@ -0,0 +1,672 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +const { Transform } = require("stream"); +const { SourceMapConsumer, SourceMapGenerator } = require("source-map"); + +/** + * Finds substrings and replaces them with other strings, keeping any input source map up-to-date. + * + * @example + * const replacement = new Replacement([ + * ["find this", "replace with this"], + * [/find this/gi, "replace with this"] + * ]); + * replacement.replace(input, inputSourceMap); + * + * @example + * const replacement = new Replacement("find this", "replace with this"); + * replacement.replace(input, inputSourceMap); + */ +class Replacement { + constructor(...definition) { + /** + * @type {function(Array, string): void} + */ + this._matchers = []; + + /** + * @type {Array} + */ + this._ranges = []; + + this.add(definition); + } + + /** + * @param {string} input + * @returns {Array} + */ + matchAll(input) { + const ranges = [...this._ranges]; + this._matchers.forEach(matcher => matcher(ranges, input)); + + let lastReplacement; + + return ranges + .sort((a, b) => a.start - b.start) + .filter(replacement => { + if (!lastReplacement || lastReplacement.end <= replacement.start) { + lastReplacement = replacement; + return true; + } + }); + } + + /** + * @param {string} input + * @param {SourceMap=} inputSourceMap + * @returns {{ output: string, sourceMap: SourceMap }} + */ + replace(input, inputSourceMap) { + const ranges = this.matchAll(input); + const offset = new Offset(); + const reader = new InputReader(input); + const sourceMap = new SourceMapSpooler(inputSourceMap); + const output = []; + + if (sourceMap.isEmpty()) { + sourceMap.initEmpty(reader.lines); + } + + ranges.forEach((range, rangeIndex) => { + output.push(reader.readTo(range.start)); + output.push(range.replacement); + + const inputStart = reader.pos; + + const replacedText = reader.readTo(range.end); + if (replacedText === range.replacement) { + return; // Nothing to do + } + + const inputEnd = reader.pos; + + const replacementLines = range.replacement.split(/\n/g); + const lineDifference = replacementLines.length + inputStart.line - inputEnd.line - 1; + + const outputStart = { + line: inputStart.line + offset.lineOffset, + column: inputStart.column + offset.getColumnOffset(inputStart.line) + }; + const outputEnd = { + line: inputEnd.line + offset.lineOffset + lineDifference, + column: replacementLines.length > 1 ? + replacementLines[replacementLines.length - 1].length : + inputStart.column + offset.getColumnOffset(inputStart.line) + + range.replacement.length + } + + sourceMap.spoolTo(inputStart.line, inputStart.column, offset); + + offset.lineOffset += lineDifference; + offset.setColumnOffset(inputEnd.line, outputEnd.column - inputEnd.column); + + if (range.name || replacementLines.length === 1 && range.replacement) { + const mappingBeforeStart = sourceMap.lastMapping(); + const mappingAfterStart = sourceMap.nextMapping(); + + if (mappingAfterStart && + mappingAfterStart.generatedLine === inputStart.line && + mappingAfterStart.generatedColumn === inputStart.column + ) { + sourceMap.addMapping({ + original: { + line: mappingAfterStart.originalLine, + column: mappingAfterStart.originalColumn, + }, + generated: { + line: outputStart.line, + column: outputStart.column + }, + source: mappingAfterStart.source, + name: range.name, + }); + + } else if (mappingBeforeStart && mappingBeforeStart.generatedLine === inputStart.line) { + sourceMap.addMapping({ + original: { + line: mappingBeforeStart.originalLine + inputStart.line - mappingBeforeStart.generatedLine, + column: mappingBeforeStart.originalColumn + inputStart.column - mappingBeforeStart.generatedColumn, + }, + generated: { + line: outputStart.line, + column: outputStart.column + }, + source: mappingBeforeStart.source, + name: range.name, + }); + } + + } else if (range.replacement) { + // Map longer replacements to a virtual file defined in the source map + const generatedSourceName = sourceMap.addSourceContent(replacedText, range.replacement); + + for (var i = 0; i < replacementLines.length; i++) { + // Don't map empty lines + if (replacementLines[i]) { + sourceMap.addMapping({ + original: { + line: i + 1, + column: 0, + }, + generated: { + line: outputStart.line + i, + column: i ? 0 : outputStart.column, + }, + source: generatedSourceName, + }); + } + } + } + + sourceMap.skipTo(inputEnd.line, inputEnd.column, offset); + + // Add a source map node directly after the replacement to terminate the replacement + const mappingAfterEnd = sourceMap.nextMapping(); + const mappingBeforeEnd = sourceMap.lastMapping(); + + if (mappingAfterEnd && + mappingAfterEnd.generatedLine === inputEnd.line && + mappingAfterEnd.generatedColumn === inputEnd.column + ) { + // No extra source map node needed when the replacement is directly followed by another node + + } else if (rangeIndex + 1 < ranges.length && range.end === ranges[rangeIndex + 1].start) { + // The next replacement range is adjacent to this one + + } else if (reader.endOfLine()) { + // End of line, no point in adding a following node + + } else if (!mappingBeforeEnd || mappingBeforeEnd.generatedLine !== inputEnd.line) { + // No applicable preceding node found + + } else { + sourceMap.addMapping({ + original: { + line: mappingBeforeEnd.originalLine + inputEnd.line - mappingBeforeEnd.generatedLine, + column: mappingBeforeEnd.originalColumn + inputEnd.column - mappingBeforeEnd.generatedColumn, + }, + generated: { + line: outputEnd.line, + column: outputEnd.column + }, + source: mappingBeforeEnd.source, + }); + } + + }); + + // Flush remaining input to output and source map + output.push(reader.readToEnd()); + sourceMap.spoolToEnd(offset); + + return { + output: output.join(""), + sourceMap: sourceMap.toJSON(), + }; + } + + add(value) { + const target = this; + + function addRecursive(innerValue) { + if (innerValue != null) { + if (Array.isArray(innerValue)) { + const needle = innerValue[0]; + + if (typeof needle === "string") { + target.addText(...innerValue); + } else if (needle instanceof RegExp) { + target.addRegExp(...innerValue); + } else { + innerValue.forEach(addRecursive); + } + + } else if (innerValue instanceof Replacement) { + target._matchers.push(innerValue._matchers); + target._ranges.push(innerValue._ranges); + + } else if (typeof innerValue === "object") { + target.addRange(innerValue); + + } else { + throw new Error("Unknown replacement argument specified."); + } + } + } + + addRecursive(value); + } + + /** + * @param {RegExp} re + * @param {string|function(string, ...):string} replacement + * @param {{ name: string }=} rangeOpts + */ + addRegExp(re, replacement, rangeOpts) { + const replacementFactory = this._createReplacementFactory(replacement); + + this._matchers.push((ranges, input) => { + const isGlobalRegExp = /g/.test(re.flags); + + let match; + let isFirstIteration = true; + + while ((isFirstIteration || isGlobalRegExp) && (match = re.exec(input))) { + ranges.push(new OverwriteRange({ + ...rangeOpts, + start: match.index, + end: match.index + match[0].length, + replacement: replacementFactory(match, match.index, input), + })); + isFirstIteration = false; + } + }); + } + + /** + * @param {string} needle + * @param {string|function(string, ...):string} replacement + * @param {{ name: string }=} rangeOpts + */ + addText(needle, replacement, rangeOpts) { + const replacementFactory = this._createReplacementFactory(replacement); + + this._matchers.push((ranges, input) => { + let index = -needle.length; + + while ((index = input.indexOf(needle, index + needle.length)) >= 0) { + ranges.push(new OverwriteRange({ + ...rangeOpts, + start: index, + end: index + needle.length, + replacement: replacementFactory([needle], index, input), + })); + } + }); + } + + /** + * @param {OverwriteRange} range + */ + addRange(range) { + this._ranges.push(new OverwriteRange(range)); + } + + /** + * @param {string|function(string, ...):string} replacement + * @returns {function(Array, number, string):string} + */ + _createReplacementFactory(replacement) { + if (typeof replacement === "function") { + return (match, index, input) => replacement(...match, index, input); + } + + if (replacement == null) { + return () => ""; + } + + replacement = replacement.toString(); + + if (replacement.indexOf("$") < 0) { + return () => replacement; + } + + return (match, index, input) => + replacement.replace(/\$(\d+|[$&`'])/g, matchedPattern => { + if (matchedPattern === "$$") { + return "$"; + } + if (matchedPattern === "$&") { + return match[0]; + } + if (matchedPattern === "$`") { + return input.substring(0, index); + } + if (matchedPattern === "$'") { + return input.substring(index + match[0].length); + } + + const matchArrayIndex = Number(matchedPattern.substring(1)); + return match[matchArrayIndex]; + }); + } +} + +class InputReader { + /** + * @param {string} input + */ + constructor (input) { + // Find index of all line breaks + const lineBreakIndexes = []; + let index = -1; + while ((index = input.indexOf("\n", index + 1)) >= 0) { + lineBreakIndexes.push(index); + } + + this._input = input; + + this._inputCursorExclusive = 0; + this._output = []; + this._lineBreakIndexes = lineBreakIndexes; + + /** + * Number of lines in the input file. + * @type {number} + */ + this.lines = this._lineBreakIndexes.length + 1; + + /** + * Position of the input cursor. Line number is one-based and column number is zero-based. + * @type {{ line: number, column: number }} + */ + this.pos = { line: 1, column: 0 }; + } + + readTo(exclusiveIndex) { + let result = ""; + + if (this._inputCursorExclusive < exclusiveIndex) { + result = this._input.substring(this._inputCursorExclusive, exclusiveIndex); + this._inputCursorExclusive = exclusiveIndex; + this._updatePos(); + } + + return result; + } + + readToEnd() { + return this.readTo(this._input.length); + } + + endOfLine() { + const nextChar = this._input[this._inputCursorExclusive]; + return !nextChar || nextChar === "\r" || nextChar === "\n"; + } + + _updatePos() { + let line = this.pos.line; + while ( + line - 1 < this._lineBreakIndexes.length && + this._lineBreakIndexes[line - 1] < this._inputCursorExclusive + ) { + line++; + } + + const lineStartIndex = this._lineBreakIndexes[line - 2]; + const column = this._inputCursorExclusive - (lineStartIndex || -1) - 1; + this.pos = { line, column }; + } +} + +class SourceMapSpooler { + /** + * @param {SourceMap=} inputSourceMap + */ + constructor(inputSourceMap) { + let generator; + let file; + let sources; + let mappings = []; + + if (inputSourceMap) { + if (!(inputSourceMap instanceof SourceMapConsumer)) { + inputSourceMap = new SourceMapConsumer(inputSourceMap); + } + + generator = new SourceMapGenerator({ + file: inputSourceMap.file, + sourceRoot: inputSourceMap.sourceRoot, + }); + + inputSourceMap.sources.forEach(function(sourceFile) { + const content = inputSourceMap.sourceContentFor(sourceFile); + if (content != null) { + generator.setSourceContent(sourceFile, content); + } + }); + + inputSourceMap.eachMapping(mapping => { + mappings.push(mapping); + }); + + mappings.sort((a, b) => a.generatedLine == b.generatedLine + ? a.generatedColumn - b.generatedColumn : a.generatedLine - b.generatedLine); + + file = inputSourceMap.file; + sources = inputSourceMap.sources; + + } else { + generator = new SourceMapGenerator(); + file = "input"; + sources = []; + } + + this._generator = generator; + this._sources = new Set(sources); + this._file = file; + + this._mappingsCursor = 0; + this._mappings = mappings; + + this._contents = new Map(); + } + + lastMapping() { + return this._mappings[this._mappingsCursor - 1]; + } + + nextMapping() { + return this._mappings[this._mappingsCursor]; + } + + addMapping(mapping) { + this._generator.addMapping(mapping); + } + + isEmpty() { + return this._mappings.length === 0; + } + + initEmpty(lines) { + this._mappings = []; + + for (var i = 0; i < lines; i++) { + this._mappings.push({ + originalLine: i + 1, + originalColumn: 0, + generatedLine: i + 1, + generatedColumn: 0, + source: this._file + }); + } + } + + addSourceContent(replacedText, content) { + let sourceName = this._contents.get(content); + + if (!sourceName) { + const PREFIX = "replacement/"; + + let sourceNameWithoutNumber = PREFIX; + sourceName = sourceNameWithoutNumber + "1"; + + if (replacedText.length > 0 && replacedText.length < 25) { + replacedText = replacedText + .replace(/^[^0-9a-z-_]+|[^0-9a-z-_]+$/ig, "") + .replace(/\s+/g, "-") + .replace(/[^0-9a-z-_]/ig, ""); + + if (replacedText) { + sourceNameWithoutNumber = PREFIX + replacedText + "-"; + sourceName = PREFIX + replacedText; + } + } + + let counter = 2; + while (this._sources.has(sourceName)) { + sourceName = sourceNameWithoutNumber + counter++; + } + + this._sources.add(sourceName); + this._contents.set(content, sourceName); + this._generator.setSourceContent(sourceName, content); + } + + return sourceName; + } + + /** + * Copies source map info from input to output up to but not including the specified position. + * @param {number} line + * @param {number} column + * @param {Offset} offset + */ + spoolTo(line, column, offset) { + this._consume(line, column, offset, true); + } + + /** + * Copies remaining source map info from input to output. + * @param {number} line + * @param {number} column + * @param {Offset} offset + */ + spoolToEnd(offset) { + this._consume(null, null, offset, true); + } + + /** + * Discards source map info from input up to but not including the specified position. + * @param {number} line + * @param {number} column + * @param {Offset} offset + */ + skipTo(line, column, offset) { + this._consume(line, column, offset, false); + } + + toJSON() { + return this._generator.toJSON(); + } + + _consume(line, column, offset, keep) { + let mapping; + + while ( + (mapping = this._mappings[this._mappingsCursor]) && + ( + line == null || + mapping.generatedLine < line || + mapping.generatedLine == line && mapping.generatedColumn < column + ) + ) { + if (keep) { + this._generator.addMapping({ + original: { + line: mapping.originalLine, + column: mapping.originalColumn, + }, + generated: { + line: mapping.generatedLine + offset.lineOffset, + column: mapping.generatedColumn + offset.getColumnOffset(mapping.generatedLine), + }, + source: mapping.source, + name: mapping.name, + }); + } + + this._mappingsCursor++; + } + } + +} + +class Offset { + constructor() { + this.lineOffset = 0; + this._columnOffset = 0; + this._columnOffsetForLine = 0; + } + + setColumnOffset(lineNumber, columnOffset) { + this._columnOffsetForLine = lineNumber; + this._columnOffset = columnOffset; + } + + getColumnOffset(lineNumber) { + return this._columnOffsetForLine === lineNumber ? + this._columnOffset : 0; + } +} + +class OverwriteRange { + constructor(options) { + if (!isFinite(options.start)) { + throw new Error("A replacement start index is required."); + } + if (!isFinite(options.end)) { + throw new Error("A replacement end index is required."); + } + if (options.end < options.start) { + throw new Error("Replacement end index cannot precede its start index."); + } + + /** + * Inclusive start index. + * @type {number} + */ + this.start = options.start; + + /** + * Exclusive start index. + * @type {number} + */ + this.end = options.end; + + /** + * The replacement interval will be replaced with this string. + * @type string + */ + this.replacement = "" + options.replacement; + + /** + * Optional name that will be mapped in the source map. + * @type string + */ + this.name = options.name; + } +} + +function gulp(replacements) { + if (typeof replacements === "string" || replacements instanceof RegExp) { + replacements = Array.from(arguments); + } + + const replacer = new Replacement(replacements); + + return new Transform({ + objectMode: true, + + transform(inputFile, _, fileDone) { + const input = inputFile.contents.toString(); + + const output = replacer.replace(input, inputFile.sourceMap); + + inputFile.contents = Buffer.from(output.output); + + if (inputFile.sourceMap) { + inputFile.sourceMap = output.sourceMap; + } + + fileDone(null, inputFile); + } + }); +} + +module.exports = { Replacement, gulp }; diff --git a/jdenticon-js/build/gulp/reserved-keywords.js b/jdenticon-js/build/gulp/reserved-keywords.js new file mode 100644 index 0000000..fd33504 --- /dev/null +++ b/jdenticon-js/build/gulp/reserved-keywords.js @@ -0,0 +1,70 @@ +module.exports = new Set([ + "async", + "break", + "case", + "catch", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "export", + "extends", + "finally", + "for", + "function", + "if", + "import", + "in", + "of", + "instanceof", + "new", + "return", + "super", + "switch", + "this", + "throw", + "try", + "typeof", + "var", + "void", + "while", + "with", + "yield", + "enum", + "implements", + "interface", + "let", + "package", + "private", + "protected", + "public", + "static", + "await", + "abstract", + "boolean", + "byte", + "char", + "double", + "final", + "float", + "goto", + "int", + "long", + "native", + "short", + "synchronized", + "throws", + "transient", + "volatile", + "arguments", + "get", + "set", + "null", + "undefined", + "exports", + "module", +]); \ No newline at end of file diff --git a/jdenticon-js/build/gulp/rollup.js b/jdenticon-js/build/gulp/rollup.js new file mode 100644 index 0000000..465811f --- /dev/null +++ b/jdenticon-js/build/gulp/rollup.js @@ -0,0 +1,56 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +const { rollup } = require("rollup"); +const Vinyl = require("vinyl"); +const applySourceMap = require("vinyl-sourcemaps-apply"); +const { Transform } = require("stream"); + +function rollupStream(options) { + return new Transform({ + objectMode: true, + + transform(inputFile, _, fileDone) { + const inputOptions = { + onwarn: warn => console.warn(warn.toString()), + ...options, + input: inputFile.path, + }; + delete inputOptions.output; + + rollup(inputOptions).then(bundle => { + return bundle.generate({ + ...options.output, + sourcemap: !!inputFile.sourceMap + }); + + }).then(outputs => { + + for (const output of outputs.output) { + if (output.type === "chunk") { + const outputFile = new Vinyl({ + cwd: inputFile.cwd, + base: inputFile.base, + path: inputFile.path, + contents: Buffer.from(output.code), + }); + + if (inputFile.sourceMap) { + applySourceMap(outputFile, output.map); + } + + this.push(outputFile); + } + } + + fileDone(); + + }, err => fileDone(err)); + } + }); +} + +module.exports = rollupStream; diff --git a/jdenticon-js/build/gulp/wrap-template.js b/jdenticon-js/build/gulp/wrap-template.js new file mode 100644 index 0000000..4c11652 --- /dev/null +++ b/jdenticon-js/build/gulp/wrap-template.js @@ -0,0 +1,26 @@ +const fs = require("fs"); +const replace = require("./replacement").gulp; + +function wrapTemplate(templatePath, variables) { + let template = fs.readFileSync(templatePath).toString(); + + if (variables) { + variables.forEach(variable => { + template = template.replace(variable[0], variable[1]); + }); + } + + template = template.split(/\/\*content\*\//); + + const replacements = []; + if (template[0]) { + replacements.push([/^/, template[0]]); + } + if (template[1]) { + replacements.push([/$/, template[1]]); + } + + return replace(replacements); +} + +module.exports = wrapTemplate; diff --git a/jdenticon-js/build/jdenticon.nuspec b/jdenticon-js/build/jdenticon.nuspec new file mode 100644 index 0000000..0c10bc6 --- /dev/null +++ b/jdenticon-js/build/jdenticon.nuspec @@ -0,0 +1,27 @@ + + + + Jdenticon + #version# + Jdenticon JS + Daniel Mester Pirttijärvi + MIT + http://jdenticon.com/ + http://jdenticon.com/hosted/nuget-logo-js.png + false + JavaScript library generating identicons using SVG graphics or HTML5 canvas. + +If you intend to generate icons server-side, consider using any of the .NET packages instead: +* Jdenticon-net +* Jdenticon.AspNetCore +* Jdenticon.AspNet.Mvc +* Jdenticon.AspNet.WebApi +* Jdenticon.AspNet.WebForms + JavaScript identicon avatar library + + + + + + + \ No newline at end of file diff --git a/jdenticon-js/build/nuget/NuGet.exe b/jdenticon-js/build/nuget/NuGet.exe new file mode 100644 index 0000000..d56c578 Binary files /dev/null and b/jdenticon-js/build/nuget/NuGet.exe differ diff --git a/jdenticon-js/build/nuget/license.txt b/jdenticon-js/build/nuget/license.txt new file mode 100644 index 0000000..d28049b --- /dev/null +++ b/jdenticon-js/build/nuget/license.txt @@ -0,0 +1,70 @@ +Apache License 2.0 (Apache) +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of this License; and + +2. You must cause any modified files to carry prominent notices stating that You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/jdenticon-js/build/template-min.js b/jdenticon-js/build/template-min.js new file mode 100644 index 0000000..9f0937d --- /dev/null +++ b/jdenticon-js/build/template-min.js @@ -0,0 +1,2 @@ +// Jdenticon #version# | jdenticon.com | MIT licensed | (c) 2014-#year# Daniel Mester Pirttijärvi +/*content*/ \ No newline at end of file diff --git a/jdenticon-js/build/template-module.js b/jdenticon-js/build/template-module.js new file mode 100644 index 0000000..a923688 --- /dev/null +++ b/jdenticon-js/build/template-module.js @@ -0,0 +1,10 @@ +/** + * Jdenticon #version# + * http://jdenticon.com + * + * Built: #date# + * + * #license# + */ + +/*content*/ \ No newline at end of file diff --git a/jdenticon-js/build/template-umd.js b/jdenticon-js/build/template-umd.js new file mode 100644 index 0000000..39bd264 --- /dev/null +++ b/jdenticon-js/build/template-umd.js @@ -0,0 +1,27 @@ +/** + * Jdenticon #version# + * http://jdenticon.com + * + * Built: #date# + * + * #license# + */ + +(function (umdGlobal, factory) { + var jdenticon = factory(umdGlobal); + + // Node.js + if (typeof module !== "undefined" && "exports" in module) { + module["exports"] = jdenticon; + } + // RequireJS + else if (typeof define === "function" && define["amd"]) { + define([], function () { return jdenticon; }); + } + // No module loader + else { + umdGlobal["jdenticon"] = jdenticon; + } +})(typeof self !== "undefined" ? self : this, function (umdGlobal) { +/*content*/ +}); \ No newline at end of file diff --git a/jdenticon-js/dist/README.md b/jdenticon-js/dist/README.md new file mode 100644 index 0000000..3d4ad23 --- /dev/null +++ b/jdenticon-js/dist/README.md @@ -0,0 +1,67 @@ +# What file should I use? + +## Overview + +| Platform | Bundle | File name | +|----------|------------------|----------------------| +| Browser | Standalone (UMD) | jdenticon.js | +| | | jdenticon.min.js | +| | ES module | jdenticon-module.mjs | +| | CommonJS module | jdenticon-module.js | +| Node.js | ES module | jdenticon-node.mjs | +| | CommonJS module | jdenticon-node.js | + +## Node vs browser +There are separate bundles for Node.js and browsers. The Node.js bundles contain support for generating PNG icons, while the browser bundles have support for updating DOM elements. It is important that the right bundle is used, since a web application bundle will be significally larger if the Node bundle is imported instead of the browser bundle. + +## Don't address `dist/*` directly +In first hand, don't import a specific file from the `dist` folder. Instead import the Jdenticon package and let the package decide what file to be imported. If your bundler does not pick the right file, and you cannot configure it to do so, there are explicit exports that you can use to force it to use the correct bundle: + +| Platform | Export | Example | +|----------|----------------------|----------------------------------------------| +| Browser | jdenticon/browser | `import { toSvg } from "jdenticon/browser";` | +| Node.js | jdenticon/node | `import { toSvg } from "jdenticon/node";` | +| UMD | jdenticon/standalone | `import "jdenticon/standalone";` | + +Jdenticon has multiple public entry points: + +### ES module + +For browsers `jdenticon-module.mjs` is imported and in Node.js environments `jdenticon-node.mjs` is imported. This is the preferred way of using Jdenticon since your bundler will most likely be able to eliminiate code from Jdenticon not used in your application (a.k.a. tree-shaking). + +**Example** + +```js +import { toSvg } from "jdenticon"; + +console.log(toSvg("my value", 100)); +``` + +### CommonJS module + +If Jdenticon is imported on platforms not supporting ES modules, `jdenticon-module.js` is imported for browser environments and `jdenticon-node.js` in Node.js environments. + +**Example** + +```js +const { toSvg } = require("jdenticon"); +console.log(toSvg("my value", 100)); +// or +const jdenticon = require("jdenticon"); +console.log(jdenticon.toSvg("my value", 100)); +``` + +### Standalone browser package + +This package will render icons automatically at startup and also provides a legacy jQuery plugin, if jQuery is loaded before Jdenticon. + +**Example** + +```js +import "jdenticon/standalone"; + +// or + +import { toSvg } from "jdenticon/standalone"; +console.log(toSvg("my value", 100)); +``` \ No newline at end of file diff --git a/jdenticon-js/dist/jdenticon-module.js b/jdenticon-js/dist/jdenticon-module.js new file mode 100644 index 0000000..568033a --- /dev/null +++ b/jdenticon-js/dist/jdenticon-module.js @@ -0,0 +1,1405 @@ +/** + * Jdenticon 3.3.0 + * http://jdenticon.com + * + * Built: 2024-05-10T09:48:41.921Z + * + * MIT License + * + * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi + * + * 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. + */ + +'use strict'; + +/** + * Parses a substring of the hash as a number. + * @param {number} startPosition + * @param {number=} octets + */ +function parseHex(hash, startPosition, octets) { + return parseInt(hash.substr(startPosition, octets), 16); +} + +function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; +} + +function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); +} + +/** + * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported. + * @returns {string} + */ +function parseColor(color) { + if (/^#[0-9a-f]{3,8}$/i.test(color)) { + var result; + var colorLength = color.length; + + if (colorLength < 6) { + var r = color[1], + g = color[2], + b = color[3], + a = color[4] || ""; + result = "#" + r + r + g + g + b + b + a + a; + } + if (colorLength == 7 || colorLength > 8) { + result = color; + } + + return result; + } +} + +/** + * Converts a hexadecimal color to a CSS3 compatible color. + * @param {string} hexColor Color on the format "#RRGGBB" or "#RRGGBBAA" + * @returns {string} + */ +function toCss3Color(hexColor) { + var a = parseHex(hexColor, 7, 2); + var result; + + if (isNaN(a)) { + result = hexColor; + } else { + var r = parseHex(hexColor, 1, 2), + g = parseHex(hexColor, 3, 2), + b = parseHex(hexColor, 5, 2); + result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")"; + } + + return result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function hsl(hue, saturation, lightness) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + var result; + + if (saturation == 0) { + var partialHex = decToHex(lightness * 255); + result = partialHex + partialHex + partialHex; + } + else { + var m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation, + m1 = lightness * 2 - m2; + result = + hueToRgb(m1, m2, hue * 6 + 2) + + hueToRgb(m1, m2, hue * 6) + + hueToRgb(m1, m2, hue * 6 - 2); + } + + return "#" + result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function correctedHsl(hue, saturation, lightness) { + // The corrector specifies the perceived middle lightness for each hue + var correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(hue * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; + + return hsl(hue, saturation, lightness); +} + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. + +var GLOBAL = + typeof window !== "undefined" ? window : + typeof self !== "undefined" ? self : + typeof global !== "undefined" ? global : + {}; + +/** + * @typedef {Object} ParsedConfiguration + * @property {number} colorSaturation + * @property {number} grayscaleSaturation + * @property {string} backColor + * @property {number} iconPadding + * @property {function(number):number} hue + * @property {function(number):number} colorLightness + * @property {function(number):number} grayscaleLightness + */ + +var CONFIG_PROPERTIES = { + W/*GLOBAL*/: "jdenticon_config", + n/*MODULE*/: "config", +}; + +var rootConfigurationHolder = {}; + +/** + * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console + * when it is being used. + * @param {!Object} rootObject + */ +function defineConfigProperty(rootObject) { + rootConfigurationHolder = rootObject; +} + +/** + * Sets a new icon style configuration. The new configuration is not merged with the previous one. * + * @param {Object} newConfiguration - New configuration object. + */ +function configure(newConfiguration) { + if (arguments.length) { + rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] = newConfiguration; + } + return rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/]; +} + +/** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A + * local configuration overrides the global configuration in it entirety. This parameter can for backward + * compatibility also contain a padding value. A padding value only overrides the global padding, not the + * entire global configuration. + * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor + * explicitly to the API method. + * @returns {ParsedConfiguration} + */ +function getConfiguration(paddingOrLocalConfig, defaultPadding) { + var configObject = + typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || + rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] || + GLOBAL[CONFIG_PROPERTIES.W/*GLOBAL*/] || + { }, + + lightnessConfig = configObject["lightness"] || { }, + + // In versions < 2.1.0 there was no grayscale saturation - + // saturation was the color saturation. + saturation = configObject["saturation"] || { }, + colorSaturation = "color" in saturation ? saturation["color"] : saturation, + grayscaleSaturation = saturation["grayscale"], + + backColor = configObject["backColor"], + padding = configObject["padding"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultRange) { + var range = lightnessConfig[configName]; + + // Check if the lightness range is an array-like object. This way we ensure the + // array contain two values at the same time. + if (!(range && range.length > 1)) { + range = defaultRange; + } + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + /** + * Gets a hue allowed by the configured hue restriction, + * provided the originally computed hue. + */ + function hueFunction(originalHue) { + var hueConfig = configObject["hues"]; + var hue; + + // Check if 'hues' is an array-like object. This way we also ensure that + // the array is not empty, which would mean no hue restriction. + if (hueConfig && hueConfig.length > 0) { + // originalHue is in the range [0, 1] + // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. + hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; + } + + return typeof hue == "number" ? + + // A hue was specified. We need to convert the hue from + // degrees on any turn - e.g. 746° is a perfectly valid hue - + // to turns in the range [0, 1). + ((((hue / 360) % 1) + 1) % 1) : + + // No hue configured => use original hue + originalHue; + } + + return { + X/*hue*/: hueFunction, + o/*colorSaturation*/: typeof colorSaturation == "number" ? colorSaturation : 0.5, + F/*grayscaleSaturation*/: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, + p/*colorLightness*/: lightness("color", [0.4, 0.8]), + G/*grayscaleLightness*/: lightness("grayscale", [0.3, 0.9]), + H/*backColor*/: parseColor(backColor), + Y/*iconPadding*/: + typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : + typeof padding == "number" ? padding : + defaultPadding + } +} + +/** + * Represents a point. + */ +function Point(x, y) { + this.x = x; + this.y = y; +} + +/** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + */ +function Transform(x, y, size, rotation) { + this.q/*_x*/ = x; + this.t/*_y*/ = y; + this.I/*_size*/ = size; + this.Z/*_rotation*/ = rotation; +} + +/** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ +Transform.prototype.J/*transformIconPoint*/ = function transformIconPoint (x, y, w, h) { + var right = this.q/*_x*/ + this.I/*_size*/, + bottom = this.t/*_y*/ + this.I/*_size*/, + rotation = this.Z/*_rotation*/; + return rotation === 1 ? new Point(right - y - (h || 0), this.t/*_y*/ + x) : + rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + rotation === 3 ? new Point(this.q/*_x*/ + y, bottom - x - (w || 0)) : + new Point(this.q/*_x*/ + x, this.t/*_y*/ + y); +}; + +var NO_TRANSFORM = new Transform(0, 0, 0, 0); + + + +/** + * Provides helper functions for rendering common basic shapes. + */ +function Graphics(renderer) { + /** + * @type {Renderer} + * @private + */ + this.K/*_renderer*/ = renderer; + + /** + * @type {Transform} + */ + this.u/*currentTransform*/ = NO_TRANSFORM; +} +var Graphics__prototype = Graphics.prototype; + +/** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ +Graphics__prototype.g/*addPolygon*/ = function addPolygon (points, invert) { + var this$1 = this; + + var di = invert ? -2 : 2, + transformedPoints = []; + + for (var i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(this$1.u/*currentTransform*/.J/*transformIconPoint*/(points[i], points[i + 1])); + } + + this.K/*_renderer*/.g/*addPolygon*/(transformedPoints); +}; + +/** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ +Graphics__prototype.h/*addCircle*/ = function addCircle (x, y, size, invert) { + var p = this.u/*currentTransform*/.J/*transformIconPoint*/(x, y, size, size); + this.K/*_renderer*/.h/*addCircle*/(p, size, invert); +}; + +/** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ +Graphics__prototype.i/*addRectangle*/ = function addRectangle (x, y, w, h, invert) { + this.g/*addPolygon*/([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); +}; + +/** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ +Graphics__prototype.j/*addTriangle*/ = function addTriangle (x, y, w, h, r, invert) { + var points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.g/*addPolygon*/(points, invert); +}; + +/** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ +Graphics__prototype.L/*addRhombus*/ = function addRhombus (x, y, w, h, invert) { + this.g/*addPolygon*/([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); +}; + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + * @param {number} positionIndex + */ +function centerShape(index, g, cell, positionIndex) { + index = index % 14; + + var k, m, w, h, inner, outer; + + !index ? ( + k = cell * 0.42, + g.g/*addPolygon*/([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ])) : + + index == 1 ? ( + w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8), + + g.j/*addTriangle*/(cell - w, 0, w, h, 2)) : + + index == 2 ? ( + w = 0 | (cell / 3), + g.i/*addRectangle*/(w, w, cell - w, cell - w)) : + + index == 3 ? ( + inner = cell * 0.1, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)), + + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + g.i/*addRectangle*/(outer, outer, cell - inner - outer, cell - inner - outer)) : + + index == 4 ? ( + m = 0 | (cell * 0.15), + w = 0 | (cell * 0.5), + g.h/*addCircle*/(cell - w - m, cell - w - m, w)) : + + index == 5 ? ( + inner = cell * 0.1, + outer = inner * 4, + + // Align edge to nearest pixel in large icons + outer > 3 && (outer = 0 | outer), + + g.i/*addRectangle*/(0, 0, cell, cell), + g.g/*addPolygon*/([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true)) : + + index == 6 ? + g.g/*addPolygon*/([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]) : + + index == 7 ? + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 8 ? ( + g.i/*addRectangle*/(0, 0, cell, cell / 2), + g.i/*addRectangle*/(0, cell / 2, cell / 2, cell / 2), + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 1)) : + + index == 9 ? ( + inner = cell * 0.14, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)), + + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + g.i/*addRectangle*/(0, 0, cell, cell), + g.i/*addRectangle*/(outer, outer, cell - outer - inner, cell - outer - inner, true)) : + + index == 10 ? ( + inner = cell * 0.12, + outer = inner * 3, + + g.i/*addRectangle*/(0, 0, cell, cell), + g.h/*addCircle*/(outer, outer, cell - inner - outer, true)) : + + index == 11 ? + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 12 ? ( + m = cell * 0.25, + g.i/*addRectangle*/(0, 0, cell, cell), + g.L/*addRhombus*/(m, m, cell - m, cell - m, true)) : + + // 13 + ( + !positionIndex && ( + m = cell * 0.4, w = cell * 1.2, + g.h/*addCircle*/(m, m, w) + ) + ); +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + */ +function outerShape(index, g, cell) { + index = index % 4; + + var m; + + !index ? + g.j/*addTriangle*/(0, 0, cell, cell, 0) : + + index == 1 ? + g.j/*addTriangle*/(0, cell / 2, cell, cell / 2, 0) : + + index == 2 ? + g.L/*addRhombus*/(0, 0, cell, cell) : + + // 3 + ( + m = cell / 6, + g.h/*addCircle*/(m, m, cell - 2 * m) + ); +} + +/** + * Gets a set of identicon color candidates for a specified hue and config. + * @param {number} hue + * @param {ParsedConfiguration} config + */ +function colorTheme(hue, config) { + hue = config.X/*hue*/(hue); + return [ + // Dark gray + correctedHsl(hue, config.F/*grayscaleSaturation*/, config.G/*grayscaleLightness*/(0)), + // Mid color + correctedHsl(hue, config.o/*colorSaturation*/, config.p/*colorLightness*/(0.5)), + // Light gray + correctedHsl(hue, config.F/*grayscaleSaturation*/, config.G/*grayscaleLightness*/(1)), + // Light color + correctedHsl(hue, config.o/*colorSaturation*/, config.p/*colorLightness*/(1)), + // Dark color + correctedHsl(hue, config.o/*colorSaturation*/, config.p/*colorLightness*/(0)) + ]; +} + +/** + * Draws an identicon to a specified renderer. + * @param {Renderer} renderer + * @param {string} hash + * @param {Object|number=} config + */ +function iconGenerator(renderer, hash, config) { + var parsedConfig = getConfiguration(config, 0.08); + + // Set background color + if (parsedConfig.H/*backColor*/) { + renderer.m/*setBackground*/(parsedConfig.H/*backColor*/); + } + + // Calculate padding and round to nearest integer + var size = renderer.k/*iconSize*/; + var padding = (0.5 + size * parsedConfig.Y/*iconPadding*/) | 0; + size -= padding * 2; + + var graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + var cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + var x = 0 | (padding + size / 2 - cell * 2); + var y = 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + var shapeIndex = parseHex(hash, index, 1); + var r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.M/*beginShape*/(availableColors[selectedColorIndexes[colorIndex]]); + + for (var i = 0; i < positions.length; i++) { + graphics.u/*currentTransform*/ = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.N/*endShape*/(); + } + + // AVAILABLE COLORS + var hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, parsedConfig), + + // The index of the selected colors + selectedColorIndexes = []; + + var index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (var i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (var i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); +} + +/** + * Computes a SHA1 hash for any value and returns it as a hexadecimal string. + * + * This function is optimized for minimal code size and rather short messages. + * + * @param {string} message + */ +function sha1(message) { + var HASH_SIZE_HALF_BYTES = 40; + var BLOCK_SIZE_WORDS = 16; + + // Variables + // `var` is used to be able to minimize the number of `var` keywords. + var i = 0, + f = 0, + + // Use `encodeURI` to UTF8 encode the message without any additional libraries + // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky + // since `unescape` is deprecated. + urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding + + // This can be changed to a preallocated Uint32Array array for greater performance and larger code size + data = [], + dataSize, + + hashBuffer = [], + + a = 0x67452301, + b = 0xefcdab89, + c = ~a, + d = ~b, + e = 0xc3d2e1f0, + hash = [a, b, c, d, e], + + blockStartIndex = 0, + hexHash = ""; + + /** + * Rotates the value a specified number of bits to the left. + * @param {number} value Value to rotate + * @param {number} shift Bit count to shift. + */ + function rotl(value, shift) { + return (value << shift) | (value >>> (32 - shift)); + } + + // Message data + for ( ; i < urlEncodedMessage.length; f++) { + data[f >> 2] = data[f >> 2] | + ( + ( + urlEncodedMessage[i] == "%" + // Percent encoded byte + ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16) + // Unencoded byte + : urlEncodedMessage.charCodeAt(i++) + ) + + // Read bytes in reverse order (big endian words) + << ((3 - (f & 3)) * 8) + ); + } + + // f is now the length of the utf8 encoded message + // 7 = 8 bytes (64 bit) for message size, -1 to round down + // >> 6 = integer division with block size + dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS; + + // Message size in bits. + // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least + // significant 32 bits are set. -8 is for the '1' bit padding byte. + data[dataSize - 1] = f * 8 - 8; + + // Compute hash + for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) { + for (i = 0; i < 80; i++) { + f = rotl(a, 5) + e + ( + // Ch + i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 : + + // Parity + i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 : + + // Maj + i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc : + + // Parity + (b ^ c ^ d) + 0xca62c1d6 + ) + ( + hashBuffer[i] = i < BLOCK_SIZE_WORDS + // Bitwise OR is used to coerse `undefined` to 0 + ? (data[blockStartIndex + i] | 0) + : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1) + ); + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = f; + } + + hash[0] = a = ((hash[0] + a) | 0); + hash[1] = b = ((hash[1] + b) | 0); + hash[2] = c = ((hash[2] + c) | 0); + hash[3] = d = ((hash[3] + d) | 0); + hash[4] = e = ((hash[4] + e) | 0); + } + + // Format hex hash + for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) { + hexHash += ( + ( + // Get word (2^3 half-bytes per word) + hash[i >> 3] >>> + + // Append half-bytes in reverse order + ((7 - (i & 7)) * 4) + ) + // Clamp to half-byte + & 0xf + ).toString(16); + } + + return hexHash; +} + +/** + * Inputs a value that might be a valid hash string for Jdenticon and returns it + * if it is determined valid, otherwise a falsy value is returned. + */ +function isValidHash(hashCandidate) { + return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate; +} + +/** + * Computes a hash for the specified value. Currently SHA1 is used. This function + * always returns a valid hash. + */ +function computeHash(value) { + return sha1(value == null ? "" : "" + value); +} + + + +/** + * Renderer redirecting drawing commands to a canvas context. + * @implements {Renderer} + */ +function CanvasRenderer(ctx, iconSize) { + var canvas = ctx.canvas; + var width = canvas.width; + var height = canvas.height; + + ctx.save(); + + if (!iconSize) { + iconSize = Math.min(width, height); + + ctx.translate( + ((width - iconSize) / 2) | 0, + ((height - iconSize) / 2) | 0); + } + + /** + * @private + */ + this.l/*_ctx*/ = ctx; + this.k/*iconSize*/ = iconSize; + + ctx.clearRect(0, 0, iconSize, iconSize); +} +var CanvasRenderer__prototype = CanvasRenderer.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ +CanvasRenderer__prototype.m/*setBackground*/ = function setBackground (fillColor) { + var ctx = this.l/*_ctx*/; + var iconSize = this.k/*iconSize*/; + + ctx.fillStyle = toCss3Color(fillColor); + ctx.fillRect(0, 0, iconSize, iconSize); +}; + +/** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} fillColor Fill color on format #rrggbb[aa]. + */ +CanvasRenderer__prototype.M/*beginShape*/ = function beginShape (fillColor) { + var ctx = this.l/*_ctx*/; + ctx.fillStyle = toCss3Color(fillColor); + ctx.beginPath(); +}; + +/** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ +CanvasRenderer__prototype.N/*endShape*/ = function endShape () { + this.l/*_ctx*/.fill(); +}; + +/** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ +CanvasRenderer__prototype.g/*addPolygon*/ = function addPolygon (points) { + var ctx = this.l/*_ctx*/; + ctx.moveTo(points[0].x, points[0].y); + for (var i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); +}; + +/** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ +CanvasRenderer__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) { + var ctx = this.l/*_ctx*/, + radius = diameter / 2; + ctx.moveTo(point.x + radius, point.y + radius); + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); +}; + +/** + * Called when the icon has been completely drawn. + */ +CanvasRenderer__prototype.finish = function finish () { + this.l/*_ctx*/.restore(); +}; + +var ICON_TYPE_SVG = 1; + +var ICON_TYPE_CANVAS = 2; + +var ATTRIBUTES = { + O/*HASH*/: "data-jdenticon-hash", + v/*VALUE*/: "data-jdenticon-value" +}; + +var IS_RENDERED_PROPERTY = "jdenticonRendered"; + +var ICON_SELECTOR = "[" + ATTRIBUTES.O/*HASH*/ +"],[" + ATTRIBUTES.v/*VALUE*/ +"]"; + +var documentQuerySelectorAll = /** @type {!Function} */ ( + typeof document !== "undefined" && document.querySelectorAll.bind(document)); + +function getIdenticonType(el) { + if (el) { + var tagName = el["tagName"]; + + if (/^svg$/i.test(tagName)) { + return ICON_TYPE_SVG; + } + + if (/^canvas$/i.test(tagName) && "getContext" in el) { + return ICON_TYPE_CANVAS; + } + } +} + +/** + * Draws an identicon to a context. + * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0). + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function drawIcon(ctx, hashOrValue, size, config) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + var canvas = ctx.canvas; + if (canvas) { + canvas[IS_RENDERED_PROPERTY] = true; + } +} + +/** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ +function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; +} + +/** + * Represents an SVG path element. + */ +function SvgPath() { + /** + * This property holds the data string (path.d) of the SVG path. + * @type {string} + */ + this.A/*dataString*/ = ""; +} +var SvgPath__prototype = SvgPath.prototype; + +/** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ +SvgPath__prototype.g/*addPolygon*/ = function addPolygon (points) { + var dataString = ""; + for (var i = 0; i < points.length; i++) { + dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.A/*dataString*/ += dataString + "Z"; +}; + +/** + * Adds a circle with the current fill color to the SVG path. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ +SvgPath__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) { + var sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter), + svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " "; + + this.A/*dataString*/ += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + svgArc + svgDiameter + ",0" + + svgArc + (-svgDiameter) + ",0"; +}; + + + +/** + * Renderer producing SVG output. + * @implements {Renderer} + */ +function SvgRenderer(target) { + /** + * @type {SvgPath} + * @private + */ + this.B/*_path*/; + + /** + * @type {Object.} + * @private + */ + this.C/*_pathsByColor*/ = { }; + + /** + * @type {SvgElement|SvgWriter} + * @private + */ + this.P/*_target*/ = target; + + /** + * @type {number} + */ + this.k/*iconSize*/ = target.k/*iconSize*/; +} +var SvgRenderer__prototype = SvgRenderer.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ +SvgRenderer__prototype.m/*setBackground*/ = function setBackground (fillColor) { + var match = /^(#......)(..)?/.exec(fillColor), + opacity = match[2] ? parseHex(match[2], 0) / 255 : 1; + this.P/*_target*/.m/*setBackground*/(match[1], opacity); +}; + +/** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ +SvgRenderer__prototype.M/*beginShape*/ = function beginShape (color) { + this.B/*_path*/ = this.C/*_pathsByColor*/[color] || (this.C/*_pathsByColor*/[color] = new SvgPath()); +}; + +/** + * Marks the end of the currently drawn shape. + */ +SvgRenderer__prototype.N/*endShape*/ = function endShape () { }; + +/** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ +SvgRenderer__prototype.g/*addPolygon*/ = function addPolygon (points) { + this.B/*_path*/.g/*addPolygon*/(points); +}; + +/** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ +SvgRenderer__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) { + this.B/*_path*/.h/*addCircle*/(point, diameter, counterClockwise); +}; + +/** + * Called when the icon has been completely drawn. + */ +SvgRenderer__prototype.finish = function finish () { + var this$1 = this; + + var pathsByColor = this.C/*_pathsByColor*/; + for (var color in pathsByColor) { + // hasOwnProperty cannot be shadowed in pathsByColor + // eslint-disable-next-line no-prototype-builtins + if (pathsByColor.hasOwnProperty(color)) { + this$1.P/*_target*/.R/*appendPath*/(color, pathsByColor[color].A/*dataString*/); + } + } +}; + +var SVG_CONSTANTS = { + S/*XMLNS*/: "http://www.w3.org/2000/svg", + T/*WIDTH*/: "width", + U/*HEIGHT*/: "height", +}; + +/** + * Renderer producing SVG output. + */ +function SvgWriter(iconSize) { + /** + * @type {number} + */ + this.k/*iconSize*/ = iconSize; + + /** + * @type {string} + * @private + */ + this.D/*_s*/ = + ''; +} +var SvgWriter__prototype = SvgWriter.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ +SvgWriter__prototype.m/*setBackground*/ = function setBackground (fillColor, opacity) { + if (opacity) { + this.D/*_s*/ += ''; + } +}; + +/** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #rrggbb. + * @param {string} dataString The SVG path data string. + */ +SvgWriter__prototype.R/*appendPath*/ = function appendPath (color, dataString) { + this.D/*_s*/ += ''; +}; + +/** + * Gets the rendered image as an SVG string. + */ +SvgWriter__prototype.toString = function toString () { + return this.D/*_s*/ + ""; +}; + +/** + * Draws an identicon as an SVG string. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {string} SVG string + */ +function toSvg(hashOrValue, size, config) { + var writer = new SvgWriter(size); + iconGenerator(new SvgRenderer(writer), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + return writer.toString(); +} + +/** + * Creates a new element and adds it to the specified parent. + * @param {Element} parentNode + * @param {string} name + * @param {...(string|number)} keyValuePairs + */ +function SvgElement_append(parentNode, name) { + var keyValuePairs = [], len = arguments.length - 2; + while ( len-- > 0 ) keyValuePairs[ len ] = arguments[ len + 2 ]; + + var el = document.createElementNS(SVG_CONSTANTS.S/*XMLNS*/, name); + + for (var i = 0; i + 1 < keyValuePairs.length; i += 2) { + el.setAttribute( + /** @type {string} */(keyValuePairs[i]), + /** @type {string} */(keyValuePairs[i + 1]) + ); + } + + parentNode.appendChild(el); +} + + +/** + * Renderer producing SVG output. + */ +function SvgElement(element) { + // Don't use the clientWidth and clientHeight properties on SVG elements + // since Firefox won't serve a proper value of these properties on SVG + // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811) + // Instead use 100px as a hardcoded size (the svg viewBox will rescale + // the icon to the correct dimensions) + var iconSize = this.k/*iconSize*/ = Math.min( + (Number(element.getAttribute(SVG_CONSTANTS.T/*WIDTH*/)) || 100), + (Number(element.getAttribute(SVG_CONSTANTS.U/*HEIGHT*/)) || 100) + ); + + /** + * @type {Element} + * @private + */ + this.V/*_el*/ = element; + + // Clear current SVG child elements + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + // Set viewBox attribute to ensure the svg scales nicely. + element.setAttribute("viewBox", "0 0 " + iconSize + " " + iconSize); + element.setAttribute("preserveAspectRatio", "xMidYMid meet"); +} +var SvgElement__prototype = SvgElement.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ +SvgElement__prototype.m/*setBackground*/ = function setBackground (fillColor, opacity) { + if (opacity) { + SvgElement_append(this.V/*_el*/, "rect", + SVG_CONSTANTS.T/*WIDTH*/, "100%", + SVG_CONSTANTS.U/*HEIGHT*/, "100%", + "fill", fillColor, + "opacity", opacity); + } +}; + +/** + * Appends a path to the SVG element. + * @param {string} color Fill color on format #xxxxxx. + * @param {string} dataString The SVG path data string. + */ +SvgElement__prototype.R/*appendPath*/ = function appendPath (color, dataString) { + SvgElement_append(this.V/*_el*/, "path", + "fill", color, + "d", dataString); +}; + +/** + * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute. + */ +function updateAll() { + if (documentQuerySelectorAll) { + update(ICON_SELECTOR); + } +} + +/** + * Updates the identicon in the specified `` or `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function update(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType) { + return iconType == ICON_TYPE_SVG ? + new SvgRenderer(new SvgElement(el)) : + new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function updateCanvas(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType == ICON_TYPE_CANVAS) { + return new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function updateSvg(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType == ICON_TYPE_SVG) { + return new SvgRenderer(new SvgElement(el)); + } + }); +} + +/** + * Updates the identicon in the specified canvas or svg elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number|undefined} config + * @param {function(Element,number):Renderer} rendererFactory - Factory function for creating an icon renderer. + */ +function renderDomElement(el, hashOrValue, config, rendererFactory) { + if (typeof el === "string") { + if (documentQuerySelectorAll) { + var elements = documentQuerySelectorAll(el); + for (var i = 0; i < elements.length; i++) { + renderDomElement(elements[i], hashOrValue, config, rendererFactory); + } + } + return; + } + + // Hash selection. The result from getValidHash or computeHash is + // accepted as a valid hash. + var hash = + // 1. Explicit valid hash + isValidHash(hashOrValue) || + + // 2. Explicit value (`!= null` catches both null and undefined) + hashOrValue != null && computeHash(hashOrValue) || + + // 3. `data-jdenticon-hash` attribute + isValidHash(el.getAttribute(ATTRIBUTES.O/*HASH*/)) || + + // 4. `data-jdenticon-value` attribute. + // We want to treat an empty attribute as an empty value. + // Some browsers return empty string even if the attribute + // is not specified, so use hasAttribute to determine if + // the attribute is specified. + el.hasAttribute(ATTRIBUTES.v/*VALUE*/) && computeHash(el.getAttribute(ATTRIBUTES.v/*VALUE*/)); + + if (!hash) { + // No hash specified. Don't render an icon. + return; + } + + var renderer = rendererFactory(el, getIdenticonType(el)); + if (renderer) { + // Draw icon + iconGenerator(renderer, hash, config); + el[IS_RENDERED_PROPERTY] = true; + } +} + +// This file is compiled to dist/jdenticon-module.js + +var jdenticon = updateAll; + +defineConfigProperty(jdenticon); + +// Export public API +jdenticon["configure"] = configure; +jdenticon["drawIcon"] = drawIcon; +jdenticon["toSvg"] = toSvg; +jdenticon["update"] = update; +jdenticon["updateCanvas"] = updateCanvas; +jdenticon["updateSvg"] = updateSvg; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +jdenticon["version"] = "3.3.0"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +jdenticon["bundle"] = "browser-cjs"; + +module.exports = jdenticon; +//# sourceMappingURL=jdenticon-module.js.map diff --git a/jdenticon-js/dist/jdenticon-module.js.map b/jdenticon-js/dist/jdenticon-module.js.map new file mode 100644 index 0000000..2df7ff4 --- /dev/null +++ b/jdenticon-js/dist/jdenticon-module.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["replacement/7","src/common/parseHex.js","src/renderer/color.js","src/common/global.js","src/common/configuration.js","src/renderer/point.js","src/renderer/transform.js","src/renderer/graphics.js","replacement/1","src/renderer/shapes.js","src/renderer/colorTheme.js","src/renderer/iconGenerator.js","src/common/sha1.js","src/common/hashUtils.js","src/renderer/canvas/canvasRenderer.js","replacement/2","src/common/dom.js","src/apis/drawIcon.js","src/renderer/svg/svgPath.js","replacement/3","src/renderer/svg/svgRenderer.js","replacement/4","src/renderer/svg/constants.js","src/renderer/svg/svgWriter.js","replacement/5","src/apis/toSvg.js","src/renderer/svg/svgElement.js","replacement/6","src/apis/update.js","src/browser-cjs.js","replacement/8"],"names":["let","const","GLOBAL","MODULE","hue","colorSaturation","grayscaleSaturation","colorLightness","grayscaleLightness","backColor","iconPadding","_x","_y","_size","_rotation","transformIconPoint","_renderer","currentTransform","addPolygon","this","addCircle","addRectangle","addTriangle","addRhombus","setBackground","iconSize","beginShape","endShape","_ctx","HASH","VALUE","dataString","_path","_pathsByColor","_target","appendPath","XMLNS","WIDTH","HEIGHT","_s","_el"],"mappings":"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","file":"jdenticon-module.js","sourcesContent":["/**\r\n * Jdenticon 3.3.0\r\n * http://jdenticon.com\r\n *\r\n * Built: 2024-05-10T09:48:41.921Z\r\n * \r\n * MIT License\r\n * \r\n * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi\r\n * \r\n * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n * of this software and associated documentation files (the \"Software\"), to deal\r\n * in the Software without restriction, including without limitation the rights\r\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the Software is\r\n * furnished to do so, subject to the following conditions:\r\n * \r\n * The above copyright notice and this permission notice shall be included in all\r\n * copies or substantial portions of the Software.\r\n * \r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n * SOFTWARE.\r\n */\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Parses a substring of the hash as a number.\r\n * @param {number} startPosition \r\n * @param {number=} octets\r\n */\r\nexport function parseHex(hash, startPosition, octets) {\r\n return parseInt(hash.substr(startPosition, octets), 16);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseHex } from \"../common/parseHex\";\r\n\r\nfunction decToHex(v) {\r\n v |= 0; // Ensure integer value\r\n return v < 0 ? \"00\" :\r\n v < 16 ? \"0\" + v.toString(16) :\r\n v < 256 ? v.toString(16) :\r\n \"ff\";\r\n}\r\n\r\nfunction hueToRgb(m1, m2, h) {\r\n h = h < 0 ? h + 6 : h > 6 ? h - 6 : h;\r\n return decToHex(255 * (\r\n h < 1 ? m1 + (m2 - m1) * h :\r\n h < 3 ? m2 :\r\n h < 4 ? m1 + (m2 - m1) * (4 - h) :\r\n m1));\r\n}\r\n\r\n/**\r\n * @param {number} r Red channel [0, 255]\r\n * @param {number} g Green channel [0, 255]\r\n * @param {number} b Blue channel [0, 255]\r\n */\r\nexport function rgb(r, g, b) {\r\n return \"#\" + decToHex(r) + decToHex(g) + decToHex(b);\r\n}\r\n\r\n/**\r\n * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported.\r\n * @returns {string}\r\n */\r\nexport function parseColor(color) {\r\n if (/^#[0-9a-f]{3,8}$/i.test(color)) {\r\n let result;\r\n const colorLength = color.length;\r\n\r\n if (colorLength < 6) {\r\n const r = color[1],\r\n g = color[2],\r\n b = color[3],\r\n a = color[4] || \"\";\r\n result = \"#\" + r + r + g + g + b + b + a + a;\r\n }\r\n if (colorLength == 7 || colorLength > 8) {\r\n result = color;\r\n }\r\n \r\n return result;\r\n }\r\n}\r\n\r\n/**\r\n * Converts a hexadecimal color to a CSS3 compatible color.\r\n * @param {string} hexColor Color on the format \"#RRGGBB\" or \"#RRGGBBAA\"\r\n * @returns {string}\r\n */\r\nexport function toCss3Color(hexColor) {\r\n const a = parseHex(hexColor, 7, 2);\r\n let result;\r\n\r\n if (isNaN(a)) {\r\n result = hexColor;\r\n } else {\r\n const r = parseHex(hexColor, 1, 2),\r\n g = parseHex(hexColor, 3, 2),\r\n b = parseHex(hexColor, 5, 2);\r\n result = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + (a / 255).toFixed(2) + \")\";\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color.\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function hsl(hue, saturation, lightness) {\r\n // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color\r\n let result;\r\n\r\n if (saturation == 0) {\r\n const partialHex = decToHex(lightness * 255);\r\n result = partialHex + partialHex + partialHex;\r\n }\r\n else {\r\n const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation,\r\n m1 = lightness * 2 - m2;\r\n result =\r\n hueToRgb(m1, m2, hue * 6 + 2) +\r\n hueToRgb(m1, m2, hue * 6) +\r\n hueToRgb(m1, m2, hue * 6 - 2);\r\n }\r\n\r\n return \"#\" + result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the \"dark\" hues\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function correctedHsl(hue, saturation, lightness) {\r\n // The corrector specifies the perceived middle lightness for each hue\r\n const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ],\r\n corrector = correctors[(hue * 6 + 0.5) | 0];\r\n \r\n // Adjust the input lightness relative to the corrector\r\n lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2;\r\n \r\n return hsl(hue, saturation, lightness);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for\r\n// backward compatibility.\r\n\r\nexport const GLOBAL = \r\n typeof window !== \"undefined\" ? window :\r\n typeof self !== \"undefined\" ? self :\r\n typeof global !== \"undefined\" ? global :\r\n {};\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseColor } from \"../renderer/color\";\r\nimport { GLOBAL } from \"./global\";\r\n\r\n/**\r\n * @typedef {Object} ParsedConfiguration\r\n * @property {number} colorSaturation\r\n * @property {number} grayscaleSaturation\r\n * @property {string} backColor\r\n * @property {number} iconPadding\r\n * @property {function(number):number} hue\r\n * @property {function(number):number} colorLightness\r\n * @property {function(number):number} grayscaleLightness\r\n */\r\n\r\nexport const CONFIG_PROPERTIES = {\r\n GLOBAL: \"jdenticon_config\",\r\n MODULE: \"config\",\r\n};\r\n\r\nvar rootConfigurationHolder = {};\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is \r\n * printed in the console. To minimize bundle size, this is only used in Node bundles.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigPropertyWithWarn(rootObject) {\r\n Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, {\r\n configurable: true,\r\n get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE],\r\n set: newConfiguration => {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n console.warn(\"jdenticon.config is deprecated. Use jdenticon.configure() instead.\");\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console\r\n * when it is being used.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigProperty(rootObject) {\r\n rootConfigurationHolder = rootObject;\r\n}\r\n\r\n/**\r\n * Sets a new icon style configuration. The new configuration is not merged with the previous one. * \r\n * @param {Object} newConfiguration - New configuration object.\r\n */\r\nexport function configure(newConfiguration) {\r\n if (arguments.length) {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n }\r\n return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE];\r\n}\r\n\r\n/**\r\n * Gets the normalized current Jdenticon color configuration. Missing fields have default values.\r\n * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A\r\n * local configuration overrides the global configuration in it entirety. This parameter can for backward\r\n * compatibility also contain a padding value. A padding value only overrides the global padding, not the\r\n * entire global configuration.\r\n * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor\r\n * explicitly to the API method.\r\n * @returns {ParsedConfiguration}\r\n */\r\nexport function getConfiguration(paddingOrLocalConfig, defaultPadding) {\r\n const configObject = \r\n typeof paddingOrLocalConfig == \"object\" && paddingOrLocalConfig ||\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] ||\r\n GLOBAL[CONFIG_PROPERTIES.GLOBAL] ||\r\n { },\r\n\r\n lightnessConfig = configObject[\"lightness\"] || { },\r\n \r\n // In versions < 2.1.0 there was no grayscale saturation -\r\n // saturation was the color saturation.\r\n saturation = configObject[\"saturation\"] || { },\r\n colorSaturation = \"color\" in saturation ? saturation[\"color\"] : saturation,\r\n grayscaleSaturation = saturation[\"grayscale\"],\r\n\r\n backColor = configObject[\"backColor\"],\r\n padding = configObject[\"padding\"];\r\n \r\n /**\r\n * Creates a lightness range.\r\n */\r\n function lightness(configName, defaultRange) {\r\n let range = lightnessConfig[configName];\r\n \r\n // Check if the lightness range is an array-like object. This way we ensure the\r\n // array contain two values at the same time.\r\n if (!(range && range.length > 1)) {\r\n range = defaultRange;\r\n }\r\n\r\n /**\r\n * Gets a lightness relative the specified value in the specified lightness range.\r\n */\r\n return function (value) {\r\n value = range[0] + value * (range[1] - range[0]);\r\n return value < 0 ? 0 : value > 1 ? 1 : value;\r\n };\r\n }\r\n\r\n /**\r\n * Gets a hue allowed by the configured hue restriction,\r\n * provided the originally computed hue.\r\n */\r\n function hueFunction(originalHue) {\r\n const hueConfig = configObject[\"hues\"];\r\n let hue;\r\n \r\n // Check if 'hues' is an array-like object. This way we also ensure that\r\n // the array is not empty, which would mean no hue restriction.\r\n if (hueConfig && hueConfig.length > 0) {\r\n // originalHue is in the range [0, 1]\r\n // Multiply with 0.999 to change the range to [0, 1) and then truncate the index.\r\n hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)];\r\n }\r\n\r\n return typeof hue == \"number\" ?\r\n \r\n // A hue was specified. We need to convert the hue from\r\n // degrees on any turn - e.g. 746° is a perfectly valid hue -\r\n // to turns in the range [0, 1).\r\n ((((hue / 360) % 1) + 1) % 1) :\r\n\r\n // No hue configured => use original hue\r\n originalHue;\r\n }\r\n \r\n return {\r\n hue: hueFunction,\r\n colorSaturation: typeof colorSaturation == \"number\" ? colorSaturation : 0.5,\r\n grayscaleSaturation: typeof grayscaleSaturation == \"number\" ? grayscaleSaturation : 0,\r\n colorLightness: lightness(\"color\", [0.4, 0.8]),\r\n grayscaleLightness: lightness(\"grayscale\", [0.3, 0.9]),\r\n backColor: parseColor(backColor),\r\n iconPadding: \r\n typeof paddingOrLocalConfig == \"number\" ? paddingOrLocalConfig : \r\n typeof padding == \"number\" ? padding : \r\n defaultPadding\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Represents a point.\r\n */\r\nexport class Point {\r\n /**\r\n * @param {number} x \r\n * @param {number} y \r\n */\r\n constructor(x, y) {\r\n this.x = x;\r\n this.y = y;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Point } from \"./point\";\r\n\r\n/**\r\n * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, \r\n * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly.\r\n */\r\nexport class Transform {\r\n /**\r\n * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} size The size of the transformed rectangle.\r\n * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad\r\n */\r\n constructor(x, y, size, rotation) {\r\n this._x = x;\r\n this._y = y;\r\n this._size = size;\r\n this._rotation = rotation;\r\n }\r\n\r\n /**\r\n * Transforms the specified point based on the translation and rotation specification for this Transform.\r\n * @param {number} x x-coordinate\r\n * @param {number} y y-coordinate\r\n * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n */\r\n transformIconPoint(x, y, w, h) {\r\n const right = this._x + this._size,\r\n bottom = this._y + this._size,\r\n rotation = this._rotation;\r\n return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) :\r\n rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) :\r\n rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) :\r\n new Point(this._x + x, this._y + y);\r\n }\r\n}\r\n\r\nexport const NO_TRANSFORM = new Transform(0, 0, 0, 0);\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { NO_TRANSFORM } from \"./transform\";\r\n\r\n/**\r\n * @typedef {import(\"./renderer\").Renderer} Renderer\r\n * @typedef {import(\"./transform\").Transform} Transform\r\n */\r\n\r\n/**\r\n * Provides helper functions for rendering common basic shapes.\r\n */\r\nexport class Graphics {\r\n /**\r\n * @param {Renderer} renderer \r\n */\r\n constructor(renderer) {\r\n /**\r\n * @type {Renderer}\r\n * @private\r\n */\r\n this._renderer = renderer;\r\n\r\n /**\r\n * @type {Transform}\r\n */\r\n this.currentTransform = NO_TRANSFORM;\r\n }\r\n\r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ]\r\n * @param {boolean=} invert Specifies if the polygon will be inverted.\r\n */\r\n addPolygon(points, invert) {\r\n const di = invert ? -2 : 2,\r\n transformedPoints = [];\r\n \r\n for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) {\r\n transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1]));\r\n }\r\n \r\n this._renderer.addPolygon(transformedPoints);\r\n }\r\n \r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * Source: http://stackoverflow.com/a/2173084\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} size The size of the ellipse.\r\n * @param {boolean=} invert Specifies if the ellipse will be inverted.\r\n */\r\n addCircle(x, y, size, invert) {\r\n const p = this.currentTransform.transformIconPoint(x, y, size, size);\r\n this._renderer.addCircle(p, size, invert);\r\n }\r\n\r\n /**\r\n * Adds a rectangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle.\r\n * @param {number} w The width of the rectangle.\r\n * @param {number} h The height of the rectangle.\r\n * @param {boolean=} invert Specifies if the rectangle will be inverted.\r\n */\r\n addRectangle(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x, y, \r\n x + w, y,\r\n x + w, y + h,\r\n x, y + h\r\n ], invert);\r\n }\r\n\r\n /**\r\n * Adds a right triangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} w The width of the triangle.\r\n * @param {number} h The height of the triangle.\r\n * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle.\r\n * @param {boolean=} invert Specifies if the triangle will be inverted.\r\n */\r\n addTriangle(x, y, w, h, r, invert) {\r\n const points = [\r\n x + w, y, \r\n x + w, y + h, \r\n x, y + h,\r\n x, y\r\n ];\r\n points.splice(((r || 0) % 4) * 2, 2);\r\n this.addPolygon(points, invert);\r\n }\r\n\r\n /**\r\n * Adds a rhombus to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} w The width of the rhombus.\r\n * @param {number} h The height of the rhombus.\r\n * @param {boolean=} invert Specifies if the rhombus will be inverted.\r\n */\r\n addRhombus(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x + w / 2, y,\r\n x + w, y + h / 2,\r\n x + w / 2, y + h,\r\n x, y + h / 2\r\n ], invert);\r\n }\r\n}","\r\nvar Graphics__prototype = Graphics.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n * @param {number} positionIndex\r\n * @typedef {import('./graphics').Graphics} Graphics\r\n */\r\nexport function centerShape(index, g, cell, positionIndex) {\r\n index = index % 14;\r\n\r\n let k, m, w, h, inner, outer;\r\n\r\n !index ? (\r\n k = cell * 0.42,\r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell - k * 2,\r\n cell - k, cell,\r\n 0, cell\r\n ])) :\r\n\r\n index == 1 ? (\r\n w = 0 | (cell * 0.5), \r\n h = 0 | (cell * 0.8),\r\n\r\n g.addTriangle(cell - w, 0, w, h, 2)) :\r\n\r\n index == 2 ? (\r\n w = 0 | (cell / 3),\r\n g.addRectangle(w, w, cell - w, cell - w)) :\r\n\r\n index == 3 ? (\r\n inner = cell * 0.1,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 6 ? 1 :\r\n cell < 8 ? 2 :\r\n (0 | (cell * 0.25)),\r\n \r\n inner = \r\n inner > 1 ? (0 | inner) : // large icon => truncate decimals\r\n inner > 0.5 ? 1 : // medium size icon => fixed width\r\n inner, // small icon => anti-aliased border\r\n\r\n g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) :\r\n\r\n index == 4 ? (\r\n m = 0 | (cell * 0.15),\r\n w = 0 | (cell * 0.5),\r\n g.addCircle(cell - w - m, cell - w - m, w)) :\r\n\r\n index == 5 ? (\r\n inner = cell * 0.1,\r\n outer = inner * 4,\r\n\r\n // Align edge to nearest pixel in large icons\r\n outer > 3 && (outer = 0 | outer),\r\n \r\n g.addRectangle(0, 0, cell, cell),\r\n g.addPolygon([\r\n outer, outer,\r\n cell - inner, outer,\r\n outer + (cell - outer - inner) / 2, cell - inner\r\n ], true)) :\r\n\r\n index == 6 ? \r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell * 0.7,\r\n cell * 0.4, cell * 0.4,\r\n cell * 0.7, cell,\r\n 0, cell\r\n ]) :\r\n\r\n index == 7 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 8 ? (\r\n g.addRectangle(0, 0, cell, cell / 2),\r\n g.addRectangle(0, cell / 2, cell / 2, cell / 2),\r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) :\r\n\r\n index == 9 ? (\r\n inner = cell * 0.14,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 4 ? 1 :\r\n cell < 6 ? 2 :\r\n (0 | (cell * 0.35)),\r\n\r\n inner = \r\n cell < 8 ? inner : // small icon => anti-aliased border\r\n (0 | inner), // large icon => truncate decimals\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) :\r\n\r\n index == 10 ? (\r\n inner = cell * 0.12,\r\n outer = inner * 3,\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addCircle(outer, outer, cell - inner - outer, true)) :\r\n\r\n index == 11 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 12 ? (\r\n m = cell * 0.25,\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRhombus(m, m, cell - m, cell - m, true)) :\r\n\r\n // 13\r\n (\r\n !positionIndex && (\r\n m = cell * 0.4, w = cell * 1.2,\r\n g.addCircle(m, m, w)\r\n )\r\n );\r\n}\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n */\r\nexport function outerShape(index, g, cell) {\r\n index = index % 4;\r\n\r\n let m;\r\n\r\n !index ?\r\n g.addTriangle(0, 0, cell, cell, 0) :\r\n \r\n index == 1 ?\r\n g.addTriangle(0, cell / 2, cell, cell / 2, 0) :\r\n\r\n index == 2 ?\r\n g.addRhombus(0, 0, cell, cell) :\r\n\r\n // 3\r\n (\r\n m = cell / 6,\r\n g.addCircle(m, m, cell - 2 * m)\r\n );\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { correctedHsl } from \"./color\";\r\n\r\n/**\r\n * Gets a set of identicon color candidates for a specified hue and config.\r\n * @param {number} hue\r\n * @param {import(\"../common/configuration\").ParsedConfiguration} config\r\n */\r\nexport function colorTheme(hue, config) {\r\n hue = config.hue(hue);\r\n return [\r\n // Dark gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)),\r\n // Mid color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)),\r\n // Light gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)),\r\n // Light color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(1)),\r\n // Dark color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0))\r\n ];\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Transform } from \"./transform\";\r\nimport { Graphics } from \"./graphics\";\r\nimport { centerShape, outerShape } from \"./shapes\";\r\nimport { colorTheme } from \"./colorTheme\";\r\nimport { parseHex } from \"../common/parseHex\";\r\nimport { getConfiguration } from \"../common/configuration\";\r\n \r\n/**\r\n * Draws an identicon to a specified renderer.\r\n * @param {import('./renderer').Renderer} renderer\r\n * @param {string} hash\r\n * @param {Object|number=} config\r\n */\r\nexport function iconGenerator(renderer, hash, config) {\r\n const parsedConfig = getConfiguration(config, 0.08);\r\n\r\n // Set background color\r\n if (parsedConfig.backColor) {\r\n renderer.setBackground(parsedConfig.backColor);\r\n }\r\n \r\n // Calculate padding and round to nearest integer\r\n let size = renderer.iconSize;\r\n const padding = (0.5 + size * parsedConfig.iconPadding) | 0;\r\n size -= padding * 2;\r\n \r\n const graphics = new Graphics(renderer);\r\n \r\n // Calculate cell size and ensure it is an integer\r\n const cell = 0 | (size / 4);\r\n \r\n // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon\r\n const x = 0 | (padding + size / 2 - cell * 2);\r\n const y = 0 | (padding + size / 2 - cell * 2);\r\n\r\n function renderShape(colorIndex, shapes, index, rotationIndex, positions) {\r\n const shapeIndex = parseHex(hash, index, 1);\r\n let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0;\r\n \r\n renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]);\r\n \r\n for (let i = 0; i < positions.length; i++) {\r\n graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4);\r\n shapes(shapeIndex, graphics, cell, i);\r\n }\r\n \r\n renderer.endShape();\r\n }\r\n\r\n // AVAILABLE COLORS\r\n const hue = parseHex(hash, -7) / 0xfffffff,\r\n \r\n // Available colors for this icon\r\n availableColors = colorTheme(hue, parsedConfig),\r\n\r\n // The index of the selected colors\r\n selectedColorIndexes = [];\r\n\r\n let index;\r\n\r\n function isDuplicate(values) {\r\n if (values.indexOf(index) >= 0) {\r\n for (let i = 0; i < values.length; i++) {\r\n if (selectedColorIndexes.indexOf(values[i]) >= 0) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < 3; i++) {\r\n index = parseHex(hash, 8 + i, 1) % availableColors.length;\r\n if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo\r\n isDuplicate([2, 3])) { // Disallow light gray and light color combo\r\n index = 1;\r\n }\r\n selectedColorIndexes.push(index);\r\n }\r\n\r\n // ACTUAL RENDERING\r\n // Sides\r\n renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]);\r\n // Corners\r\n renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]);\r\n // Center\r\n renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]);\r\n \r\n renderer.finish();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Computes a SHA1 hash for any value and returns it as a hexadecimal string.\r\n * \r\n * This function is optimized for minimal code size and rather short messages.\r\n * \r\n * @param {string} message \r\n */\r\nexport function sha1(message) {\r\n const HASH_SIZE_HALF_BYTES = 40;\r\n const BLOCK_SIZE_WORDS = 16;\r\n\r\n // Variables\r\n // `var` is used to be able to minimize the number of `var` keywords.\r\n var i = 0,\r\n f = 0,\r\n \r\n // Use `encodeURI` to UTF8 encode the message without any additional libraries\r\n // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky\r\n // since `unescape` is deprecated.\r\n urlEncodedMessage = encodeURI(message) + \"%80\", // trailing '1' bit padding\r\n \r\n // This can be changed to a preallocated Uint32Array array for greater performance and larger code size\r\n data = [],\r\n dataSize,\r\n \r\n hashBuffer = [],\r\n\r\n a = 0x67452301,\r\n b = 0xefcdab89,\r\n c = ~a,\r\n d = ~b,\r\n e = 0xc3d2e1f0,\r\n hash = [a, b, c, d, e],\r\n\r\n blockStartIndex = 0,\r\n hexHash = \"\";\r\n\r\n /**\r\n * Rotates the value a specified number of bits to the left.\r\n * @param {number} value Value to rotate\r\n * @param {number} shift Bit count to shift.\r\n */\r\n function rotl(value, shift) {\r\n return (value << shift) | (value >>> (32 - shift));\r\n }\r\n\r\n // Message data\r\n for ( ; i < urlEncodedMessage.length; f++) {\r\n data[f >> 2] = data[f >> 2] |\r\n (\r\n (\r\n urlEncodedMessage[i] == \"%\"\r\n // Percent encoded byte\r\n ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)\r\n // Unencoded byte\r\n : urlEncodedMessage.charCodeAt(i++)\r\n )\r\n\r\n // Read bytes in reverse order (big endian words)\r\n << ((3 - (f & 3)) * 8)\r\n );\r\n }\r\n\r\n // f is now the length of the utf8 encoded message\r\n // 7 = 8 bytes (64 bit) for message size, -1 to round down\r\n // >> 6 = integer division with block size\r\n dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;\r\n\r\n // Message size in bits.\r\n // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least\r\n // significant 32 bits are set. -8 is for the '1' bit padding byte.\r\n data[dataSize - 1] = f * 8 - 8;\r\n \r\n // Compute hash\r\n for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {\r\n for (i = 0; i < 80; i++) {\r\n f = rotl(a, 5) + e + (\r\n // Ch\r\n i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :\r\n \r\n // Parity\r\n i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :\r\n \r\n // Maj\r\n i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :\r\n \r\n // Parity\r\n (b ^ c ^ d) + 0xca62c1d6\r\n ) + ( \r\n hashBuffer[i] = i < BLOCK_SIZE_WORDS\r\n // Bitwise OR is used to coerse `undefined` to 0\r\n ? (data[blockStartIndex + i] | 0)\r\n : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)\r\n );\r\n\r\n e = d;\r\n d = c;\r\n c = rotl(b, 30);\r\n b = a;\r\n a = f;\r\n }\r\n\r\n hash[0] = a = ((hash[0] + a) | 0);\r\n hash[1] = b = ((hash[1] + b) | 0);\r\n hash[2] = c = ((hash[2] + c) | 0);\r\n hash[3] = d = ((hash[3] + d) | 0);\r\n hash[4] = e = ((hash[4] + e) | 0);\r\n }\r\n\r\n // Format hex hash\r\n for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {\r\n hexHash += (\r\n (\r\n // Get word (2^3 half-bytes per word)\r\n hash[i >> 3] >>>\r\n\r\n // Append half-bytes in reverse order\r\n ((7 - (i & 7)) * 4)\r\n ) \r\n // Clamp to half-byte\r\n & 0xf\r\n ).toString(16);\r\n }\r\n\r\n return hexHash;\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { sha1 } from \"./sha1\";\r\n\r\n/**\r\n * Inputs a value that might be a valid hash string for Jdenticon and returns it \r\n * if it is determined valid, otherwise a falsy value is returned.\r\n */\r\nexport function isValidHash(hashCandidate) {\r\n return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate;\r\n}\r\n\r\n/**\r\n * Computes a hash for the specified value. Currently SHA1 is used. This function\r\n * always returns a valid hash.\r\n */\r\nexport function computeHash(value) {\r\n return sha1(value == null ? \"\" : \"\" + value);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { toCss3Color } from \"../color\";\r\n\r\n/**\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import('../point').Point} Point\r\n */\r\n\r\n/**\r\n * Renderer redirecting drawing commands to a canvas context.\r\n * @implements {Renderer}\r\n */\r\nexport class CanvasRenderer {\r\n /**\r\n * @param {number=} iconSize\r\n */\r\n constructor(ctx, iconSize) {\r\n const canvas = ctx.canvas; \r\n const width = canvas.width;\r\n const height = canvas.height;\r\n \r\n ctx.save();\r\n \r\n if (!iconSize) {\r\n iconSize = Math.min(width, height);\r\n \r\n ctx.translate(\r\n ((width - iconSize) / 2) | 0,\r\n ((height - iconSize) / 2) | 0);\r\n }\r\n\r\n /**\r\n * @private\r\n */\r\n this._ctx = ctx;\r\n this.iconSize = iconSize;\r\n \r\n ctx.clearRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const ctx = this._ctx;\r\n const iconSize = this.iconSize;\r\n\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.fillRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} fillColor Fill color on format #rrggbb[aa].\r\n */\r\n beginShape(fillColor) {\r\n const ctx = this._ctx;\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.beginPath();\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas.\r\n */\r\n endShape() {\r\n this._ctx.fill();\r\n }\r\n\r\n /**\r\n * Adds a polygon to the rendering queue.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n const ctx = this._ctx;\r\n ctx.moveTo(points[0].x, points[0].y);\r\n for (let i = 1; i < points.length; i++) {\r\n ctx.lineTo(points[i].x, points[i].y);\r\n }\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Adds a circle to the rendering queue.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const ctx = this._ctx,\r\n radius = diameter / 2;\r\n ctx.moveTo(point.x + radius, point.y + radius);\r\n ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise);\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() {\r\n this._ctx.restore();\r\n }\r\n}\r\n","\r\nvar CanvasRenderer__prototype = CanvasRenderer.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const ICON_TYPE_SVG = 1;\r\n\r\nexport const ICON_TYPE_CANVAS = 2;\r\n\r\nexport const ATTRIBUTES = {\r\n HASH: \"data-jdenticon-hash\",\r\n VALUE: \"data-jdenticon-value\"\r\n};\r\n\r\nexport const IS_RENDERED_PROPERTY = \"jdenticonRendered\";\r\n\r\nexport const ICON_SELECTOR = \"[\" + ATTRIBUTES.HASH +\"],[\" + ATTRIBUTES.VALUE +\"]\";\r\n\r\nexport const documentQuerySelectorAll = /** @type {!Function} */ (\r\n typeof document !== \"undefined\" && document.querySelectorAll.bind(document));\r\n\r\nexport function getIdenticonType(el) {\r\n if (el) {\r\n const tagName = el[\"tagName\"];\r\n\r\n if (/^svg$/i.test(tagName)) {\r\n return ICON_TYPE_SVG;\r\n }\r\n\r\n if (/^canvas$/i.test(tagName) && \"getContext\" in el) {\r\n return ICON_TYPE_CANVAS;\r\n }\r\n }\r\n}\r\n\r\nexport function whenDocumentIsReady(/** @type {Function} */ callback) {\r\n function loadedHandler() {\r\n document.removeEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.removeEventListener(\"load\", loadedHandler);\r\n setTimeout(callback, 0); // Give scripts a chance to run\r\n }\r\n \r\n if (typeof document !== \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof setTimeout !== \"undefined\"\r\n ) {\r\n if (document.readyState === \"loading\") {\r\n document.addEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.addEventListener(\"load\", loadedHandler);\r\n } else {\r\n // Document already loaded. The load events above likely won't be raised\r\n setTimeout(callback, 0);\r\n }\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { IS_RENDERED_PROPERTY } from \"../common/dom\";\r\n\r\n/**\r\n * Draws an identicon to a context.\r\n * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0).\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function drawIcon(ctx, hashOrValue, size, config) {\r\n if (!ctx) {\r\n throw new Error(\"No canvas specified.\");\r\n }\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n\r\n const canvas = ctx.canvas;\r\n if (canvas) {\r\n canvas[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Prepares a measure to be used as a measure in an SVG path, by\r\n * rounding the measure to a single decimal. This reduces the file\r\n * size of the generated SVG with more than 50% in some cases.\r\n */\r\nfunction svgValue(value) {\r\n return ((value * 10 + 0.5) | 0) / 10;\r\n}\r\n\r\n/**\r\n * Represents an SVG path element.\r\n */\r\nexport class SvgPath {\r\n constructor() {\r\n /**\r\n * This property holds the data string (path.d) of the SVG path.\r\n * @type {string}\r\n */\r\n this.dataString = \"\";\r\n }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG path.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n let dataString = \"\";\r\n for (let i = 0; i < points.length; i++) {\r\n dataString += (i ? \"L\" : \"M\") + svgValue(points[i].x) + \" \" + svgValue(points[i].y);\r\n }\r\n this.dataString += dataString + \"Z\";\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG path.\r\n * @param {import('../point').Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const sweepFlag = counterClockwise ? 0 : 1,\r\n svgRadius = svgValue(diameter / 2),\r\n svgDiameter = svgValue(diameter),\r\n svgArc = \"a\" + svgRadius + \",\" + svgRadius + \" 0 1,\" + sweepFlag + \" \";\r\n \r\n this.dataString += \r\n \"M\" + svgValue(point.x) + \" \" + svgValue(point.y + diameter / 2) +\r\n svgArc + svgDiameter + \",0\" + \r\n svgArc + (-svgDiameter) + \",0\";\r\n }\r\n}\r\n\r\n","\r\nvar SvgPath__prototype = SvgPath.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SvgPath } from \"./svgPath\";\r\nimport { parseHex } from \"../../common/parseHex\";\r\n\r\n/**\r\n * @typedef {import(\"../point\").Point} Point\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import(\"./svgElement\").SvgElement} SvgElement\r\n * @typedef {import(\"./svgWriter\").SvgWriter} SvgWriter\r\n */\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n * @implements {Renderer}\r\n */\r\nexport class SvgRenderer {\r\n /**\r\n * @param {SvgElement|SvgWriter} target \r\n */\r\n constructor(target) {\r\n /**\r\n * @type {SvgPath}\r\n * @private\r\n */\r\n this._path;\r\n\r\n /**\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._pathsByColor = { };\r\n\r\n /**\r\n * @type {SvgElement|SvgWriter}\r\n * @private\r\n */\r\n this._target = target;\r\n\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = target.iconSize;\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const match = /^(#......)(..)?/.exec(fillColor),\r\n opacity = match[2] ? parseHex(match[2], 0) / 255 : 1;\r\n this._target.setBackground(match[1], opacity);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n */\r\n beginShape(color) {\r\n this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath());\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape.\r\n */\r\n endShape() { }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n this._path.addPolygon(points);\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n this._path.addCircle(point, diameter, counterClockwise);\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() { \r\n const pathsByColor = this._pathsByColor;\r\n for (let color in pathsByColor) {\r\n // hasOwnProperty cannot be shadowed in pathsByColor\r\n // eslint-disable-next-line no-prototype-builtins\r\n if (pathsByColor.hasOwnProperty(color)) {\r\n this._target.appendPath(color, pathsByColor[color].dataString);\r\n }\r\n }\r\n }\r\n}\r\n","\r\nvar SvgRenderer__prototype = SvgRenderer.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const SVG_CONSTANTS = {\r\n XMLNS: \"http://www.w3.org/2000/svg\",\r\n WIDTH: \"width\",\r\n HEIGHT: \"height\",\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgWriter {\r\n /**\r\n * @param {number} iconSize - Icon width and height in pixels.\r\n */\r\n constructor(iconSize) {\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = iconSize;\r\n\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n this._s =\r\n '';\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n this._s += '';\r\n }\r\n }\r\n\r\n /**\r\n * Writes a path to the SVG string.\r\n * @param {string} color Fill color on format #rrggbb.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n this._s += '';\r\n }\r\n\r\n /**\r\n * Gets the rendered image as an SVG string.\r\n */\r\n toString() {\r\n return this._s + \"\";\r\n }\r\n}\r\n","\r\nvar SvgWriter__prototype = SvgWriter.prototype;","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgWriter } from \"../renderer/svg/svgWriter\";\r\n\r\n/**\r\n * Draws an identicon as an SVG string.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {string} SVG string\r\n */\r\nexport function toSvg(hashOrValue, size, config) {\r\n const writer = new SvgWriter(size);\r\n iconGenerator(new SvgRenderer(writer), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue),\r\n config);\r\n return writer.toString();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Creates a new element and adds it to the specified parent.\r\n * @param {Element} parentNode\r\n * @param {string} name\r\n * @param {...(string|number)} keyValuePairs\r\n */\r\nfunction SvgElement_append(parentNode, name, ...keyValuePairs) {\r\n const el = document.createElementNS(SVG_CONSTANTS.XMLNS, name);\r\n \r\n for (let i = 0; i + 1 < keyValuePairs.length; i += 2) {\r\n el.setAttribute(\r\n /** @type {string} */(keyValuePairs[i]),\r\n /** @type {string} */(keyValuePairs[i + 1]),\r\n );\r\n }\r\n\r\n parentNode.appendChild(el);\r\n}\r\n\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgElement {\r\n /**\r\n * @param {Element} element - Target element\r\n */\r\n constructor(element) {\r\n // Don't use the clientWidth and clientHeight properties on SVG elements\r\n // since Firefox won't serve a proper value of these properties on SVG\r\n // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811)\r\n // Instead use 100px as a hardcoded size (the svg viewBox will rescale \r\n // the icon to the correct dimensions)\r\n const iconSize = this.iconSize = Math.min(\r\n (Number(element.getAttribute(SVG_CONSTANTS.WIDTH)) || 100),\r\n (Number(element.getAttribute(SVG_CONSTANTS.HEIGHT)) || 100)\r\n );\r\n \r\n /**\r\n * @type {Element}\r\n * @private\r\n */\r\n this._el = element;\r\n \r\n // Clear current SVG child elements\r\n while (element.firstChild) {\r\n element.removeChild(element.firstChild);\r\n }\r\n \r\n // Set viewBox attribute to ensure the svg scales nicely.\r\n element.setAttribute(\"viewBox\", \"0 0 \" + iconSize + \" \" + iconSize);\r\n element.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n SvgElement_append(this._el, \"rect\",\r\n SVG_CONSTANTS.WIDTH, \"100%\",\r\n SVG_CONSTANTS.HEIGHT, \"100%\",\r\n \"fill\", fillColor,\r\n \"opacity\", opacity);\r\n }\r\n }\r\n\r\n /**\r\n * Appends a path to the SVG element.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n SvgElement_append(this._el, \"path\",\r\n \"fill\", color,\r\n \"d\", dataString);\r\n }\r\n}\r\n","\r\nvar SvgElement__prototype = SvgElement.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { ATTRIBUTES, ICON_SELECTOR, IS_RENDERED_PROPERTY, documentQuerySelectorAll } from \"../common/dom\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgElement } from \"../renderer/svg/svgElement\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { ICON_TYPE_CANVAS, ICON_TYPE_SVG, getIdenticonType } from \"../common/dom\";\r\n\r\n\r\n/**\r\n * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute.\r\n */\r\nexport function updateAll() {\r\n if (documentQuerySelectorAll) {\r\n update(ICON_SELECTOR);\r\n }\r\n}\r\n\r\n/**\r\n * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute that have not already\r\n * been rendered.\r\n */\r\nexport function updateAllConditional() {\r\n if (documentQuerySelectorAll) {\r\n /** @type {NodeListOf} */\r\n const elements = documentQuerySelectorAll(ICON_SELECTOR);\r\n \r\n for (let i = 0; i < elements.length; i++) {\r\n const el = elements[i];\r\n if (!el[IS_RENDERED_PROPERTY]) {\r\n update(el);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` or `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * `` or ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function update(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType) {\r\n return iconType == ICON_TYPE_SVG ? \r\n new SvgRenderer(new SvgElement(el)) : \r\n new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext(\"2d\"));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function updateCanvas(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType == ICON_TYPE_CANVAS) {\r\n return new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext(\"2d\"));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function updateSvg(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType == ICON_TYPE_SVG) {\r\n return new SvgRenderer(new SvgElement(el));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified canvas or svg elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * `` or ``, or a CSS selector to such an element.\r\n * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number|undefined} config\r\n * @param {function(Element,number):import(\"../renderer/renderer\").Renderer} rendererFactory - Factory function for creating an icon renderer.\r\n */\r\nfunction renderDomElement(el, hashOrValue, config, rendererFactory) {\r\n if (typeof el === \"string\") {\r\n if (documentQuerySelectorAll) {\r\n const elements = documentQuerySelectorAll(el);\r\n for (let i = 0; i < elements.length; i++) {\r\n renderDomElement(elements[i], hashOrValue, config, rendererFactory);\r\n }\r\n }\r\n return;\r\n }\r\n \r\n // Hash selection. The result from getValidHash or computeHash is \r\n // accepted as a valid hash.\r\n const hash = \r\n // 1. Explicit valid hash\r\n isValidHash(hashOrValue) ||\r\n \r\n // 2. Explicit value (`!= null` catches both null and undefined)\r\n hashOrValue != null && computeHash(hashOrValue) ||\r\n \r\n // 3. `data-jdenticon-hash` attribute\r\n isValidHash(el.getAttribute(ATTRIBUTES.HASH)) ||\r\n \r\n // 4. `data-jdenticon-value` attribute. \r\n // We want to treat an empty attribute as an empty value. \r\n // Some browsers return empty string even if the attribute \r\n // is not specified, so use hasAttribute to determine if \r\n // the attribute is specified.\r\n el.hasAttribute(ATTRIBUTES.VALUE) && computeHash(el.getAttribute(ATTRIBUTES.VALUE));\r\n \r\n if (!hash) {\r\n // No hash specified. Don't render an icon.\r\n return;\r\n }\r\n \r\n const renderer = rendererFactory(el, getIdenticonType(el));\r\n if (renderer) {\r\n // Draw icon\r\n iconGenerator(renderer, hash, config);\r\n el[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// This file is compiled to dist/jdenticon-module.js\r\n\r\nimport { defineConfigProperty } from \"./common/configuration\";\r\nimport { configure } from \"./apis/configure\";\r\nimport { drawIcon } from \"./apis/drawIcon\";\r\nimport { toSvg } from \"./apis/toSvg\";\r\nimport { update, updateAll, updateCanvas, updateSvg } from \"./apis/update\";\r\n\r\nconst jdenticon = updateAll;\r\n\r\ndefineConfigProperty(jdenticon);\r\n\r\n// Export public API\r\njdenticon[\"configure\"] = configure;\r\njdenticon[\"drawIcon\"] = drawIcon;\r\njdenticon[\"toSvg\"] = toSvg;\r\njdenticon[\"update\"] = update;\r\njdenticon[\"updateCanvas\"] = updateCanvas;\r\njdenticon[\"updateSvg\"] = updateSvg;\r\n\r\n/**\r\n * Specifies the version of the Jdenticon package in use.\r\n * @type {string}\r\n */\r\njdenticon[\"version\"] = \"#version#\";\r\n\r\n/**\r\n * Specifies which bundle of Jdenticon that is used.\r\n * @type {string}\r\n */\r\njdenticon[\"bundle\"] = \"browser-cjs\";\r\n\r\nmodule.exports = jdenticon;","\n//# sourceMappingURL=jdenticon-module.js.map\n"]} \ No newline at end of file diff --git a/jdenticon-js/dist/jdenticon-module.mjs b/jdenticon-js/dist/jdenticon-module.mjs new file mode 100644 index 0000000..3b9b1b8 --- /dev/null +++ b/jdenticon-js/dist/jdenticon-module.mjs @@ -0,0 +1,1399 @@ +/** + * Jdenticon 3.3.0 + * http://jdenticon.com + * + * Built: 2024-05-10T09:48:41.921Z + * + * MIT License + * + * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi + * + * 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. + */ + +/** + * Parses a substring of the hash as a number. + * @param {number} startPosition + * @param {number=} octets + */ +function parseHex(hash, startPosition, octets) { + return parseInt(hash.substr(startPosition, octets), 16); +} + +function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; +} + +function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); +} + +/** + * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported. + * @returns {string} + */ +function parseColor(color) { + if (/^#[0-9a-f]{3,8}$/i.test(color)) { + let result; + const colorLength = color.length; + + if (colorLength < 6) { + const r = color[1], + g = color[2], + b = color[3], + a = color[4] || ""; + result = "#" + r + r + g + g + b + b + a + a; + } + if (colorLength == 7 || colorLength > 8) { + result = color; + } + + return result; + } +} + +/** + * Converts a hexadecimal color to a CSS3 compatible color. + * @param {string} hexColor Color on the format "#RRGGBB" or "#RRGGBBAA" + * @returns {string} + */ +function toCss3Color(hexColor) { + const a = parseHex(hexColor, 7, 2); + let result; + + if (isNaN(a)) { + result = hexColor; + } else { + const r = parseHex(hexColor, 1, 2), + g = parseHex(hexColor, 3, 2), + b = parseHex(hexColor, 5, 2); + result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")"; + } + + return result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function hsl(hue, saturation, lightness) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + let result; + + if (saturation == 0) { + const partialHex = decToHex(lightness * 255); + result = partialHex + partialHex + partialHex; + } + else { + const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation, + m1 = lightness * 2 - m2; + result = + hueToRgb(m1, m2, hue * 6 + 2) + + hueToRgb(m1, m2, hue * 6) + + hueToRgb(m1, m2, hue * 6 - 2); + } + + return "#" + result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function correctedHsl(hue, saturation, lightness) { + // The corrector specifies the perceived middle lightness for each hue + const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(hue * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; + + return hsl(hue, saturation, lightness); +} + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. + +const GLOBAL = + typeof window !== "undefined" ? window : + typeof self !== "undefined" ? self : + typeof global !== "undefined" ? global : + {}; + +/** + * @typedef {Object} ParsedConfiguration + * @property {number} colorSaturation + * @property {number} grayscaleSaturation + * @property {string} backColor + * @property {number} iconPadding + * @property {function(number):number} hue + * @property {function(number):number} colorLightness + * @property {function(number):number} grayscaleLightness + */ + +const CONFIG_PROPERTIES = { + V/*GLOBAL*/: "jdenticon_config", + n/*MODULE*/: "config", +}; + +var rootConfigurationHolder = {}; + +/** + * Sets a new icon style configuration. The new configuration is not merged with the previous one. * + * @param {Object} newConfiguration - New configuration object. + */ +function configure(newConfiguration) { + if (arguments.length) { + rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] = newConfiguration; + } + return rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/]; +} + +/** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A + * local configuration overrides the global configuration in it entirety. This parameter can for backward + * compatibility also contain a padding value. A padding value only overrides the global padding, not the + * entire global configuration. + * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor + * explicitly to the API method. + * @returns {ParsedConfiguration} + */ +function getConfiguration(paddingOrLocalConfig, defaultPadding) { + const configObject = + typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || + rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] || + GLOBAL[CONFIG_PROPERTIES.V/*GLOBAL*/] || + { }, + + lightnessConfig = configObject["lightness"] || { }, + + // In versions < 2.1.0 there was no grayscale saturation - + // saturation was the color saturation. + saturation = configObject["saturation"] || { }, + colorSaturation = "color" in saturation ? saturation["color"] : saturation, + grayscaleSaturation = saturation["grayscale"], + + backColor = configObject["backColor"], + padding = configObject["padding"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultRange) { + let range = lightnessConfig[configName]; + + // Check if the lightness range is an array-like object. This way we ensure the + // array contain two values at the same time. + if (!(range && range.length > 1)) { + range = defaultRange; + } + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + /** + * Gets a hue allowed by the configured hue restriction, + * provided the originally computed hue. + */ + function hueFunction(originalHue) { + const hueConfig = configObject["hues"]; + let hue; + + // Check if 'hues' is an array-like object. This way we also ensure that + // the array is not empty, which would mean no hue restriction. + if (hueConfig && hueConfig.length > 0) { + // originalHue is in the range [0, 1] + // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. + hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; + } + + return typeof hue == "number" ? + + // A hue was specified. We need to convert the hue from + // degrees on any turn - e.g. 746° is a perfectly valid hue - + // to turns in the range [0, 1). + ((((hue / 360) % 1) + 1) % 1) : + + // No hue configured => use original hue + originalHue; + } + + return { + W/*hue*/: hueFunction, + o/*colorSaturation*/: typeof colorSaturation == "number" ? colorSaturation : 0.5, + D/*grayscaleSaturation*/: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, + p/*colorLightness*/: lightness("color", [0.4, 0.8]), + F/*grayscaleLightness*/: lightness("grayscale", [0.3, 0.9]), + G/*backColor*/: parseColor(backColor), + X/*iconPadding*/: + typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : + typeof padding == "number" ? padding : + defaultPadding + } +} + +/** + * Represents a point. + */ +class Point { + /** + * @param {number} x + * @param {number} y + */ + constructor(x, y) { + this.x = x; + this.y = y; + } +} + +/** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + */ +class Transform { + /** + * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle. + * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle. + * @param {number} size The size of the transformed rectangle. + * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad + */ + constructor(x, y, size, rotation) { + this.q/*_x*/ = x; + this.t/*_y*/ = y; + this.H/*_size*/ = size; + this.Y/*_rotation*/ = rotation; + } + + /** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ + I/*transformIconPoint*/(x, y, w, h) { + const right = this.q/*_x*/ + this.H/*_size*/, + bottom = this.t/*_y*/ + this.H/*_size*/, + rotation = this.Y/*_rotation*/; + return rotation === 1 ? new Point(right - y - (h || 0), this.t/*_y*/ + x) : + rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + rotation === 3 ? new Point(this.q/*_x*/ + y, bottom - x - (w || 0)) : + new Point(this.q/*_x*/ + x, this.t/*_y*/ + y); + } +} + +const NO_TRANSFORM = new Transform(0, 0, 0, 0); + + + +/** + * Provides helper functions for rendering common basic shapes. + */ +class Graphics { + /** + * @param {Renderer} renderer + */ + constructor(renderer) { + /** + * @type {Renderer} + * @private + */ + this.J/*_renderer*/ = renderer; + + /** + * @type {Transform} + */ + this.u/*currentTransform*/ = NO_TRANSFORM; + } + + /** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ + g/*addPolygon*/(points, invert) { + const di = invert ? -2 : 2, + transformedPoints = []; + + for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(this.u/*currentTransform*/.I/*transformIconPoint*/(points[i], points[i + 1])); + } + + this.J/*_renderer*/.g/*addPolygon*/(transformedPoints); + } + + /** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ + h/*addCircle*/(x, y, size, invert) { + const p = this.u/*currentTransform*/.I/*transformIconPoint*/(x, y, size, size); + this.J/*_renderer*/.h/*addCircle*/(p, size, invert); + } + + /** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ + i/*addRectangle*/(x, y, w, h, invert) { + this.g/*addPolygon*/([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); + } + + /** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ + j/*addTriangle*/(x, y, w, h, r, invert) { + const points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.g/*addPolygon*/(points, invert); + } + + /** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ + K/*addRhombus*/(x, y, w, h, invert) { + this.g/*addPolygon*/([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); + } +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + * @param {number} positionIndex + */ +function centerShape(index, g, cell, positionIndex) { + index = index % 14; + + let k, m, w, h, inner, outer; + + !index ? ( + k = cell * 0.42, + g.g/*addPolygon*/([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ])) : + + index == 1 ? ( + w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8), + + g.j/*addTriangle*/(cell - w, 0, w, h, 2)) : + + index == 2 ? ( + w = 0 | (cell / 3), + g.i/*addRectangle*/(w, w, cell - w, cell - w)) : + + index == 3 ? ( + inner = cell * 0.1, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)), + + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + g.i/*addRectangle*/(outer, outer, cell - inner - outer, cell - inner - outer)) : + + index == 4 ? ( + m = 0 | (cell * 0.15), + w = 0 | (cell * 0.5), + g.h/*addCircle*/(cell - w - m, cell - w - m, w)) : + + index == 5 ? ( + inner = cell * 0.1, + outer = inner * 4, + + // Align edge to nearest pixel in large icons + outer > 3 && (outer = 0 | outer), + + g.i/*addRectangle*/(0, 0, cell, cell), + g.g/*addPolygon*/([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true)) : + + index == 6 ? + g.g/*addPolygon*/([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]) : + + index == 7 ? + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 8 ? ( + g.i/*addRectangle*/(0, 0, cell, cell / 2), + g.i/*addRectangle*/(0, cell / 2, cell / 2, cell / 2), + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 1)) : + + index == 9 ? ( + inner = cell * 0.14, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)), + + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + g.i/*addRectangle*/(0, 0, cell, cell), + g.i/*addRectangle*/(outer, outer, cell - outer - inner, cell - outer - inner, true)) : + + index == 10 ? ( + inner = cell * 0.12, + outer = inner * 3, + + g.i/*addRectangle*/(0, 0, cell, cell), + g.h/*addCircle*/(outer, outer, cell - inner - outer, true)) : + + index == 11 ? + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 12 ? ( + m = cell * 0.25, + g.i/*addRectangle*/(0, 0, cell, cell), + g.K/*addRhombus*/(m, m, cell - m, cell - m, true)) : + + // 13 + ( + !positionIndex && ( + m = cell * 0.4, w = cell * 1.2, + g.h/*addCircle*/(m, m, w) + ) + ); +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + */ +function outerShape(index, g, cell) { + index = index % 4; + + let m; + + !index ? + g.j/*addTriangle*/(0, 0, cell, cell, 0) : + + index == 1 ? + g.j/*addTriangle*/(0, cell / 2, cell, cell / 2, 0) : + + index == 2 ? + g.K/*addRhombus*/(0, 0, cell, cell) : + + // 3 + ( + m = cell / 6, + g.h/*addCircle*/(m, m, cell - 2 * m) + ); +} + +/** + * Gets a set of identicon color candidates for a specified hue and config. + * @param {number} hue + * @param {ParsedConfiguration} config + */ +function colorTheme(hue, config) { + hue = config.W/*hue*/(hue); + return [ + // Dark gray + correctedHsl(hue, config.D/*grayscaleSaturation*/, config.F/*grayscaleLightness*/(0)), + // Mid color + correctedHsl(hue, config.o/*colorSaturation*/, config.p/*colorLightness*/(0.5)), + // Light gray + correctedHsl(hue, config.D/*grayscaleSaturation*/, config.F/*grayscaleLightness*/(1)), + // Light color + correctedHsl(hue, config.o/*colorSaturation*/, config.p/*colorLightness*/(1)), + // Dark color + correctedHsl(hue, config.o/*colorSaturation*/, config.p/*colorLightness*/(0)) + ]; +} + +/** + * Draws an identicon to a specified renderer. + * @param {Renderer} renderer + * @param {string} hash + * @param {Object|number=} config + */ +function iconGenerator(renderer, hash, config) { + const parsedConfig = getConfiguration(config, 0.08); + + // Set background color + if (parsedConfig.G/*backColor*/) { + renderer.m/*setBackground*/(parsedConfig.G/*backColor*/); + } + + // Calculate padding and round to nearest integer + let size = renderer.k/*iconSize*/; + const padding = (0.5 + size * parsedConfig.X/*iconPadding*/) | 0; + size -= padding * 2; + + const graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + const cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + const x = 0 | (padding + size / 2 - cell * 2); + const y = 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + const shapeIndex = parseHex(hash, index, 1); + let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.L/*beginShape*/(availableColors[selectedColorIndexes[colorIndex]]); + + for (let i = 0; i < positions.length; i++) { + graphics.u/*currentTransform*/ = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.M/*endShape*/(); + } + + // AVAILABLE COLORS + const hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, parsedConfig), + + // The index of the selected colors + selectedColorIndexes = []; + + let index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (let i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (let i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); +} + +/** + * Computes a SHA1 hash for any value and returns it as a hexadecimal string. + * + * This function is optimized for minimal code size and rather short messages. + * + * @param {string} message + */ +function sha1(message) { + const HASH_SIZE_HALF_BYTES = 40; + const BLOCK_SIZE_WORDS = 16; + + // Variables + // `var` is used to be able to minimize the number of `var` keywords. + var i = 0, + f = 0, + + // Use `encodeURI` to UTF8 encode the message without any additional libraries + // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky + // since `unescape` is deprecated. + urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding + + // This can be changed to a preallocated Uint32Array array for greater performance and larger code size + data = [], + dataSize, + + hashBuffer = [], + + a = 0x67452301, + b = 0xefcdab89, + c = ~a, + d = ~b, + e = 0xc3d2e1f0, + hash = [a, b, c, d, e], + + blockStartIndex = 0, + hexHash = ""; + + /** + * Rotates the value a specified number of bits to the left. + * @param {number} value Value to rotate + * @param {number} shift Bit count to shift. + */ + function rotl(value, shift) { + return (value << shift) | (value >>> (32 - shift)); + } + + // Message data + for ( ; i < urlEncodedMessage.length; f++) { + data[f >> 2] = data[f >> 2] | + ( + ( + urlEncodedMessage[i] == "%" + // Percent encoded byte + ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16) + // Unencoded byte + : urlEncodedMessage.charCodeAt(i++) + ) + + // Read bytes in reverse order (big endian words) + << ((3 - (f & 3)) * 8) + ); + } + + // f is now the length of the utf8 encoded message + // 7 = 8 bytes (64 bit) for message size, -1 to round down + // >> 6 = integer division with block size + dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS; + + // Message size in bits. + // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least + // significant 32 bits are set. -8 is for the '1' bit padding byte. + data[dataSize - 1] = f * 8 - 8; + + // Compute hash + for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) { + for (i = 0; i < 80; i++) { + f = rotl(a, 5) + e + ( + // Ch + i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 : + + // Parity + i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 : + + // Maj + i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc : + + // Parity + (b ^ c ^ d) + 0xca62c1d6 + ) + ( + hashBuffer[i] = i < BLOCK_SIZE_WORDS + // Bitwise OR is used to coerse `undefined` to 0 + ? (data[blockStartIndex + i] | 0) + : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1) + ); + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = f; + } + + hash[0] = a = ((hash[0] + a) | 0); + hash[1] = b = ((hash[1] + b) | 0); + hash[2] = c = ((hash[2] + c) | 0); + hash[3] = d = ((hash[3] + d) | 0); + hash[4] = e = ((hash[4] + e) | 0); + } + + // Format hex hash + for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) { + hexHash += ( + ( + // Get word (2^3 half-bytes per word) + hash[i >> 3] >>> + + // Append half-bytes in reverse order + ((7 - (i & 7)) * 4) + ) + // Clamp to half-byte + & 0xf + ).toString(16); + } + + return hexHash; +} + +/** + * Inputs a value that might be a valid hash string for Jdenticon and returns it + * if it is determined valid, otherwise a falsy value is returned. + */ +function isValidHash(hashCandidate) { + return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate; +} + +/** + * Computes a hash for the specified value. Currently SHA1 is used. This function + * always returns a valid hash. + */ +function computeHash(value) { + return sha1(value == null ? "" : "" + value); +} + + + +/** + * Renderer redirecting drawing commands to a canvas context. + * @implements {Renderer} + */ +class CanvasRenderer { + /** + * @param {number=} iconSize + */ + constructor(ctx, iconSize) { + const canvas = ctx.canvas; + const width = canvas.width; + const height = canvas.height; + + ctx.save(); + + if (!iconSize) { + iconSize = Math.min(width, height); + + ctx.translate( + ((width - iconSize) / 2) | 0, + ((height - iconSize) / 2) | 0); + } + + /** + * @private + */ + this.l/*_ctx*/ = ctx; + this.k/*iconSize*/ = iconSize; + + ctx.clearRect(0, 0, iconSize, iconSize); + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + m/*setBackground*/(fillColor) { + const ctx = this.l/*_ctx*/; + const iconSize = this.k/*iconSize*/; + + ctx.fillStyle = toCss3Color(fillColor); + ctx.fillRect(0, 0, iconSize, iconSize); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} fillColor Fill color on format #rrggbb[aa]. + */ + L/*beginShape*/(fillColor) { + const ctx = this.l/*_ctx*/; + ctx.fillStyle = toCss3Color(fillColor); + ctx.beginPath(); + } + + /** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ + M/*endShape*/() { + this.l/*_ctx*/.fill(); + } + + /** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ + g/*addPolygon*/(points) { + const ctx = this.l/*_ctx*/; + ctx.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); + } + + /** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + h/*addCircle*/(point, diameter, counterClockwise) { + const ctx = this.l/*_ctx*/, + radius = diameter / 2; + ctx.moveTo(point.x + radius, point.y + radius); + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + this.l/*_ctx*/.restore(); + } +} + +const ICON_TYPE_SVG = 1; + +const ICON_TYPE_CANVAS = 2; + +const ATTRIBUTES = { + Z/*HASH*/: "data-jdenticon-hash", + N/*VALUE*/: "data-jdenticon-value" +}; + +const IS_RENDERED_PROPERTY = "jdenticonRendered"; + +const documentQuerySelectorAll = /** @type {!Function} */ ( + typeof document !== "undefined" && document.querySelectorAll.bind(document)); + +function getIdenticonType(el) { + if (el) { + const tagName = el["tagName"]; + + if (/^svg$/i.test(tagName)) { + return ICON_TYPE_SVG; + } + + if (/^canvas$/i.test(tagName) && "getContext" in el) { + return ICON_TYPE_CANVAS; + } + } +} + +/** + * Draws an identicon to a context. + * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0). + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function drawIcon(ctx, hashOrValue, size, config) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + const canvas = ctx.canvas; + if (canvas) { + canvas[IS_RENDERED_PROPERTY] = true; + } +} + +/** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ +function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; +} + +/** + * Represents an SVG path element. + */ +class SvgPath { + constructor() { + /** + * This property holds the data string (path.d) of the SVG path. + * @type {string} + */ + this.v/*dataString*/ = ""; + } + + /** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ + g/*addPolygon*/(points) { + let dataString = ""; + for (let i = 0; i < points.length; i++) { + dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.v/*dataString*/ += dataString + "Z"; + } + + /** + * Adds a circle with the current fill color to the SVG path. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + h/*addCircle*/(point, diameter, counterClockwise) { + const sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter), + svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " "; + + this.v/*dataString*/ += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + svgArc + svgDiameter + ",0" + + svgArc + (-svgDiameter) + ",0"; + } +} + + + +/** + * Renderer producing SVG output. + * @implements {Renderer} + */ +class SvgRenderer { + /** + * @param {SvgElement|SvgWriter} target + */ + constructor(target) { + /** + * @type {SvgPath} + * @private + */ + this.A/*_path*/; + + /** + * @type {Object.} + * @private + */ + this.B/*_pathsByColor*/ = { }; + + /** + * @type {SvgElement|SvgWriter} + * @private + */ + this.O/*_target*/ = target; + + /** + * @type {number} + */ + this.k/*iconSize*/ = target.k/*iconSize*/; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + m/*setBackground*/(fillColor) { + const match = /^(#......)(..)?/.exec(fillColor), + opacity = match[2] ? parseHex(match[2], 0) / 255 : 1; + this.O/*_target*/.m/*setBackground*/(match[1], opacity); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ + L/*beginShape*/(color) { + this.A/*_path*/ = this.B/*_pathsByColor*/[color] || (this.B/*_pathsByColor*/[color] = new SvgPath()); + } + + /** + * Marks the end of the currently drawn shape. + */ + M/*endShape*/() { } + + /** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ + g/*addPolygon*/(points) { + this.A/*_path*/.g/*addPolygon*/(points); + } + + /** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + h/*addCircle*/(point, diameter, counterClockwise) { + this.A/*_path*/.h/*addCircle*/(point, diameter, counterClockwise); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + const pathsByColor = this.B/*_pathsByColor*/; + for (let color in pathsByColor) { + // hasOwnProperty cannot be shadowed in pathsByColor + // eslint-disable-next-line no-prototype-builtins + if (pathsByColor.hasOwnProperty(color)) { + this.O/*_target*/.P/*appendPath*/(color, pathsByColor[color].v/*dataString*/); + } + } + } +} + +const SVG_CONSTANTS = { + R/*XMLNS*/: "http://www.w3.org/2000/svg", + S/*WIDTH*/: "width", + T/*HEIGHT*/: "height", +}; + +/** + * Renderer producing SVG output. + */ +class SvgWriter { + /** + * @param {number} iconSize - Icon width and height in pixels. + */ + constructor(iconSize) { + /** + * @type {number} + */ + this.k/*iconSize*/ = iconSize; + + /** + * @type {string} + * @private + */ + this.C/*_s*/ = + ''; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ + m/*setBackground*/(fillColor, opacity) { + if (opacity) { + this.C/*_s*/ += ''; + } + } + + /** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #rrggbb. + * @param {string} dataString The SVG path data string. + */ + P/*appendPath*/(color, dataString) { + this.C/*_s*/ += ''; + } + + /** + * Gets the rendered image as an SVG string. + */ + toString() { + return this.C/*_s*/ + ""; + } +} + +/** + * Draws an identicon as an SVG string. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {string} SVG string + */ +function toSvg(hashOrValue, size, config) { + const writer = new SvgWriter(size); + iconGenerator(new SvgRenderer(writer), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + return writer.toString(); +} + +/** + * Creates a new element and adds it to the specified parent. + * @param {Element} parentNode + * @param {string} name + * @param {...(string|number)} keyValuePairs + */ +function SvgElement_append(parentNode, name, ...keyValuePairs) { + const el = document.createElementNS(SVG_CONSTANTS.R/*XMLNS*/, name); + + for (let i = 0; i + 1 < keyValuePairs.length; i += 2) { + el.setAttribute( + /** @type {string} */(keyValuePairs[i]), + /** @type {string} */(keyValuePairs[i + 1]), + ); + } + + parentNode.appendChild(el); +} + + +/** + * Renderer producing SVG output. + */ +class SvgElement { + /** + * @param {Element} element - Target element + */ + constructor(element) { + // Don't use the clientWidth and clientHeight properties on SVG elements + // since Firefox won't serve a proper value of these properties on SVG + // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811) + // Instead use 100px as a hardcoded size (the svg viewBox will rescale + // the icon to the correct dimensions) + const iconSize = this.k/*iconSize*/ = Math.min( + (Number(element.getAttribute(SVG_CONSTANTS.S/*WIDTH*/)) || 100), + (Number(element.getAttribute(SVG_CONSTANTS.T/*HEIGHT*/)) || 100) + ); + + /** + * @type {Element} + * @private + */ + this.U/*_el*/ = element; + + // Clear current SVG child elements + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + // Set viewBox attribute to ensure the svg scales nicely. + element.setAttribute("viewBox", "0 0 " + iconSize + " " + iconSize); + element.setAttribute("preserveAspectRatio", "xMidYMid meet"); + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ + m/*setBackground*/(fillColor, opacity) { + if (opacity) { + SvgElement_append(this.U/*_el*/, "rect", + SVG_CONSTANTS.S/*WIDTH*/, "100%", + SVG_CONSTANTS.T/*HEIGHT*/, "100%", + "fill", fillColor, + "opacity", opacity); + } + } + + /** + * Appends a path to the SVG element. + * @param {string} color Fill color on format #xxxxxx. + * @param {string} dataString The SVG path data string. + */ + P/*appendPath*/(color, dataString) { + SvgElement_append(this.U/*_el*/, "path", + "fill", color, + "d", dataString); + } +} + +/** + * Updates the identicon in the specified `` or `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function update(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType) { + return iconType == ICON_TYPE_SVG ? + new SvgRenderer(new SvgElement(el)) : + new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function updateCanvas(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType == ICON_TYPE_CANVAS) { + return new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function updateSvg(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType == ICON_TYPE_SVG) { + return new SvgRenderer(new SvgElement(el)); + } + }); +} + +/** + * Updates the identicon in the specified canvas or svg elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number|undefined} config + * @param {function(Element,number):Renderer} rendererFactory - Factory function for creating an icon renderer. + */ +function renderDomElement(el, hashOrValue, config, rendererFactory) { + if (typeof el === "string") { + if (documentQuerySelectorAll) { + const elements = documentQuerySelectorAll(el); + for (let i = 0; i < elements.length; i++) { + renderDomElement(elements[i], hashOrValue, config, rendererFactory); + } + } + return; + } + + // Hash selection. The result from getValidHash or computeHash is + // accepted as a valid hash. + const hash = + // 1. Explicit valid hash + isValidHash(hashOrValue) || + + // 2. Explicit value (`!= null` catches both null and undefined) + hashOrValue != null && computeHash(hashOrValue) || + + // 3. `data-jdenticon-hash` attribute + isValidHash(el.getAttribute(ATTRIBUTES.Z/*HASH*/)) || + + // 4. `data-jdenticon-value` attribute. + // We want to treat an empty attribute as an empty value. + // Some browsers return empty string even if the attribute + // is not specified, so use hasAttribute to determine if + // the attribute is specified. + el.hasAttribute(ATTRIBUTES.N/*VALUE*/) && computeHash(el.getAttribute(ATTRIBUTES.N/*VALUE*/)); + + if (!hash) { + // No hash specified. Don't render an icon. + return; + } + + const renderer = rendererFactory(el, getIdenticonType(el)); + if (renderer) { + // Draw icon + iconGenerator(renderer, hash, config); + el[IS_RENDERED_PROPERTY] = true; + } +} + +// This file is compiled to dist/jdenticon-module.mjs + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +const version = "3.3.0"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +const bundle = "browser-esm"; + +export { bundle, configure, drawIcon, toSvg, update, updateCanvas, updateSvg, version }; +//# sourceMappingURL=jdenticon-module.mjs.map diff --git a/jdenticon-js/dist/jdenticon-module.mjs.map b/jdenticon-js/dist/jdenticon-module.mjs.map new file mode 100644 index 0000000..8ac4fac --- /dev/null +++ b/jdenticon-js/dist/jdenticon-module.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["replacement/1","src/common/parseHex.js","src/renderer/color.js","src/common/global.js","src/common/configuration.js","src/renderer/point.js","src/renderer/transform.js","src/renderer/graphics.js","src/renderer/shapes.js","src/renderer/colorTheme.js","src/renderer/iconGenerator.js","src/common/sha1.js","src/common/hashUtils.js","src/renderer/canvas/canvasRenderer.js","src/common/dom.js","src/apis/drawIcon.js","src/renderer/svg/svgPath.js","src/renderer/svg/svgRenderer.js","src/renderer/svg/constants.js","src/renderer/svg/svgWriter.js","src/apis/toSvg.js","src/renderer/svg/svgElement.js","src/apis/update.js","src/browser-esm.js","replacement/2"],"names":["GLOBAL","MODULE","hue","colorSaturation","grayscaleSaturation","colorLightness","grayscaleLightness","backColor","iconPadding","_x","_y","_size","_rotation","transformIconPoint","_renderer","currentTransform","addPolygon","addCircle","addRectangle","addTriangle","addRhombus","setBackground","iconSize","beginShape","endShape","_ctx","HASH","VALUE","dataString","_path","_pathsByColor","_target","appendPath","XMLNS","WIDTH","HEIGHT","_s","_el"],"mappings":"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","file":"jdenticon-module.mjs","sourcesContent":["/**\r\n * Jdenticon 3.3.0\r\n * http://jdenticon.com\r\n *\r\n * Built: 2024-05-10T09:48:41.921Z\r\n * \r\n * MIT License\r\n * \r\n * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi\r\n * \r\n * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n * of this software and associated documentation files (the \"Software\"), to deal\r\n * in the Software without restriction, including without limitation the rights\r\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the Software is\r\n * furnished to do so, subject to the following conditions:\r\n * \r\n * The above copyright notice and this permission notice shall be included in all\r\n * copies or substantial portions of the Software.\r\n * \r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n * SOFTWARE.\r\n */\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Parses a substring of the hash as a number.\r\n * @param {number} startPosition \r\n * @param {number=} octets\r\n */\r\nexport function parseHex(hash, startPosition, octets) {\r\n return parseInt(hash.substr(startPosition, octets), 16);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseHex } from \"../common/parseHex\";\r\n\r\nfunction decToHex(v) {\r\n v |= 0; // Ensure integer value\r\n return v < 0 ? \"00\" :\r\n v < 16 ? \"0\" + v.toString(16) :\r\n v < 256 ? v.toString(16) :\r\n \"ff\";\r\n}\r\n\r\nfunction hueToRgb(m1, m2, h) {\r\n h = h < 0 ? h + 6 : h > 6 ? h - 6 : h;\r\n return decToHex(255 * (\r\n h < 1 ? m1 + (m2 - m1) * h :\r\n h < 3 ? m2 :\r\n h < 4 ? m1 + (m2 - m1) * (4 - h) :\r\n m1));\r\n}\r\n\r\n/**\r\n * @param {number} r Red channel [0, 255]\r\n * @param {number} g Green channel [0, 255]\r\n * @param {number} b Blue channel [0, 255]\r\n */\r\nexport function rgb(r, g, b) {\r\n return \"#\" + decToHex(r) + decToHex(g) + decToHex(b);\r\n}\r\n\r\n/**\r\n * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported.\r\n * @returns {string}\r\n */\r\nexport function parseColor(color) {\r\n if (/^#[0-9a-f]{3,8}$/i.test(color)) {\r\n let result;\r\n const colorLength = color.length;\r\n\r\n if (colorLength < 6) {\r\n const r = color[1],\r\n g = color[2],\r\n b = color[3],\r\n a = color[4] || \"\";\r\n result = \"#\" + r + r + g + g + b + b + a + a;\r\n }\r\n if (colorLength == 7 || colorLength > 8) {\r\n result = color;\r\n }\r\n \r\n return result;\r\n }\r\n}\r\n\r\n/**\r\n * Converts a hexadecimal color to a CSS3 compatible color.\r\n * @param {string} hexColor Color on the format \"#RRGGBB\" or \"#RRGGBBAA\"\r\n * @returns {string}\r\n */\r\nexport function toCss3Color(hexColor) {\r\n const a = parseHex(hexColor, 7, 2);\r\n let result;\r\n\r\n if (isNaN(a)) {\r\n result = hexColor;\r\n } else {\r\n const r = parseHex(hexColor, 1, 2),\r\n g = parseHex(hexColor, 3, 2),\r\n b = parseHex(hexColor, 5, 2);\r\n result = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + (a / 255).toFixed(2) + \")\";\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color.\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function hsl(hue, saturation, lightness) {\r\n // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color\r\n let result;\r\n\r\n if (saturation == 0) {\r\n const partialHex = decToHex(lightness * 255);\r\n result = partialHex + partialHex + partialHex;\r\n }\r\n else {\r\n const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation,\r\n m1 = lightness * 2 - m2;\r\n result =\r\n hueToRgb(m1, m2, hue * 6 + 2) +\r\n hueToRgb(m1, m2, hue * 6) +\r\n hueToRgb(m1, m2, hue * 6 - 2);\r\n }\r\n\r\n return \"#\" + result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the \"dark\" hues\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function correctedHsl(hue, saturation, lightness) {\r\n // The corrector specifies the perceived middle lightness for each hue\r\n const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ],\r\n corrector = correctors[(hue * 6 + 0.5) | 0];\r\n \r\n // Adjust the input lightness relative to the corrector\r\n lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2;\r\n \r\n return hsl(hue, saturation, lightness);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for\r\n// backward compatibility.\r\n\r\nexport const GLOBAL = \r\n typeof window !== \"undefined\" ? window :\r\n typeof self !== \"undefined\" ? self :\r\n typeof global !== \"undefined\" ? global :\r\n {};\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseColor } from \"../renderer/color\";\r\nimport { GLOBAL } from \"./global\";\r\n\r\n/**\r\n * @typedef {Object} ParsedConfiguration\r\n * @property {number} colorSaturation\r\n * @property {number} grayscaleSaturation\r\n * @property {string} backColor\r\n * @property {number} iconPadding\r\n * @property {function(number):number} hue\r\n * @property {function(number):number} colorLightness\r\n * @property {function(number):number} grayscaleLightness\r\n */\r\n\r\nexport const CONFIG_PROPERTIES = {\r\n GLOBAL: \"jdenticon_config\",\r\n MODULE: \"config\",\r\n};\r\n\r\nvar rootConfigurationHolder = {};\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is \r\n * printed in the console. To minimize bundle size, this is only used in Node bundles.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigPropertyWithWarn(rootObject) {\r\n Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, {\r\n configurable: true,\r\n get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE],\r\n set: newConfiguration => {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n console.warn(\"jdenticon.config is deprecated. Use jdenticon.configure() instead.\");\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console\r\n * when it is being used.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigProperty(rootObject) {\r\n rootConfigurationHolder = rootObject;\r\n}\r\n\r\n/**\r\n * Sets a new icon style configuration. The new configuration is not merged with the previous one. * \r\n * @param {Object} newConfiguration - New configuration object.\r\n */\r\nexport function configure(newConfiguration) {\r\n if (arguments.length) {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n }\r\n return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE];\r\n}\r\n\r\n/**\r\n * Gets the normalized current Jdenticon color configuration. Missing fields have default values.\r\n * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A\r\n * local configuration overrides the global configuration in it entirety. This parameter can for backward\r\n * compatibility also contain a padding value. A padding value only overrides the global padding, not the\r\n * entire global configuration.\r\n * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor\r\n * explicitly to the API method.\r\n * @returns {ParsedConfiguration}\r\n */\r\nexport function getConfiguration(paddingOrLocalConfig, defaultPadding) {\r\n const configObject = \r\n typeof paddingOrLocalConfig == \"object\" && paddingOrLocalConfig ||\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] ||\r\n GLOBAL[CONFIG_PROPERTIES.GLOBAL] ||\r\n { },\r\n\r\n lightnessConfig = configObject[\"lightness\"] || { },\r\n \r\n // In versions < 2.1.0 there was no grayscale saturation -\r\n // saturation was the color saturation.\r\n saturation = configObject[\"saturation\"] || { },\r\n colorSaturation = \"color\" in saturation ? saturation[\"color\"] : saturation,\r\n grayscaleSaturation = saturation[\"grayscale\"],\r\n\r\n backColor = configObject[\"backColor\"],\r\n padding = configObject[\"padding\"];\r\n \r\n /**\r\n * Creates a lightness range.\r\n */\r\n function lightness(configName, defaultRange) {\r\n let range = lightnessConfig[configName];\r\n \r\n // Check if the lightness range is an array-like object. This way we ensure the\r\n // array contain two values at the same time.\r\n if (!(range && range.length > 1)) {\r\n range = defaultRange;\r\n }\r\n\r\n /**\r\n * Gets a lightness relative the specified value in the specified lightness range.\r\n */\r\n return function (value) {\r\n value = range[0] + value * (range[1] - range[0]);\r\n return value < 0 ? 0 : value > 1 ? 1 : value;\r\n };\r\n }\r\n\r\n /**\r\n * Gets a hue allowed by the configured hue restriction,\r\n * provided the originally computed hue.\r\n */\r\n function hueFunction(originalHue) {\r\n const hueConfig = configObject[\"hues\"];\r\n let hue;\r\n \r\n // Check if 'hues' is an array-like object. This way we also ensure that\r\n // the array is not empty, which would mean no hue restriction.\r\n if (hueConfig && hueConfig.length > 0) {\r\n // originalHue is in the range [0, 1]\r\n // Multiply with 0.999 to change the range to [0, 1) and then truncate the index.\r\n hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)];\r\n }\r\n\r\n return typeof hue == \"number\" ?\r\n \r\n // A hue was specified. We need to convert the hue from\r\n // degrees on any turn - e.g. 746° is a perfectly valid hue -\r\n // to turns in the range [0, 1).\r\n ((((hue / 360) % 1) + 1) % 1) :\r\n\r\n // No hue configured => use original hue\r\n originalHue;\r\n }\r\n \r\n return {\r\n hue: hueFunction,\r\n colorSaturation: typeof colorSaturation == \"number\" ? colorSaturation : 0.5,\r\n grayscaleSaturation: typeof grayscaleSaturation == \"number\" ? grayscaleSaturation : 0,\r\n colorLightness: lightness(\"color\", [0.4, 0.8]),\r\n grayscaleLightness: lightness(\"grayscale\", [0.3, 0.9]),\r\n backColor: parseColor(backColor),\r\n iconPadding: \r\n typeof paddingOrLocalConfig == \"number\" ? paddingOrLocalConfig : \r\n typeof padding == \"number\" ? padding : \r\n defaultPadding\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Represents a point.\r\n */\r\nexport class Point {\r\n /**\r\n * @param {number} x \r\n * @param {number} y \r\n */\r\n constructor(x, y) {\r\n this.x = x;\r\n this.y = y;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Point } from \"./point\";\r\n\r\n/**\r\n * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, \r\n * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly.\r\n */\r\nexport class Transform {\r\n /**\r\n * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} size The size of the transformed rectangle.\r\n * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad\r\n */\r\n constructor(x, y, size, rotation) {\r\n this._x = x;\r\n this._y = y;\r\n this._size = size;\r\n this._rotation = rotation;\r\n }\r\n\r\n /**\r\n * Transforms the specified point based on the translation and rotation specification for this Transform.\r\n * @param {number} x x-coordinate\r\n * @param {number} y y-coordinate\r\n * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n */\r\n transformIconPoint(x, y, w, h) {\r\n const right = this._x + this._size,\r\n bottom = this._y + this._size,\r\n rotation = this._rotation;\r\n return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) :\r\n rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) :\r\n rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) :\r\n new Point(this._x + x, this._y + y);\r\n }\r\n}\r\n\r\nexport const NO_TRANSFORM = new Transform(0, 0, 0, 0);\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { NO_TRANSFORM } from \"./transform\";\r\n\r\n/**\r\n * @typedef {import(\"./renderer\").Renderer} Renderer\r\n * @typedef {import(\"./transform\").Transform} Transform\r\n */\r\n\r\n/**\r\n * Provides helper functions for rendering common basic shapes.\r\n */\r\nexport class Graphics {\r\n /**\r\n * @param {Renderer} renderer \r\n */\r\n constructor(renderer) {\r\n /**\r\n * @type {Renderer}\r\n * @private\r\n */\r\n this._renderer = renderer;\r\n\r\n /**\r\n * @type {Transform}\r\n */\r\n this.currentTransform = NO_TRANSFORM;\r\n }\r\n\r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ]\r\n * @param {boolean=} invert Specifies if the polygon will be inverted.\r\n */\r\n addPolygon(points, invert) {\r\n const di = invert ? -2 : 2,\r\n transformedPoints = [];\r\n \r\n for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) {\r\n transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1]));\r\n }\r\n \r\n this._renderer.addPolygon(transformedPoints);\r\n }\r\n \r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * Source: http://stackoverflow.com/a/2173084\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} size The size of the ellipse.\r\n * @param {boolean=} invert Specifies if the ellipse will be inverted.\r\n */\r\n addCircle(x, y, size, invert) {\r\n const p = this.currentTransform.transformIconPoint(x, y, size, size);\r\n this._renderer.addCircle(p, size, invert);\r\n }\r\n\r\n /**\r\n * Adds a rectangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle.\r\n * @param {number} w The width of the rectangle.\r\n * @param {number} h The height of the rectangle.\r\n * @param {boolean=} invert Specifies if the rectangle will be inverted.\r\n */\r\n addRectangle(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x, y, \r\n x + w, y,\r\n x + w, y + h,\r\n x, y + h\r\n ], invert);\r\n }\r\n\r\n /**\r\n * Adds a right triangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} w The width of the triangle.\r\n * @param {number} h The height of the triangle.\r\n * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle.\r\n * @param {boolean=} invert Specifies if the triangle will be inverted.\r\n */\r\n addTriangle(x, y, w, h, r, invert) {\r\n const points = [\r\n x + w, y, \r\n x + w, y + h, \r\n x, y + h,\r\n x, y\r\n ];\r\n points.splice(((r || 0) % 4) * 2, 2);\r\n this.addPolygon(points, invert);\r\n }\r\n\r\n /**\r\n * Adds a rhombus to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} w The width of the rhombus.\r\n * @param {number} h The height of the rhombus.\r\n * @param {boolean=} invert Specifies if the rhombus will be inverted.\r\n */\r\n addRhombus(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x + w / 2, y,\r\n x + w, y + h / 2,\r\n x + w / 2, y + h,\r\n x, y + h / 2\r\n ], invert);\r\n }\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n * @param {number} positionIndex\r\n * @typedef {import('./graphics').Graphics} Graphics\r\n */\r\nexport function centerShape(index, g, cell, positionIndex) {\r\n index = index % 14;\r\n\r\n let k, m, w, h, inner, outer;\r\n\r\n !index ? (\r\n k = cell * 0.42,\r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell - k * 2,\r\n cell - k, cell,\r\n 0, cell\r\n ])) :\r\n\r\n index == 1 ? (\r\n w = 0 | (cell * 0.5), \r\n h = 0 | (cell * 0.8),\r\n\r\n g.addTriangle(cell - w, 0, w, h, 2)) :\r\n\r\n index == 2 ? (\r\n w = 0 | (cell / 3),\r\n g.addRectangle(w, w, cell - w, cell - w)) :\r\n\r\n index == 3 ? (\r\n inner = cell * 0.1,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 6 ? 1 :\r\n cell < 8 ? 2 :\r\n (0 | (cell * 0.25)),\r\n \r\n inner = \r\n inner > 1 ? (0 | inner) : // large icon => truncate decimals\r\n inner > 0.5 ? 1 : // medium size icon => fixed width\r\n inner, // small icon => anti-aliased border\r\n\r\n g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) :\r\n\r\n index == 4 ? (\r\n m = 0 | (cell * 0.15),\r\n w = 0 | (cell * 0.5),\r\n g.addCircle(cell - w - m, cell - w - m, w)) :\r\n\r\n index == 5 ? (\r\n inner = cell * 0.1,\r\n outer = inner * 4,\r\n\r\n // Align edge to nearest pixel in large icons\r\n outer > 3 && (outer = 0 | outer),\r\n \r\n g.addRectangle(0, 0, cell, cell),\r\n g.addPolygon([\r\n outer, outer,\r\n cell - inner, outer,\r\n outer + (cell - outer - inner) / 2, cell - inner\r\n ], true)) :\r\n\r\n index == 6 ? \r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell * 0.7,\r\n cell * 0.4, cell * 0.4,\r\n cell * 0.7, cell,\r\n 0, cell\r\n ]) :\r\n\r\n index == 7 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 8 ? (\r\n g.addRectangle(0, 0, cell, cell / 2),\r\n g.addRectangle(0, cell / 2, cell / 2, cell / 2),\r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) :\r\n\r\n index == 9 ? (\r\n inner = cell * 0.14,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 4 ? 1 :\r\n cell < 6 ? 2 :\r\n (0 | (cell * 0.35)),\r\n\r\n inner = \r\n cell < 8 ? inner : // small icon => anti-aliased border\r\n (0 | inner), // large icon => truncate decimals\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) :\r\n\r\n index == 10 ? (\r\n inner = cell * 0.12,\r\n outer = inner * 3,\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addCircle(outer, outer, cell - inner - outer, true)) :\r\n\r\n index == 11 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 12 ? (\r\n m = cell * 0.25,\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRhombus(m, m, cell - m, cell - m, true)) :\r\n\r\n // 13\r\n (\r\n !positionIndex && (\r\n m = cell * 0.4, w = cell * 1.2,\r\n g.addCircle(m, m, w)\r\n )\r\n );\r\n}\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n */\r\nexport function outerShape(index, g, cell) {\r\n index = index % 4;\r\n\r\n let m;\r\n\r\n !index ?\r\n g.addTriangle(0, 0, cell, cell, 0) :\r\n \r\n index == 1 ?\r\n g.addTriangle(0, cell / 2, cell, cell / 2, 0) :\r\n\r\n index == 2 ?\r\n g.addRhombus(0, 0, cell, cell) :\r\n\r\n // 3\r\n (\r\n m = cell / 6,\r\n g.addCircle(m, m, cell - 2 * m)\r\n );\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { correctedHsl } from \"./color\";\r\n\r\n/**\r\n * Gets a set of identicon color candidates for a specified hue and config.\r\n * @param {number} hue\r\n * @param {import(\"../common/configuration\").ParsedConfiguration} config\r\n */\r\nexport function colorTheme(hue, config) {\r\n hue = config.hue(hue);\r\n return [\r\n // Dark gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)),\r\n // Mid color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)),\r\n // Light gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)),\r\n // Light color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(1)),\r\n // Dark color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0))\r\n ];\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Transform } from \"./transform\";\r\nimport { Graphics } from \"./graphics\";\r\nimport { centerShape, outerShape } from \"./shapes\";\r\nimport { colorTheme } from \"./colorTheme\";\r\nimport { parseHex } from \"../common/parseHex\";\r\nimport { getConfiguration } from \"../common/configuration\";\r\n \r\n/**\r\n * Draws an identicon to a specified renderer.\r\n * @param {import('./renderer').Renderer} renderer\r\n * @param {string} hash\r\n * @param {Object|number=} config\r\n */\r\nexport function iconGenerator(renderer, hash, config) {\r\n const parsedConfig = getConfiguration(config, 0.08);\r\n\r\n // Set background color\r\n if (parsedConfig.backColor) {\r\n renderer.setBackground(parsedConfig.backColor);\r\n }\r\n \r\n // Calculate padding and round to nearest integer\r\n let size = renderer.iconSize;\r\n const padding = (0.5 + size * parsedConfig.iconPadding) | 0;\r\n size -= padding * 2;\r\n \r\n const graphics = new Graphics(renderer);\r\n \r\n // Calculate cell size and ensure it is an integer\r\n const cell = 0 | (size / 4);\r\n \r\n // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon\r\n const x = 0 | (padding + size / 2 - cell * 2);\r\n const y = 0 | (padding + size / 2 - cell * 2);\r\n\r\n function renderShape(colorIndex, shapes, index, rotationIndex, positions) {\r\n const shapeIndex = parseHex(hash, index, 1);\r\n let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0;\r\n \r\n renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]);\r\n \r\n for (let i = 0; i < positions.length; i++) {\r\n graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4);\r\n shapes(shapeIndex, graphics, cell, i);\r\n }\r\n \r\n renderer.endShape();\r\n }\r\n\r\n // AVAILABLE COLORS\r\n const hue = parseHex(hash, -7) / 0xfffffff,\r\n \r\n // Available colors for this icon\r\n availableColors = colorTheme(hue, parsedConfig),\r\n\r\n // The index of the selected colors\r\n selectedColorIndexes = [];\r\n\r\n let index;\r\n\r\n function isDuplicate(values) {\r\n if (values.indexOf(index) >= 0) {\r\n for (let i = 0; i < values.length; i++) {\r\n if (selectedColorIndexes.indexOf(values[i]) >= 0) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < 3; i++) {\r\n index = parseHex(hash, 8 + i, 1) % availableColors.length;\r\n if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo\r\n isDuplicate([2, 3])) { // Disallow light gray and light color combo\r\n index = 1;\r\n }\r\n selectedColorIndexes.push(index);\r\n }\r\n\r\n // ACTUAL RENDERING\r\n // Sides\r\n renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]);\r\n // Corners\r\n renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]);\r\n // Center\r\n renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]);\r\n \r\n renderer.finish();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Computes a SHA1 hash for any value and returns it as a hexadecimal string.\r\n * \r\n * This function is optimized for minimal code size and rather short messages.\r\n * \r\n * @param {string} message \r\n */\r\nexport function sha1(message) {\r\n const HASH_SIZE_HALF_BYTES = 40;\r\n const BLOCK_SIZE_WORDS = 16;\r\n\r\n // Variables\r\n // `var` is used to be able to minimize the number of `var` keywords.\r\n var i = 0,\r\n f = 0,\r\n \r\n // Use `encodeURI` to UTF8 encode the message without any additional libraries\r\n // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky\r\n // since `unescape` is deprecated.\r\n urlEncodedMessage = encodeURI(message) + \"%80\", // trailing '1' bit padding\r\n \r\n // This can be changed to a preallocated Uint32Array array for greater performance and larger code size\r\n data = [],\r\n dataSize,\r\n \r\n hashBuffer = [],\r\n\r\n a = 0x67452301,\r\n b = 0xefcdab89,\r\n c = ~a,\r\n d = ~b,\r\n e = 0xc3d2e1f0,\r\n hash = [a, b, c, d, e],\r\n\r\n blockStartIndex = 0,\r\n hexHash = \"\";\r\n\r\n /**\r\n * Rotates the value a specified number of bits to the left.\r\n * @param {number} value Value to rotate\r\n * @param {number} shift Bit count to shift.\r\n */\r\n function rotl(value, shift) {\r\n return (value << shift) | (value >>> (32 - shift));\r\n }\r\n\r\n // Message data\r\n for ( ; i < urlEncodedMessage.length; f++) {\r\n data[f >> 2] = data[f >> 2] |\r\n (\r\n (\r\n urlEncodedMessage[i] == \"%\"\r\n // Percent encoded byte\r\n ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)\r\n // Unencoded byte\r\n : urlEncodedMessage.charCodeAt(i++)\r\n )\r\n\r\n // Read bytes in reverse order (big endian words)\r\n << ((3 - (f & 3)) * 8)\r\n );\r\n }\r\n\r\n // f is now the length of the utf8 encoded message\r\n // 7 = 8 bytes (64 bit) for message size, -1 to round down\r\n // >> 6 = integer division with block size\r\n dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;\r\n\r\n // Message size in bits.\r\n // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least\r\n // significant 32 bits are set. -8 is for the '1' bit padding byte.\r\n data[dataSize - 1] = f * 8 - 8;\r\n \r\n // Compute hash\r\n for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {\r\n for (i = 0; i < 80; i++) {\r\n f = rotl(a, 5) + e + (\r\n // Ch\r\n i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :\r\n \r\n // Parity\r\n i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :\r\n \r\n // Maj\r\n i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :\r\n \r\n // Parity\r\n (b ^ c ^ d) + 0xca62c1d6\r\n ) + ( \r\n hashBuffer[i] = i < BLOCK_SIZE_WORDS\r\n // Bitwise OR is used to coerse `undefined` to 0\r\n ? (data[blockStartIndex + i] | 0)\r\n : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)\r\n );\r\n\r\n e = d;\r\n d = c;\r\n c = rotl(b, 30);\r\n b = a;\r\n a = f;\r\n }\r\n\r\n hash[0] = a = ((hash[0] + a) | 0);\r\n hash[1] = b = ((hash[1] + b) | 0);\r\n hash[2] = c = ((hash[2] + c) | 0);\r\n hash[3] = d = ((hash[3] + d) | 0);\r\n hash[4] = e = ((hash[4] + e) | 0);\r\n }\r\n\r\n // Format hex hash\r\n for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {\r\n hexHash += (\r\n (\r\n // Get word (2^3 half-bytes per word)\r\n hash[i >> 3] >>>\r\n\r\n // Append half-bytes in reverse order\r\n ((7 - (i & 7)) * 4)\r\n ) \r\n // Clamp to half-byte\r\n & 0xf\r\n ).toString(16);\r\n }\r\n\r\n return hexHash;\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { sha1 } from \"./sha1\";\r\n\r\n/**\r\n * Inputs a value that might be a valid hash string for Jdenticon and returns it \r\n * if it is determined valid, otherwise a falsy value is returned.\r\n */\r\nexport function isValidHash(hashCandidate) {\r\n return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate;\r\n}\r\n\r\n/**\r\n * Computes a hash for the specified value. Currently SHA1 is used. This function\r\n * always returns a valid hash.\r\n */\r\nexport function computeHash(value) {\r\n return sha1(value == null ? \"\" : \"\" + value);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { toCss3Color } from \"../color\";\r\n\r\n/**\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import('../point').Point} Point\r\n */\r\n\r\n/**\r\n * Renderer redirecting drawing commands to a canvas context.\r\n * @implements {Renderer}\r\n */\r\nexport class CanvasRenderer {\r\n /**\r\n * @param {number=} iconSize\r\n */\r\n constructor(ctx, iconSize) {\r\n const canvas = ctx.canvas; \r\n const width = canvas.width;\r\n const height = canvas.height;\r\n \r\n ctx.save();\r\n \r\n if (!iconSize) {\r\n iconSize = Math.min(width, height);\r\n \r\n ctx.translate(\r\n ((width - iconSize) / 2) | 0,\r\n ((height - iconSize) / 2) | 0);\r\n }\r\n\r\n /**\r\n * @private\r\n */\r\n this._ctx = ctx;\r\n this.iconSize = iconSize;\r\n \r\n ctx.clearRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const ctx = this._ctx;\r\n const iconSize = this.iconSize;\r\n\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.fillRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} fillColor Fill color on format #rrggbb[aa].\r\n */\r\n beginShape(fillColor) {\r\n const ctx = this._ctx;\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.beginPath();\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas.\r\n */\r\n endShape() {\r\n this._ctx.fill();\r\n }\r\n\r\n /**\r\n * Adds a polygon to the rendering queue.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n const ctx = this._ctx;\r\n ctx.moveTo(points[0].x, points[0].y);\r\n for (let i = 1; i < points.length; i++) {\r\n ctx.lineTo(points[i].x, points[i].y);\r\n }\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Adds a circle to the rendering queue.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const ctx = this._ctx,\r\n radius = diameter / 2;\r\n ctx.moveTo(point.x + radius, point.y + radius);\r\n ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise);\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() {\r\n this._ctx.restore();\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const ICON_TYPE_SVG = 1;\r\n\r\nexport const ICON_TYPE_CANVAS = 2;\r\n\r\nexport const ATTRIBUTES = {\r\n HASH: \"data-jdenticon-hash\",\r\n VALUE: \"data-jdenticon-value\"\r\n};\r\n\r\nexport const IS_RENDERED_PROPERTY = \"jdenticonRendered\";\r\n\r\nexport const ICON_SELECTOR = \"[\" + ATTRIBUTES.HASH +\"],[\" + ATTRIBUTES.VALUE +\"]\";\r\n\r\nexport const documentQuerySelectorAll = /** @type {!Function} */ (\r\n typeof document !== \"undefined\" && document.querySelectorAll.bind(document));\r\n\r\nexport function getIdenticonType(el) {\r\n if (el) {\r\n const tagName = el[\"tagName\"];\r\n\r\n if (/^svg$/i.test(tagName)) {\r\n return ICON_TYPE_SVG;\r\n }\r\n\r\n if (/^canvas$/i.test(tagName) && \"getContext\" in el) {\r\n return ICON_TYPE_CANVAS;\r\n }\r\n }\r\n}\r\n\r\nexport function whenDocumentIsReady(/** @type {Function} */ callback) {\r\n function loadedHandler() {\r\n document.removeEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.removeEventListener(\"load\", loadedHandler);\r\n setTimeout(callback, 0); // Give scripts a chance to run\r\n }\r\n \r\n if (typeof document !== \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof setTimeout !== \"undefined\"\r\n ) {\r\n if (document.readyState === \"loading\") {\r\n document.addEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.addEventListener(\"load\", loadedHandler);\r\n } else {\r\n // Document already loaded. The load events above likely won't be raised\r\n setTimeout(callback, 0);\r\n }\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { IS_RENDERED_PROPERTY } from \"../common/dom\";\r\n\r\n/**\r\n * Draws an identicon to a context.\r\n * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0).\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function drawIcon(ctx, hashOrValue, size, config) {\r\n if (!ctx) {\r\n throw new Error(\"No canvas specified.\");\r\n }\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n\r\n const canvas = ctx.canvas;\r\n if (canvas) {\r\n canvas[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Prepares a measure to be used as a measure in an SVG path, by\r\n * rounding the measure to a single decimal. This reduces the file\r\n * size of the generated SVG with more than 50% in some cases.\r\n */\r\nfunction svgValue(value) {\r\n return ((value * 10 + 0.5) | 0) / 10;\r\n}\r\n\r\n/**\r\n * Represents an SVG path element.\r\n */\r\nexport class SvgPath {\r\n constructor() {\r\n /**\r\n * This property holds the data string (path.d) of the SVG path.\r\n * @type {string}\r\n */\r\n this.dataString = \"\";\r\n }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG path.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n let dataString = \"\";\r\n for (let i = 0; i < points.length; i++) {\r\n dataString += (i ? \"L\" : \"M\") + svgValue(points[i].x) + \" \" + svgValue(points[i].y);\r\n }\r\n this.dataString += dataString + \"Z\";\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG path.\r\n * @param {import('../point').Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const sweepFlag = counterClockwise ? 0 : 1,\r\n svgRadius = svgValue(diameter / 2),\r\n svgDiameter = svgValue(diameter),\r\n svgArc = \"a\" + svgRadius + \",\" + svgRadius + \" 0 1,\" + sweepFlag + \" \";\r\n \r\n this.dataString += \r\n \"M\" + svgValue(point.x) + \" \" + svgValue(point.y + diameter / 2) +\r\n svgArc + svgDiameter + \",0\" + \r\n svgArc + (-svgDiameter) + \",0\";\r\n }\r\n}\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SvgPath } from \"./svgPath\";\r\nimport { parseHex } from \"../../common/parseHex\";\r\n\r\n/**\r\n * @typedef {import(\"../point\").Point} Point\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import(\"./svgElement\").SvgElement} SvgElement\r\n * @typedef {import(\"./svgWriter\").SvgWriter} SvgWriter\r\n */\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n * @implements {Renderer}\r\n */\r\nexport class SvgRenderer {\r\n /**\r\n * @param {SvgElement|SvgWriter} target \r\n */\r\n constructor(target) {\r\n /**\r\n * @type {SvgPath}\r\n * @private\r\n */\r\n this._path;\r\n\r\n /**\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._pathsByColor = { };\r\n\r\n /**\r\n * @type {SvgElement|SvgWriter}\r\n * @private\r\n */\r\n this._target = target;\r\n\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = target.iconSize;\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const match = /^(#......)(..)?/.exec(fillColor),\r\n opacity = match[2] ? parseHex(match[2], 0) / 255 : 1;\r\n this._target.setBackground(match[1], opacity);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n */\r\n beginShape(color) {\r\n this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath());\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape.\r\n */\r\n endShape() { }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n this._path.addPolygon(points);\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n this._path.addCircle(point, diameter, counterClockwise);\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() { \r\n const pathsByColor = this._pathsByColor;\r\n for (let color in pathsByColor) {\r\n // hasOwnProperty cannot be shadowed in pathsByColor\r\n // eslint-disable-next-line no-prototype-builtins\r\n if (pathsByColor.hasOwnProperty(color)) {\r\n this._target.appendPath(color, pathsByColor[color].dataString);\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const SVG_CONSTANTS = {\r\n XMLNS: \"http://www.w3.org/2000/svg\",\r\n WIDTH: \"width\",\r\n HEIGHT: \"height\",\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgWriter {\r\n /**\r\n * @param {number} iconSize - Icon width and height in pixels.\r\n */\r\n constructor(iconSize) {\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = iconSize;\r\n\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n this._s =\r\n '';\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n this._s += '';\r\n }\r\n }\r\n\r\n /**\r\n * Writes a path to the SVG string.\r\n * @param {string} color Fill color on format #rrggbb.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n this._s += '';\r\n }\r\n\r\n /**\r\n * Gets the rendered image as an SVG string.\r\n */\r\n toString() {\r\n return this._s + \"\";\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgWriter } from \"../renderer/svg/svgWriter\";\r\n\r\n/**\r\n * Draws an identicon as an SVG string.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {string} SVG string\r\n */\r\nexport function toSvg(hashOrValue, size, config) {\r\n const writer = new SvgWriter(size);\r\n iconGenerator(new SvgRenderer(writer), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue),\r\n config);\r\n return writer.toString();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Creates a new element and adds it to the specified parent.\r\n * @param {Element} parentNode\r\n * @param {string} name\r\n * @param {...(string|number)} keyValuePairs\r\n */\r\nfunction SvgElement_append(parentNode, name, ...keyValuePairs) {\r\n const el = document.createElementNS(SVG_CONSTANTS.XMLNS, name);\r\n \r\n for (let i = 0; i + 1 < keyValuePairs.length; i += 2) {\r\n el.setAttribute(\r\n /** @type {string} */(keyValuePairs[i]),\r\n /** @type {string} */(keyValuePairs[i + 1]),\r\n );\r\n }\r\n\r\n parentNode.appendChild(el);\r\n}\r\n\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgElement {\r\n /**\r\n * @param {Element} element - Target element\r\n */\r\n constructor(element) {\r\n // Don't use the clientWidth and clientHeight properties on SVG elements\r\n // since Firefox won't serve a proper value of these properties on SVG\r\n // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811)\r\n // Instead use 100px as a hardcoded size (the svg viewBox will rescale \r\n // the icon to the correct dimensions)\r\n const iconSize = this.iconSize = Math.min(\r\n (Number(element.getAttribute(SVG_CONSTANTS.WIDTH)) || 100),\r\n (Number(element.getAttribute(SVG_CONSTANTS.HEIGHT)) || 100)\r\n );\r\n \r\n /**\r\n * @type {Element}\r\n * @private\r\n */\r\n this._el = element;\r\n \r\n // Clear current SVG child elements\r\n while (element.firstChild) {\r\n element.removeChild(element.firstChild);\r\n }\r\n \r\n // Set viewBox attribute to ensure the svg scales nicely.\r\n element.setAttribute(\"viewBox\", \"0 0 \" + iconSize + \" \" + iconSize);\r\n element.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n SvgElement_append(this._el, \"rect\",\r\n SVG_CONSTANTS.WIDTH, \"100%\",\r\n SVG_CONSTANTS.HEIGHT, \"100%\",\r\n \"fill\", fillColor,\r\n \"opacity\", opacity);\r\n }\r\n }\r\n\r\n /**\r\n * Appends a path to the SVG element.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n SvgElement_append(this._el, \"path\",\r\n \"fill\", color,\r\n \"d\", dataString);\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { ATTRIBUTES, ICON_SELECTOR, IS_RENDERED_PROPERTY, documentQuerySelectorAll } from \"../common/dom\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgElement } from \"../renderer/svg/svgElement\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { ICON_TYPE_CANVAS, ICON_TYPE_SVG, getIdenticonType } from \"../common/dom\";\r\n\r\n\r\n/**\r\n * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute.\r\n */\r\nexport function updateAll() {\r\n if (documentQuerySelectorAll) {\r\n update(ICON_SELECTOR);\r\n }\r\n}\r\n\r\n/**\r\n * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute that have not already\r\n * been rendered.\r\n */\r\nexport function updateAllConditional() {\r\n if (documentQuerySelectorAll) {\r\n /** @type {NodeListOf} */\r\n const elements = documentQuerySelectorAll(ICON_SELECTOR);\r\n \r\n for (let i = 0; i < elements.length; i++) {\r\n const el = elements[i];\r\n if (!el[IS_RENDERED_PROPERTY]) {\r\n update(el);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` or `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * `` or ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function update(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType) {\r\n return iconType == ICON_TYPE_SVG ? \r\n new SvgRenderer(new SvgElement(el)) : \r\n new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext(\"2d\"));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function updateCanvas(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType == ICON_TYPE_CANVAS) {\r\n return new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext(\"2d\"));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function updateSvg(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType == ICON_TYPE_SVG) {\r\n return new SvgRenderer(new SvgElement(el));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified canvas or svg elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * `` or ``, or a CSS selector to such an element.\r\n * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number|undefined} config\r\n * @param {function(Element,number):import(\"../renderer/renderer\").Renderer} rendererFactory - Factory function for creating an icon renderer.\r\n */\r\nfunction renderDomElement(el, hashOrValue, config, rendererFactory) {\r\n if (typeof el === \"string\") {\r\n if (documentQuerySelectorAll) {\r\n const elements = documentQuerySelectorAll(el);\r\n for (let i = 0; i < elements.length; i++) {\r\n renderDomElement(elements[i], hashOrValue, config, rendererFactory);\r\n }\r\n }\r\n return;\r\n }\r\n \r\n // Hash selection. The result from getValidHash or computeHash is \r\n // accepted as a valid hash.\r\n const hash = \r\n // 1. Explicit valid hash\r\n isValidHash(hashOrValue) ||\r\n \r\n // 2. Explicit value (`!= null` catches both null and undefined)\r\n hashOrValue != null && computeHash(hashOrValue) ||\r\n \r\n // 3. `data-jdenticon-hash` attribute\r\n isValidHash(el.getAttribute(ATTRIBUTES.HASH)) ||\r\n \r\n // 4. `data-jdenticon-value` attribute. \r\n // We want to treat an empty attribute as an empty value. \r\n // Some browsers return empty string even if the attribute \r\n // is not specified, so use hasAttribute to determine if \r\n // the attribute is specified.\r\n el.hasAttribute(ATTRIBUTES.VALUE) && computeHash(el.getAttribute(ATTRIBUTES.VALUE));\r\n \r\n if (!hash) {\r\n // No hash specified. Don't render an icon.\r\n return;\r\n }\r\n \r\n const renderer = rendererFactory(el, getIdenticonType(el));\r\n if (renderer) {\r\n // Draw icon\r\n iconGenerator(renderer, hash, config);\r\n el[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// This file is compiled to dist/jdenticon-module.mjs\r\n\r\nexport { configure } from \"./apis/configure\";\r\nexport { drawIcon } from \"./apis/drawIcon\";\r\nexport { toSvg } from \"./apis/toSvg\";\r\nexport { update, updateCanvas, updateSvg } from \"./apis/update\";\r\n\r\n/**\r\n * Specifies the version of the Jdenticon package in use.\r\n * @type {string}\r\n */\r\nexport const version = \"#version#\";\r\n\r\n/**\r\n * Specifies which bundle of Jdenticon that is used.\r\n * @type {string}\r\n */\r\nexport const bundle = \"browser-esm\";\r\n","\n//# sourceMappingURL=jdenticon-module.mjs.map\n"]} \ No newline at end of file diff --git a/jdenticon-js/dist/jdenticon-node.js b/jdenticon-js/dist/jdenticon-node.js new file mode 100644 index 0000000..c780ed3 --- /dev/null +++ b/jdenticon-js/dist/jdenticon-node.js @@ -0,0 +1,1276 @@ +/** + * Jdenticon 3.3.0 + * http://jdenticon.com + * + * Built: 2024-05-10T09:48:41.921Z + * + * MIT License + * + * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi + * + * 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. + */ + +'use strict'; + +var canvasRenderer = require('canvas-renderer'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +var canvasRenderer__default = /*#__PURE__*/_interopDefaultLegacy(canvasRenderer); + +/** + * Parses a substring of the hash as a number. + * @param {number} startPosition + * @param {number=} octets + */ +function parseHex(hash, startPosition, octets) { + return parseInt(hash.substr(startPosition, octets), 16); +} + +function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; +} + +function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); +} + +/** + * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported. + * @returns {string} + */ +function parseColor(color) { + if (/^#[0-9a-f]{3,8}$/i.test(color)) { + let result; + const colorLength = color.length; + + if (colorLength < 6) { + const r = color[1], + g = color[2], + b = color[3], + a = color[4] || ""; + result = "#" + r + r + g + g + b + b + a + a; + } + if (colorLength == 7 || colorLength > 8) { + result = color; + } + + return result; + } +} + +/** + * Converts a hexadecimal color to a CSS3 compatible color. + * @param {string} hexColor Color on the format "#RRGGBB" or "#RRGGBBAA" + * @returns {string} + */ +function toCss3Color(hexColor) { + const a = parseHex(hexColor, 7, 2); + let result; + + if (isNaN(a)) { + result = hexColor; + } else { + const r = parseHex(hexColor, 1, 2), + g = parseHex(hexColor, 3, 2), + b = parseHex(hexColor, 5, 2); + result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")"; + } + + return result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function hsl(hue, saturation, lightness) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + let result; + + if (saturation == 0) { + const partialHex = decToHex(lightness * 255); + result = partialHex + partialHex + partialHex; + } + else { + const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation, + m1 = lightness * 2 - m2; + result = + hueToRgb(m1, m2, hue * 6 + 2) + + hueToRgb(m1, m2, hue * 6) + + hueToRgb(m1, m2, hue * 6 - 2); + } + + return "#" + result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function correctedHsl(hue, saturation, lightness) { + // The corrector specifies the perceived middle lightness for each hue + const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(hue * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; + + return hsl(hue, saturation, lightness); +} + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. + +const GLOBAL = + typeof window !== "undefined" ? window : + typeof self !== "undefined" ? self : + typeof global !== "undefined" ? global : + {}; + +/** + * @typedef {Object} ParsedConfiguration + * @property {number} colorSaturation + * @property {number} grayscaleSaturation + * @property {string} backColor + * @property {number} iconPadding + * @property {function(number):number} hue + * @property {function(number):number} colorLightness + * @property {function(number):number} grayscaleLightness + */ + +const CONFIG_PROPERTIES = { + GLOBAL: "jdenticon_config", + MODULE: "config", +}; + +var rootConfigurationHolder = {}; + +/** + * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is + * printed in the console. To minimize bundle size, this is only used in Node bundles. + * @param {!Object} rootObject + */ +function defineConfigPropertyWithWarn(rootObject) { + Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, { + configurable: true, + get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE], + set: newConfiguration => { + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration; + console.warn("jdenticon.config is deprecated. Use jdenticon.configure() instead."); + }, + }); +} + +/** + * Sets a new icon style configuration. The new configuration is not merged with the previous one. * + * @param {Object} newConfiguration - New configuration object. + */ +function configure(newConfiguration) { + if (arguments.length) { + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration; + } + return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE]; +} + +/** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A + * local configuration overrides the global configuration in it entirety. This parameter can for backward + * compatibility also contain a padding value. A padding value only overrides the global padding, not the + * entire global configuration. + * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor + * explicitly to the API method. + * @returns {ParsedConfiguration} + */ +function getConfiguration(paddingOrLocalConfig, defaultPadding) { + const configObject = + typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] || + GLOBAL[CONFIG_PROPERTIES.GLOBAL] || + { }, + + lightnessConfig = configObject["lightness"] || { }, + + // In versions < 2.1.0 there was no grayscale saturation - + // saturation was the color saturation. + saturation = configObject["saturation"] || { }, + colorSaturation = "color" in saturation ? saturation["color"] : saturation, + grayscaleSaturation = saturation["grayscale"], + + backColor = configObject["backColor"], + padding = configObject["padding"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultRange) { + let range = lightnessConfig[configName]; + + // Check if the lightness range is an array-like object. This way we ensure the + // array contain two values at the same time. + if (!(range && range.length > 1)) { + range = defaultRange; + } + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + /** + * Gets a hue allowed by the configured hue restriction, + * provided the originally computed hue. + */ + function hueFunction(originalHue) { + const hueConfig = configObject["hues"]; + let hue; + + // Check if 'hues' is an array-like object. This way we also ensure that + // the array is not empty, which would mean no hue restriction. + if (hueConfig && hueConfig.length > 0) { + // originalHue is in the range [0, 1] + // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. + hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; + } + + return typeof hue == "number" ? + + // A hue was specified. We need to convert the hue from + // degrees on any turn - e.g. 746° is a perfectly valid hue - + // to turns in the range [0, 1). + ((((hue / 360) % 1) + 1) % 1) : + + // No hue configured => use original hue + originalHue; + } + + return { + hue: hueFunction, + colorSaturation: typeof colorSaturation == "number" ? colorSaturation : 0.5, + grayscaleSaturation: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, + colorLightness: lightness("color", [0.4, 0.8]), + grayscaleLightness: lightness("grayscale", [0.3, 0.9]), + backColor: parseColor(backColor), + iconPadding: + typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : + typeof padding == "number" ? padding : + defaultPadding + } +} + +/** + * Represents a point. + */ +class Point { + /** + * @param {number} x + * @param {number} y + */ + constructor(x, y) { + this.x = x; + this.y = y; + } +} + +/** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + */ +class Transform { + /** + * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle. + * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle. + * @param {number} size The size of the transformed rectangle. + * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad + */ + constructor(x, y, size, rotation) { + this._x = x; + this._y = y; + this._size = size; + this._rotation = rotation; + } + + /** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ + transformIconPoint(x, y, w, h) { + const right = this._x + this._size, + bottom = this._y + this._size, + rotation = this._rotation; + return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) : + rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) : + new Point(this._x + x, this._y + y); + } +} + +const NO_TRANSFORM = new Transform(0, 0, 0, 0); + + + +/** + * Provides helper functions for rendering common basic shapes. + */ +class Graphics { + /** + * @param {Renderer} renderer + */ + constructor(renderer) { + /** + * @type {Renderer} + * @private + */ + this._renderer = renderer; + + /** + * @type {Transform} + */ + this.currentTransform = NO_TRANSFORM; + } + + /** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ + addPolygon(points, invert) { + const di = invert ? -2 : 2, + transformedPoints = []; + + for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1])); + } + + this._renderer.addPolygon(transformedPoints); + } + + /** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ + addCircle(x, y, size, invert) { + const p = this.currentTransform.transformIconPoint(x, y, size, size); + this._renderer.addCircle(p, size, invert); + } + + /** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ + addRectangle(x, y, w, h, invert) { + this.addPolygon([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); + } + + /** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ + addTriangle(x, y, w, h, r, invert) { + const points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.addPolygon(points, invert); + } + + /** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ + addRhombus(x, y, w, h, invert) { + this.addPolygon([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); + } +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + * @param {number} positionIndex + */ +function centerShape(index, g, cell, positionIndex) { + index = index % 14; + + let k, m, w, h, inner, outer; + + !index ? ( + k = cell * 0.42, + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ])) : + + index == 1 ? ( + w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8), + + g.addTriangle(cell - w, 0, w, h, 2)) : + + index == 2 ? ( + w = 0 | (cell / 3), + g.addRectangle(w, w, cell - w, cell - w)) : + + index == 3 ? ( + inner = cell * 0.1, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)), + + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) : + + index == 4 ? ( + m = 0 | (cell * 0.15), + w = 0 | (cell * 0.5), + g.addCircle(cell - w - m, cell - w - m, w)) : + + index == 5 ? ( + inner = cell * 0.1, + outer = inner * 4, + + // Align edge to nearest pixel in large icons + outer > 3 && (outer = 0 | outer), + + g.addRectangle(0, 0, cell, cell), + g.addPolygon([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true)) : + + index == 6 ? + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]) : + + index == 7 ? + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 8 ? ( + g.addRectangle(0, 0, cell, cell / 2), + g.addRectangle(0, cell / 2, cell / 2, cell / 2), + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) : + + index == 9 ? ( + inner = cell * 0.14, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)), + + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + g.addRectangle(0, 0, cell, cell), + g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) : + + index == 10 ? ( + inner = cell * 0.12, + outer = inner * 3, + + g.addRectangle(0, 0, cell, cell), + g.addCircle(outer, outer, cell - inner - outer, true)) : + + index == 11 ? + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 12 ? ( + m = cell * 0.25, + g.addRectangle(0, 0, cell, cell), + g.addRhombus(m, m, cell - m, cell - m, true)) : + + // 13 + ( + !positionIndex && ( + m = cell * 0.4, w = cell * 1.2, + g.addCircle(m, m, w) + ) + ); +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + */ +function outerShape(index, g, cell) { + index = index % 4; + + let m; + + !index ? + g.addTriangle(0, 0, cell, cell, 0) : + + index == 1 ? + g.addTriangle(0, cell / 2, cell, cell / 2, 0) : + + index == 2 ? + g.addRhombus(0, 0, cell, cell) : + + // 3 + ( + m = cell / 6, + g.addCircle(m, m, cell - 2 * m) + ); +} + +/** + * Gets a set of identicon color candidates for a specified hue and config. + * @param {number} hue + * @param {ParsedConfiguration} config + */ +function colorTheme(hue, config) { + hue = config.hue(hue); + return [ + // Dark gray + correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)), + // Mid color + correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)), + // Light gray + correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)), + // Light color + correctedHsl(hue, config.colorSaturation, config.colorLightness(1)), + // Dark color + correctedHsl(hue, config.colorSaturation, config.colorLightness(0)) + ]; +} + +/** + * Draws an identicon to a specified renderer. + * @param {Renderer} renderer + * @param {string} hash + * @param {Object|number=} config + */ +function iconGenerator(renderer, hash, config) { + const parsedConfig = getConfiguration(config, 0.08); + + // Set background color + if (parsedConfig.backColor) { + renderer.setBackground(parsedConfig.backColor); + } + + // Calculate padding and round to nearest integer + let size = renderer.iconSize; + const padding = (0.5 + size * parsedConfig.iconPadding) | 0; + size -= padding * 2; + + const graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + const cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + const x = 0 | (padding + size / 2 - cell * 2); + const y = 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + const shapeIndex = parseHex(hash, index, 1); + let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]); + + for (let i = 0; i < positions.length; i++) { + graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.endShape(); + } + + // AVAILABLE COLORS + const hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, parsedConfig), + + // The index of the selected colors + selectedColorIndexes = []; + + let index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (let i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (let i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); +} + +/** + * Computes a SHA1 hash for any value and returns it as a hexadecimal string. + * + * This function is optimized for minimal code size and rather short messages. + * + * @param {string} message + */ +function sha1(message) { + const HASH_SIZE_HALF_BYTES = 40; + const BLOCK_SIZE_WORDS = 16; + + // Variables + // `var` is used to be able to minimize the number of `var` keywords. + var i = 0, + f = 0, + + // Use `encodeURI` to UTF8 encode the message without any additional libraries + // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky + // since `unescape` is deprecated. + urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding + + // This can be changed to a preallocated Uint32Array array for greater performance and larger code size + data = [], + dataSize, + + hashBuffer = [], + + a = 0x67452301, + b = 0xefcdab89, + c = ~a, + d = ~b, + e = 0xc3d2e1f0, + hash = [a, b, c, d, e], + + blockStartIndex = 0, + hexHash = ""; + + /** + * Rotates the value a specified number of bits to the left. + * @param {number} value Value to rotate + * @param {number} shift Bit count to shift. + */ + function rotl(value, shift) { + return (value << shift) | (value >>> (32 - shift)); + } + + // Message data + for ( ; i < urlEncodedMessage.length; f++) { + data[f >> 2] = data[f >> 2] | + ( + ( + urlEncodedMessage[i] == "%" + // Percent encoded byte + ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16) + // Unencoded byte + : urlEncodedMessage.charCodeAt(i++) + ) + + // Read bytes in reverse order (big endian words) + << ((3 - (f & 3)) * 8) + ); + } + + // f is now the length of the utf8 encoded message + // 7 = 8 bytes (64 bit) for message size, -1 to round down + // >> 6 = integer division with block size + dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS; + + // Message size in bits. + // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least + // significant 32 bits are set. -8 is for the '1' bit padding byte. + data[dataSize - 1] = f * 8 - 8; + + // Compute hash + for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) { + for (i = 0; i < 80; i++) { + f = rotl(a, 5) + e + ( + // Ch + i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 : + + // Parity + i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 : + + // Maj + i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc : + + // Parity + (b ^ c ^ d) + 0xca62c1d6 + ) + ( + hashBuffer[i] = i < BLOCK_SIZE_WORDS + // Bitwise OR is used to coerse `undefined` to 0 + ? (data[blockStartIndex + i] | 0) + : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1) + ); + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = f; + } + + hash[0] = a = ((hash[0] + a) | 0); + hash[1] = b = ((hash[1] + b) | 0); + hash[2] = c = ((hash[2] + c) | 0); + hash[3] = d = ((hash[3] + d) | 0); + hash[4] = e = ((hash[4] + e) | 0); + } + + // Format hex hash + for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) { + hexHash += ( + ( + // Get word (2^3 half-bytes per word) + hash[i >> 3] >>> + + // Append half-bytes in reverse order + ((7 - (i & 7)) * 4) + ) + // Clamp to half-byte + & 0xf + ).toString(16); + } + + return hexHash; +} + +/** + * Inputs a value that might be a valid hash string for Jdenticon and returns it + * if it is determined valid, otherwise a falsy value is returned. + */ +function isValidHash(hashCandidate) { + return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate; +} + +/** + * Computes a hash for the specified value. Currently SHA1 is used. This function + * always returns a valid hash. + */ +function computeHash(value) { + return sha1(value == null ? "" : "" + value); +} + + + +/** + * Renderer redirecting drawing commands to a canvas context. + * @implements {Renderer} + */ +class CanvasRenderer { + /** + * @param {number=} iconSize + */ + constructor(ctx, iconSize) { + const canvas = ctx.canvas; + const width = canvas.width; + const height = canvas.height; + + ctx.save(); + + if (!iconSize) { + iconSize = Math.min(width, height); + + ctx.translate( + ((width - iconSize) / 2) | 0, + ((height - iconSize) / 2) | 0); + } + + /** + * @private + */ + this._ctx = ctx; + this.iconSize = iconSize; + + ctx.clearRect(0, 0, iconSize, iconSize); + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + setBackground(fillColor) { + const ctx = this._ctx; + const iconSize = this.iconSize; + + ctx.fillStyle = toCss3Color(fillColor); + ctx.fillRect(0, 0, iconSize, iconSize); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} fillColor Fill color on format #rrggbb[aa]. + */ + beginShape(fillColor) { + const ctx = this._ctx; + ctx.fillStyle = toCss3Color(fillColor); + ctx.beginPath(); + } + + /** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ + endShape() { + this._ctx.fill(); + } + + /** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ + addPolygon(points) { + const ctx = this._ctx; + ctx.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); + } + + /** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + const ctx = this._ctx, + radius = diameter / 2; + ctx.moveTo(point.x + radius, point.y + radius); + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + this._ctx.restore(); + } +} + +const IS_RENDERED_PROPERTY = "jdenticonRendered"; + +/** @type {!Function} */ ( + typeof document !== "undefined" && document.querySelectorAll.bind(document)); + +/** + * Draws an identicon to a context. + * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0). + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function drawIcon(ctx, hashOrValue, size, config) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + const canvas = ctx.canvas; + if (canvas) { + canvas[IS_RENDERED_PROPERTY] = true; + } +} + +/** + * Draws an identicon as PNG. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {Buffer} PNG data + */ +function toPng(hashOrValue, size, config) { + const canvas = canvasRenderer__default["default"].createCanvas(size, size); + const ctx = canvas.getContext("2d"); + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + return canvas.toPng({ "Software": "Jdenticon" }); +} + +/** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ +function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; +} + +/** + * Represents an SVG path element. + */ +class SvgPath { + constructor() { + /** + * This property holds the data string (path.d) of the SVG path. + * @type {string} + */ + this.dataString = ""; + } + + /** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ + addPolygon(points) { + let dataString = ""; + for (let i = 0; i < points.length; i++) { + dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.dataString += dataString + "Z"; + } + + /** + * Adds a circle with the current fill color to the SVG path. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + const sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter), + svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " "; + + this.dataString += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + svgArc + svgDiameter + ",0" + + svgArc + (-svgDiameter) + ",0"; + } +} + + + +/** + * Renderer producing SVG output. + * @implements {Renderer} + */ +class SvgRenderer { + /** + * @param {SvgElement|SvgWriter} target + */ + constructor(target) { + /** + * @type {SvgPath} + * @private + */ + this._path; + + /** + * @type {Object.} + * @private + */ + this._pathsByColor = { }; + + /** + * @type {SvgElement|SvgWriter} + * @private + */ + this._target = target; + + /** + * @type {number} + */ + this.iconSize = target.iconSize; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + setBackground(fillColor) { + const match = /^(#......)(..)?/.exec(fillColor), + opacity = match[2] ? parseHex(match[2], 0) / 255 : 1; + this._target.setBackground(match[1], opacity); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ + beginShape(color) { + this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath()); + } + + /** + * Marks the end of the currently drawn shape. + */ + endShape() { } + + /** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ + addPolygon(points) { + this._path.addPolygon(points); + } + + /** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + this._path.addCircle(point, diameter, counterClockwise); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + const pathsByColor = this._pathsByColor; + for (let color in pathsByColor) { + // hasOwnProperty cannot be shadowed in pathsByColor + // eslint-disable-next-line no-prototype-builtins + if (pathsByColor.hasOwnProperty(color)) { + this._target.appendPath(color, pathsByColor[color].dataString); + } + } + } +} + +const SVG_CONSTANTS = { + XMLNS: "http://www.w3.org/2000/svg", + WIDTH: "width", + HEIGHT: "height", +}; + +/** + * Renderer producing SVG output. + */ +class SvgWriter { + /** + * @param {number} iconSize - Icon width and height in pixels. + */ + constructor(iconSize) { + /** + * @type {number} + */ + this.iconSize = iconSize; + + /** + * @type {string} + * @private + */ + this._s = + ''; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ + setBackground(fillColor, opacity) { + if (opacity) { + this._s += ''; + } + } + + /** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #rrggbb. + * @param {string} dataString The SVG path data string. + */ + appendPath(color, dataString) { + this._s += ''; + } + + /** + * Gets the rendered image as an SVG string. + */ + toString() { + return this._s + ""; + } +} + +/** + * Draws an identicon as an SVG string. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {string} SVG string + */ +function toSvg(hashOrValue, size, config) { + const writer = new SvgWriter(size); + iconGenerator(new SvgRenderer(writer), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + return writer.toString(); +} + +// This file is compiled to dist/jdenticon-node.js + +if (typeof process === "undefined" && + typeof window !== "undefined" && + typeof document !== "undefined" +) { + console.warn( + "Jdenticon: 'dist/jdenticon-node.js' is only intended for Node.js environments and will increase your " + + "bundle size when included in browser bundles. If you want to run Jdenticon in the browser, please add a " + + "reference to 'dist/jdenticon.js' or 'dist/jdenticon.min.js' instead."); +} + +/** + * @throws {Error} + */ +function jdenticon() { + throw new Error("jdenticon() is not supported on Node.js."); +} + +defineConfigPropertyWithWarn(jdenticon); + +jdenticon.configure = configure; +jdenticon.drawIcon = drawIcon; +jdenticon.toPng = toPng; +jdenticon.toSvg = toSvg; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +jdenticon.version = "3.3.0"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +jdenticon.bundle = "node-cjs"; + +/** + * @throws {Error} + */ +jdenticon.update = function update() { + throw new Error("jdenticon.update() is not supported on Node.js."); +}; + +/** + * @throws {Error} + */ +jdenticon.updateCanvas = function updateCanvas() { + throw new Error("jdenticon.updateCanvas() is not supported on Node.js."); +}; + +/** + * @throws {Error} + */ +jdenticon.updateSvg = function updateSvg() { + throw new Error("jdenticon.updateSvg() is not supported on Node.js."); +}; + +module.exports = jdenticon; +//# sourceMappingURL=jdenticon-node.js.map diff --git a/jdenticon-js/dist/jdenticon-node.js.map b/jdenticon-js/dist/jdenticon-node.js.map new file mode 100644 index 0000000..19dadac --- /dev/null +++ b/jdenticon-js/dist/jdenticon-node.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["replacement/1","src/common/parseHex.js","src/renderer/color.js","src/common/global.js","src/common/configuration.js","src/renderer/point.js","src/renderer/transform.js","src/renderer/graphics.js","src/renderer/shapes.js","src/renderer/colorTheme.js","src/renderer/iconGenerator.js","src/common/sha1.js","src/common/hashUtils.js","src/renderer/canvas/canvasRenderer.js","src/common/dom.js","src/apis/drawIcon.js","src/apis/toPng.js","src/renderer/svg/svgPath.js","src/renderer/svg/svgRenderer.js","src/renderer/svg/constants.js","src/renderer/svg/svgWriter.js","src/apis/toSvg.js","src/node-cjs.js","replacement/2"],"names":["canvasRenderer"],"mappings":"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","file":"jdenticon-node.js","sourcesContent":["/**\r\n * Jdenticon 3.3.0\r\n * http://jdenticon.com\r\n *\r\n * Built: 2024-05-10T09:48:41.921Z\r\n * \r\n * MIT License\r\n * \r\n * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi\r\n * \r\n * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n * of this software and associated documentation files (the \"Software\"), to deal\r\n * in the Software without restriction, including without limitation the rights\r\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the Software is\r\n * furnished to do so, subject to the following conditions:\r\n * \r\n * The above copyright notice and this permission notice shall be included in all\r\n * copies or substantial portions of the Software.\r\n * \r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n * SOFTWARE.\r\n */\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Parses a substring of the hash as a number.\r\n * @param {number} startPosition \r\n * @param {number=} octets\r\n */\r\nexport function parseHex(hash, startPosition, octets) {\r\n return parseInt(hash.substr(startPosition, octets), 16);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseHex } from \"../common/parseHex\";\r\n\r\nfunction decToHex(v) {\r\n v |= 0; // Ensure integer value\r\n return v < 0 ? \"00\" :\r\n v < 16 ? \"0\" + v.toString(16) :\r\n v < 256 ? v.toString(16) :\r\n \"ff\";\r\n}\r\n\r\nfunction hueToRgb(m1, m2, h) {\r\n h = h < 0 ? h + 6 : h > 6 ? h - 6 : h;\r\n return decToHex(255 * (\r\n h < 1 ? m1 + (m2 - m1) * h :\r\n h < 3 ? m2 :\r\n h < 4 ? m1 + (m2 - m1) * (4 - h) :\r\n m1));\r\n}\r\n\r\n/**\r\n * @param {number} r Red channel [0, 255]\r\n * @param {number} g Green channel [0, 255]\r\n * @param {number} b Blue channel [0, 255]\r\n */\r\nexport function rgb(r, g, b) {\r\n return \"#\" + decToHex(r) + decToHex(g) + decToHex(b);\r\n}\r\n\r\n/**\r\n * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported.\r\n * @returns {string}\r\n */\r\nexport function parseColor(color) {\r\n if (/^#[0-9a-f]{3,8}$/i.test(color)) {\r\n let result;\r\n const colorLength = color.length;\r\n\r\n if (colorLength < 6) {\r\n const r = color[1],\r\n g = color[2],\r\n b = color[3],\r\n a = color[4] || \"\";\r\n result = \"#\" + r + r + g + g + b + b + a + a;\r\n }\r\n if (colorLength == 7 || colorLength > 8) {\r\n result = color;\r\n }\r\n \r\n return result;\r\n }\r\n}\r\n\r\n/**\r\n * Converts a hexadecimal color to a CSS3 compatible color.\r\n * @param {string} hexColor Color on the format \"#RRGGBB\" or \"#RRGGBBAA\"\r\n * @returns {string}\r\n */\r\nexport function toCss3Color(hexColor) {\r\n const a = parseHex(hexColor, 7, 2);\r\n let result;\r\n\r\n if (isNaN(a)) {\r\n result = hexColor;\r\n } else {\r\n const r = parseHex(hexColor, 1, 2),\r\n g = parseHex(hexColor, 3, 2),\r\n b = parseHex(hexColor, 5, 2);\r\n result = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + (a / 255).toFixed(2) + \")\";\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color.\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function hsl(hue, saturation, lightness) {\r\n // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color\r\n let result;\r\n\r\n if (saturation == 0) {\r\n const partialHex = decToHex(lightness * 255);\r\n result = partialHex + partialHex + partialHex;\r\n }\r\n else {\r\n const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation,\r\n m1 = lightness * 2 - m2;\r\n result =\r\n hueToRgb(m1, m2, hue * 6 + 2) +\r\n hueToRgb(m1, m2, hue * 6) +\r\n hueToRgb(m1, m2, hue * 6 - 2);\r\n }\r\n\r\n return \"#\" + result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the \"dark\" hues\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function correctedHsl(hue, saturation, lightness) {\r\n // The corrector specifies the perceived middle lightness for each hue\r\n const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ],\r\n corrector = correctors[(hue * 6 + 0.5) | 0];\r\n \r\n // Adjust the input lightness relative to the corrector\r\n lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2;\r\n \r\n return hsl(hue, saturation, lightness);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for\r\n// backward compatibility.\r\n\r\nexport const GLOBAL = \r\n typeof window !== \"undefined\" ? window :\r\n typeof self !== \"undefined\" ? self :\r\n typeof global !== \"undefined\" ? global :\r\n {};\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseColor } from \"../renderer/color\";\r\nimport { GLOBAL } from \"./global\";\r\n\r\n/**\r\n * @typedef {Object} ParsedConfiguration\r\n * @property {number} colorSaturation\r\n * @property {number} grayscaleSaturation\r\n * @property {string} backColor\r\n * @property {number} iconPadding\r\n * @property {function(number):number} hue\r\n * @property {function(number):number} colorLightness\r\n * @property {function(number):number} grayscaleLightness\r\n */\r\n\r\nexport const CONFIG_PROPERTIES = {\r\n GLOBAL: \"jdenticon_config\",\r\n MODULE: \"config\",\r\n};\r\n\r\nvar rootConfigurationHolder = {};\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is \r\n * printed in the console. To minimize bundle size, this is only used in Node bundles.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigPropertyWithWarn(rootObject) {\r\n Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, {\r\n configurable: true,\r\n get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE],\r\n set: newConfiguration => {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n console.warn(\"jdenticon.config is deprecated. Use jdenticon.configure() instead.\");\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console\r\n * when it is being used.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigProperty(rootObject) {\r\n rootConfigurationHolder = rootObject;\r\n}\r\n\r\n/**\r\n * Sets a new icon style configuration. The new configuration is not merged with the previous one. * \r\n * @param {Object} newConfiguration - New configuration object.\r\n */\r\nexport function configure(newConfiguration) {\r\n if (arguments.length) {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n }\r\n return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE];\r\n}\r\n\r\n/**\r\n * Gets the normalized current Jdenticon color configuration. Missing fields have default values.\r\n * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A\r\n * local configuration overrides the global configuration in it entirety. This parameter can for backward\r\n * compatibility also contain a padding value. A padding value only overrides the global padding, not the\r\n * entire global configuration.\r\n * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor\r\n * explicitly to the API method.\r\n * @returns {ParsedConfiguration}\r\n */\r\nexport function getConfiguration(paddingOrLocalConfig, defaultPadding) {\r\n const configObject = \r\n typeof paddingOrLocalConfig == \"object\" && paddingOrLocalConfig ||\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] ||\r\n GLOBAL[CONFIG_PROPERTIES.GLOBAL] ||\r\n { },\r\n\r\n lightnessConfig = configObject[\"lightness\"] || { },\r\n \r\n // In versions < 2.1.0 there was no grayscale saturation -\r\n // saturation was the color saturation.\r\n saturation = configObject[\"saturation\"] || { },\r\n colorSaturation = \"color\" in saturation ? saturation[\"color\"] : saturation,\r\n grayscaleSaturation = saturation[\"grayscale\"],\r\n\r\n backColor = configObject[\"backColor\"],\r\n padding = configObject[\"padding\"];\r\n \r\n /**\r\n * Creates a lightness range.\r\n */\r\n function lightness(configName, defaultRange) {\r\n let range = lightnessConfig[configName];\r\n \r\n // Check if the lightness range is an array-like object. This way we ensure the\r\n // array contain two values at the same time.\r\n if (!(range && range.length > 1)) {\r\n range = defaultRange;\r\n }\r\n\r\n /**\r\n * Gets a lightness relative the specified value in the specified lightness range.\r\n */\r\n return function (value) {\r\n value = range[0] + value * (range[1] - range[0]);\r\n return value < 0 ? 0 : value > 1 ? 1 : value;\r\n };\r\n }\r\n\r\n /**\r\n * Gets a hue allowed by the configured hue restriction,\r\n * provided the originally computed hue.\r\n */\r\n function hueFunction(originalHue) {\r\n const hueConfig = configObject[\"hues\"];\r\n let hue;\r\n \r\n // Check if 'hues' is an array-like object. This way we also ensure that\r\n // the array is not empty, which would mean no hue restriction.\r\n if (hueConfig && hueConfig.length > 0) {\r\n // originalHue is in the range [0, 1]\r\n // Multiply with 0.999 to change the range to [0, 1) and then truncate the index.\r\n hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)];\r\n }\r\n\r\n return typeof hue == \"number\" ?\r\n \r\n // A hue was specified. We need to convert the hue from\r\n // degrees on any turn - e.g. 746° is a perfectly valid hue -\r\n // to turns in the range [0, 1).\r\n ((((hue / 360) % 1) + 1) % 1) :\r\n\r\n // No hue configured => use original hue\r\n originalHue;\r\n }\r\n \r\n return {\r\n hue: hueFunction,\r\n colorSaturation: typeof colorSaturation == \"number\" ? colorSaturation : 0.5,\r\n grayscaleSaturation: typeof grayscaleSaturation == \"number\" ? grayscaleSaturation : 0,\r\n colorLightness: lightness(\"color\", [0.4, 0.8]),\r\n grayscaleLightness: lightness(\"grayscale\", [0.3, 0.9]),\r\n backColor: parseColor(backColor),\r\n iconPadding: \r\n typeof paddingOrLocalConfig == \"number\" ? paddingOrLocalConfig : \r\n typeof padding == \"number\" ? padding : \r\n defaultPadding\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Represents a point.\r\n */\r\nexport class Point {\r\n /**\r\n * @param {number} x \r\n * @param {number} y \r\n */\r\n constructor(x, y) {\r\n this.x = x;\r\n this.y = y;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Point } from \"./point\";\r\n\r\n/**\r\n * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, \r\n * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly.\r\n */\r\nexport class Transform {\r\n /**\r\n * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} size The size of the transformed rectangle.\r\n * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad\r\n */\r\n constructor(x, y, size, rotation) {\r\n this._x = x;\r\n this._y = y;\r\n this._size = size;\r\n this._rotation = rotation;\r\n }\r\n\r\n /**\r\n * Transforms the specified point based on the translation and rotation specification for this Transform.\r\n * @param {number} x x-coordinate\r\n * @param {number} y y-coordinate\r\n * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n */\r\n transformIconPoint(x, y, w, h) {\r\n const right = this._x + this._size,\r\n bottom = this._y + this._size,\r\n rotation = this._rotation;\r\n return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) :\r\n rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) :\r\n rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) :\r\n new Point(this._x + x, this._y + y);\r\n }\r\n}\r\n\r\nexport const NO_TRANSFORM = new Transform(0, 0, 0, 0);\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { NO_TRANSFORM } from \"./transform\";\r\n\r\n/**\r\n * @typedef {import(\"./renderer\").Renderer} Renderer\r\n * @typedef {import(\"./transform\").Transform} Transform\r\n */\r\n\r\n/**\r\n * Provides helper functions for rendering common basic shapes.\r\n */\r\nexport class Graphics {\r\n /**\r\n * @param {Renderer} renderer \r\n */\r\n constructor(renderer) {\r\n /**\r\n * @type {Renderer}\r\n * @private\r\n */\r\n this._renderer = renderer;\r\n\r\n /**\r\n * @type {Transform}\r\n */\r\n this.currentTransform = NO_TRANSFORM;\r\n }\r\n\r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ]\r\n * @param {boolean=} invert Specifies if the polygon will be inverted.\r\n */\r\n addPolygon(points, invert) {\r\n const di = invert ? -2 : 2,\r\n transformedPoints = [];\r\n \r\n for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) {\r\n transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1]));\r\n }\r\n \r\n this._renderer.addPolygon(transformedPoints);\r\n }\r\n \r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * Source: http://stackoverflow.com/a/2173084\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} size The size of the ellipse.\r\n * @param {boolean=} invert Specifies if the ellipse will be inverted.\r\n */\r\n addCircle(x, y, size, invert) {\r\n const p = this.currentTransform.transformIconPoint(x, y, size, size);\r\n this._renderer.addCircle(p, size, invert);\r\n }\r\n\r\n /**\r\n * Adds a rectangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle.\r\n * @param {number} w The width of the rectangle.\r\n * @param {number} h The height of the rectangle.\r\n * @param {boolean=} invert Specifies if the rectangle will be inverted.\r\n */\r\n addRectangle(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x, y, \r\n x + w, y,\r\n x + w, y + h,\r\n x, y + h\r\n ], invert);\r\n }\r\n\r\n /**\r\n * Adds a right triangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} w The width of the triangle.\r\n * @param {number} h The height of the triangle.\r\n * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle.\r\n * @param {boolean=} invert Specifies if the triangle will be inverted.\r\n */\r\n addTriangle(x, y, w, h, r, invert) {\r\n const points = [\r\n x + w, y, \r\n x + w, y + h, \r\n x, y + h,\r\n x, y\r\n ];\r\n points.splice(((r || 0) % 4) * 2, 2);\r\n this.addPolygon(points, invert);\r\n }\r\n\r\n /**\r\n * Adds a rhombus to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} w The width of the rhombus.\r\n * @param {number} h The height of the rhombus.\r\n * @param {boolean=} invert Specifies if the rhombus will be inverted.\r\n */\r\n addRhombus(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x + w / 2, y,\r\n x + w, y + h / 2,\r\n x + w / 2, y + h,\r\n x, y + h / 2\r\n ], invert);\r\n }\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n * @param {number} positionIndex\r\n * @typedef {import('./graphics').Graphics} Graphics\r\n */\r\nexport function centerShape(index, g, cell, positionIndex) {\r\n index = index % 14;\r\n\r\n let k, m, w, h, inner, outer;\r\n\r\n !index ? (\r\n k = cell * 0.42,\r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell - k * 2,\r\n cell - k, cell,\r\n 0, cell\r\n ])) :\r\n\r\n index == 1 ? (\r\n w = 0 | (cell * 0.5), \r\n h = 0 | (cell * 0.8),\r\n\r\n g.addTriangle(cell - w, 0, w, h, 2)) :\r\n\r\n index == 2 ? (\r\n w = 0 | (cell / 3),\r\n g.addRectangle(w, w, cell - w, cell - w)) :\r\n\r\n index == 3 ? (\r\n inner = cell * 0.1,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 6 ? 1 :\r\n cell < 8 ? 2 :\r\n (0 | (cell * 0.25)),\r\n \r\n inner = \r\n inner > 1 ? (0 | inner) : // large icon => truncate decimals\r\n inner > 0.5 ? 1 : // medium size icon => fixed width\r\n inner, // small icon => anti-aliased border\r\n\r\n g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) :\r\n\r\n index == 4 ? (\r\n m = 0 | (cell * 0.15),\r\n w = 0 | (cell * 0.5),\r\n g.addCircle(cell - w - m, cell - w - m, w)) :\r\n\r\n index == 5 ? (\r\n inner = cell * 0.1,\r\n outer = inner * 4,\r\n\r\n // Align edge to nearest pixel in large icons\r\n outer > 3 && (outer = 0 | outer),\r\n \r\n g.addRectangle(0, 0, cell, cell),\r\n g.addPolygon([\r\n outer, outer,\r\n cell - inner, outer,\r\n outer + (cell - outer - inner) / 2, cell - inner\r\n ], true)) :\r\n\r\n index == 6 ? \r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell * 0.7,\r\n cell * 0.4, cell * 0.4,\r\n cell * 0.7, cell,\r\n 0, cell\r\n ]) :\r\n\r\n index == 7 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 8 ? (\r\n g.addRectangle(0, 0, cell, cell / 2),\r\n g.addRectangle(0, cell / 2, cell / 2, cell / 2),\r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) :\r\n\r\n index == 9 ? (\r\n inner = cell * 0.14,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 4 ? 1 :\r\n cell < 6 ? 2 :\r\n (0 | (cell * 0.35)),\r\n\r\n inner = \r\n cell < 8 ? inner : // small icon => anti-aliased border\r\n (0 | inner), // large icon => truncate decimals\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) :\r\n\r\n index == 10 ? (\r\n inner = cell * 0.12,\r\n outer = inner * 3,\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addCircle(outer, outer, cell - inner - outer, true)) :\r\n\r\n index == 11 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 12 ? (\r\n m = cell * 0.25,\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRhombus(m, m, cell - m, cell - m, true)) :\r\n\r\n // 13\r\n (\r\n !positionIndex && (\r\n m = cell * 0.4, w = cell * 1.2,\r\n g.addCircle(m, m, w)\r\n )\r\n );\r\n}\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n */\r\nexport function outerShape(index, g, cell) {\r\n index = index % 4;\r\n\r\n let m;\r\n\r\n !index ?\r\n g.addTriangle(0, 0, cell, cell, 0) :\r\n \r\n index == 1 ?\r\n g.addTriangle(0, cell / 2, cell, cell / 2, 0) :\r\n\r\n index == 2 ?\r\n g.addRhombus(0, 0, cell, cell) :\r\n\r\n // 3\r\n (\r\n m = cell / 6,\r\n g.addCircle(m, m, cell - 2 * m)\r\n );\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { correctedHsl } from \"./color\";\r\n\r\n/**\r\n * Gets a set of identicon color candidates for a specified hue and config.\r\n * @param {number} hue\r\n * @param {import(\"../common/configuration\").ParsedConfiguration} config\r\n */\r\nexport function colorTheme(hue, config) {\r\n hue = config.hue(hue);\r\n return [\r\n // Dark gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)),\r\n // Mid color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)),\r\n // Light gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)),\r\n // Light color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(1)),\r\n // Dark color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0))\r\n ];\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Transform } from \"./transform\";\r\nimport { Graphics } from \"./graphics\";\r\nimport { centerShape, outerShape } from \"./shapes\";\r\nimport { colorTheme } from \"./colorTheme\";\r\nimport { parseHex } from \"../common/parseHex\";\r\nimport { getConfiguration } from \"../common/configuration\";\r\n \r\n/**\r\n * Draws an identicon to a specified renderer.\r\n * @param {import('./renderer').Renderer} renderer\r\n * @param {string} hash\r\n * @param {Object|number=} config\r\n */\r\nexport function iconGenerator(renderer, hash, config) {\r\n const parsedConfig = getConfiguration(config, 0.08);\r\n\r\n // Set background color\r\n if (parsedConfig.backColor) {\r\n renderer.setBackground(parsedConfig.backColor);\r\n }\r\n \r\n // Calculate padding and round to nearest integer\r\n let size = renderer.iconSize;\r\n const padding = (0.5 + size * parsedConfig.iconPadding) | 0;\r\n size -= padding * 2;\r\n \r\n const graphics = new Graphics(renderer);\r\n \r\n // Calculate cell size and ensure it is an integer\r\n const cell = 0 | (size / 4);\r\n \r\n // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon\r\n const x = 0 | (padding + size / 2 - cell * 2);\r\n const y = 0 | (padding + size / 2 - cell * 2);\r\n\r\n function renderShape(colorIndex, shapes, index, rotationIndex, positions) {\r\n const shapeIndex = parseHex(hash, index, 1);\r\n let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0;\r\n \r\n renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]);\r\n \r\n for (let i = 0; i < positions.length; i++) {\r\n graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4);\r\n shapes(shapeIndex, graphics, cell, i);\r\n }\r\n \r\n renderer.endShape();\r\n }\r\n\r\n // AVAILABLE COLORS\r\n const hue = parseHex(hash, -7) / 0xfffffff,\r\n \r\n // Available colors for this icon\r\n availableColors = colorTheme(hue, parsedConfig),\r\n\r\n // The index of the selected colors\r\n selectedColorIndexes = [];\r\n\r\n let index;\r\n\r\n function isDuplicate(values) {\r\n if (values.indexOf(index) >= 0) {\r\n for (let i = 0; i < values.length; i++) {\r\n if (selectedColorIndexes.indexOf(values[i]) >= 0) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < 3; i++) {\r\n index = parseHex(hash, 8 + i, 1) % availableColors.length;\r\n if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo\r\n isDuplicate([2, 3])) { // Disallow light gray and light color combo\r\n index = 1;\r\n }\r\n selectedColorIndexes.push(index);\r\n }\r\n\r\n // ACTUAL RENDERING\r\n // Sides\r\n renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]);\r\n // Corners\r\n renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]);\r\n // Center\r\n renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]);\r\n \r\n renderer.finish();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Computes a SHA1 hash for any value and returns it as a hexadecimal string.\r\n * \r\n * This function is optimized for minimal code size and rather short messages.\r\n * \r\n * @param {string} message \r\n */\r\nexport function sha1(message) {\r\n const HASH_SIZE_HALF_BYTES = 40;\r\n const BLOCK_SIZE_WORDS = 16;\r\n\r\n // Variables\r\n // `var` is used to be able to minimize the number of `var` keywords.\r\n var i = 0,\r\n f = 0,\r\n \r\n // Use `encodeURI` to UTF8 encode the message without any additional libraries\r\n // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky\r\n // since `unescape` is deprecated.\r\n urlEncodedMessage = encodeURI(message) + \"%80\", // trailing '1' bit padding\r\n \r\n // This can be changed to a preallocated Uint32Array array for greater performance and larger code size\r\n data = [],\r\n dataSize,\r\n \r\n hashBuffer = [],\r\n\r\n a = 0x67452301,\r\n b = 0xefcdab89,\r\n c = ~a,\r\n d = ~b,\r\n e = 0xc3d2e1f0,\r\n hash = [a, b, c, d, e],\r\n\r\n blockStartIndex = 0,\r\n hexHash = \"\";\r\n\r\n /**\r\n * Rotates the value a specified number of bits to the left.\r\n * @param {number} value Value to rotate\r\n * @param {number} shift Bit count to shift.\r\n */\r\n function rotl(value, shift) {\r\n return (value << shift) | (value >>> (32 - shift));\r\n }\r\n\r\n // Message data\r\n for ( ; i < urlEncodedMessage.length; f++) {\r\n data[f >> 2] = data[f >> 2] |\r\n (\r\n (\r\n urlEncodedMessage[i] == \"%\"\r\n // Percent encoded byte\r\n ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)\r\n // Unencoded byte\r\n : urlEncodedMessage.charCodeAt(i++)\r\n )\r\n\r\n // Read bytes in reverse order (big endian words)\r\n << ((3 - (f & 3)) * 8)\r\n );\r\n }\r\n\r\n // f is now the length of the utf8 encoded message\r\n // 7 = 8 bytes (64 bit) for message size, -1 to round down\r\n // >> 6 = integer division with block size\r\n dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;\r\n\r\n // Message size in bits.\r\n // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least\r\n // significant 32 bits are set. -8 is for the '1' bit padding byte.\r\n data[dataSize - 1] = f * 8 - 8;\r\n \r\n // Compute hash\r\n for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {\r\n for (i = 0; i < 80; i++) {\r\n f = rotl(a, 5) + e + (\r\n // Ch\r\n i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :\r\n \r\n // Parity\r\n i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :\r\n \r\n // Maj\r\n i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :\r\n \r\n // Parity\r\n (b ^ c ^ d) + 0xca62c1d6\r\n ) + ( \r\n hashBuffer[i] = i < BLOCK_SIZE_WORDS\r\n // Bitwise OR is used to coerse `undefined` to 0\r\n ? (data[blockStartIndex + i] | 0)\r\n : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)\r\n );\r\n\r\n e = d;\r\n d = c;\r\n c = rotl(b, 30);\r\n b = a;\r\n a = f;\r\n }\r\n\r\n hash[0] = a = ((hash[0] + a) | 0);\r\n hash[1] = b = ((hash[1] + b) | 0);\r\n hash[2] = c = ((hash[2] + c) | 0);\r\n hash[3] = d = ((hash[3] + d) | 0);\r\n hash[4] = e = ((hash[4] + e) | 0);\r\n }\r\n\r\n // Format hex hash\r\n for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {\r\n hexHash += (\r\n (\r\n // Get word (2^3 half-bytes per word)\r\n hash[i >> 3] >>>\r\n\r\n // Append half-bytes in reverse order\r\n ((7 - (i & 7)) * 4)\r\n ) \r\n // Clamp to half-byte\r\n & 0xf\r\n ).toString(16);\r\n }\r\n\r\n return hexHash;\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { sha1 } from \"./sha1\";\r\n\r\n/**\r\n * Inputs a value that might be a valid hash string for Jdenticon and returns it \r\n * if it is determined valid, otherwise a falsy value is returned.\r\n */\r\nexport function isValidHash(hashCandidate) {\r\n return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate;\r\n}\r\n\r\n/**\r\n * Computes a hash for the specified value. Currently SHA1 is used. This function\r\n * always returns a valid hash.\r\n */\r\nexport function computeHash(value) {\r\n return sha1(value == null ? \"\" : \"\" + value);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { toCss3Color } from \"../color\";\r\n\r\n/**\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import('../point').Point} Point\r\n */\r\n\r\n/**\r\n * Renderer redirecting drawing commands to a canvas context.\r\n * @implements {Renderer}\r\n */\r\nexport class CanvasRenderer {\r\n /**\r\n * @param {number=} iconSize\r\n */\r\n constructor(ctx, iconSize) {\r\n const canvas = ctx.canvas; \r\n const width = canvas.width;\r\n const height = canvas.height;\r\n \r\n ctx.save();\r\n \r\n if (!iconSize) {\r\n iconSize = Math.min(width, height);\r\n \r\n ctx.translate(\r\n ((width - iconSize) / 2) | 0,\r\n ((height - iconSize) / 2) | 0);\r\n }\r\n\r\n /**\r\n * @private\r\n */\r\n this._ctx = ctx;\r\n this.iconSize = iconSize;\r\n \r\n ctx.clearRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const ctx = this._ctx;\r\n const iconSize = this.iconSize;\r\n\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.fillRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} fillColor Fill color on format #rrggbb[aa].\r\n */\r\n beginShape(fillColor) {\r\n const ctx = this._ctx;\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.beginPath();\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas.\r\n */\r\n endShape() {\r\n this._ctx.fill();\r\n }\r\n\r\n /**\r\n * Adds a polygon to the rendering queue.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n const ctx = this._ctx;\r\n ctx.moveTo(points[0].x, points[0].y);\r\n for (let i = 1; i < points.length; i++) {\r\n ctx.lineTo(points[i].x, points[i].y);\r\n }\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Adds a circle to the rendering queue.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const ctx = this._ctx,\r\n radius = diameter / 2;\r\n ctx.moveTo(point.x + radius, point.y + radius);\r\n ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise);\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() {\r\n this._ctx.restore();\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const ICON_TYPE_SVG = 1;\r\n\r\nexport const ICON_TYPE_CANVAS = 2;\r\n\r\nexport const ATTRIBUTES = {\r\n HASH: \"data-jdenticon-hash\",\r\n VALUE: \"data-jdenticon-value\"\r\n};\r\n\r\nexport const IS_RENDERED_PROPERTY = \"jdenticonRendered\";\r\n\r\nexport const ICON_SELECTOR = \"[\" + ATTRIBUTES.HASH +\"],[\" + ATTRIBUTES.VALUE +\"]\";\r\n\r\nexport const documentQuerySelectorAll = /** @type {!Function} */ (\r\n typeof document !== \"undefined\" && document.querySelectorAll.bind(document));\r\n\r\nexport function getIdenticonType(el) {\r\n if (el) {\r\n const tagName = el[\"tagName\"];\r\n\r\n if (/^svg$/i.test(tagName)) {\r\n return ICON_TYPE_SVG;\r\n }\r\n\r\n if (/^canvas$/i.test(tagName) && \"getContext\" in el) {\r\n return ICON_TYPE_CANVAS;\r\n }\r\n }\r\n}\r\n\r\nexport function whenDocumentIsReady(/** @type {Function} */ callback) {\r\n function loadedHandler() {\r\n document.removeEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.removeEventListener(\"load\", loadedHandler);\r\n setTimeout(callback, 0); // Give scripts a chance to run\r\n }\r\n \r\n if (typeof document !== \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof setTimeout !== \"undefined\"\r\n ) {\r\n if (document.readyState === \"loading\") {\r\n document.addEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.addEventListener(\"load\", loadedHandler);\r\n } else {\r\n // Document already loaded. The load events above likely won't be raised\r\n setTimeout(callback, 0);\r\n }\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { IS_RENDERED_PROPERTY } from \"../common/dom\";\r\n\r\n/**\r\n * Draws an identicon to a context.\r\n * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0).\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function drawIcon(ctx, hashOrValue, size, config) {\r\n if (!ctx) {\r\n throw new Error(\"No canvas specified.\");\r\n }\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n\r\n const canvas = ctx.canvas;\r\n if (canvas) {\r\n canvas[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","import canvasRenderer from \"canvas-renderer\";\r\nimport { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\n\r\n/**\r\n * Draws an identicon as PNG.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {Buffer} PNG data\r\n */\r\nexport function toPng(hashOrValue, size, config) {\r\n const canvas = canvasRenderer.createCanvas(size, size);\r\n const ctx = canvas.getContext(\"2d\");\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n \r\n return canvas.toPng({ \"Software\": \"Jdenticon\" });\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Prepares a measure to be used as a measure in an SVG path, by\r\n * rounding the measure to a single decimal. This reduces the file\r\n * size of the generated SVG with more than 50% in some cases.\r\n */\r\nfunction svgValue(value) {\r\n return ((value * 10 + 0.5) | 0) / 10;\r\n}\r\n\r\n/**\r\n * Represents an SVG path element.\r\n */\r\nexport class SvgPath {\r\n constructor() {\r\n /**\r\n * This property holds the data string (path.d) of the SVG path.\r\n * @type {string}\r\n */\r\n this.dataString = \"\";\r\n }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG path.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n let dataString = \"\";\r\n for (let i = 0; i < points.length; i++) {\r\n dataString += (i ? \"L\" : \"M\") + svgValue(points[i].x) + \" \" + svgValue(points[i].y);\r\n }\r\n this.dataString += dataString + \"Z\";\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG path.\r\n * @param {import('../point').Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const sweepFlag = counterClockwise ? 0 : 1,\r\n svgRadius = svgValue(diameter / 2),\r\n svgDiameter = svgValue(diameter),\r\n svgArc = \"a\" + svgRadius + \",\" + svgRadius + \" 0 1,\" + sweepFlag + \" \";\r\n \r\n this.dataString += \r\n \"M\" + svgValue(point.x) + \" \" + svgValue(point.y + diameter / 2) +\r\n svgArc + svgDiameter + \",0\" + \r\n svgArc + (-svgDiameter) + \",0\";\r\n }\r\n}\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SvgPath } from \"./svgPath\";\r\nimport { parseHex } from \"../../common/parseHex\";\r\n\r\n/**\r\n * @typedef {import(\"../point\").Point} Point\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import(\"./svgElement\").SvgElement} SvgElement\r\n * @typedef {import(\"./svgWriter\").SvgWriter} SvgWriter\r\n */\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n * @implements {Renderer}\r\n */\r\nexport class SvgRenderer {\r\n /**\r\n * @param {SvgElement|SvgWriter} target \r\n */\r\n constructor(target) {\r\n /**\r\n * @type {SvgPath}\r\n * @private\r\n */\r\n this._path;\r\n\r\n /**\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._pathsByColor = { };\r\n\r\n /**\r\n * @type {SvgElement|SvgWriter}\r\n * @private\r\n */\r\n this._target = target;\r\n\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = target.iconSize;\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const match = /^(#......)(..)?/.exec(fillColor),\r\n opacity = match[2] ? parseHex(match[2], 0) / 255 : 1;\r\n this._target.setBackground(match[1], opacity);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n */\r\n beginShape(color) {\r\n this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath());\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape.\r\n */\r\n endShape() { }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n this._path.addPolygon(points);\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n this._path.addCircle(point, diameter, counterClockwise);\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() { \r\n const pathsByColor = this._pathsByColor;\r\n for (let color in pathsByColor) {\r\n // hasOwnProperty cannot be shadowed in pathsByColor\r\n // eslint-disable-next-line no-prototype-builtins\r\n if (pathsByColor.hasOwnProperty(color)) {\r\n this._target.appendPath(color, pathsByColor[color].dataString);\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const SVG_CONSTANTS = {\r\n XMLNS: \"http://www.w3.org/2000/svg\",\r\n WIDTH: \"width\",\r\n HEIGHT: \"height\",\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgWriter {\r\n /**\r\n * @param {number} iconSize - Icon width and height in pixels.\r\n */\r\n constructor(iconSize) {\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = iconSize;\r\n\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n this._s =\r\n '';\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n this._s += '';\r\n }\r\n }\r\n\r\n /**\r\n * Writes a path to the SVG string.\r\n * @param {string} color Fill color on format #rrggbb.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n this._s += '';\r\n }\r\n\r\n /**\r\n * Gets the rendered image as an SVG string.\r\n */\r\n toString() {\r\n return this._s + \"\";\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgWriter } from \"../renderer/svg/svgWriter\";\r\n\r\n/**\r\n * Draws an identicon as an SVG string.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {string} SVG string\r\n */\r\nexport function toSvg(hashOrValue, size, config) {\r\n const writer = new SvgWriter(size);\r\n iconGenerator(new SvgRenderer(writer), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue),\r\n config);\r\n return writer.toString();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// This file is compiled to dist/jdenticon-node.js\r\n\r\nif (typeof process === \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof document !== \"undefined\"\r\n) {\r\n console.warn(\r\n \"Jdenticon: 'dist/jdenticon-node.js' is only intended for Node.js environments and will increase your \" +\r\n \"bundle size when included in browser bundles. If you want to run Jdenticon in the browser, please add a \" +\r\n \"reference to 'dist/jdenticon.js' or 'dist/jdenticon.min.js' instead.\");\r\n}\r\n\r\nimport { defineConfigPropertyWithWarn } from \"./common/configuration\";\r\nimport { configure } from \"./apis/configure\";\r\nimport { drawIcon } from \"./apis/drawIcon\";\r\nimport { toPng } from \"./apis/toPng\";\r\nimport { toSvg } from \"./apis/toSvg\";\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\nfunction jdenticon() {\r\n throw new Error(\"jdenticon() is not supported on Node.js.\");\r\n}\r\n\r\ndefineConfigPropertyWithWarn(jdenticon);\r\n\r\njdenticon.configure = configure;\r\njdenticon.drawIcon = drawIcon;\r\njdenticon.toPng = toPng;\r\njdenticon.toSvg = toSvg;\r\n\r\n/**\r\n * Specifies the version of the Jdenticon package in use.\r\n * @type {string}\r\n */\r\njdenticon.version = \"#version#\";\r\n\r\n/**\r\n * Specifies which bundle of Jdenticon that is used.\r\n * @type {string}\r\n */\r\njdenticon.bundle = \"node-cjs\";\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\njdenticon.update = function update() {\r\n throw new Error(\"jdenticon.update() is not supported on Node.js.\");\r\n};\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\njdenticon.updateCanvas = function updateCanvas() {\r\n throw new Error(\"jdenticon.updateCanvas() is not supported on Node.js.\");\r\n};\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\njdenticon.updateSvg = function updateSvg() {\r\n throw new Error(\"jdenticon.updateSvg() is not supported on Node.js.\");\r\n};\r\n\r\nmodule.exports = jdenticon;","\n//# sourceMappingURL=jdenticon-node.js.map\n"]} \ No newline at end of file diff --git a/jdenticon-js/dist/jdenticon-node.mjs b/jdenticon-js/dist/jdenticon-node.mjs new file mode 100644 index 0000000..aa50476 --- /dev/null +++ b/jdenticon-js/dist/jdenticon-node.mjs @@ -0,0 +1,1240 @@ +/** + * Jdenticon 3.3.0 + * http://jdenticon.com + * + * Built: 2024-05-10T09:48:41.921Z + * + * MIT License + * + * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi + * + * 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. + */ + +import canvasRenderer from 'canvas-renderer'; + +/** + * Parses a substring of the hash as a number. + * @param {number} startPosition + * @param {number=} octets + */ +function parseHex(hash, startPosition, octets) { + return parseInt(hash.substr(startPosition, octets), 16); +} + +function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; +} + +function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); +} + +/** + * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported. + * @returns {string} + */ +function parseColor(color) { + if (/^#[0-9a-f]{3,8}$/i.test(color)) { + let result; + const colorLength = color.length; + + if (colorLength < 6) { + const r = color[1], + g = color[2], + b = color[3], + a = color[4] || ""; + result = "#" + r + r + g + g + b + b + a + a; + } + if (colorLength == 7 || colorLength > 8) { + result = color; + } + + return result; + } +} + +/** + * Converts a hexadecimal color to a CSS3 compatible color. + * @param {string} hexColor Color on the format "#RRGGBB" or "#RRGGBBAA" + * @returns {string} + */ +function toCss3Color(hexColor) { + const a = parseHex(hexColor, 7, 2); + let result; + + if (isNaN(a)) { + result = hexColor; + } else { + const r = parseHex(hexColor, 1, 2), + g = parseHex(hexColor, 3, 2), + b = parseHex(hexColor, 5, 2); + result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")"; + } + + return result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function hsl(hue, saturation, lightness) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + let result; + + if (saturation == 0) { + const partialHex = decToHex(lightness * 255); + result = partialHex + partialHex + partialHex; + } + else { + const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation, + m1 = lightness * 2 - m2; + result = + hueToRgb(m1, m2, hue * 6 + 2) + + hueToRgb(m1, m2, hue * 6) + + hueToRgb(m1, m2, hue * 6 - 2); + } + + return "#" + result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function correctedHsl(hue, saturation, lightness) { + // The corrector specifies the perceived middle lightness for each hue + const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(hue * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; + + return hsl(hue, saturation, lightness); +} + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. + +const GLOBAL = + typeof window !== "undefined" ? window : + typeof self !== "undefined" ? self : + typeof global !== "undefined" ? global : + {}; + +/** + * @typedef {Object} ParsedConfiguration + * @property {number} colorSaturation + * @property {number} grayscaleSaturation + * @property {string} backColor + * @property {number} iconPadding + * @property {function(number):number} hue + * @property {function(number):number} colorLightness + * @property {function(number):number} grayscaleLightness + */ + +const CONFIG_PROPERTIES = { + GLOBAL: "jdenticon_config", + MODULE: "config", +}; + +var rootConfigurationHolder = {}; + +/** + * Sets a new icon style configuration. The new configuration is not merged with the previous one. * + * @param {Object} newConfiguration - New configuration object. + */ +function configure(newConfiguration) { + if (arguments.length) { + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration; + } + return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE]; +} + +/** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A + * local configuration overrides the global configuration in it entirety. This parameter can for backward + * compatibility also contain a padding value. A padding value only overrides the global padding, not the + * entire global configuration. + * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor + * explicitly to the API method. + * @returns {ParsedConfiguration} + */ +function getConfiguration(paddingOrLocalConfig, defaultPadding) { + const configObject = + typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] || + GLOBAL[CONFIG_PROPERTIES.GLOBAL] || + { }, + + lightnessConfig = configObject["lightness"] || { }, + + // In versions < 2.1.0 there was no grayscale saturation - + // saturation was the color saturation. + saturation = configObject["saturation"] || { }, + colorSaturation = "color" in saturation ? saturation["color"] : saturation, + grayscaleSaturation = saturation["grayscale"], + + backColor = configObject["backColor"], + padding = configObject["padding"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultRange) { + let range = lightnessConfig[configName]; + + // Check if the lightness range is an array-like object. This way we ensure the + // array contain two values at the same time. + if (!(range && range.length > 1)) { + range = defaultRange; + } + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + /** + * Gets a hue allowed by the configured hue restriction, + * provided the originally computed hue. + */ + function hueFunction(originalHue) { + const hueConfig = configObject["hues"]; + let hue; + + // Check if 'hues' is an array-like object. This way we also ensure that + // the array is not empty, which would mean no hue restriction. + if (hueConfig && hueConfig.length > 0) { + // originalHue is in the range [0, 1] + // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. + hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; + } + + return typeof hue == "number" ? + + // A hue was specified. We need to convert the hue from + // degrees on any turn - e.g. 746° is a perfectly valid hue - + // to turns in the range [0, 1). + ((((hue / 360) % 1) + 1) % 1) : + + // No hue configured => use original hue + originalHue; + } + + return { + hue: hueFunction, + colorSaturation: typeof colorSaturation == "number" ? colorSaturation : 0.5, + grayscaleSaturation: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, + colorLightness: lightness("color", [0.4, 0.8]), + grayscaleLightness: lightness("grayscale", [0.3, 0.9]), + backColor: parseColor(backColor), + iconPadding: + typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : + typeof padding == "number" ? padding : + defaultPadding + } +} + +/** + * Represents a point. + */ +class Point { + /** + * @param {number} x + * @param {number} y + */ + constructor(x, y) { + this.x = x; + this.y = y; + } +} + +/** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + */ +class Transform { + /** + * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle. + * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle. + * @param {number} size The size of the transformed rectangle. + * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad + */ + constructor(x, y, size, rotation) { + this._x = x; + this._y = y; + this._size = size; + this._rotation = rotation; + } + + /** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ + transformIconPoint(x, y, w, h) { + const right = this._x + this._size, + bottom = this._y + this._size, + rotation = this._rotation; + return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) : + rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) : + new Point(this._x + x, this._y + y); + } +} + +const NO_TRANSFORM = new Transform(0, 0, 0, 0); + + + +/** + * Provides helper functions for rendering common basic shapes. + */ +class Graphics { + /** + * @param {Renderer} renderer + */ + constructor(renderer) { + /** + * @type {Renderer} + * @private + */ + this._renderer = renderer; + + /** + * @type {Transform} + */ + this.currentTransform = NO_TRANSFORM; + } + + /** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ + addPolygon(points, invert) { + const di = invert ? -2 : 2, + transformedPoints = []; + + for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1])); + } + + this._renderer.addPolygon(transformedPoints); + } + + /** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ + addCircle(x, y, size, invert) { + const p = this.currentTransform.transformIconPoint(x, y, size, size); + this._renderer.addCircle(p, size, invert); + } + + /** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ + addRectangle(x, y, w, h, invert) { + this.addPolygon([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); + } + + /** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ + addTriangle(x, y, w, h, r, invert) { + const points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.addPolygon(points, invert); + } + + /** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ + addRhombus(x, y, w, h, invert) { + this.addPolygon([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); + } +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + * @param {number} positionIndex + */ +function centerShape(index, g, cell, positionIndex) { + index = index % 14; + + let k, m, w, h, inner, outer; + + !index ? ( + k = cell * 0.42, + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ])) : + + index == 1 ? ( + w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8), + + g.addTriangle(cell - w, 0, w, h, 2)) : + + index == 2 ? ( + w = 0 | (cell / 3), + g.addRectangle(w, w, cell - w, cell - w)) : + + index == 3 ? ( + inner = cell * 0.1, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)), + + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) : + + index == 4 ? ( + m = 0 | (cell * 0.15), + w = 0 | (cell * 0.5), + g.addCircle(cell - w - m, cell - w - m, w)) : + + index == 5 ? ( + inner = cell * 0.1, + outer = inner * 4, + + // Align edge to nearest pixel in large icons + outer > 3 && (outer = 0 | outer), + + g.addRectangle(0, 0, cell, cell), + g.addPolygon([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true)) : + + index == 6 ? + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]) : + + index == 7 ? + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 8 ? ( + g.addRectangle(0, 0, cell, cell / 2), + g.addRectangle(0, cell / 2, cell / 2, cell / 2), + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) : + + index == 9 ? ( + inner = cell * 0.14, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)), + + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + g.addRectangle(0, 0, cell, cell), + g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) : + + index == 10 ? ( + inner = cell * 0.12, + outer = inner * 3, + + g.addRectangle(0, 0, cell, cell), + g.addCircle(outer, outer, cell - inner - outer, true)) : + + index == 11 ? + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 12 ? ( + m = cell * 0.25, + g.addRectangle(0, 0, cell, cell), + g.addRhombus(m, m, cell - m, cell - m, true)) : + + // 13 + ( + !positionIndex && ( + m = cell * 0.4, w = cell * 1.2, + g.addCircle(m, m, w) + ) + ); +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + */ +function outerShape(index, g, cell) { + index = index % 4; + + let m; + + !index ? + g.addTriangle(0, 0, cell, cell, 0) : + + index == 1 ? + g.addTriangle(0, cell / 2, cell, cell / 2, 0) : + + index == 2 ? + g.addRhombus(0, 0, cell, cell) : + + // 3 + ( + m = cell / 6, + g.addCircle(m, m, cell - 2 * m) + ); +} + +/** + * Gets a set of identicon color candidates for a specified hue and config. + * @param {number} hue + * @param {ParsedConfiguration} config + */ +function colorTheme(hue, config) { + hue = config.hue(hue); + return [ + // Dark gray + correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)), + // Mid color + correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)), + // Light gray + correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)), + // Light color + correctedHsl(hue, config.colorSaturation, config.colorLightness(1)), + // Dark color + correctedHsl(hue, config.colorSaturation, config.colorLightness(0)) + ]; +} + +/** + * Draws an identicon to a specified renderer. + * @param {Renderer} renderer + * @param {string} hash + * @param {Object|number=} config + */ +function iconGenerator(renderer, hash, config) { + const parsedConfig = getConfiguration(config, 0.08); + + // Set background color + if (parsedConfig.backColor) { + renderer.setBackground(parsedConfig.backColor); + } + + // Calculate padding and round to nearest integer + let size = renderer.iconSize; + const padding = (0.5 + size * parsedConfig.iconPadding) | 0; + size -= padding * 2; + + const graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + const cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + const x = 0 | (padding + size / 2 - cell * 2); + const y = 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + const shapeIndex = parseHex(hash, index, 1); + let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]); + + for (let i = 0; i < positions.length; i++) { + graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.endShape(); + } + + // AVAILABLE COLORS + const hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, parsedConfig), + + // The index of the selected colors + selectedColorIndexes = []; + + let index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (let i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (let i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); +} + +/** + * Computes a SHA1 hash for any value and returns it as a hexadecimal string. + * + * This function is optimized for minimal code size and rather short messages. + * + * @param {string} message + */ +function sha1(message) { + const HASH_SIZE_HALF_BYTES = 40; + const BLOCK_SIZE_WORDS = 16; + + // Variables + // `var` is used to be able to minimize the number of `var` keywords. + var i = 0, + f = 0, + + // Use `encodeURI` to UTF8 encode the message without any additional libraries + // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky + // since `unescape` is deprecated. + urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding + + // This can be changed to a preallocated Uint32Array array for greater performance and larger code size + data = [], + dataSize, + + hashBuffer = [], + + a = 0x67452301, + b = 0xefcdab89, + c = ~a, + d = ~b, + e = 0xc3d2e1f0, + hash = [a, b, c, d, e], + + blockStartIndex = 0, + hexHash = ""; + + /** + * Rotates the value a specified number of bits to the left. + * @param {number} value Value to rotate + * @param {number} shift Bit count to shift. + */ + function rotl(value, shift) { + return (value << shift) | (value >>> (32 - shift)); + } + + // Message data + for ( ; i < urlEncodedMessage.length; f++) { + data[f >> 2] = data[f >> 2] | + ( + ( + urlEncodedMessage[i] == "%" + // Percent encoded byte + ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16) + // Unencoded byte + : urlEncodedMessage.charCodeAt(i++) + ) + + // Read bytes in reverse order (big endian words) + << ((3 - (f & 3)) * 8) + ); + } + + // f is now the length of the utf8 encoded message + // 7 = 8 bytes (64 bit) for message size, -1 to round down + // >> 6 = integer division with block size + dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS; + + // Message size in bits. + // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least + // significant 32 bits are set. -8 is for the '1' bit padding byte. + data[dataSize - 1] = f * 8 - 8; + + // Compute hash + for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) { + for (i = 0; i < 80; i++) { + f = rotl(a, 5) + e + ( + // Ch + i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 : + + // Parity + i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 : + + // Maj + i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc : + + // Parity + (b ^ c ^ d) + 0xca62c1d6 + ) + ( + hashBuffer[i] = i < BLOCK_SIZE_WORDS + // Bitwise OR is used to coerse `undefined` to 0 + ? (data[blockStartIndex + i] | 0) + : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1) + ); + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = f; + } + + hash[0] = a = ((hash[0] + a) | 0); + hash[1] = b = ((hash[1] + b) | 0); + hash[2] = c = ((hash[2] + c) | 0); + hash[3] = d = ((hash[3] + d) | 0); + hash[4] = e = ((hash[4] + e) | 0); + } + + // Format hex hash + for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) { + hexHash += ( + ( + // Get word (2^3 half-bytes per word) + hash[i >> 3] >>> + + // Append half-bytes in reverse order + ((7 - (i & 7)) * 4) + ) + // Clamp to half-byte + & 0xf + ).toString(16); + } + + return hexHash; +} + +/** + * Inputs a value that might be a valid hash string for Jdenticon and returns it + * if it is determined valid, otherwise a falsy value is returned. + */ +function isValidHash(hashCandidate) { + return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate; +} + +/** + * Computes a hash for the specified value. Currently SHA1 is used. This function + * always returns a valid hash. + */ +function computeHash(value) { + return sha1(value == null ? "" : "" + value); +} + + + +/** + * Renderer redirecting drawing commands to a canvas context. + * @implements {Renderer} + */ +class CanvasRenderer { + /** + * @param {number=} iconSize + */ + constructor(ctx, iconSize) { + const canvas = ctx.canvas; + const width = canvas.width; + const height = canvas.height; + + ctx.save(); + + if (!iconSize) { + iconSize = Math.min(width, height); + + ctx.translate( + ((width - iconSize) / 2) | 0, + ((height - iconSize) / 2) | 0); + } + + /** + * @private + */ + this._ctx = ctx; + this.iconSize = iconSize; + + ctx.clearRect(0, 0, iconSize, iconSize); + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + setBackground(fillColor) { + const ctx = this._ctx; + const iconSize = this.iconSize; + + ctx.fillStyle = toCss3Color(fillColor); + ctx.fillRect(0, 0, iconSize, iconSize); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} fillColor Fill color on format #rrggbb[aa]. + */ + beginShape(fillColor) { + const ctx = this._ctx; + ctx.fillStyle = toCss3Color(fillColor); + ctx.beginPath(); + } + + /** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ + endShape() { + this._ctx.fill(); + } + + /** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ + addPolygon(points) { + const ctx = this._ctx; + ctx.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); + } + + /** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + const ctx = this._ctx, + radius = diameter / 2; + ctx.moveTo(point.x + radius, point.y + radius); + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + this._ctx.restore(); + } +} + +const IS_RENDERED_PROPERTY = "jdenticonRendered"; + +/** @type {!Function} */ ( + typeof document !== "undefined" && document.querySelectorAll.bind(document)); + +/** + * Draws an identicon to a context. + * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0). + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function drawIcon(ctx, hashOrValue, size, config) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + const canvas = ctx.canvas; + if (canvas) { + canvas[IS_RENDERED_PROPERTY] = true; + } +} + +/** + * Draws an identicon as PNG. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {Buffer} PNG data + */ +function toPng(hashOrValue, size, config) { + const canvas = canvasRenderer.createCanvas(size, size); + const ctx = canvas.getContext("2d"); + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + return canvas.toPng({ "Software": "Jdenticon" }); +} + +/** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ +function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; +} + +/** + * Represents an SVG path element. + */ +class SvgPath { + constructor() { + /** + * This property holds the data string (path.d) of the SVG path. + * @type {string} + */ + this.dataString = ""; + } + + /** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ + addPolygon(points) { + let dataString = ""; + for (let i = 0; i < points.length; i++) { + dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.dataString += dataString + "Z"; + } + + /** + * Adds a circle with the current fill color to the SVG path. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + const sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter), + svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " "; + + this.dataString += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + svgArc + svgDiameter + ",0" + + svgArc + (-svgDiameter) + ",0"; + } +} + + + +/** + * Renderer producing SVG output. + * @implements {Renderer} + */ +class SvgRenderer { + /** + * @param {SvgElement|SvgWriter} target + */ + constructor(target) { + /** + * @type {SvgPath} + * @private + */ + this._path; + + /** + * @type {Object.} + * @private + */ + this._pathsByColor = { }; + + /** + * @type {SvgElement|SvgWriter} + * @private + */ + this._target = target; + + /** + * @type {number} + */ + this.iconSize = target.iconSize; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + setBackground(fillColor) { + const match = /^(#......)(..)?/.exec(fillColor), + opacity = match[2] ? parseHex(match[2], 0) / 255 : 1; + this._target.setBackground(match[1], opacity); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ + beginShape(color) { + this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath()); + } + + /** + * Marks the end of the currently drawn shape. + */ + endShape() { } + + /** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ + addPolygon(points) { + this._path.addPolygon(points); + } + + /** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + this._path.addCircle(point, diameter, counterClockwise); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + const pathsByColor = this._pathsByColor; + for (let color in pathsByColor) { + // hasOwnProperty cannot be shadowed in pathsByColor + // eslint-disable-next-line no-prototype-builtins + if (pathsByColor.hasOwnProperty(color)) { + this._target.appendPath(color, pathsByColor[color].dataString); + } + } + } +} + +const SVG_CONSTANTS = { + XMLNS: "http://www.w3.org/2000/svg", + WIDTH: "width", + HEIGHT: "height", +}; + +/** + * Renderer producing SVG output. + */ +class SvgWriter { + /** + * @param {number} iconSize - Icon width and height in pixels. + */ + constructor(iconSize) { + /** + * @type {number} + */ + this.iconSize = iconSize; + + /** + * @type {string} + * @private + */ + this._s = + ''; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ + setBackground(fillColor, opacity) { + if (opacity) { + this._s += ''; + } + } + + /** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #rrggbb. + * @param {string} dataString The SVG path data string. + */ + appendPath(color, dataString) { + this._s += ''; + } + + /** + * Gets the rendered image as an SVG string. + */ + toString() { + return this._s + ""; + } +} + +/** + * Draws an identicon as an SVG string. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {string} SVG string + */ +function toSvg(hashOrValue, size, config) { + const writer = new SvgWriter(size); + iconGenerator(new SvgRenderer(writer), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + return writer.toString(); +} + +// This file is compiled to dist/jdenticon-node.mjs + +if (typeof process === "undefined" && + typeof window !== "undefined" && + typeof document !== "undefined" +) { + console.warn( + "Jdenticon: 'dist/jdenticon-node.mjs' is only intended for Node.js environments and will increase your " + + "bundle size when included in browser bundles. If you want to run Jdenticon in the browser, please add a " + + "reference to 'dist/jdenticon.js' or 'dist/jdenticon.min.js' instead."); +} + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +const version = "3.3.0"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +const bundle = "node-esm"; + +/** + * @throws {Error} + */ +function update() { + throw new Error("jdenticon.update() is not supported on Node.js."); +} + +/** + * @throws {Error} + */ +function updateCanvas() { + throw new Error("jdenticon.updateCanvas() is not supported on Node.js."); +} + +/** + * @throws {Error} + */ +function updateSvg() { + throw new Error("jdenticon.updateSvg() is not supported on Node.js."); +} + +export { bundle, configure, drawIcon, toPng, toSvg, update, updateCanvas, updateSvg, version }; +//# sourceMappingURL=jdenticon-node.mjs.map diff --git a/jdenticon-js/dist/jdenticon-node.mjs.map b/jdenticon-js/dist/jdenticon-node.mjs.map new file mode 100644 index 0000000..b400a9f --- /dev/null +++ b/jdenticon-js/dist/jdenticon-node.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["replacement/1","src/common/parseHex.js","src/renderer/color.js","src/common/global.js","src/common/configuration.js","src/renderer/point.js","src/renderer/transform.js","src/renderer/graphics.js","src/renderer/shapes.js","src/renderer/colorTheme.js","src/renderer/iconGenerator.js","src/common/sha1.js","src/common/hashUtils.js","src/renderer/canvas/canvasRenderer.js","src/common/dom.js","src/apis/drawIcon.js","src/apis/toPng.js","src/renderer/svg/svgPath.js","src/renderer/svg/svgRenderer.js","src/renderer/svg/constants.js","src/renderer/svg/svgWriter.js","src/apis/toSvg.js","src/node-esm.js","replacement/2"],"names":[],"mappings":"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","file":"jdenticon-node.mjs","sourcesContent":["/**\r\n * Jdenticon 3.3.0\r\n * http://jdenticon.com\r\n *\r\n * Built: 2024-05-10T09:48:41.921Z\r\n * \r\n * MIT License\r\n * \r\n * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi\r\n * \r\n * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n * of this software and associated documentation files (the \"Software\"), to deal\r\n * in the Software without restriction, including without limitation the rights\r\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the Software is\r\n * furnished to do so, subject to the following conditions:\r\n * \r\n * The above copyright notice and this permission notice shall be included in all\r\n * copies or substantial portions of the Software.\r\n * \r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n * SOFTWARE.\r\n */\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Parses a substring of the hash as a number.\r\n * @param {number} startPosition \r\n * @param {number=} octets\r\n */\r\nexport function parseHex(hash, startPosition, octets) {\r\n return parseInt(hash.substr(startPosition, octets), 16);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseHex } from \"../common/parseHex\";\r\n\r\nfunction decToHex(v) {\r\n v |= 0; // Ensure integer value\r\n return v < 0 ? \"00\" :\r\n v < 16 ? \"0\" + v.toString(16) :\r\n v < 256 ? v.toString(16) :\r\n \"ff\";\r\n}\r\n\r\nfunction hueToRgb(m1, m2, h) {\r\n h = h < 0 ? h + 6 : h > 6 ? h - 6 : h;\r\n return decToHex(255 * (\r\n h < 1 ? m1 + (m2 - m1) * h :\r\n h < 3 ? m2 :\r\n h < 4 ? m1 + (m2 - m1) * (4 - h) :\r\n m1));\r\n}\r\n\r\n/**\r\n * @param {number} r Red channel [0, 255]\r\n * @param {number} g Green channel [0, 255]\r\n * @param {number} b Blue channel [0, 255]\r\n */\r\nexport function rgb(r, g, b) {\r\n return \"#\" + decToHex(r) + decToHex(g) + decToHex(b);\r\n}\r\n\r\n/**\r\n * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported.\r\n * @returns {string}\r\n */\r\nexport function parseColor(color) {\r\n if (/^#[0-9a-f]{3,8}$/i.test(color)) {\r\n let result;\r\n const colorLength = color.length;\r\n\r\n if (colorLength < 6) {\r\n const r = color[1],\r\n g = color[2],\r\n b = color[3],\r\n a = color[4] || \"\";\r\n result = \"#\" + r + r + g + g + b + b + a + a;\r\n }\r\n if (colorLength == 7 || colorLength > 8) {\r\n result = color;\r\n }\r\n \r\n return result;\r\n }\r\n}\r\n\r\n/**\r\n * Converts a hexadecimal color to a CSS3 compatible color.\r\n * @param {string} hexColor Color on the format \"#RRGGBB\" or \"#RRGGBBAA\"\r\n * @returns {string}\r\n */\r\nexport function toCss3Color(hexColor) {\r\n const a = parseHex(hexColor, 7, 2);\r\n let result;\r\n\r\n if (isNaN(a)) {\r\n result = hexColor;\r\n } else {\r\n const r = parseHex(hexColor, 1, 2),\r\n g = parseHex(hexColor, 3, 2),\r\n b = parseHex(hexColor, 5, 2);\r\n result = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + (a / 255).toFixed(2) + \")\";\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color.\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function hsl(hue, saturation, lightness) {\r\n // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color\r\n let result;\r\n\r\n if (saturation == 0) {\r\n const partialHex = decToHex(lightness * 255);\r\n result = partialHex + partialHex + partialHex;\r\n }\r\n else {\r\n const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation,\r\n m1 = lightness * 2 - m2;\r\n result =\r\n hueToRgb(m1, m2, hue * 6 + 2) +\r\n hueToRgb(m1, m2, hue * 6) +\r\n hueToRgb(m1, m2, hue * 6 - 2);\r\n }\r\n\r\n return \"#\" + result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the \"dark\" hues\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function correctedHsl(hue, saturation, lightness) {\r\n // The corrector specifies the perceived middle lightness for each hue\r\n const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ],\r\n corrector = correctors[(hue * 6 + 0.5) | 0];\r\n \r\n // Adjust the input lightness relative to the corrector\r\n lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2;\r\n \r\n return hsl(hue, saturation, lightness);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for\r\n// backward compatibility.\r\n\r\nexport const GLOBAL = \r\n typeof window !== \"undefined\" ? window :\r\n typeof self !== \"undefined\" ? self :\r\n typeof global !== \"undefined\" ? global :\r\n {};\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseColor } from \"../renderer/color\";\r\nimport { GLOBAL } from \"./global\";\r\n\r\n/**\r\n * @typedef {Object} ParsedConfiguration\r\n * @property {number} colorSaturation\r\n * @property {number} grayscaleSaturation\r\n * @property {string} backColor\r\n * @property {number} iconPadding\r\n * @property {function(number):number} hue\r\n * @property {function(number):number} colorLightness\r\n * @property {function(number):number} grayscaleLightness\r\n */\r\n\r\nexport const CONFIG_PROPERTIES = {\r\n GLOBAL: \"jdenticon_config\",\r\n MODULE: \"config\",\r\n};\r\n\r\nvar rootConfigurationHolder = {};\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is \r\n * printed in the console. To minimize bundle size, this is only used in Node bundles.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigPropertyWithWarn(rootObject) {\r\n Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, {\r\n configurable: true,\r\n get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE],\r\n set: newConfiguration => {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n console.warn(\"jdenticon.config is deprecated. Use jdenticon.configure() instead.\");\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console\r\n * when it is being used.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigProperty(rootObject) {\r\n rootConfigurationHolder = rootObject;\r\n}\r\n\r\n/**\r\n * Sets a new icon style configuration. The new configuration is not merged with the previous one. * \r\n * @param {Object} newConfiguration - New configuration object.\r\n */\r\nexport function configure(newConfiguration) {\r\n if (arguments.length) {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n }\r\n return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE];\r\n}\r\n\r\n/**\r\n * Gets the normalized current Jdenticon color configuration. Missing fields have default values.\r\n * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A\r\n * local configuration overrides the global configuration in it entirety. This parameter can for backward\r\n * compatibility also contain a padding value. A padding value only overrides the global padding, not the\r\n * entire global configuration.\r\n * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor\r\n * explicitly to the API method.\r\n * @returns {ParsedConfiguration}\r\n */\r\nexport function getConfiguration(paddingOrLocalConfig, defaultPadding) {\r\n const configObject = \r\n typeof paddingOrLocalConfig == \"object\" && paddingOrLocalConfig ||\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] ||\r\n GLOBAL[CONFIG_PROPERTIES.GLOBAL] ||\r\n { },\r\n\r\n lightnessConfig = configObject[\"lightness\"] || { },\r\n \r\n // In versions < 2.1.0 there was no grayscale saturation -\r\n // saturation was the color saturation.\r\n saturation = configObject[\"saturation\"] || { },\r\n colorSaturation = \"color\" in saturation ? saturation[\"color\"] : saturation,\r\n grayscaleSaturation = saturation[\"grayscale\"],\r\n\r\n backColor = configObject[\"backColor\"],\r\n padding = configObject[\"padding\"];\r\n \r\n /**\r\n * Creates a lightness range.\r\n */\r\n function lightness(configName, defaultRange) {\r\n let range = lightnessConfig[configName];\r\n \r\n // Check if the lightness range is an array-like object. This way we ensure the\r\n // array contain two values at the same time.\r\n if (!(range && range.length > 1)) {\r\n range = defaultRange;\r\n }\r\n\r\n /**\r\n * Gets a lightness relative the specified value in the specified lightness range.\r\n */\r\n return function (value) {\r\n value = range[0] + value * (range[1] - range[0]);\r\n return value < 0 ? 0 : value > 1 ? 1 : value;\r\n };\r\n }\r\n\r\n /**\r\n * Gets a hue allowed by the configured hue restriction,\r\n * provided the originally computed hue.\r\n */\r\n function hueFunction(originalHue) {\r\n const hueConfig = configObject[\"hues\"];\r\n let hue;\r\n \r\n // Check if 'hues' is an array-like object. This way we also ensure that\r\n // the array is not empty, which would mean no hue restriction.\r\n if (hueConfig && hueConfig.length > 0) {\r\n // originalHue is in the range [0, 1]\r\n // Multiply with 0.999 to change the range to [0, 1) and then truncate the index.\r\n hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)];\r\n }\r\n\r\n return typeof hue == \"number\" ?\r\n \r\n // A hue was specified. We need to convert the hue from\r\n // degrees on any turn - e.g. 746° is a perfectly valid hue -\r\n // to turns in the range [0, 1).\r\n ((((hue / 360) % 1) + 1) % 1) :\r\n\r\n // No hue configured => use original hue\r\n originalHue;\r\n }\r\n \r\n return {\r\n hue: hueFunction,\r\n colorSaturation: typeof colorSaturation == \"number\" ? colorSaturation : 0.5,\r\n grayscaleSaturation: typeof grayscaleSaturation == \"number\" ? grayscaleSaturation : 0,\r\n colorLightness: lightness(\"color\", [0.4, 0.8]),\r\n grayscaleLightness: lightness(\"grayscale\", [0.3, 0.9]),\r\n backColor: parseColor(backColor),\r\n iconPadding: \r\n typeof paddingOrLocalConfig == \"number\" ? paddingOrLocalConfig : \r\n typeof padding == \"number\" ? padding : \r\n defaultPadding\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Represents a point.\r\n */\r\nexport class Point {\r\n /**\r\n * @param {number} x \r\n * @param {number} y \r\n */\r\n constructor(x, y) {\r\n this.x = x;\r\n this.y = y;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Point } from \"./point\";\r\n\r\n/**\r\n * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, \r\n * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly.\r\n */\r\nexport class Transform {\r\n /**\r\n * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} size The size of the transformed rectangle.\r\n * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad\r\n */\r\n constructor(x, y, size, rotation) {\r\n this._x = x;\r\n this._y = y;\r\n this._size = size;\r\n this._rotation = rotation;\r\n }\r\n\r\n /**\r\n * Transforms the specified point based on the translation and rotation specification for this Transform.\r\n * @param {number} x x-coordinate\r\n * @param {number} y y-coordinate\r\n * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n */\r\n transformIconPoint(x, y, w, h) {\r\n const right = this._x + this._size,\r\n bottom = this._y + this._size,\r\n rotation = this._rotation;\r\n return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) :\r\n rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) :\r\n rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) :\r\n new Point(this._x + x, this._y + y);\r\n }\r\n}\r\n\r\nexport const NO_TRANSFORM = new Transform(0, 0, 0, 0);\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { NO_TRANSFORM } from \"./transform\";\r\n\r\n/**\r\n * @typedef {import(\"./renderer\").Renderer} Renderer\r\n * @typedef {import(\"./transform\").Transform} Transform\r\n */\r\n\r\n/**\r\n * Provides helper functions for rendering common basic shapes.\r\n */\r\nexport class Graphics {\r\n /**\r\n * @param {Renderer} renderer \r\n */\r\n constructor(renderer) {\r\n /**\r\n * @type {Renderer}\r\n * @private\r\n */\r\n this._renderer = renderer;\r\n\r\n /**\r\n * @type {Transform}\r\n */\r\n this.currentTransform = NO_TRANSFORM;\r\n }\r\n\r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ]\r\n * @param {boolean=} invert Specifies if the polygon will be inverted.\r\n */\r\n addPolygon(points, invert) {\r\n const di = invert ? -2 : 2,\r\n transformedPoints = [];\r\n \r\n for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) {\r\n transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1]));\r\n }\r\n \r\n this._renderer.addPolygon(transformedPoints);\r\n }\r\n \r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * Source: http://stackoverflow.com/a/2173084\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} size The size of the ellipse.\r\n * @param {boolean=} invert Specifies if the ellipse will be inverted.\r\n */\r\n addCircle(x, y, size, invert) {\r\n const p = this.currentTransform.transformIconPoint(x, y, size, size);\r\n this._renderer.addCircle(p, size, invert);\r\n }\r\n\r\n /**\r\n * Adds a rectangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle.\r\n * @param {number} w The width of the rectangle.\r\n * @param {number} h The height of the rectangle.\r\n * @param {boolean=} invert Specifies if the rectangle will be inverted.\r\n */\r\n addRectangle(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x, y, \r\n x + w, y,\r\n x + w, y + h,\r\n x, y + h\r\n ], invert);\r\n }\r\n\r\n /**\r\n * Adds a right triangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} w The width of the triangle.\r\n * @param {number} h The height of the triangle.\r\n * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle.\r\n * @param {boolean=} invert Specifies if the triangle will be inverted.\r\n */\r\n addTriangle(x, y, w, h, r, invert) {\r\n const points = [\r\n x + w, y, \r\n x + w, y + h, \r\n x, y + h,\r\n x, y\r\n ];\r\n points.splice(((r || 0) % 4) * 2, 2);\r\n this.addPolygon(points, invert);\r\n }\r\n\r\n /**\r\n * Adds a rhombus to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} w The width of the rhombus.\r\n * @param {number} h The height of the rhombus.\r\n * @param {boolean=} invert Specifies if the rhombus will be inverted.\r\n */\r\n addRhombus(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x + w / 2, y,\r\n x + w, y + h / 2,\r\n x + w / 2, y + h,\r\n x, y + h / 2\r\n ], invert);\r\n }\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n * @param {number} positionIndex\r\n * @typedef {import('./graphics').Graphics} Graphics\r\n */\r\nexport function centerShape(index, g, cell, positionIndex) {\r\n index = index % 14;\r\n\r\n let k, m, w, h, inner, outer;\r\n\r\n !index ? (\r\n k = cell * 0.42,\r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell - k * 2,\r\n cell - k, cell,\r\n 0, cell\r\n ])) :\r\n\r\n index == 1 ? (\r\n w = 0 | (cell * 0.5), \r\n h = 0 | (cell * 0.8),\r\n\r\n g.addTriangle(cell - w, 0, w, h, 2)) :\r\n\r\n index == 2 ? (\r\n w = 0 | (cell / 3),\r\n g.addRectangle(w, w, cell - w, cell - w)) :\r\n\r\n index == 3 ? (\r\n inner = cell * 0.1,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 6 ? 1 :\r\n cell < 8 ? 2 :\r\n (0 | (cell * 0.25)),\r\n \r\n inner = \r\n inner > 1 ? (0 | inner) : // large icon => truncate decimals\r\n inner > 0.5 ? 1 : // medium size icon => fixed width\r\n inner, // small icon => anti-aliased border\r\n\r\n g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) :\r\n\r\n index == 4 ? (\r\n m = 0 | (cell * 0.15),\r\n w = 0 | (cell * 0.5),\r\n g.addCircle(cell - w - m, cell - w - m, w)) :\r\n\r\n index == 5 ? (\r\n inner = cell * 0.1,\r\n outer = inner * 4,\r\n\r\n // Align edge to nearest pixel in large icons\r\n outer > 3 && (outer = 0 | outer),\r\n \r\n g.addRectangle(0, 0, cell, cell),\r\n g.addPolygon([\r\n outer, outer,\r\n cell - inner, outer,\r\n outer + (cell - outer - inner) / 2, cell - inner\r\n ], true)) :\r\n\r\n index == 6 ? \r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell * 0.7,\r\n cell * 0.4, cell * 0.4,\r\n cell * 0.7, cell,\r\n 0, cell\r\n ]) :\r\n\r\n index == 7 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 8 ? (\r\n g.addRectangle(0, 0, cell, cell / 2),\r\n g.addRectangle(0, cell / 2, cell / 2, cell / 2),\r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) :\r\n\r\n index == 9 ? (\r\n inner = cell * 0.14,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 4 ? 1 :\r\n cell < 6 ? 2 :\r\n (0 | (cell * 0.35)),\r\n\r\n inner = \r\n cell < 8 ? inner : // small icon => anti-aliased border\r\n (0 | inner), // large icon => truncate decimals\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) :\r\n\r\n index == 10 ? (\r\n inner = cell * 0.12,\r\n outer = inner * 3,\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addCircle(outer, outer, cell - inner - outer, true)) :\r\n\r\n index == 11 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 12 ? (\r\n m = cell * 0.25,\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRhombus(m, m, cell - m, cell - m, true)) :\r\n\r\n // 13\r\n (\r\n !positionIndex && (\r\n m = cell * 0.4, w = cell * 1.2,\r\n g.addCircle(m, m, w)\r\n )\r\n );\r\n}\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n */\r\nexport function outerShape(index, g, cell) {\r\n index = index % 4;\r\n\r\n let m;\r\n\r\n !index ?\r\n g.addTriangle(0, 0, cell, cell, 0) :\r\n \r\n index == 1 ?\r\n g.addTriangle(0, cell / 2, cell, cell / 2, 0) :\r\n\r\n index == 2 ?\r\n g.addRhombus(0, 0, cell, cell) :\r\n\r\n // 3\r\n (\r\n m = cell / 6,\r\n g.addCircle(m, m, cell - 2 * m)\r\n );\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { correctedHsl } from \"./color\";\r\n\r\n/**\r\n * Gets a set of identicon color candidates for a specified hue and config.\r\n * @param {number} hue\r\n * @param {import(\"../common/configuration\").ParsedConfiguration} config\r\n */\r\nexport function colorTheme(hue, config) {\r\n hue = config.hue(hue);\r\n return [\r\n // Dark gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)),\r\n // Mid color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)),\r\n // Light gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)),\r\n // Light color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(1)),\r\n // Dark color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0))\r\n ];\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Transform } from \"./transform\";\r\nimport { Graphics } from \"./graphics\";\r\nimport { centerShape, outerShape } from \"./shapes\";\r\nimport { colorTheme } from \"./colorTheme\";\r\nimport { parseHex } from \"../common/parseHex\";\r\nimport { getConfiguration } from \"../common/configuration\";\r\n \r\n/**\r\n * Draws an identicon to a specified renderer.\r\n * @param {import('./renderer').Renderer} renderer\r\n * @param {string} hash\r\n * @param {Object|number=} config\r\n */\r\nexport function iconGenerator(renderer, hash, config) {\r\n const parsedConfig = getConfiguration(config, 0.08);\r\n\r\n // Set background color\r\n if (parsedConfig.backColor) {\r\n renderer.setBackground(parsedConfig.backColor);\r\n }\r\n \r\n // Calculate padding and round to nearest integer\r\n let size = renderer.iconSize;\r\n const padding = (0.5 + size * parsedConfig.iconPadding) | 0;\r\n size -= padding * 2;\r\n \r\n const graphics = new Graphics(renderer);\r\n \r\n // Calculate cell size and ensure it is an integer\r\n const cell = 0 | (size / 4);\r\n \r\n // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon\r\n const x = 0 | (padding + size / 2 - cell * 2);\r\n const y = 0 | (padding + size / 2 - cell * 2);\r\n\r\n function renderShape(colorIndex, shapes, index, rotationIndex, positions) {\r\n const shapeIndex = parseHex(hash, index, 1);\r\n let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0;\r\n \r\n renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]);\r\n \r\n for (let i = 0; i < positions.length; i++) {\r\n graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4);\r\n shapes(shapeIndex, graphics, cell, i);\r\n }\r\n \r\n renderer.endShape();\r\n }\r\n\r\n // AVAILABLE COLORS\r\n const hue = parseHex(hash, -7) / 0xfffffff,\r\n \r\n // Available colors for this icon\r\n availableColors = colorTheme(hue, parsedConfig),\r\n\r\n // The index of the selected colors\r\n selectedColorIndexes = [];\r\n\r\n let index;\r\n\r\n function isDuplicate(values) {\r\n if (values.indexOf(index) >= 0) {\r\n for (let i = 0; i < values.length; i++) {\r\n if (selectedColorIndexes.indexOf(values[i]) >= 0) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < 3; i++) {\r\n index = parseHex(hash, 8 + i, 1) % availableColors.length;\r\n if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo\r\n isDuplicate([2, 3])) { // Disallow light gray and light color combo\r\n index = 1;\r\n }\r\n selectedColorIndexes.push(index);\r\n }\r\n\r\n // ACTUAL RENDERING\r\n // Sides\r\n renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]);\r\n // Corners\r\n renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]);\r\n // Center\r\n renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]);\r\n \r\n renderer.finish();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Computes a SHA1 hash for any value and returns it as a hexadecimal string.\r\n * \r\n * This function is optimized for minimal code size and rather short messages.\r\n * \r\n * @param {string} message \r\n */\r\nexport function sha1(message) {\r\n const HASH_SIZE_HALF_BYTES = 40;\r\n const BLOCK_SIZE_WORDS = 16;\r\n\r\n // Variables\r\n // `var` is used to be able to minimize the number of `var` keywords.\r\n var i = 0,\r\n f = 0,\r\n \r\n // Use `encodeURI` to UTF8 encode the message without any additional libraries\r\n // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky\r\n // since `unescape` is deprecated.\r\n urlEncodedMessage = encodeURI(message) + \"%80\", // trailing '1' bit padding\r\n \r\n // This can be changed to a preallocated Uint32Array array for greater performance and larger code size\r\n data = [],\r\n dataSize,\r\n \r\n hashBuffer = [],\r\n\r\n a = 0x67452301,\r\n b = 0xefcdab89,\r\n c = ~a,\r\n d = ~b,\r\n e = 0xc3d2e1f0,\r\n hash = [a, b, c, d, e],\r\n\r\n blockStartIndex = 0,\r\n hexHash = \"\";\r\n\r\n /**\r\n * Rotates the value a specified number of bits to the left.\r\n * @param {number} value Value to rotate\r\n * @param {number} shift Bit count to shift.\r\n */\r\n function rotl(value, shift) {\r\n return (value << shift) | (value >>> (32 - shift));\r\n }\r\n\r\n // Message data\r\n for ( ; i < urlEncodedMessage.length; f++) {\r\n data[f >> 2] = data[f >> 2] |\r\n (\r\n (\r\n urlEncodedMessage[i] == \"%\"\r\n // Percent encoded byte\r\n ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)\r\n // Unencoded byte\r\n : urlEncodedMessage.charCodeAt(i++)\r\n )\r\n\r\n // Read bytes in reverse order (big endian words)\r\n << ((3 - (f & 3)) * 8)\r\n );\r\n }\r\n\r\n // f is now the length of the utf8 encoded message\r\n // 7 = 8 bytes (64 bit) for message size, -1 to round down\r\n // >> 6 = integer division with block size\r\n dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;\r\n\r\n // Message size in bits.\r\n // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least\r\n // significant 32 bits are set. -8 is for the '1' bit padding byte.\r\n data[dataSize - 1] = f * 8 - 8;\r\n \r\n // Compute hash\r\n for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {\r\n for (i = 0; i < 80; i++) {\r\n f = rotl(a, 5) + e + (\r\n // Ch\r\n i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :\r\n \r\n // Parity\r\n i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :\r\n \r\n // Maj\r\n i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :\r\n \r\n // Parity\r\n (b ^ c ^ d) + 0xca62c1d6\r\n ) + ( \r\n hashBuffer[i] = i < BLOCK_SIZE_WORDS\r\n // Bitwise OR is used to coerse `undefined` to 0\r\n ? (data[blockStartIndex + i] | 0)\r\n : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)\r\n );\r\n\r\n e = d;\r\n d = c;\r\n c = rotl(b, 30);\r\n b = a;\r\n a = f;\r\n }\r\n\r\n hash[0] = a = ((hash[0] + a) | 0);\r\n hash[1] = b = ((hash[1] + b) | 0);\r\n hash[2] = c = ((hash[2] + c) | 0);\r\n hash[3] = d = ((hash[3] + d) | 0);\r\n hash[4] = e = ((hash[4] + e) | 0);\r\n }\r\n\r\n // Format hex hash\r\n for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {\r\n hexHash += (\r\n (\r\n // Get word (2^3 half-bytes per word)\r\n hash[i >> 3] >>>\r\n\r\n // Append half-bytes in reverse order\r\n ((7 - (i & 7)) * 4)\r\n ) \r\n // Clamp to half-byte\r\n & 0xf\r\n ).toString(16);\r\n }\r\n\r\n return hexHash;\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { sha1 } from \"./sha1\";\r\n\r\n/**\r\n * Inputs a value that might be a valid hash string for Jdenticon and returns it \r\n * if it is determined valid, otherwise a falsy value is returned.\r\n */\r\nexport function isValidHash(hashCandidate) {\r\n return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate;\r\n}\r\n\r\n/**\r\n * Computes a hash for the specified value. Currently SHA1 is used. This function\r\n * always returns a valid hash.\r\n */\r\nexport function computeHash(value) {\r\n return sha1(value == null ? \"\" : \"\" + value);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { toCss3Color } from \"../color\";\r\n\r\n/**\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import('../point').Point} Point\r\n */\r\n\r\n/**\r\n * Renderer redirecting drawing commands to a canvas context.\r\n * @implements {Renderer}\r\n */\r\nexport class CanvasRenderer {\r\n /**\r\n * @param {number=} iconSize\r\n */\r\n constructor(ctx, iconSize) {\r\n const canvas = ctx.canvas; \r\n const width = canvas.width;\r\n const height = canvas.height;\r\n \r\n ctx.save();\r\n \r\n if (!iconSize) {\r\n iconSize = Math.min(width, height);\r\n \r\n ctx.translate(\r\n ((width - iconSize) / 2) | 0,\r\n ((height - iconSize) / 2) | 0);\r\n }\r\n\r\n /**\r\n * @private\r\n */\r\n this._ctx = ctx;\r\n this.iconSize = iconSize;\r\n \r\n ctx.clearRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const ctx = this._ctx;\r\n const iconSize = this.iconSize;\r\n\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.fillRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} fillColor Fill color on format #rrggbb[aa].\r\n */\r\n beginShape(fillColor) {\r\n const ctx = this._ctx;\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.beginPath();\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas.\r\n */\r\n endShape() {\r\n this._ctx.fill();\r\n }\r\n\r\n /**\r\n * Adds a polygon to the rendering queue.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n const ctx = this._ctx;\r\n ctx.moveTo(points[0].x, points[0].y);\r\n for (let i = 1; i < points.length; i++) {\r\n ctx.lineTo(points[i].x, points[i].y);\r\n }\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Adds a circle to the rendering queue.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const ctx = this._ctx,\r\n radius = diameter / 2;\r\n ctx.moveTo(point.x + radius, point.y + radius);\r\n ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise);\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() {\r\n this._ctx.restore();\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const ICON_TYPE_SVG = 1;\r\n\r\nexport const ICON_TYPE_CANVAS = 2;\r\n\r\nexport const ATTRIBUTES = {\r\n HASH: \"data-jdenticon-hash\",\r\n VALUE: \"data-jdenticon-value\"\r\n};\r\n\r\nexport const IS_RENDERED_PROPERTY = \"jdenticonRendered\";\r\n\r\nexport const ICON_SELECTOR = \"[\" + ATTRIBUTES.HASH +\"],[\" + ATTRIBUTES.VALUE +\"]\";\r\n\r\nexport const documentQuerySelectorAll = /** @type {!Function} */ (\r\n typeof document !== \"undefined\" && document.querySelectorAll.bind(document));\r\n\r\nexport function getIdenticonType(el) {\r\n if (el) {\r\n const tagName = el[\"tagName\"];\r\n\r\n if (/^svg$/i.test(tagName)) {\r\n return ICON_TYPE_SVG;\r\n }\r\n\r\n if (/^canvas$/i.test(tagName) && \"getContext\" in el) {\r\n return ICON_TYPE_CANVAS;\r\n }\r\n }\r\n}\r\n\r\nexport function whenDocumentIsReady(/** @type {Function} */ callback) {\r\n function loadedHandler() {\r\n document.removeEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.removeEventListener(\"load\", loadedHandler);\r\n setTimeout(callback, 0); // Give scripts a chance to run\r\n }\r\n \r\n if (typeof document !== \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof setTimeout !== \"undefined\"\r\n ) {\r\n if (document.readyState === \"loading\") {\r\n document.addEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.addEventListener(\"load\", loadedHandler);\r\n } else {\r\n // Document already loaded. The load events above likely won't be raised\r\n setTimeout(callback, 0);\r\n }\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { IS_RENDERED_PROPERTY } from \"../common/dom\";\r\n\r\n/**\r\n * Draws an identicon to a context.\r\n * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0).\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function drawIcon(ctx, hashOrValue, size, config) {\r\n if (!ctx) {\r\n throw new Error(\"No canvas specified.\");\r\n }\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n\r\n const canvas = ctx.canvas;\r\n if (canvas) {\r\n canvas[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","import canvasRenderer from \"canvas-renderer\";\r\nimport { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\n\r\n/**\r\n * Draws an identicon as PNG.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {Buffer} PNG data\r\n */\r\nexport function toPng(hashOrValue, size, config) {\r\n const canvas = canvasRenderer.createCanvas(size, size);\r\n const ctx = canvas.getContext(\"2d\");\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n \r\n return canvas.toPng({ \"Software\": \"Jdenticon\" });\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Prepares a measure to be used as a measure in an SVG path, by\r\n * rounding the measure to a single decimal. This reduces the file\r\n * size of the generated SVG with more than 50% in some cases.\r\n */\r\nfunction svgValue(value) {\r\n return ((value * 10 + 0.5) | 0) / 10;\r\n}\r\n\r\n/**\r\n * Represents an SVG path element.\r\n */\r\nexport class SvgPath {\r\n constructor() {\r\n /**\r\n * This property holds the data string (path.d) of the SVG path.\r\n * @type {string}\r\n */\r\n this.dataString = \"\";\r\n }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG path.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n let dataString = \"\";\r\n for (let i = 0; i < points.length; i++) {\r\n dataString += (i ? \"L\" : \"M\") + svgValue(points[i].x) + \" \" + svgValue(points[i].y);\r\n }\r\n this.dataString += dataString + \"Z\";\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG path.\r\n * @param {import('../point').Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const sweepFlag = counterClockwise ? 0 : 1,\r\n svgRadius = svgValue(diameter / 2),\r\n svgDiameter = svgValue(diameter),\r\n svgArc = \"a\" + svgRadius + \",\" + svgRadius + \" 0 1,\" + sweepFlag + \" \";\r\n \r\n this.dataString += \r\n \"M\" + svgValue(point.x) + \" \" + svgValue(point.y + diameter / 2) +\r\n svgArc + svgDiameter + \",0\" + \r\n svgArc + (-svgDiameter) + \",0\";\r\n }\r\n}\r\n\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SvgPath } from \"./svgPath\";\r\nimport { parseHex } from \"../../common/parseHex\";\r\n\r\n/**\r\n * @typedef {import(\"../point\").Point} Point\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import(\"./svgElement\").SvgElement} SvgElement\r\n * @typedef {import(\"./svgWriter\").SvgWriter} SvgWriter\r\n */\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n * @implements {Renderer}\r\n */\r\nexport class SvgRenderer {\r\n /**\r\n * @param {SvgElement|SvgWriter} target \r\n */\r\n constructor(target) {\r\n /**\r\n * @type {SvgPath}\r\n * @private\r\n */\r\n this._path;\r\n\r\n /**\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._pathsByColor = { };\r\n\r\n /**\r\n * @type {SvgElement|SvgWriter}\r\n * @private\r\n */\r\n this._target = target;\r\n\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = target.iconSize;\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const match = /^(#......)(..)?/.exec(fillColor),\r\n opacity = match[2] ? parseHex(match[2], 0) / 255 : 1;\r\n this._target.setBackground(match[1], opacity);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n */\r\n beginShape(color) {\r\n this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath());\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape.\r\n */\r\n endShape() { }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n this._path.addPolygon(points);\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n this._path.addCircle(point, diameter, counterClockwise);\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() { \r\n const pathsByColor = this._pathsByColor;\r\n for (let color in pathsByColor) {\r\n // hasOwnProperty cannot be shadowed in pathsByColor\r\n // eslint-disable-next-line no-prototype-builtins\r\n if (pathsByColor.hasOwnProperty(color)) {\r\n this._target.appendPath(color, pathsByColor[color].dataString);\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const SVG_CONSTANTS = {\r\n XMLNS: \"http://www.w3.org/2000/svg\",\r\n WIDTH: \"width\",\r\n HEIGHT: \"height\",\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgWriter {\r\n /**\r\n * @param {number} iconSize - Icon width and height in pixels.\r\n */\r\n constructor(iconSize) {\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = iconSize;\r\n\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n this._s =\r\n '';\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n this._s += '';\r\n }\r\n }\r\n\r\n /**\r\n * Writes a path to the SVG string.\r\n * @param {string} color Fill color on format #rrggbb.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n this._s += '';\r\n }\r\n\r\n /**\r\n * Gets the rendered image as an SVG string.\r\n */\r\n toString() {\r\n return this._s + \"\";\r\n }\r\n}\r\n","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgWriter } from \"../renderer/svg/svgWriter\";\r\n\r\n/**\r\n * Draws an identicon as an SVG string.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {string} SVG string\r\n */\r\nexport function toSvg(hashOrValue, size, config) {\r\n const writer = new SvgWriter(size);\r\n iconGenerator(new SvgRenderer(writer), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue),\r\n config);\r\n return writer.toString();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// This file is compiled to dist/jdenticon-node.mjs\r\n\r\nif (typeof process === \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof document !== \"undefined\"\r\n) {\r\n console.warn(\r\n \"Jdenticon: 'dist/jdenticon-node.mjs' is only intended for Node.js environments and will increase your \" +\r\n \"bundle size when included in browser bundles. If you want to run Jdenticon in the browser, please add a \" +\r\n \"reference to 'dist/jdenticon.js' or 'dist/jdenticon.min.js' instead.\");\r\n}\r\n\r\nexport { configure } from \"./apis/configure\";\r\nexport { drawIcon } from \"./apis/drawIcon\";\r\nexport { toPng } from \"./apis/toPng\";\r\nexport { toSvg } from \"./apis/toSvg\";\r\n\r\n/**\r\n * Specifies the version of the Jdenticon package in use.\r\n * @type {string}\r\n */\r\nexport const version = \"#version#\";\r\n\r\n/**\r\n * Specifies which bundle of Jdenticon that is used.\r\n * @type {string}\r\n */\r\nexport const bundle = \"node-esm\";\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\nexport function update() {\r\n throw new Error(\"jdenticon.update() is not supported on Node.js.\");\r\n}\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\nexport function updateCanvas() {\r\n throw new Error(\"jdenticon.updateCanvas() is not supported on Node.js.\");\r\n}\r\n\r\n/**\r\n * @throws {Error}\r\n */\r\nexport function updateSvg() {\r\n throw new Error(\"jdenticon.updateSvg() is not supported on Node.js.\");\r\n}\r\n","\n//# sourceMappingURL=jdenticon-node.mjs.map\n"]} \ No newline at end of file diff --git a/jdenticon-js/dist/jdenticon.js b/jdenticon-js/dist/jdenticon.js new file mode 100644 index 0000000..8edeb74 --- /dev/null +++ b/jdenticon-js/dist/jdenticon.js @@ -0,0 +1,1507 @@ +/** + * Jdenticon 3.3.0 + * http://jdenticon.com + * + * Built: 2024-05-10T09:48:41.921Z + * + * MIT License + * + * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi + * + * 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. + */ + +(function (umdGlobal, factory) { + var jdenticon = factory(umdGlobal); + + // Node.js + if (typeof module !== "undefined" && "exports" in module) { + module["exports"] = jdenticon; + } + // RequireJS + else if (typeof define === "function" && define["amd"]) { + define([], function () { return jdenticon; }); + } + // No module loader + else { + umdGlobal["jdenticon"] = jdenticon; + } +})(typeof self !== "undefined" ? self : this, function (umdGlobal) { +'use strict'; + +/** + * Parses a substring of the hash as a number. + * @param {number} startPosition + * @param {number=} octets + */ +function parseHex(hash, startPosition, octets) { + return parseInt(hash.substr(startPosition, octets), 16); +} + +function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; +} + +function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); +} + +/** + * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported. + * @returns {string} + */ +function parseColor(color) { + if (/^#[0-9a-f]{3,8}$/i.test(color)) { + var result; + var colorLength = color.length; + + if (colorLength < 6) { + var r = color[1], + g = color[2], + b = color[3], + a = color[4] || ""; + result = "#" + r + r + g + g + b + b + a + a; + } + if (colorLength == 7 || colorLength > 8) { + result = color; + } + + return result; + } +} + +/** + * Converts a hexadecimal color to a CSS3 compatible color. + * @param {string} hexColor Color on the format "#RRGGBB" or "#RRGGBBAA" + * @returns {string} + */ +function toCss3Color(hexColor) { + var a = parseHex(hexColor, 7, 2); + var result; + + if (isNaN(a)) { + result = hexColor; + } else { + var r = parseHex(hexColor, 1, 2), + g = parseHex(hexColor, 3, 2), + b = parseHex(hexColor, 5, 2); + result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")"; + } + + return result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function hsl(hue, saturation, lightness) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + var result; + + if (saturation == 0) { + var partialHex = decToHex(lightness * 255); + result = partialHex + partialHex + partialHex; + } + else { + var m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation, + m1 = lightness * 2 - m2; + result = + hueToRgb(m1, m2, hue * 6 + 2) + + hueToRgb(m1, m2, hue * 6) + + hueToRgb(m1, m2, hue * 6 - 2); + } + + return "#" + result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +function correctedHsl(hue, saturation, lightness) { + // The corrector specifies the perceived middle lightness for each hue + var correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(hue * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; + + return hsl(hue, saturation, lightness); +} + +/* global umdGlobal */ + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. +var GLOBAL = umdGlobal; + +/** + * @typedef {Object} ParsedConfiguration + * @property {number} colorSaturation + * @property {number} grayscaleSaturation + * @property {string} backColor + * @property {number} iconPadding + * @property {function(number):number} hue + * @property {function(number):number} colorLightness + * @property {function(number):number} grayscaleLightness + */ + +var CONFIG_PROPERTIES = { + G/*GLOBAL*/: "jdenticon_config", + n/*MODULE*/: "config", +}; + +var rootConfigurationHolder = {}; + +/** + * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console + * when it is being used. + * @param {!Object} rootObject + */ +function defineConfigProperty(rootObject) { + rootConfigurationHolder = rootObject; +} + +/** + * Sets a new icon style configuration. The new configuration is not merged with the previous one. * + * @param {Object} newConfiguration - New configuration object. + */ +function configure(newConfiguration) { + if (arguments.length) { + rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] = newConfiguration; + } + return rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/]; +} + +/** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A + * local configuration overrides the global configuration in it entirety. This parameter can for backward + * compatibility also contain a padding value. A padding value only overrides the global padding, not the + * entire global configuration. + * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor + * explicitly to the API method. + * @returns {ParsedConfiguration} + */ +function getConfiguration(paddingOrLocalConfig, defaultPadding) { + var configObject = + typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || + rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] || + GLOBAL[CONFIG_PROPERTIES.G/*GLOBAL*/] || + { }, + + lightnessConfig = configObject["lightness"] || { }, + + // In versions < 2.1.0 there was no grayscale saturation - + // saturation was the color saturation. + saturation = configObject["saturation"] || { }, + colorSaturation = "color" in saturation ? saturation["color"] : saturation, + grayscaleSaturation = saturation["grayscale"], + + backColor = configObject["backColor"], + padding = configObject["padding"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultRange) { + var range = lightnessConfig[configName]; + + // Check if the lightness range is an array-like object. This way we ensure the + // array contain two values at the same time. + if (!(range && range.length > 1)) { + range = defaultRange; + } + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + /** + * Gets a hue allowed by the configured hue restriction, + * provided the originally computed hue. + */ + function hueFunction(originalHue) { + var hueConfig = configObject["hues"]; + var hue; + + // Check if 'hues' is an array-like object. This way we also ensure that + // the array is not empty, which would mean no hue restriction. + if (hueConfig && hueConfig.length > 0) { + // originalHue is in the range [0, 1] + // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. + hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; + } + + return typeof hue == "number" ? + + // A hue was specified. We need to convert the hue from + // degrees on any turn - e.g. 746° is a perfectly valid hue - + // to turns in the range [0, 1). + ((((hue / 360) % 1) + 1) % 1) : + + // No hue configured => use original hue + originalHue; + } + + return { + X/*hue*/: hueFunction, + p/*colorSaturation*/: typeof colorSaturation == "number" ? colorSaturation : 0.5, + H/*grayscaleSaturation*/: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, + q/*colorLightness*/: lightness("color", [0.4, 0.8]), + I/*grayscaleLightness*/: lightness("grayscale", [0.3, 0.9]), + J/*backColor*/: parseColor(backColor), + Y/*iconPadding*/: + typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : + typeof padding == "number" ? padding : + defaultPadding + } +} + +var ICON_TYPE_SVG = 1; + +var ICON_TYPE_CANVAS = 2; + +var ATTRIBUTES = { + t/*HASH*/: "data-jdenticon-hash", + o/*VALUE*/: "data-jdenticon-value" +}; + +var IS_RENDERED_PROPERTY = "jdenticonRendered"; + +var ICON_SELECTOR = "[" + ATTRIBUTES.t/*HASH*/ +"],[" + ATTRIBUTES.o/*VALUE*/ +"]"; + +var documentQuerySelectorAll = /** @type {!Function} */ ( + typeof document !== "undefined" && document.querySelectorAll.bind(document)); + +function getIdenticonType(el) { + if (el) { + var tagName = el["tagName"]; + + if (/^svg$/i.test(tagName)) { + return ICON_TYPE_SVG; + } + + if (/^canvas$/i.test(tagName) && "getContext" in el) { + return ICON_TYPE_CANVAS; + } + } +} + +function whenDocumentIsReady(/** @type {Function} */ callback) { + function loadedHandler() { + document.removeEventListener("DOMContentLoaded", loadedHandler); + window.removeEventListener("load", loadedHandler); + setTimeout(callback, 0); // Give scripts a chance to run + } + + if (typeof document !== "undefined" && + typeof window !== "undefined" && + typeof setTimeout !== "undefined" + ) { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", loadedHandler); + window.addEventListener("load", loadedHandler); + } else { + // Document already loaded. The load events above likely won't be raised + setTimeout(callback, 0); + } + } +} + +function observer(updateCallback) { + if (typeof MutationObserver != "undefined") { + var mutationObserver = new MutationObserver(function onmutation(mutations) { + for (var mutationIndex = 0; mutationIndex < mutations.length; mutationIndex++) { + var mutation = mutations[mutationIndex]; + var addedNodes = mutation.addedNodes; + + for (var addedNodeIndex = 0; addedNodes && addedNodeIndex < addedNodes.length; addedNodeIndex++) { + var addedNode = addedNodes[addedNodeIndex]; + + // Skip other types of nodes than element nodes, since they might not support + // the querySelectorAll method => runtime error. + if (addedNode.nodeType == 1) { + if (getIdenticonType(addedNode)) { + updateCallback(addedNode); + } + else { + var icons = /** @type {Element} */(addedNode).querySelectorAll(ICON_SELECTOR); + for (var iconIndex = 0; iconIndex < icons.length; iconIndex++) { + updateCallback(icons[iconIndex]); + } + } + } + } + + if (mutation.type == "attributes" && getIdenticonType(mutation.target)) { + updateCallback(mutation.target); + } + } + }); + + mutationObserver.observe(document.body, { + "childList": true, + "attributes": true, + "attributeFilter": [ATTRIBUTES.o/*VALUE*/, ATTRIBUTES.t/*HASH*/, "width", "height"], + "subtree": true, + }); + } +} + +/** + * Represents a point. + */ +function Point(x, y) { + this.x = x; + this.y = y; +} + +/** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + */ +function Transform(x, y, size, rotation) { + this.u/*_x*/ = x; + this.v/*_y*/ = y; + this.K/*_size*/ = size; + this.Z/*_rotation*/ = rotation; +} + +/** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ +Transform.prototype.L/*transformIconPoint*/ = function transformIconPoint (x, y, w, h) { + var right = this.u/*_x*/ + this.K/*_size*/, + bottom = this.v/*_y*/ + this.K/*_size*/, + rotation = this.Z/*_rotation*/; + return rotation === 1 ? new Point(right - y - (h || 0), this.v/*_y*/ + x) : + rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + rotation === 3 ? new Point(this.u/*_x*/ + y, bottom - x - (w || 0)) : + new Point(this.u/*_x*/ + x, this.v/*_y*/ + y); +}; + +var NO_TRANSFORM = new Transform(0, 0, 0, 0); + + + +/** + * Provides helper functions for rendering common basic shapes. + */ +function Graphics(renderer) { + /** + * @type {Renderer} + * @private + */ + this.M/*_renderer*/ = renderer; + + /** + * @type {Transform} + */ + this.A/*currentTransform*/ = NO_TRANSFORM; +} +var Graphics__prototype = Graphics.prototype; + +/** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ +Graphics__prototype.g/*addPolygon*/ = function addPolygon (points, invert) { + var this$1 = this; + + var di = invert ? -2 : 2, + transformedPoints = []; + + for (var i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(this$1.A/*currentTransform*/.L/*transformIconPoint*/(points[i], points[i + 1])); + } + + this.M/*_renderer*/.g/*addPolygon*/(transformedPoints); +}; + +/** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ +Graphics__prototype.h/*addCircle*/ = function addCircle (x, y, size, invert) { + var p = this.A/*currentTransform*/.L/*transformIconPoint*/(x, y, size, size); + this.M/*_renderer*/.h/*addCircle*/(p, size, invert); +}; + +/** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ +Graphics__prototype.i/*addRectangle*/ = function addRectangle (x, y, w, h, invert) { + this.g/*addPolygon*/([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); +}; + +/** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ +Graphics__prototype.j/*addTriangle*/ = function addTriangle (x, y, w, h, r, invert) { + var points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.g/*addPolygon*/(points, invert); +}; + +/** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ +Graphics__prototype.N/*addRhombus*/ = function addRhombus (x, y, w, h, invert) { + this.g/*addPolygon*/([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); +}; + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + * @param {number} positionIndex + */ +function centerShape(index, g, cell, positionIndex) { + index = index % 14; + + var k, m, w, h, inner, outer; + + !index ? ( + k = cell * 0.42, + g.g/*addPolygon*/([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ])) : + + index == 1 ? ( + w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8), + + g.j/*addTriangle*/(cell - w, 0, w, h, 2)) : + + index == 2 ? ( + w = 0 | (cell / 3), + g.i/*addRectangle*/(w, w, cell - w, cell - w)) : + + index == 3 ? ( + inner = cell * 0.1, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)), + + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + g.i/*addRectangle*/(outer, outer, cell - inner - outer, cell - inner - outer)) : + + index == 4 ? ( + m = 0 | (cell * 0.15), + w = 0 | (cell * 0.5), + g.h/*addCircle*/(cell - w - m, cell - w - m, w)) : + + index == 5 ? ( + inner = cell * 0.1, + outer = inner * 4, + + // Align edge to nearest pixel in large icons + outer > 3 && (outer = 0 | outer), + + g.i/*addRectangle*/(0, 0, cell, cell), + g.g/*addPolygon*/([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true)) : + + index == 6 ? + g.g/*addPolygon*/([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]) : + + index == 7 ? + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 8 ? ( + g.i/*addRectangle*/(0, 0, cell, cell / 2), + g.i/*addRectangle*/(0, cell / 2, cell / 2, cell / 2), + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 1)) : + + index == 9 ? ( + inner = cell * 0.14, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)), + + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + g.i/*addRectangle*/(0, 0, cell, cell), + g.i/*addRectangle*/(outer, outer, cell - outer - inner, cell - outer - inner, true)) : + + index == 10 ? ( + inner = cell * 0.12, + outer = inner * 3, + + g.i/*addRectangle*/(0, 0, cell, cell), + g.h/*addCircle*/(outer, outer, cell - inner - outer, true)) : + + index == 11 ? + g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 12 ? ( + m = cell * 0.25, + g.i/*addRectangle*/(0, 0, cell, cell), + g.N/*addRhombus*/(m, m, cell - m, cell - m, true)) : + + // 13 + ( + !positionIndex && ( + m = cell * 0.4, w = cell * 1.2, + g.h/*addCircle*/(m, m, w) + ) + ); +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + */ +function outerShape(index, g, cell) { + index = index % 4; + + var m; + + !index ? + g.j/*addTriangle*/(0, 0, cell, cell, 0) : + + index == 1 ? + g.j/*addTriangle*/(0, cell / 2, cell, cell / 2, 0) : + + index == 2 ? + g.N/*addRhombus*/(0, 0, cell, cell) : + + // 3 + ( + m = cell / 6, + g.h/*addCircle*/(m, m, cell - 2 * m) + ); +} + +/** + * Gets a set of identicon color candidates for a specified hue and config. + * @param {number} hue + * @param {ParsedConfiguration} config + */ +function colorTheme(hue, config) { + hue = config.X/*hue*/(hue); + return [ + // Dark gray + correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(0)), + // Mid color + correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0.5)), + // Light gray + correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(1)), + // Light color + correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(1)), + // Dark color + correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0)) + ]; +} + +/** + * Draws an identicon to a specified renderer. + * @param {Renderer} renderer + * @param {string} hash + * @param {Object|number=} config + */ +function iconGenerator(renderer, hash, config) { + var parsedConfig = getConfiguration(config, 0.08); + + // Set background color + if (parsedConfig.J/*backColor*/) { + renderer.m/*setBackground*/(parsedConfig.J/*backColor*/); + } + + // Calculate padding and round to nearest integer + var size = renderer.k/*iconSize*/; + var padding = (0.5 + size * parsedConfig.Y/*iconPadding*/) | 0; + size -= padding * 2; + + var graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + var cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + var x = 0 | (padding + size / 2 - cell * 2); + var y = 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + var shapeIndex = parseHex(hash, index, 1); + var r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.O/*beginShape*/(availableColors[selectedColorIndexes[colorIndex]]); + + for (var i = 0; i < positions.length; i++) { + graphics.A/*currentTransform*/ = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.P/*endShape*/(); + } + + // AVAILABLE COLORS + var hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, parsedConfig), + + // The index of the selected colors + selectedColorIndexes = []; + + var index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (var i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (var i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); +} + +/** + * Computes a SHA1 hash for any value and returns it as a hexadecimal string. + * + * This function is optimized for minimal code size and rather short messages. + * + * @param {string} message + */ +function sha1(message) { + var HASH_SIZE_HALF_BYTES = 40; + var BLOCK_SIZE_WORDS = 16; + + // Variables + // `var` is used to be able to minimize the number of `var` keywords. + var i = 0, + f = 0, + + // Use `encodeURI` to UTF8 encode the message without any additional libraries + // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky + // since `unescape` is deprecated. + urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding + + // This can be changed to a preallocated Uint32Array array for greater performance and larger code size + data = [], + dataSize, + + hashBuffer = [], + + a = 0x67452301, + b = 0xefcdab89, + c = ~a, + d = ~b, + e = 0xc3d2e1f0, + hash = [a, b, c, d, e], + + blockStartIndex = 0, + hexHash = ""; + + /** + * Rotates the value a specified number of bits to the left. + * @param {number} value Value to rotate + * @param {number} shift Bit count to shift. + */ + function rotl(value, shift) { + return (value << shift) | (value >>> (32 - shift)); + } + + // Message data + for ( ; i < urlEncodedMessage.length; f++) { + data[f >> 2] = data[f >> 2] | + ( + ( + urlEncodedMessage[i] == "%" + // Percent encoded byte + ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16) + // Unencoded byte + : urlEncodedMessage.charCodeAt(i++) + ) + + // Read bytes in reverse order (big endian words) + << ((3 - (f & 3)) * 8) + ); + } + + // f is now the length of the utf8 encoded message + // 7 = 8 bytes (64 bit) for message size, -1 to round down + // >> 6 = integer division with block size + dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS; + + // Message size in bits. + // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least + // significant 32 bits are set. -8 is for the '1' bit padding byte. + data[dataSize - 1] = f * 8 - 8; + + // Compute hash + for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) { + for (i = 0; i < 80; i++) { + f = rotl(a, 5) + e + ( + // Ch + i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 : + + // Parity + i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 : + + // Maj + i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc : + + // Parity + (b ^ c ^ d) + 0xca62c1d6 + ) + ( + hashBuffer[i] = i < BLOCK_SIZE_WORDS + // Bitwise OR is used to coerse `undefined` to 0 + ? (data[blockStartIndex + i] | 0) + : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1) + ); + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = f; + } + + hash[0] = a = ((hash[0] + a) | 0); + hash[1] = b = ((hash[1] + b) | 0); + hash[2] = c = ((hash[2] + c) | 0); + hash[3] = d = ((hash[3] + d) | 0); + hash[4] = e = ((hash[4] + e) | 0); + } + + // Format hex hash + for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) { + hexHash += ( + ( + // Get word (2^3 half-bytes per word) + hash[i >> 3] >>> + + // Append half-bytes in reverse order + ((7 - (i & 7)) * 4) + ) + // Clamp to half-byte + & 0xf + ).toString(16); + } + + return hexHash; +} + +/** + * Inputs a value that might be a valid hash string for Jdenticon and returns it + * if it is determined valid, otherwise a falsy value is returned. + */ +function isValidHash(hashCandidate) { + return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate; +} + +/** + * Computes a hash for the specified value. Currently SHA1 is used. This function + * always returns a valid hash. + */ +function computeHash(value) { + return sha1(value == null ? "" : "" + value); +} + + + +/** + * Renderer redirecting drawing commands to a canvas context. + * @implements {Renderer} + */ +function CanvasRenderer(ctx, iconSize) { + var canvas = ctx.canvas; + var width = canvas.width; + var height = canvas.height; + + ctx.save(); + + if (!iconSize) { + iconSize = Math.min(width, height); + + ctx.translate( + ((width - iconSize) / 2) | 0, + ((height - iconSize) / 2) | 0); + } + + /** + * @private + */ + this.l/*_ctx*/ = ctx; + this.k/*iconSize*/ = iconSize; + + ctx.clearRect(0, 0, iconSize, iconSize); +} +var CanvasRenderer__prototype = CanvasRenderer.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ +CanvasRenderer__prototype.m/*setBackground*/ = function setBackground (fillColor) { + var ctx = this.l/*_ctx*/; + var iconSize = this.k/*iconSize*/; + + ctx.fillStyle = toCss3Color(fillColor); + ctx.fillRect(0, 0, iconSize, iconSize); +}; + +/** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} fillColor Fill color on format #rrggbb[aa]. + */ +CanvasRenderer__prototype.O/*beginShape*/ = function beginShape (fillColor) { + var ctx = this.l/*_ctx*/; + ctx.fillStyle = toCss3Color(fillColor); + ctx.beginPath(); +}; + +/** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ +CanvasRenderer__prototype.P/*endShape*/ = function endShape () { + this.l/*_ctx*/.fill(); +}; + +/** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ +CanvasRenderer__prototype.g/*addPolygon*/ = function addPolygon (points) { + var ctx = this.l/*_ctx*/; + ctx.moveTo(points[0].x, points[0].y); + for (var i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); +}; + +/** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ +CanvasRenderer__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) { + var ctx = this.l/*_ctx*/, + radius = diameter / 2; + ctx.moveTo(point.x + radius, point.y + radius); + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); +}; + +/** + * Called when the icon has been completely drawn. + */ +CanvasRenderer__prototype.finish = function finish () { + this.l/*_ctx*/.restore(); +}; + +/** + * Draws an identicon to a context. + * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0). + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function drawIcon(ctx, hashOrValue, size, config) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + var canvas = ctx.canvas; + if (canvas) { + canvas[IS_RENDERED_PROPERTY] = true; + } +} + +/** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ +function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; +} + +/** + * Represents an SVG path element. + */ +function SvgPath() { + /** + * This property holds the data string (path.d) of the SVG path. + * @type {string} + */ + this.B/*dataString*/ = ""; +} +var SvgPath__prototype = SvgPath.prototype; + +/** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ +SvgPath__prototype.g/*addPolygon*/ = function addPolygon (points) { + var dataString = ""; + for (var i = 0; i < points.length; i++) { + dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.B/*dataString*/ += dataString + "Z"; +}; + +/** + * Adds a circle with the current fill color to the SVG path. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ +SvgPath__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) { + var sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter), + svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " "; + + this.B/*dataString*/ += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + svgArc + svgDiameter + ",0" + + svgArc + (-svgDiameter) + ",0"; +}; + + + +/** + * Renderer producing SVG output. + * @implements {Renderer} + */ +function SvgRenderer(target) { + /** + * @type {SvgPath} + * @private + */ + this.C/*_path*/; + + /** + * @type {Object.} + * @private + */ + this.D/*_pathsByColor*/ = { }; + + /** + * @type {SvgElement|SvgWriter} + * @private + */ + this.R/*_target*/ = target; + + /** + * @type {number} + */ + this.k/*iconSize*/ = target.k/*iconSize*/; +} +var SvgRenderer__prototype = SvgRenderer.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ +SvgRenderer__prototype.m/*setBackground*/ = function setBackground (fillColor) { + var match = /^(#......)(..)?/.exec(fillColor), + opacity = match[2] ? parseHex(match[2], 0) / 255 : 1; + this.R/*_target*/.m/*setBackground*/(match[1], opacity); +}; + +/** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ +SvgRenderer__prototype.O/*beginShape*/ = function beginShape (color) { + this.C/*_path*/ = this.D/*_pathsByColor*/[color] || (this.D/*_pathsByColor*/[color] = new SvgPath()); +}; + +/** + * Marks the end of the currently drawn shape. + */ +SvgRenderer__prototype.P/*endShape*/ = function endShape () { }; + +/** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ +SvgRenderer__prototype.g/*addPolygon*/ = function addPolygon (points) { + this.C/*_path*/.g/*addPolygon*/(points); +}; + +/** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ +SvgRenderer__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) { + this.C/*_path*/.h/*addCircle*/(point, diameter, counterClockwise); +}; + +/** + * Called when the icon has been completely drawn. + */ +SvgRenderer__prototype.finish = function finish () { + var this$1 = this; + + var pathsByColor = this.D/*_pathsByColor*/; + for (var color in pathsByColor) { + // hasOwnProperty cannot be shadowed in pathsByColor + // eslint-disable-next-line no-prototype-builtins + if (pathsByColor.hasOwnProperty(color)) { + this$1.R/*_target*/.S/*appendPath*/(color, pathsByColor[color].B/*dataString*/); + } + } +}; + +var SVG_CONSTANTS = { + T/*XMLNS*/: "http://www.w3.org/2000/svg", + U/*WIDTH*/: "width", + V/*HEIGHT*/: "height", +}; + +/** + * Renderer producing SVG output. + */ +function SvgWriter(iconSize) { + /** + * @type {number} + */ + this.k/*iconSize*/ = iconSize; + + /** + * @type {string} + * @private + */ + this.F/*_s*/ = + ''; +} +var SvgWriter__prototype = SvgWriter.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ +SvgWriter__prototype.m/*setBackground*/ = function setBackground (fillColor, opacity) { + if (opacity) { + this.F/*_s*/ += ''; + } +}; + +/** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #rrggbb. + * @param {string} dataString The SVG path data string. + */ +SvgWriter__prototype.S/*appendPath*/ = function appendPath (color, dataString) { + this.F/*_s*/ += ''; +}; + +/** + * Gets the rendered image as an SVG string. + */ +SvgWriter__prototype.toString = function toString () { + return this.F/*_s*/ + ""; +}; + +/** + * Draws an identicon as an SVG string. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {string} SVG string + */ +function toSvg(hashOrValue, size, config) { + var writer = new SvgWriter(size); + iconGenerator(new SvgRenderer(writer), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + return writer.toString(); +} + +/** + * Creates a new element and adds it to the specified parent. + * @param {Element} parentNode + * @param {string} name + * @param {...(string|number)} keyValuePairs + */ +function SvgElement_append(parentNode, name) { + var keyValuePairs = [], len = arguments.length - 2; + while ( len-- > 0 ) keyValuePairs[ len ] = arguments[ len + 2 ]; + + var el = document.createElementNS(SVG_CONSTANTS.T/*XMLNS*/, name); + + for (var i = 0; i + 1 < keyValuePairs.length; i += 2) { + el.setAttribute( + /** @type {string} */(keyValuePairs[i]), + /** @type {string} */(keyValuePairs[i + 1]) + ); + } + + parentNode.appendChild(el); +} + + +/** + * Renderer producing SVG output. + */ +function SvgElement(element) { + // Don't use the clientWidth and clientHeight properties on SVG elements + // since Firefox won't serve a proper value of these properties on SVG + // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811) + // Instead use 100px as a hardcoded size (the svg viewBox will rescale + // the icon to the correct dimensions) + var iconSize = this.k/*iconSize*/ = Math.min( + (Number(element.getAttribute(SVG_CONSTANTS.U/*WIDTH*/)) || 100), + (Number(element.getAttribute(SVG_CONSTANTS.V/*HEIGHT*/)) || 100) + ); + + /** + * @type {Element} + * @private + */ + this.W/*_el*/ = element; + + // Clear current SVG child elements + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + // Set viewBox attribute to ensure the svg scales nicely. + element.setAttribute("viewBox", "0 0 " + iconSize + " " + iconSize); + element.setAttribute("preserveAspectRatio", "xMidYMid meet"); +} +var SvgElement__prototype = SvgElement.prototype; + +/** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ +SvgElement__prototype.m/*setBackground*/ = function setBackground (fillColor, opacity) { + if (opacity) { + SvgElement_append(this.W/*_el*/, "rect", + SVG_CONSTANTS.U/*WIDTH*/, "100%", + SVG_CONSTANTS.V/*HEIGHT*/, "100%", + "fill", fillColor, + "opacity", opacity); + } +}; + +/** + * Appends a path to the SVG element. + * @param {string} color Fill color on format #xxxxxx. + * @param {string} dataString The SVG path data string. + */ +SvgElement__prototype.S/*appendPath*/ = function appendPath (color, dataString) { + SvgElement_append(this.W/*_el*/, "path", + "fill", color, + "d", dataString); +}; + +/** + * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute. + */ +function updateAll() { + if (documentQuerySelectorAll) { + update(ICON_SELECTOR); + } +} + +/** + * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute that have not already + * been rendered. + */ +function updateAllConditional() { + if (documentQuerySelectorAll) { + /** @type {NodeListOf} */ + var elements = documentQuerySelectorAll(ICON_SELECTOR); + + for (var i = 0; i < elements.length; i++) { + var el = elements[i]; + if (!el[IS_RENDERED_PROPERTY]) { + update(el); + } + } + } +} + +/** + * Updates the identicon in the specified `` or `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function update(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType) { + return iconType == ICON_TYPE_SVG ? + new SvgRenderer(new SvgElement(el)) : + new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified canvas or svg elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number|undefined} config + * @param {function(Element,number):Renderer} rendererFactory - Factory function for creating an icon renderer. + */ +function renderDomElement(el, hashOrValue, config, rendererFactory) { + if (typeof el === "string") { + if (documentQuerySelectorAll) { + var elements = documentQuerySelectorAll(el); + for (var i = 0; i < elements.length; i++) { + renderDomElement(elements[i], hashOrValue, config, rendererFactory); + } + } + return; + } + + // Hash selection. The result from getValidHash or computeHash is + // accepted as a valid hash. + var hash = + // 1. Explicit valid hash + isValidHash(hashOrValue) || + + // 2. Explicit value (`!= null` catches both null and undefined) + hashOrValue != null && computeHash(hashOrValue) || + + // 3. `data-jdenticon-hash` attribute + isValidHash(el.getAttribute(ATTRIBUTES.t/*HASH*/)) || + + // 4. `data-jdenticon-value` attribute. + // We want to treat an empty attribute as an empty value. + // Some browsers return empty string even if the attribute + // is not specified, so use hasAttribute to determine if + // the attribute is specified. + el.hasAttribute(ATTRIBUTES.o/*VALUE*/) && computeHash(el.getAttribute(ATTRIBUTES.o/*VALUE*/)); + + if (!hash) { + // No hash specified. Don't render an icon. + return; + } + + var renderer = rendererFactory(el, getIdenticonType(el)); + if (renderer) { + // Draw icon + iconGenerator(renderer, hash, config); + el[IS_RENDERED_PROPERTY] = true; + } +} + +/** + * Renders an identicon for all matching supported elements. + * + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. If not + * specified the `data-jdenticon-hash` and `data-jdenticon-value` attributes of each element will be + * evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +function jdenticonJqueryPlugin(hashOrValue, config) { + this["each"](function (index, el) { + update(el, hashOrValue, config); + }); + return this; +} + +// This file is compiled to dist/jdenticon.js and dist/jdenticon.min.js + +var jdenticon = updateAll; + +defineConfigProperty(jdenticon); + +// Export public API +jdenticon["configure"] = configure; +jdenticon["drawIcon"] = drawIcon; +jdenticon["toSvg"] = toSvg; +jdenticon["update"] = update; +jdenticon["updateCanvas"] = update; +jdenticon["updateSvg"] = update; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +jdenticon["version"] = "3.3.0"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +jdenticon["bundle"] = "browser-umd"; + +// Basic jQuery plugin +var jQuery = GLOBAL["jQuery"]; +if (jQuery) { + jQuery["fn"]["jdenticon"] = jdenticonJqueryPlugin; +} + +/** + * This function is called once upon page load. + */ +function jdenticonStartup() { + var replaceMode = ( + jdenticon[CONFIG_PROPERTIES.n/*MODULE*/] || + GLOBAL[CONFIG_PROPERTIES.G/*GLOBAL*/] || + { } + )["replaceMode"]; + + if (replaceMode != "never") { + updateAllConditional(); + + if (replaceMode == "observe") { + observer(update); + } + } +} + +// Schedule to render all identicons on the page once it has been loaded. +whenDocumentIsReady(jdenticonStartup); + +return jdenticon; + +}); \ No newline at end of file diff --git a/jdenticon-js/dist/jdenticon.min.js b/jdenticon-js/dist/jdenticon.min.js new file mode 100644 index 0000000..99f4037 --- /dev/null +++ b/jdenticon-js/dist/jdenticon.min.js @@ -0,0 +1,3 @@ +// Jdenticon 3.3.0 | jdenticon.com | MIT licensed | (c) 2014-2024 Daniel Mester Pirttijärvi +!function(t,n){var e=function(t){"use strict";function n(t,n,e){return parseInt(t.substr(n,e),16)}function e(t){return(t|=0)<0?"00":t<16?"0"+t.toString(16):t<256?t.toString(16):"ff"}function i(t,n,i){return e(255*((i=i<0?i+6:i>6?i-6:i)<1?t+(n-t)*i:i<3?n:i<4?t+(n-t)*(4-i):t))}function r(t){if(/^#[0-9a-f]{3,8}$/i.test(t)){var n,e=t.length;if(e<6){var i=t[1],r=t[2],o=t[3],u=t[4]||"";n="#"+i+i+r+r+o+o+u+u}return(7==e||e>8)&&(n=t),n}}function o(t){var e,i=n(t,7,2);isNaN(i)?e=t:e="rgba("+n(t,1,2)+","+n(t,3,2)+","+n(t,5,2)+","+(i/255).toFixed(2)+")";return e}function u(t,n,r){var o;if(0==n){var u=e(255*r);o=u+u+u}else{var f=r<=.5?r*(n+1):r+n-r*n,a=2*r-f;o=i(a,f,6*t+2)+i(a,f,6*t)+i(a,f,6*t-2)}return"#"+o}function f(t,n,e){var i=[.55,.5,.5,.46,.6,.55,.55][6*t+.5|0];return u(t,n,e=e<.5?e*i*2:i+(e-.5)*(1-i)*2)}var a=t,s={G:"jdenticon_config",n:"config"},h={};function c(t){h=t}function v(t){return arguments.length&&(h[s.n]=t),h[s.n]}function d(t,n){var e="object"==typeof t&&t||h[s.n]||a[s.G]||{},i=e.lightness||{},o=e.saturation||{},u="color"in o?o.color:o,f=o.grayscale,c=e.backColor,v=e.padding;function d(t,n){var e=i[t];return e&&e.length>1||(e=n),function(t){return(t=e[0]+t*(e[1]-e[0]))<0?0:t>1?1:t}}function l(t){var n,i=e.hues;return i&&i.length>0&&(n=i[0|.999*t*i.length]),"number"==typeof n?(n/360%1+1)%1:t}return{X:l,p:"number"==typeof u?u:.5,H:"number"==typeof f?f:0,q:d("color",[.4,.8]),I:d("grayscale",[.3,.9]),J:r(c),Y:"number"==typeof t?t:"number"==typeof v?v:n}}var l=1,g=2,p={t:"data-jdenticon-hash",o:"data-jdenticon-value"},y="jdenticonRendered",m="["+p.t+"],["+p.o+"]",w="undefined"!=typeof document&&document.querySelectorAll.bind(document);function b(t){if(t){var n=t.tagName;if(/^svg$/i.test(n))return l;if(/^canvas$/i.test(n)&&"getContext"in t)return g}}function x(t){function n(){document.removeEventListener("DOMContentLoaded",n),window.removeEventListener("load",n),setTimeout(t,0)}"undefined"!=typeof document&&"undefined"!=typeof window&&"undefined"!=typeof setTimeout&&("loading"===document.readyState?(document.addEventListener("DOMContentLoaded",n),window.addEventListener("load",n)):setTimeout(t,0))}function A(t){"undefined"!=typeof MutationObserver&&new MutationObserver((function(n){for(var e=0;e1?0|a:a>.5?1:a,n.i(s,s,e-a-s,e-a-s)):4==t?(o=0|.15*e,u=0|.5*e,n.h(e-u-o,e-u-o,u)):5==t?((s=4*(a=.1*e))>3&&(s|=0),n.i(0,0,e,e),n.g([s,s,e-a,s,s+(e-s-a)/2,e-a],!0)):6==t?n.g([0,0,e,0,e,.7*e,.4*e,.4*e,.7*e,e,0,e]):7==t?n.j(e/2,e/2,e/2,e/2,3):8==t?(n.i(0,0,e,e/2),n.i(0,e/2,e/2,e/2),n.j(e/2,e/2,e/2,e/2,1)):9==t?(a=.14*e,s=e<4?1:e<6?2:0|.35*e,a=e<8?a:0|a,n.i(0,0,e,e),n.i(s,s,e-s-a,e-s-a,!0)):10==t?(s=3*(a=.12*e),n.i(0,0,e,e),n.h(s,s,e-a-s,!0)):11==t?n.j(e/2,e/2,e/2,e/2,3):12==t?(o=.25*e,n.i(0,0,e,e),n.N(o,o,e-o,e-o,!0)):!i&&(o=.4*e,u=1.2*e,n.h(o,o,u)):(r=.42*e,n.g([0,0,e,0,e,e-2*r,e-r,e,0,e]))}function O(t,n,e){var i;(t%=4)?1==t?n.j(0,e/2,e,e/2,0):2==t?n.N(0,0,e,e):(i=e/6,n.h(i,i,e-2*i)):n.j(0,0,e,e,0)}function T(t,n){return[f(t=n.X(t),n.H,n.I(0)),f(t,n.p,n.q(.5)),f(t,n.H,n.I(1)),f(t,n.p,n.q(1)),f(t,n.p,n.q(0))]}function k(t,e,i){var r=d(i,.08);r.J&&t.m(r.J);var o=t.k,u=.5+o*r.Y|0;o-=2*u;var f=new M(t),a=0|o/4,s=0|u+o/2-2*a,h=0|u+o/2-2*a;function c(i,r,o,u,c){var v=n(e,o,1),d=u?n(e,u,1):0;t.O(l[g[i]]);for(var p=0;p=0)for(var n=0;n=0)return!0}for(var y=0;y<3;y++)v=n(e,8+y,1)%l.length,(p([0,4])||p([2,3]))&&(v=1),g.push(v);c(0,O,2,3,[[1,0],[2,0],[2,3],[1,3],[0,1],[3,1],[3,2],[0,2]]),c(1,O,4,5,[[0,0],[3,0],[3,3],[0,3]]),c(2,N,1,null,[[1,1],[2,1],[2,2],[1,2]]),t.finish()}function I(t){var n,e=40,i=16,r=0,o=0,u=encodeURI(t)+"%80",f=[],a=[],s=1732584193,h=4023233417,c=~s,v=~h,d=3285377520,l=[s,h,c,v,d],g=0,p="";function y(t,n){return t<>>32-n}for(;r>2]=f[o>>2]|("%"==u[r]?parseInt(u.substring(r+1,r+=3),16):u.charCodeAt(r++))<<8*(3-(3&o));for(f[(n=(1+(o+7>>6))*i)-1]=8*o-8;g>3]>>>4*(7-(7&r))&15).toString(16);return p}function P(t){return/^[0-9a-f]{11,}$/i.test(t)&&t}function R(t){return I(null==t?"":""+t)}function F(t,n){var e=t.canvas,i=e.width,r=e.height;t.save(),n||(n=Math.min(i,r),t.translate((i-n)/2|0,(r-n)/2|0)),this.l=t,this.k=n,t.clearRect(0,0,n,n)}L.g=function(t,n){for(var e=this,i=n?-2:2,r=[],o=n?t.length-2:0;o=0;o+=i)r.push(e.A.L(t[o],t[o+1]));this.M.g(r)},L.h=function(t,n,e,i){var r=this.A.L(t,n,e,e);this.M.h(r,e,i)},L.i=function(t,n,e,i,r){this.g([t,n,t+e,n,t+e,n+i,t,n+i],r)},L.j=function(t,n,e,i,r,o){var u=[t+e,n,t+e,n+i,t,n+i,t,n];u.splice((r||0)%4*2,2),this.g(u,o)},L.N=function(t,n,e,i,r){this.g([t+e/2,n,t+e,n+i/2,t+e/2,n+i,t,n+i/2],r)};var q=F.prototype;function B(t,n,e,i){if(!t)throw new Error("No canvas specified.");k(new F(t,e),P(n)||R(n),i);var r=t.canvas;r&&(r[y]=!0)}function D(t){return(10*t+.5|0)/10}function E(){this.B=""}q.m=function(t){var n=this.l,e=this.k;n.fillStyle=o(t),n.fillRect(0,0,e,e)},q.O=function(t){var n=this.l;n.fillStyle=o(t),n.beginPath()},q.P=function(){this.l.fill()},q.g=function(t){var n=this.l;n.moveTo(t[0].x,t[0].y);for(var e=1;e'}var K=J.prototype;function V(t,n,e){var i=new J(n);return k(new $(i),P(t)||R(t),e),i.toString()}function W(t,n){for(var e=[],i=arguments.length-2;i-- >0;)e[i]=arguments[i+2];for(var r=document.createElementNS(H.T,n),o=0;o+1')},K.S=function(t,n){this.F+=''},K.toString=function(){return this.F+""};var Z=Y.prototype;function X(){w&&_(m)}function Q(){if(w)for(var t=w(m),n=0;n 6 ? h - 6 : h;\r\n return decToHex(255 * (\r\n h < 1 ? m1 + (m2 - m1) * h :\r\n h < 3 ? m2 :\r\n h < 4 ? m1 + (m2 - m1) * (4 - h) :\r\n m1));\r\n}\r\n\r\n/**\r\n * @param {number} r Red channel [0, 255]\r\n * @param {number} g Green channel [0, 255]\r\n * @param {number} b Blue channel [0, 255]\r\n */\r\nexport function rgb(r, g, b) {\r\n return \"#\" + decToHex(r) + decToHex(g) + decToHex(b);\r\n}\r\n\r\n/**\r\n * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported.\r\n * @returns {string}\r\n */\r\nexport function parseColor(color) {\r\n if (/^#[0-9a-f]{3,8}$/i.test(color)) {\r\n let result;\r\n const colorLength = color.length;\r\n\r\n if (colorLength < 6) {\r\n const r = color[1],\r\n g = color[2],\r\n b = color[3],\r\n a = color[4] || \"\";\r\n result = \"#\" + r + r + g + g + b + b + a + a;\r\n }\r\n if (colorLength == 7 || colorLength > 8) {\r\n result = color;\r\n }\r\n \r\n return result;\r\n }\r\n}\r\n\r\n/**\r\n * Converts a hexadecimal color to a CSS3 compatible color.\r\n * @param {string} hexColor Color on the format \"#RRGGBB\" or \"#RRGGBBAA\"\r\n * @returns {string}\r\n */\r\nexport function toCss3Color(hexColor) {\r\n const a = parseHex(hexColor, 7, 2);\r\n let result;\r\n\r\n if (isNaN(a)) {\r\n result = hexColor;\r\n } else {\r\n const r = parseHex(hexColor, 1, 2),\r\n g = parseHex(hexColor, 3, 2),\r\n b = parseHex(hexColor, 5, 2);\r\n result = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + (a / 255).toFixed(2) + \")\";\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color.\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function hsl(hue, saturation, lightness) {\r\n // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color\r\n let result;\r\n\r\n if (saturation == 0) {\r\n const partialHex = decToHex(lightness * 255);\r\n result = partialHex + partialHex + partialHex;\r\n }\r\n else {\r\n const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation,\r\n m1 = lightness * 2 - m2;\r\n result =\r\n hueToRgb(m1, m2, hue * 6 + 2) +\r\n hueToRgb(m1, m2, hue * 6) +\r\n hueToRgb(m1, m2, hue * 6 - 2);\r\n }\r\n\r\n return \"#\" + result;\r\n}\r\n\r\n/**\r\n * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the \"dark\" hues\r\n * @param {number} hue Hue in range [0, 1]\r\n * @param {number} saturation Saturation in range [0, 1]\r\n * @param {number} lightness Lightness in range [0, 1]\r\n * @returns {string}\r\n */\r\nexport function correctedHsl(hue, saturation, lightness) {\r\n // The corrector specifies the perceived middle lightness for each hue\r\n const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ],\r\n corrector = correctors[(hue * 6 + 0.5) | 0];\r\n \r\n // Adjust the input lightness relative to the corrector\r\n lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2;\r\n \r\n return hsl(hue, saturation, lightness);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n/* global umdGlobal */\r\n\r\n// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for\r\n// backward compatibility.\r\nexport const GLOBAL = umdGlobal;\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { parseColor } from \"../renderer/color\";\r\nimport { GLOBAL } from \"./global\";\r\n\r\n/**\r\n * @typedef {Object} ParsedConfiguration\r\n * @property {number} colorSaturation\r\n * @property {number} grayscaleSaturation\r\n * @property {string} backColor\r\n * @property {number} iconPadding\r\n * @property {function(number):number} hue\r\n * @property {function(number):number} colorLightness\r\n * @property {function(number):number} grayscaleLightness\r\n */\r\n\r\nexport const CONFIG_PROPERTIES = {\r\n GLOBAL: \"jdenticon_config\",\r\n MODULE: \"config\",\r\n};\r\n\r\nvar rootConfigurationHolder = {};\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is \r\n * printed in the console. To minimize bundle size, this is only used in Node bundles.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigPropertyWithWarn(rootObject) {\r\n Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, {\r\n configurable: true,\r\n get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE],\r\n set: newConfiguration => {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n console.warn(\"jdenticon.config is deprecated. Use jdenticon.configure() instead.\");\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console\r\n * when it is being used.\r\n * @param {!Object} rootObject \r\n */\r\nexport function defineConfigProperty(rootObject) {\r\n rootConfigurationHolder = rootObject;\r\n}\r\n\r\n/**\r\n * Sets a new icon style configuration. The new configuration is not merged with the previous one. * \r\n * @param {Object} newConfiguration - New configuration object.\r\n */\r\nexport function configure(newConfiguration) {\r\n if (arguments.length) {\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration;\r\n }\r\n return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE];\r\n}\r\n\r\n/**\r\n * Gets the normalized current Jdenticon color configuration. Missing fields have default values.\r\n * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A\r\n * local configuration overrides the global configuration in it entirety. This parameter can for backward\r\n * compatibility also contain a padding value. A padding value only overrides the global padding, not the\r\n * entire global configuration.\r\n * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor\r\n * explicitly to the API method.\r\n * @returns {ParsedConfiguration}\r\n */\r\nexport function getConfiguration(paddingOrLocalConfig, defaultPadding) {\r\n const configObject = \r\n typeof paddingOrLocalConfig == \"object\" && paddingOrLocalConfig ||\r\n rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] ||\r\n GLOBAL[CONFIG_PROPERTIES.GLOBAL] ||\r\n { },\r\n\r\n lightnessConfig = configObject[\"lightness\"] || { },\r\n \r\n // In versions < 2.1.0 there was no grayscale saturation -\r\n // saturation was the color saturation.\r\n saturation = configObject[\"saturation\"] || { },\r\n colorSaturation = \"color\" in saturation ? saturation[\"color\"] : saturation,\r\n grayscaleSaturation = saturation[\"grayscale\"],\r\n\r\n backColor = configObject[\"backColor\"],\r\n padding = configObject[\"padding\"];\r\n \r\n /**\r\n * Creates a lightness range.\r\n */\r\n function lightness(configName, defaultRange) {\r\n let range = lightnessConfig[configName];\r\n \r\n // Check if the lightness range is an array-like object. This way we ensure the\r\n // array contain two values at the same time.\r\n if (!(range && range.length > 1)) {\r\n range = defaultRange;\r\n }\r\n\r\n /**\r\n * Gets a lightness relative the specified value in the specified lightness range.\r\n */\r\n return function (value) {\r\n value = range[0] + value * (range[1] - range[0]);\r\n return value < 0 ? 0 : value > 1 ? 1 : value;\r\n };\r\n }\r\n\r\n /**\r\n * Gets a hue allowed by the configured hue restriction,\r\n * provided the originally computed hue.\r\n */\r\n function hueFunction(originalHue) {\r\n const hueConfig = configObject[\"hues\"];\r\n let hue;\r\n \r\n // Check if 'hues' is an array-like object. This way we also ensure that\r\n // the array is not empty, which would mean no hue restriction.\r\n if (hueConfig && hueConfig.length > 0) {\r\n // originalHue is in the range [0, 1]\r\n // Multiply with 0.999 to change the range to [0, 1) and then truncate the index.\r\n hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)];\r\n }\r\n\r\n return typeof hue == \"number\" ?\r\n \r\n // A hue was specified. We need to convert the hue from\r\n // degrees on any turn - e.g. 746° is a perfectly valid hue -\r\n // to turns in the range [0, 1).\r\n ((((hue / 360) % 1) + 1) % 1) :\r\n\r\n // No hue configured => use original hue\r\n originalHue;\r\n }\r\n \r\n return {\r\n hue: hueFunction,\r\n colorSaturation: typeof colorSaturation == \"number\" ? colorSaturation : 0.5,\r\n grayscaleSaturation: typeof grayscaleSaturation == \"number\" ? grayscaleSaturation : 0,\r\n colorLightness: lightness(\"color\", [0.4, 0.8]),\r\n grayscaleLightness: lightness(\"grayscale\", [0.3, 0.9]),\r\n backColor: parseColor(backColor),\r\n iconPadding: \r\n typeof paddingOrLocalConfig == \"number\" ? paddingOrLocalConfig : \r\n typeof padding == \"number\" ? padding : \r\n defaultPadding\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const ICON_TYPE_SVG = 1;\r\n\r\nexport const ICON_TYPE_CANVAS = 2;\r\n\r\nexport const ATTRIBUTES = {\r\n HASH: \"data-jdenticon-hash\",\r\n VALUE: \"data-jdenticon-value\"\r\n};\r\n\r\nexport const IS_RENDERED_PROPERTY = \"jdenticonRendered\";\r\n\r\nexport const ICON_SELECTOR = \"[\" + ATTRIBUTES.HASH +\"],[\" + ATTRIBUTES.VALUE +\"]\";\r\n\r\nexport const documentQuerySelectorAll = /** @type {!Function} */ (\r\n typeof document !== \"undefined\" && document.querySelectorAll.bind(document));\r\n\r\nexport function getIdenticonType(el) {\r\n if (el) {\r\n const tagName = el[\"tagName\"];\r\n\r\n if (/^svg$/i.test(tagName)) {\r\n return ICON_TYPE_SVG;\r\n }\r\n\r\n if (/^canvas$/i.test(tagName) && \"getContext\" in el) {\r\n return ICON_TYPE_CANVAS;\r\n }\r\n }\r\n}\r\n\r\nexport function whenDocumentIsReady(/** @type {Function} */ callback) {\r\n function loadedHandler() {\r\n document.removeEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.removeEventListener(\"load\", loadedHandler);\r\n setTimeout(callback, 0); // Give scripts a chance to run\r\n }\r\n \r\n if (typeof document !== \"undefined\" &&\r\n typeof window !== \"undefined\" &&\r\n typeof setTimeout !== \"undefined\"\r\n ) {\r\n if (document.readyState === \"loading\") {\r\n document.addEventListener(\"DOMContentLoaded\", loadedHandler);\r\n window.addEventListener(\"load\", loadedHandler);\r\n } else {\r\n // Document already loaded. The load events above likely won't be raised\r\n setTimeout(callback, 0);\r\n }\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { ATTRIBUTES, ICON_SELECTOR, getIdenticonType } from \"./dom\";\r\n\r\nexport function observer(updateCallback) {\r\n if (typeof MutationObserver != \"undefined\") {\r\n const mutationObserver = new MutationObserver(function onmutation(mutations) {\r\n for (let mutationIndex = 0; mutationIndex < mutations.length; mutationIndex++) {\r\n const mutation = mutations[mutationIndex];\r\n const addedNodes = mutation.addedNodes;\r\n \r\n for (let addedNodeIndex = 0; addedNodes && addedNodeIndex < addedNodes.length; addedNodeIndex++) {\r\n const addedNode = addedNodes[addedNodeIndex];\r\n \r\n // Skip other types of nodes than element nodes, since they might not support\r\n // the querySelectorAll method => runtime error.\r\n if (addedNode.nodeType == Node.ELEMENT_NODE) {\r\n if (getIdenticonType(addedNode)) {\r\n updateCallback(addedNode);\r\n }\r\n else {\r\n const icons = /** @type {Element} */(addedNode).querySelectorAll(ICON_SELECTOR);\r\n for (let iconIndex = 0; iconIndex < icons.length; iconIndex++) {\r\n updateCallback(icons[iconIndex]);\r\n }\r\n }\r\n }\r\n }\r\n \r\n if (mutation.type == \"attributes\" && getIdenticonType(mutation.target)) {\r\n updateCallback(mutation.target);\r\n }\r\n }\r\n });\r\n\r\n mutationObserver.observe(document.body, {\r\n \"childList\": true,\r\n \"attributes\": true,\r\n \"attributeFilter\": [ATTRIBUTES.VALUE, ATTRIBUTES.HASH, \"width\", \"height\"],\r\n \"subtree\": true,\r\n });\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Represents a point.\r\n */\r\nexport class Point {\r\n /**\r\n * @param {number} x \r\n * @param {number} y \r\n */\r\n constructor(x, y) {\r\n this.x = x;\r\n this.y = y;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Point } from \"./point\";\r\n\r\n/**\r\n * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, \r\n * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly.\r\n */\r\nexport class Transform {\r\n /**\r\n * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle.\r\n * @param {number} size The size of the transformed rectangle.\r\n * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad\r\n */\r\n constructor(x, y, size, rotation) {\r\n this._x = x;\r\n this._y = y;\r\n this._size = size;\r\n this._rotation = rotation;\r\n }\r\n\r\n /**\r\n * Transforms the specified point based on the translation and rotation specification for this Transform.\r\n * @param {number} x x-coordinate\r\n * @param {number} y y-coordinate\r\n * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle.\r\n */\r\n transformIconPoint(x, y, w, h) {\r\n const right = this._x + this._size,\r\n bottom = this._y + this._size,\r\n rotation = this._rotation;\r\n return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) :\r\n rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) :\r\n rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) :\r\n new Point(this._x + x, this._y + y);\r\n }\r\n}\r\n\r\nexport const NO_TRANSFORM = new Transform(0, 0, 0, 0);\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { NO_TRANSFORM } from \"./transform\";\r\n\r\n/**\r\n * @typedef {import(\"./renderer\").Renderer} Renderer\r\n * @typedef {import(\"./transform\").Transform} Transform\r\n */\r\n\r\n/**\r\n * Provides helper functions for rendering common basic shapes.\r\n */\r\nexport class Graphics {\r\n /**\r\n * @param {Renderer} renderer \r\n */\r\n constructor(renderer) {\r\n /**\r\n * @type {Renderer}\r\n * @private\r\n */\r\n this._renderer = renderer;\r\n\r\n /**\r\n * @type {Transform}\r\n */\r\n this.currentTransform = NO_TRANSFORM;\r\n }\r\n\r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ]\r\n * @param {boolean=} invert Specifies if the polygon will be inverted.\r\n */\r\n addPolygon(points, invert) {\r\n const di = invert ? -2 : 2,\r\n transformedPoints = [];\r\n \r\n for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) {\r\n transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1]));\r\n }\r\n \r\n this._renderer.addPolygon(transformedPoints);\r\n }\r\n \r\n /**\r\n * Adds a polygon to the underlying renderer.\r\n * Source: http://stackoverflow.com/a/2173084\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse.\r\n * @param {number} size The size of the ellipse.\r\n * @param {boolean=} invert Specifies if the ellipse will be inverted.\r\n */\r\n addCircle(x, y, size, invert) {\r\n const p = this.currentTransform.transformIconPoint(x, y, size, size);\r\n this._renderer.addCircle(p, size, invert);\r\n }\r\n\r\n /**\r\n * Adds a rectangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle.\r\n * @param {number} w The width of the rectangle.\r\n * @param {number} h The height of the rectangle.\r\n * @param {boolean=} invert Specifies if the rectangle will be inverted.\r\n */\r\n addRectangle(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x, y, \r\n x + w, y,\r\n x + w, y + h,\r\n x, y + h\r\n ], invert);\r\n }\r\n\r\n /**\r\n * Adds a right triangle to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle.\r\n * @param {number} w The width of the triangle.\r\n * @param {number} h The height of the triangle.\r\n * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle.\r\n * @param {boolean=} invert Specifies if the triangle will be inverted.\r\n */\r\n addTriangle(x, y, w, h, r, invert) {\r\n const points = [\r\n x + w, y, \r\n x + w, y + h, \r\n x, y + h,\r\n x, y\r\n ];\r\n points.splice(((r || 0) % 4) * 2, 2);\r\n this.addPolygon(points, invert);\r\n }\r\n\r\n /**\r\n * Adds a rhombus to the underlying renderer.\r\n * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus.\r\n * @param {number} w The width of the rhombus.\r\n * @param {number} h The height of the rhombus.\r\n * @param {boolean=} invert Specifies if the rhombus will be inverted.\r\n */\r\n addRhombus(x, y, w, h, invert) {\r\n this.addPolygon([\r\n x + w / 2, y,\r\n x + w, y + h / 2,\r\n x + w / 2, y + h,\r\n x, y + h / 2\r\n ], invert);\r\n }\r\n}","\r\nvar Graphics__prototype = Graphics.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n * @param {number} positionIndex\r\n * @typedef {import('./graphics').Graphics} Graphics\r\n */\r\nexport function centerShape(index, g, cell, positionIndex) {\r\n index = index % 14;\r\n\r\n let k, m, w, h, inner, outer;\r\n\r\n !index ? (\r\n k = cell * 0.42,\r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell - k * 2,\r\n cell - k, cell,\r\n 0, cell\r\n ])) :\r\n\r\n index == 1 ? (\r\n w = 0 | (cell * 0.5), \r\n h = 0 | (cell * 0.8),\r\n\r\n g.addTriangle(cell - w, 0, w, h, 2)) :\r\n\r\n index == 2 ? (\r\n w = 0 | (cell / 3),\r\n g.addRectangle(w, w, cell - w, cell - w)) :\r\n\r\n index == 3 ? (\r\n inner = cell * 0.1,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 6 ? 1 :\r\n cell < 8 ? 2 :\r\n (0 | (cell * 0.25)),\r\n \r\n inner = \r\n inner > 1 ? (0 | inner) : // large icon => truncate decimals\r\n inner > 0.5 ? 1 : // medium size icon => fixed width\r\n inner, // small icon => anti-aliased border\r\n\r\n g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) :\r\n\r\n index == 4 ? (\r\n m = 0 | (cell * 0.15),\r\n w = 0 | (cell * 0.5),\r\n g.addCircle(cell - w - m, cell - w - m, w)) :\r\n\r\n index == 5 ? (\r\n inner = cell * 0.1,\r\n outer = inner * 4,\r\n\r\n // Align edge to nearest pixel in large icons\r\n outer > 3 && (outer = 0 | outer),\r\n \r\n g.addRectangle(0, 0, cell, cell),\r\n g.addPolygon([\r\n outer, outer,\r\n cell - inner, outer,\r\n outer + (cell - outer - inner) / 2, cell - inner\r\n ], true)) :\r\n\r\n index == 6 ? \r\n g.addPolygon([\r\n 0, 0,\r\n cell, 0,\r\n cell, cell * 0.7,\r\n cell * 0.4, cell * 0.4,\r\n cell * 0.7, cell,\r\n 0, cell\r\n ]) :\r\n\r\n index == 7 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 8 ? (\r\n g.addRectangle(0, 0, cell, cell / 2),\r\n g.addRectangle(0, cell / 2, cell / 2, cell / 2),\r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) :\r\n\r\n index == 9 ? (\r\n inner = cell * 0.14,\r\n // Use fixed outer border widths in small icons to ensure the border is drawn\r\n outer = \r\n cell < 4 ? 1 :\r\n cell < 6 ? 2 :\r\n (0 | (cell * 0.35)),\r\n\r\n inner = \r\n cell < 8 ? inner : // small icon => anti-aliased border\r\n (0 | inner), // large icon => truncate decimals\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) :\r\n\r\n index == 10 ? (\r\n inner = cell * 0.12,\r\n outer = inner * 3,\r\n\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addCircle(outer, outer, cell - inner - outer, true)) :\r\n\r\n index == 11 ? \r\n g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) :\r\n\r\n index == 12 ? (\r\n m = cell * 0.25,\r\n g.addRectangle(0, 0, cell, cell),\r\n g.addRhombus(m, m, cell - m, cell - m, true)) :\r\n\r\n // 13\r\n (\r\n !positionIndex && (\r\n m = cell * 0.4, w = cell * 1.2,\r\n g.addCircle(m, m, w)\r\n )\r\n );\r\n}\r\n\r\n/**\r\n * @param {number} index\r\n * @param {Graphics} g\r\n * @param {number} cell\r\n */\r\nexport function outerShape(index, g, cell) {\r\n index = index % 4;\r\n\r\n let m;\r\n\r\n !index ?\r\n g.addTriangle(0, 0, cell, cell, 0) :\r\n \r\n index == 1 ?\r\n g.addTriangle(0, cell / 2, cell, cell / 2, 0) :\r\n\r\n index == 2 ?\r\n g.addRhombus(0, 0, cell, cell) :\r\n\r\n // 3\r\n (\r\n m = cell / 6,\r\n g.addCircle(m, m, cell - 2 * m)\r\n );\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { correctedHsl } from \"./color\";\r\n\r\n/**\r\n * Gets a set of identicon color candidates for a specified hue and config.\r\n * @param {number} hue\r\n * @param {import(\"../common/configuration\").ParsedConfiguration} config\r\n */\r\nexport function colorTheme(hue, config) {\r\n hue = config.hue(hue);\r\n return [\r\n // Dark gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)),\r\n // Mid color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)),\r\n // Light gray\r\n correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)),\r\n // Light color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(1)),\r\n // Dark color\r\n correctedHsl(hue, config.colorSaturation, config.colorLightness(0))\r\n ];\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { Transform } from \"./transform\";\r\nimport { Graphics } from \"./graphics\";\r\nimport { centerShape, outerShape } from \"./shapes\";\r\nimport { colorTheme } from \"./colorTheme\";\r\nimport { parseHex } from \"../common/parseHex\";\r\nimport { getConfiguration } from \"../common/configuration\";\r\n \r\n/**\r\n * Draws an identicon to a specified renderer.\r\n * @param {import('./renderer').Renderer} renderer\r\n * @param {string} hash\r\n * @param {Object|number=} config\r\n */\r\nexport function iconGenerator(renderer, hash, config) {\r\n const parsedConfig = getConfiguration(config, 0.08);\r\n\r\n // Set background color\r\n if (parsedConfig.backColor) {\r\n renderer.setBackground(parsedConfig.backColor);\r\n }\r\n \r\n // Calculate padding and round to nearest integer\r\n let size = renderer.iconSize;\r\n const padding = (0.5 + size * parsedConfig.iconPadding) | 0;\r\n size -= padding * 2;\r\n \r\n const graphics = new Graphics(renderer);\r\n \r\n // Calculate cell size and ensure it is an integer\r\n const cell = 0 | (size / 4);\r\n \r\n // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon\r\n const x = 0 | (padding + size / 2 - cell * 2);\r\n const y = 0 | (padding + size / 2 - cell * 2);\r\n\r\n function renderShape(colorIndex, shapes, index, rotationIndex, positions) {\r\n const shapeIndex = parseHex(hash, index, 1);\r\n let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0;\r\n \r\n renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]);\r\n \r\n for (let i = 0; i < positions.length; i++) {\r\n graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4);\r\n shapes(shapeIndex, graphics, cell, i);\r\n }\r\n \r\n renderer.endShape();\r\n }\r\n\r\n // AVAILABLE COLORS\r\n const hue = parseHex(hash, -7) / 0xfffffff,\r\n \r\n // Available colors for this icon\r\n availableColors = colorTheme(hue, parsedConfig),\r\n\r\n // The index of the selected colors\r\n selectedColorIndexes = [];\r\n\r\n let index;\r\n\r\n function isDuplicate(values) {\r\n if (values.indexOf(index) >= 0) {\r\n for (let i = 0; i < values.length; i++) {\r\n if (selectedColorIndexes.indexOf(values[i]) >= 0) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0; i < 3; i++) {\r\n index = parseHex(hash, 8 + i, 1) % availableColors.length;\r\n if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo\r\n isDuplicate([2, 3])) { // Disallow light gray and light color combo\r\n index = 1;\r\n }\r\n selectedColorIndexes.push(index);\r\n }\r\n\r\n // ACTUAL RENDERING\r\n // Sides\r\n renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]);\r\n // Corners\r\n renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]);\r\n // Center\r\n renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]);\r\n \r\n renderer.finish();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Computes a SHA1 hash for any value and returns it as a hexadecimal string.\r\n * \r\n * This function is optimized for minimal code size and rather short messages.\r\n * \r\n * @param {string} message \r\n */\r\nexport function sha1(message) {\r\n const HASH_SIZE_HALF_BYTES = 40;\r\n const BLOCK_SIZE_WORDS = 16;\r\n\r\n // Variables\r\n // `var` is used to be able to minimize the number of `var` keywords.\r\n var i = 0,\r\n f = 0,\r\n \r\n // Use `encodeURI` to UTF8 encode the message without any additional libraries\r\n // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky\r\n // since `unescape` is deprecated.\r\n urlEncodedMessage = encodeURI(message) + \"%80\", // trailing '1' bit padding\r\n \r\n // This can be changed to a preallocated Uint32Array array for greater performance and larger code size\r\n data = [],\r\n dataSize,\r\n \r\n hashBuffer = [],\r\n\r\n a = 0x67452301,\r\n b = 0xefcdab89,\r\n c = ~a,\r\n d = ~b,\r\n e = 0xc3d2e1f0,\r\n hash = [a, b, c, d, e],\r\n\r\n blockStartIndex = 0,\r\n hexHash = \"\";\r\n\r\n /**\r\n * Rotates the value a specified number of bits to the left.\r\n * @param {number} value Value to rotate\r\n * @param {number} shift Bit count to shift.\r\n */\r\n function rotl(value, shift) {\r\n return (value << shift) | (value >>> (32 - shift));\r\n }\r\n\r\n // Message data\r\n for ( ; i < urlEncodedMessage.length; f++) {\r\n data[f >> 2] = data[f >> 2] |\r\n (\r\n (\r\n urlEncodedMessage[i] == \"%\"\r\n // Percent encoded byte\r\n ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)\r\n // Unencoded byte\r\n : urlEncodedMessage.charCodeAt(i++)\r\n )\r\n\r\n // Read bytes in reverse order (big endian words)\r\n << ((3 - (f & 3)) * 8)\r\n );\r\n }\r\n\r\n // f is now the length of the utf8 encoded message\r\n // 7 = 8 bytes (64 bit) for message size, -1 to round down\r\n // >> 6 = integer division with block size\r\n dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;\r\n\r\n // Message size in bits.\r\n // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least\r\n // significant 32 bits are set. -8 is for the '1' bit padding byte.\r\n data[dataSize - 1] = f * 8 - 8;\r\n \r\n // Compute hash\r\n for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {\r\n for (i = 0; i < 80; i++) {\r\n f = rotl(a, 5) + e + (\r\n // Ch\r\n i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :\r\n \r\n // Parity\r\n i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :\r\n \r\n // Maj\r\n i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :\r\n \r\n // Parity\r\n (b ^ c ^ d) + 0xca62c1d6\r\n ) + ( \r\n hashBuffer[i] = i < BLOCK_SIZE_WORDS\r\n // Bitwise OR is used to coerse `undefined` to 0\r\n ? (data[blockStartIndex + i] | 0)\r\n : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)\r\n );\r\n\r\n e = d;\r\n d = c;\r\n c = rotl(b, 30);\r\n b = a;\r\n a = f;\r\n }\r\n\r\n hash[0] = a = ((hash[0] + a) | 0);\r\n hash[1] = b = ((hash[1] + b) | 0);\r\n hash[2] = c = ((hash[2] + c) | 0);\r\n hash[3] = d = ((hash[3] + d) | 0);\r\n hash[4] = e = ((hash[4] + e) | 0);\r\n }\r\n\r\n // Format hex hash\r\n for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {\r\n hexHash += (\r\n (\r\n // Get word (2^3 half-bytes per word)\r\n hash[i >> 3] >>>\r\n\r\n // Append half-bytes in reverse order\r\n ((7 - (i & 7)) * 4)\r\n ) \r\n // Clamp to half-byte\r\n & 0xf\r\n ).toString(16);\r\n }\r\n\r\n return hexHash;\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { sha1 } from \"./sha1\";\r\n\r\n/**\r\n * Inputs a value that might be a valid hash string for Jdenticon and returns it \r\n * if it is determined valid, otherwise a falsy value is returned.\r\n */\r\nexport function isValidHash(hashCandidate) {\r\n return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate;\r\n}\r\n\r\n/**\r\n * Computes a hash for the specified value. Currently SHA1 is used. This function\r\n * always returns a valid hash.\r\n */\r\nexport function computeHash(value) {\r\n return sha1(value == null ? \"\" : \"\" + value);\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { toCss3Color } from \"../color\";\r\n\r\n/**\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import('../point').Point} Point\r\n */\r\n\r\n/**\r\n * Renderer redirecting drawing commands to a canvas context.\r\n * @implements {Renderer}\r\n */\r\nexport class CanvasRenderer {\r\n /**\r\n * @param {number=} iconSize\r\n */\r\n constructor(ctx, iconSize) {\r\n const canvas = ctx.canvas; \r\n const width = canvas.width;\r\n const height = canvas.height;\r\n \r\n ctx.save();\r\n \r\n if (!iconSize) {\r\n iconSize = Math.min(width, height);\r\n \r\n ctx.translate(\r\n ((width - iconSize) / 2) | 0,\r\n ((height - iconSize) / 2) | 0);\r\n }\r\n\r\n /**\r\n * @private\r\n */\r\n this._ctx = ctx;\r\n this.iconSize = iconSize;\r\n \r\n ctx.clearRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const ctx = this._ctx;\r\n const iconSize = this.iconSize;\r\n\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.fillRect(0, 0, iconSize, iconSize);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} fillColor Fill color on format #rrggbb[aa].\r\n */\r\n beginShape(fillColor) {\r\n const ctx = this._ctx;\r\n ctx.fillStyle = toCss3Color(fillColor);\r\n ctx.beginPath();\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas.\r\n */\r\n endShape() {\r\n this._ctx.fill();\r\n }\r\n\r\n /**\r\n * Adds a polygon to the rendering queue.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n const ctx = this._ctx;\r\n ctx.moveTo(points[0].x, points[0].y);\r\n for (let i = 1; i < points.length; i++) {\r\n ctx.lineTo(points[i].x, points[i].y);\r\n }\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Adds a circle to the rendering queue.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const ctx = this._ctx,\r\n radius = diameter / 2;\r\n ctx.moveTo(point.x + radius, point.y + radius);\r\n ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise);\r\n ctx.closePath();\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() {\r\n this._ctx.restore();\r\n }\r\n}\r\n","\r\nvar CanvasRenderer__prototype = CanvasRenderer.prototype;","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { IS_RENDERED_PROPERTY } from \"../common/dom\";\r\n\r\n/**\r\n * Draws an identicon to a context.\r\n * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0).\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function drawIcon(ctx, hashOrValue, size, config) {\r\n if (!ctx) {\r\n throw new Error(\"No canvas specified.\");\r\n }\r\n \r\n iconGenerator(new CanvasRenderer(ctx, size), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue), \r\n config);\r\n\r\n const canvas = ctx.canvas;\r\n if (canvas) {\r\n canvas[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n/**\r\n * Prepares a measure to be used as a measure in an SVG path, by\r\n * rounding the measure to a single decimal. This reduces the file\r\n * size of the generated SVG with more than 50% in some cases.\r\n */\r\nfunction svgValue(value) {\r\n return ((value * 10 + 0.5) | 0) / 10;\r\n}\r\n\r\n/**\r\n * Represents an SVG path element.\r\n */\r\nexport class SvgPath {\r\n constructor() {\r\n /**\r\n * This property holds the data string (path.d) of the SVG path.\r\n * @type {string}\r\n */\r\n this.dataString = \"\";\r\n }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG path.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n let dataString = \"\";\r\n for (let i = 0; i < points.length; i++) {\r\n dataString += (i ? \"L\" : \"M\") + svgValue(points[i].x) + \" \" + svgValue(points[i].y);\r\n }\r\n this.dataString += dataString + \"Z\";\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG path.\r\n * @param {import('../point').Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n const sweepFlag = counterClockwise ? 0 : 1,\r\n svgRadius = svgValue(diameter / 2),\r\n svgDiameter = svgValue(diameter),\r\n svgArc = \"a\" + svgRadius + \",\" + svgRadius + \" 0 1,\" + sweepFlag + \" \";\r\n \r\n this.dataString += \r\n \"M\" + svgValue(point.x) + \" \" + svgValue(point.y + diameter / 2) +\r\n svgArc + svgDiameter + \",0\" + \r\n svgArc + (-svgDiameter) + \",0\";\r\n }\r\n}\r\n\r\n","\r\nvar SvgPath__prototype = SvgPath.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SvgPath } from \"./svgPath\";\r\nimport { parseHex } from \"../../common/parseHex\";\r\n\r\n/**\r\n * @typedef {import(\"../point\").Point} Point\r\n * @typedef {import(\"../renderer\").Renderer} Renderer\r\n * @typedef {import(\"./svgElement\").SvgElement} SvgElement\r\n * @typedef {import(\"./svgWriter\").SvgWriter} SvgWriter\r\n */\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n * @implements {Renderer}\r\n */\r\nexport class SvgRenderer {\r\n /**\r\n * @param {SvgElement|SvgWriter} target \r\n */\r\n constructor(target) {\r\n /**\r\n * @type {SvgPath}\r\n * @private\r\n */\r\n this._path;\r\n\r\n /**\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._pathsByColor = { };\r\n\r\n /**\r\n * @type {SvgElement|SvgWriter}\r\n * @private\r\n */\r\n this._target = target;\r\n\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = target.iconSize;\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb[aa].\r\n */\r\n setBackground(fillColor) {\r\n const match = /^(#......)(..)?/.exec(fillColor),\r\n opacity = match[2] ? parseHex(match[2], 0) / 255 : 1;\r\n this._target.setBackground(match[1], opacity);\r\n }\r\n\r\n /**\r\n * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n */\r\n beginShape(color) {\r\n this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath());\r\n }\r\n\r\n /**\r\n * Marks the end of the currently drawn shape.\r\n */\r\n endShape() { }\r\n\r\n /**\r\n * Adds a polygon with the current fill color to the SVG.\r\n * @param points An array of Point objects.\r\n */\r\n addPolygon(points) {\r\n this._path.addPolygon(points);\r\n }\r\n\r\n /**\r\n * Adds a circle with the current fill color to the SVG.\r\n * @param {Point} point The upper left corner of the circle bounding box.\r\n * @param {number} diameter The diameter of the circle.\r\n * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).\r\n */\r\n addCircle(point, diameter, counterClockwise) {\r\n this._path.addCircle(point, diameter, counterClockwise);\r\n }\r\n\r\n /**\r\n * Called when the icon has been completely drawn.\r\n */\r\n finish() { \r\n const pathsByColor = this._pathsByColor;\r\n for (let color in pathsByColor) {\r\n // hasOwnProperty cannot be shadowed in pathsByColor\r\n // eslint-disable-next-line no-prototype-builtins\r\n if (pathsByColor.hasOwnProperty(color)) {\r\n this._target.appendPath(color, pathsByColor[color].dataString);\r\n }\r\n }\r\n }\r\n}\r\n","\r\nvar SvgRenderer__prototype = SvgRenderer.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nexport const SVG_CONSTANTS = {\r\n XMLNS: \"http://www.w3.org/2000/svg\",\r\n WIDTH: \"width\",\r\n HEIGHT: \"height\",\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgWriter {\r\n /**\r\n * @param {number} iconSize - Icon width and height in pixels.\r\n */\r\n constructor(iconSize) {\r\n /**\r\n * @type {number}\r\n */\r\n this.iconSize = iconSize;\r\n\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n this._s =\r\n '';\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n this._s += '';\r\n }\r\n }\r\n\r\n /**\r\n * Writes a path to the SVG string.\r\n * @param {string} color Fill color on format #rrggbb.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n this._s += '';\r\n }\r\n\r\n /**\r\n * Gets the rendered image as an SVG string.\r\n */\r\n toString() {\r\n return this._s + \"\";\r\n }\r\n}\r\n","\r\nvar SvgWriter__prototype = SvgWriter.prototype;","import { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgWriter } from \"../renderer/svg/svgWriter\";\r\n\r\n/**\r\n * Draws an identicon as an SVG string.\r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.\r\n * @param {number} size - Icon size in pixels.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n * @returns {string} SVG string\r\n */\r\nexport function toSvg(hashOrValue, size, config) {\r\n const writer = new SvgWriter(size);\r\n iconGenerator(new SvgRenderer(writer), \r\n isValidHash(hashOrValue) || computeHash(hashOrValue),\r\n config);\r\n return writer.toString();\r\n}\r\n","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { SVG_CONSTANTS } from \"./constants\";\r\n\r\n/**\r\n * Creates a new element and adds it to the specified parent.\r\n * @param {Element} parentNode\r\n * @param {string} name\r\n * @param {...(string|number)} keyValuePairs\r\n */\r\nfunction SvgElement_append(parentNode, name, ...keyValuePairs) {\r\n const el = document.createElementNS(SVG_CONSTANTS.XMLNS, name);\r\n \r\n for (let i = 0; i + 1 < keyValuePairs.length; i += 2) {\r\n el.setAttribute(\r\n /** @type {string} */(keyValuePairs[i]),\r\n /** @type {string} */(keyValuePairs[i + 1]),\r\n );\r\n }\r\n\r\n parentNode.appendChild(el);\r\n}\r\n\r\n\r\n/**\r\n * Renderer producing SVG output.\r\n */\r\nexport class SvgElement {\r\n /**\r\n * @param {Element} element - Target element\r\n */\r\n constructor(element) {\r\n // Don't use the clientWidth and clientHeight properties on SVG elements\r\n // since Firefox won't serve a proper value of these properties on SVG\r\n // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811)\r\n // Instead use 100px as a hardcoded size (the svg viewBox will rescale \r\n // the icon to the correct dimensions)\r\n const iconSize = this.iconSize = Math.min(\r\n (Number(element.getAttribute(SVG_CONSTANTS.WIDTH)) || 100),\r\n (Number(element.getAttribute(SVG_CONSTANTS.HEIGHT)) || 100)\r\n );\r\n \r\n /**\r\n * @type {Element}\r\n * @private\r\n */\r\n this._el = element;\r\n \r\n // Clear current SVG child elements\r\n while (element.firstChild) {\r\n element.removeChild(element.firstChild);\r\n }\r\n \r\n // Set viewBox attribute to ensure the svg scales nicely.\r\n element.setAttribute(\"viewBox\", \"0 0 \" + iconSize + \" \" + iconSize);\r\n element.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\");\r\n }\r\n\r\n /**\r\n * Fills the background with the specified color.\r\n * @param {string} fillColor Fill color on the format #rrggbb.\r\n * @param {number} opacity Opacity in the range [0.0, 1.0].\r\n */\r\n setBackground(fillColor, opacity) {\r\n if (opacity) {\r\n SvgElement_append(this._el, \"rect\",\r\n SVG_CONSTANTS.WIDTH, \"100%\",\r\n SVG_CONSTANTS.HEIGHT, \"100%\",\r\n \"fill\", fillColor,\r\n \"opacity\", opacity);\r\n }\r\n }\r\n\r\n /**\r\n * Appends a path to the SVG element.\r\n * @param {string} color Fill color on format #xxxxxx.\r\n * @param {string} dataString The SVG path data string.\r\n */\r\n appendPath(color, dataString) {\r\n SvgElement_append(this._el, \"path\",\r\n \"fill\", color,\r\n \"d\", dataString);\r\n }\r\n}\r\n","\r\nvar SvgElement__prototype = SvgElement.prototype;","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\nimport { iconGenerator } from \"../renderer/iconGenerator\";\r\nimport { isValidHash, computeHash } from \"../common/hashUtils\";\r\nimport { ATTRIBUTES, ICON_SELECTOR, IS_RENDERED_PROPERTY, documentQuerySelectorAll } from \"../common/dom\";\r\nimport { SvgRenderer } from \"../renderer/svg/svgRenderer\";\r\nimport { SvgElement } from \"../renderer/svg/svgElement\";\r\nimport { CanvasRenderer } from \"../renderer/canvas/canvasRenderer\";\r\nimport { ICON_TYPE_CANVAS, ICON_TYPE_SVG, getIdenticonType } from \"../common/dom\";\r\n\r\n\r\n/**\r\n * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute.\r\n */\r\nexport function updateAll() {\r\n if (documentQuerySelectorAll) {\r\n update(ICON_SELECTOR);\r\n }\r\n}\r\n\r\n/**\r\n * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute that have not already\r\n * been rendered.\r\n */\r\nexport function updateAllConditional() {\r\n if (documentQuerySelectorAll) {\r\n /** @type {NodeListOf} */\r\n const elements = documentQuerySelectorAll(ICON_SELECTOR);\r\n \r\n for (let i = 0; i < elements.length; i++) {\r\n const el = elements[i];\r\n if (!el[IS_RENDERED_PROPERTY]) {\r\n update(el);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` or `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * `` or ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function update(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType) {\r\n return iconType == ICON_TYPE_SVG ? \r\n new SvgRenderer(new SvgElement(el)) : \r\n new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext(\"2d\"));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function updateCanvas(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType == ICON_TYPE_CANVAS) {\r\n return new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext(\"2d\"));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified `` elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * ``, or a CSS selector to such an element.\r\n * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any\r\n * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function updateSvg(el, hashOrValue, config) {\r\n renderDomElement(el, hashOrValue, config, function (el, iconType) {\r\n if (iconType == ICON_TYPE_SVG) {\r\n return new SvgRenderer(new SvgElement(el));\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Updates the identicon in the specified canvas or svg elements.\r\n * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type\r\n * `` or ``, or a CSS selector to such an element.\r\n * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or\r\n * `data-jdenticon-value` attribute will be evaluated.\r\n * @param {Object|number|undefined} config\r\n * @param {function(Element,number):import(\"../renderer/renderer\").Renderer} rendererFactory - Factory function for creating an icon renderer.\r\n */\r\nfunction renderDomElement(el, hashOrValue, config, rendererFactory) {\r\n if (typeof el === \"string\") {\r\n if (documentQuerySelectorAll) {\r\n const elements = documentQuerySelectorAll(el);\r\n for (let i = 0; i < elements.length; i++) {\r\n renderDomElement(elements[i], hashOrValue, config, rendererFactory);\r\n }\r\n }\r\n return;\r\n }\r\n \r\n // Hash selection. The result from getValidHash or computeHash is \r\n // accepted as a valid hash.\r\n const hash = \r\n // 1. Explicit valid hash\r\n isValidHash(hashOrValue) ||\r\n \r\n // 2. Explicit value (`!= null` catches both null and undefined)\r\n hashOrValue != null && computeHash(hashOrValue) ||\r\n \r\n // 3. `data-jdenticon-hash` attribute\r\n isValidHash(el.getAttribute(ATTRIBUTES.HASH)) ||\r\n \r\n // 4. `data-jdenticon-value` attribute. \r\n // We want to treat an empty attribute as an empty value. \r\n // Some browsers return empty string even if the attribute \r\n // is not specified, so use hasAttribute to determine if \r\n // the attribute is specified.\r\n el.hasAttribute(ATTRIBUTES.VALUE) && computeHash(el.getAttribute(ATTRIBUTES.VALUE));\r\n \r\n if (!hash) {\r\n // No hash specified. Don't render an icon.\r\n return;\r\n }\r\n \r\n const renderer = rendererFactory(el, getIdenticonType(el));\r\n if (renderer) {\r\n // Draw icon\r\n iconGenerator(renderer, hash, config);\r\n el[IS_RENDERED_PROPERTY] = true;\r\n }\r\n}\r\n","import { update } from \"./update\";\r\n\r\n/**\r\n * Renders an identicon for all matching supported elements.\r\n * \r\n * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. If not \r\n * specified the `data-jdenticon-hash` and `data-jdenticon-value` attributes of each element will be\r\n * evaluated.\r\n * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any global\r\n * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be\r\n * specified in place of a configuration object.\r\n */\r\nexport function jdenticonJqueryPlugin(hashOrValue, config) {\r\n this[\"each\"](function (index, el) {\r\n update(el, hashOrValue, config);\r\n });\r\n return this;\r\n}","/**\r\n * Jdenticon\r\n * https://github.com/dmester/jdenticon\r\n * Copyright © Daniel Mester Pirttijärvi\r\n */\r\n\r\n// This file is compiled to dist/jdenticon.js and dist/jdenticon.min.js\r\n\r\nimport { CONFIG_PROPERTIES, defineConfigProperty } from \"./common/configuration\";\r\nimport { observer } from \"./common/observer\";\r\nimport { configure } from \"./apis/configure\";\r\nimport { drawIcon } from \"./apis/drawIcon\";\r\nimport { toSvg } from \"./apis/toSvg\";\r\nimport { update, updateAll, updateAllConditional } from \"./apis/update\";\r\nimport { jdenticonJqueryPlugin } from \"./apis/jquery\";\r\nimport { GLOBAL } from \"./common/global\";\r\nimport { whenDocumentIsReady } from \"./common/dom\";\r\n\r\nconst jdenticon = updateAll;\r\n\r\ndefineConfigProperty(jdenticon);\r\n\r\n// Export public API\r\njdenticon[\"configure\"] = configure;\r\njdenticon[\"drawIcon\"] = drawIcon;\r\njdenticon[\"toSvg\"] = toSvg;\r\njdenticon[\"update\"] = update;\r\njdenticon[\"updateCanvas\"] = update;\r\njdenticon[\"updateSvg\"] = update;\r\n\r\n/**\r\n * Specifies the version of the Jdenticon package in use.\r\n * @type {string}\r\n */\r\njdenticon[\"version\"] = \"#version#\";\r\n\r\n/**\r\n * Specifies which bundle of Jdenticon that is used.\r\n * @type {string}\r\n */\r\njdenticon[\"bundle\"] = \"browser-umd\";\r\n\r\n// Basic jQuery plugin\r\nconst jQuery = GLOBAL[\"jQuery\"];\r\nif (jQuery) {\r\n jQuery[\"fn\"][\"jdenticon\"] = jdenticonJqueryPlugin;\r\n}\r\n\r\n/**\r\n * This function is called once upon page load.\r\n */\r\nfunction jdenticonStartup() {\r\n const replaceMode = (\r\n jdenticon[CONFIG_PROPERTIES.MODULE] ||\r\n GLOBAL[CONFIG_PROPERTIES.GLOBAL] ||\r\n { }\r\n )[\"replaceMode\"];\r\n \r\n if (replaceMode != \"never\") {\r\n updateAllConditional();\r\n \r\n if (replaceMode == \"observe\") {\r\n observer(update);\r\n }\r\n }\r\n}\r\n\r\n// Schedule to render all identicons on the page once it has been loaded.\r\nwhenDocumentIsReady(jdenticonStartup);\r\n\r\nmodule.exports = jdenticon;\r\n","\r\n});"]} \ No newline at end of file diff --git a/jdenticon-js/gulpfile.js b/jdenticon-js/gulpfile.js new file mode 100644 index 0000000..5ff91ea --- /dev/null +++ b/jdenticon-js/gulpfile.js @@ -0,0 +1,298 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ +"use strict"; + +const del = require("del"); +const fs = require("fs"); +const path = require("path"); +const crypto = require("crypto"); +const { exec } = require("child_process"); +const { promisify } = require("util"); +const pack = require("./package.json"); + +// Gulp dependencies +const gulp = require("gulp"); +const rename = require("gulp-rename"); +const terser = require("gulp-terser"); +const zip = require("gulp-zip"); +const replace = require("./build/gulp/replacement").gulp; +const wrapTemplate = require("./build/gulp/wrap-template"); +const buble = require("gulp-buble"); +const sourcemaps = require("gulp-sourcemaps"); +const preMinify = require("./build/gulp/pre-minify"); +const removeJsDocImports = require("./build/gulp/remove-jsdoc-imports"); +const removeMappedSource = require("./build/gulp/remove-mapped-source"); + +// Rollup dependencies +const rollup = require("./build/gulp/rollup"); +const commonjs = require( "@rollup/plugin-commonjs"); +const stripBanner = require("rollup-plugin-strip-banner"); +const alias = require("@rollup/plugin-alias"); + +// Constants +const LICENSE = fs.readFileSync("./LICENSE").toString(); +const VARIABLES = [ + [/#version#/g, pack.version], + [/#year#/g, new Date().getFullYear()], + [/#date#/g, new Date().toISOString()], + + // Keep line prefix, e.g. " * " for license banners in JavaScript. + [/(.*)#license#/gm, "$1" + LICENSE.trim().replace(/\n/g, "\n$1")], +]; + +function umdSrc() { + return gulp.src("./src/browser-umd.js") + .pipe(sourcemaps.init()) + .pipe(rollup({ + output: { format: "cjs" }, + plugins: [ + stripBanner(), + alias({ + entries: [ + { find: /^(.*[\/\\])global$/, replacement: "$1global.umd" }, + ] + }), + ], + })) + + .pipe(rename(function (path) { path.basename = "notmapped"; path.extname = ".js" })) + + .pipe(buble()) + .pipe(preMinify()) + .pipe(removeJsDocImports()) + + // The UMD template expects a factory function body, so replace export with a return for the factory function. + .pipe(replace("module.exports = ", "return ")) + + .pipe(replace(VARIABLES)) + .pipe(wrapTemplate("./build/template-umd.js", VARIABLES)); +} + +gulp.task("clean", function () { + return del(["./~jdenticon.nuspec", "./obj/output"]); +}); + +gulp.task("build-umd", function () { + return umdSrc() + .pipe(rename(function (path) { path.basename = "jdenticon"; path.extname = ".js" })) + .pipe(gulp.dest("dist")) + + .pipe(rename(function (path) { path.basename = "jdenticon-" + pack.version; path.extname = ".js" })) + .pipe(gulp.dest("obj/output")); +}); + +gulp.task("build-umd-min", function () { + return umdSrc() + .pipe(terser()) + .pipe(wrapTemplate("./build/template-min.js", VARIABLES)) + + .pipe(removeMappedSource("notmapped.js")) + + .pipe(rename(function (path) { path.basename = "jdenticon"; path.extname = ".min.js" })) + .pipe(sourcemaps.write(".", { includeContent: true })) + .pipe(gulp.dest("dist")) + + .pipe(rename(function (path) { path.basename = "jdenticon-" + pack.version; path.extname = ".min.js" })) + .pipe(sourcemaps.write(".", { includeContent: true })) + .pipe(gulp.dest("obj/output")); +}); + +gulp.task("build-cjs", function () { + return gulp.src("./src/browser-cjs.js") + .pipe(sourcemaps.init()) + .pipe(rollup({ + output: { format: "cjs" }, + plugins: [ stripBanner() ], + })) + + .pipe(rename(function (path) { path.basename = "notmapped"; path.extname = ".js" })) + .pipe(buble()) + .pipe(preMinify()) + .pipe(removeJsDocImports()) + + // Replace variables + .pipe(replace(VARIABLES)) + .pipe(wrapTemplate("./build/template-module.js", VARIABLES)) + + .pipe(removeMappedSource("notmapped.js")) + + .pipe(rename(function (path) { path.basename = "jdenticon-module"; path.extname = ".js" })) + .pipe(replace(/[\r\n]*$/, "\n//# sourceMappingURL=jdenticon-module.js.map\n")) + .pipe(sourcemaps.write("./", { includeContent: true, addComment: false })) + .pipe(gulp.dest("dist")) + + .pipe(rename(function (path) { path.basename = "jdenticon-module-" + pack.version; path.extname = ".js" })) + .pipe(gulp.dest("obj/output")) +}); + +gulp.task("build-esm", function () { + return gulp.src("./src/browser-esm.js") + .pipe(sourcemaps.init()) + .pipe(rollup({ + output: { format: "esm" }, + plugins: [ stripBanner() ], + })) + + .pipe(preMinify()) + .pipe(removeJsDocImports()) + + // Replace variables + .pipe(replace(VARIABLES)) + .pipe(wrapTemplate("./build/template-module.js", VARIABLES)) + + .pipe(rename(function (path) { path.basename = "jdenticon-module"; path.extname = ".mjs" })) + .pipe(replace(/[\r\n]*$/, "\n//# sourceMappingURL=jdenticon-module.mjs.map\n")) + .pipe(sourcemaps.write("./", { includeContent: true, addComment: false })) + .pipe(gulp.dest("dist")) + + .pipe(rename(function (path) { path.basename = "jdenticon-module-" + pack.version; path.extname = ".mjs" })) + .pipe(gulp.dest("obj/output")) +}); + +gulp.task("build-node-cjs", function () { + return gulp.src("./src/node-cjs.js") + .pipe(sourcemaps.init()) + .pipe(rollup({ + external: [ "canvas-renderer" ], + plugins: [ stripBanner(), commonjs() ], + output: { format: "cjs" }, + })) + + .pipe(removeJsDocImports()) + + .pipe(replace(VARIABLES)) + .pipe(wrapTemplate("./build/template-module.js", VARIABLES)) + + .pipe(rename(path => { path.basename = "jdenticon-node"; path.extname = ".js" })) + .pipe(replace(/[\r\n]*$/, "\n//# sourceMappingURL=jdenticon-node.js.map\n")) + .pipe(sourcemaps.write("./", { includeContent: true, addComment: false })) + .pipe(gulp.dest("./dist")) + + .pipe(rename(function (path) { path.basename = "jdenticon-node-" + pack.version; path.extname = ".js" })) + .pipe(gulp.dest("./obj/output")); +}); + +gulp.task("build-node-esm", function () { + return gulp.src("./src/node-esm.js") + .pipe(sourcemaps.init()) + .pipe(rollup({ + external: [ "canvas-renderer" ], + plugins: [ stripBanner(), commonjs() ], + output: { format: "esm" }, + })) + + .pipe(removeJsDocImports()) + + .pipe(replace(VARIABLES)) + .pipe(wrapTemplate("./build/template-module.js", VARIABLES)) + + .pipe(rename(path => { path.basename = "jdenticon-node"; path.extname = ".mjs" })) + .pipe(replace(/[\r\n]*$/, "\n//# sourceMappingURL=jdenticon-node.mjs.map\n")) + .pipe(sourcemaps.write("./", { includeContent: true, addComment: false })) + + .pipe(gulp.dest("./dist")) + + .pipe(rename(function (path) { path.basename = "jdenticon-node-" + pack.version; path.extname = ".mjs" })) + .pipe(gulp.dest("./obj/output")); +}); + +gulp.task("update-license-year", function () { + return gulp.src("./LICENSE") + .pipe(replace(/\(c\) 2014-\d+/, "(c) 2014-" + new Date().getFullYear())) + .pipe(gulp.dest("./")) +}); + +gulp.task("update-readme", function () { + return gulp.src("./README.md") + .pipe(replace([ + [/@\d{1,2}\.\d{1,3}\.\d{1,3}/, "@" + pack.version], + [/(?<=integrity=\"([^-]+)-).*?(?=\")/, (match, algorithm) => { + const min = fs.readFileSync("./dist/jdenticon.min.js"); + return crypto.createHash(algorithm).update(min).digest("base64"); + }], + ])) + .pipe(gulp.dest("./")) +}); + +gulp.task("install-jdenticon-test", function () { + const globs = pack.files + .map(file => { + const isDirectory = + !/\*/.test(file) && + fs.existsSync(file) && + fs.lstatSync(file).isDirectory(); + return isDirectory ? path.join(file, "**") : file; + }); + + // Simulate an installed Jdenticon package. Cannot use actual npm command, since it won't install Jdenticon in a + // subfolder to the Jdenticon source package itself. + return gulp.src(["./package.json", ...globs], { base: "./" }) + .pipe(gulp.dest("./test/node_modules/jdenticon")); +}); + +gulp.task("build", gulp.series("clean", gulp.parallel( + "build-umd", "build-umd-min", + "build-esm", "build-cjs", + "build-node-cjs", "build-node-esm", +), "install-jdenticon-test")); + +gulp.task("clean-tests", function () { + return del(["./obj/test/unit/**"]); +}); + +gulp.task("build-unit-tests-js", function () { + return gulp.src("./test/unit/*.js", { base: "./" }) + .pipe(sourcemaps.init()) + .pipe(rollup({ + external: [ "canvas-renderer", "fs", "tap" ], + plugins: [ commonjs() ], + output: { format: "cjs" }, + })) + .pipe(sourcemaps.write("./")) + .pipe(gulp.dest("./obj")) +}); + +gulp.task("build-unit-tests", gulp.series("clean-tests", "build-unit-tests-js")); + +gulp.task("prepare-release", function () { + return gulp.src(["./LICENSE", "./README.md"]) + .pipe(replace(VARIABLES)) + .pipe(rename(function (path) { path.extname = ".txt" })) + .pipe(gulp.dest("obj/output")); +}); + +gulp.task("prepare-nuget", function () { + return gulp.src(["./build/jdenticon.nuspec"]) + .pipe(replace(VARIABLES)) + .pipe(rename(function (path) { path.basename = "~" + path.basename })) + .pipe(gulp.dest("./")); +}); + +gulp.task("nuget", async function () { + var command = "\"./build/nuget/nuget.exe\" pack ~jdenticon.nuspec -OutputDirectory releases"; + + if (process.platform !== "win32") { + command = "mono " + command; + } + + await promisify(exec)(command); + + await del(["./~jdenticon.nuspec"]); +}); + +gulp.task("create-package", function () { + return gulp.src(["./obj/output/*"]) + .pipe(zip("jdenticon-" + pack.version + ".zip")) + .pipe(gulp.dest("releases")); +}); + +gulp.task("release", gulp.series( + "build", + "update-license-year", "update-readme", + "prepare-release", + "create-package", + "prepare-nuget", "nuget", +)); diff --git a/jdenticon-js/node/package.json b/jdenticon-js/node/package.json new file mode 100644 index 0000000..034fbd3 --- /dev/null +++ b/jdenticon-js/node/package.json @@ -0,0 +1,4 @@ +{ + "main": "../dist/jdenticon-node", + "types": "../types/module.d.ts" +} diff --git a/jdenticon-js/package-lock.json b/jdenticon-js/package-lock.json new file mode 100644 index 0000000..e1d2226 --- /dev/null +++ b/jdenticon-js/package-lock.json @@ -0,0 +1,15357 @@ +{ + "name": "jdenticon", + "version": "3.3.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "jdenticon", + "version": "3.3.0", + "license": "MIT", + "dependencies": { + "canvas-renderer": "~2.2.0" + }, + "bin": { + "jdenticon": "bin/jdenticon.js" + }, + "devDependencies": { + "@rollup/plugin-alias": "^3.1.9", + "@rollup/plugin-commonjs": "^22.0.2", + "@rollup/plugin-node-resolve": "^13.3.0", + "@types/jquery": "^3.5.14", + "@types/node14": "npm:@types/node@14", + "@types/node16": "npm:@types/node@16", + "acorn": "^8.8.0", + "blink-diff": "^1.0.13", + "buble": "^0.20.0", + "del": "^6.1.1", + "eslint": "^8.21.0", + "express": "^4.18.1", + "gulp": "^4.0.2", + "gulp-buble": "^0.9.0", + "gulp-rename": "^2.0.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-terser": "^2.1.0", + "gulp-zip": "^5.1.0", + "module-alias": "^2.2.2", + "pngjs": "^6.0.0", + "rollup": "^2.77.2", + "rollup-plugin-strip-banner": "^2.0.0", + "rollup-plugin-terser": "^7.0.2", + "selenium-webdriver": "^4.20.0", + "source-map-loader": "^1.1.3", + "tap": "^16.3.0", + "typescript3": "npm:typescript@^3.2.4", + "typescript4": "npm:typescript@^4.7.4", + "webpack4": "npm:webpack@4", + "webpack5": "npm:webpack@5" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/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 + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@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 + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/traverse/node_modules/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 + }, + "node_modules/@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/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 + }, + "node_modules/@gulp-sourcemaps/identity-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", + "dev": true, + "dependencies": { + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", + "source-map": "^0.6.0", + "through2": "^3.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/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, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", + "dev": true, + "dependencies": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/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 + }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/plugin-alias": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz", + "integrity": "sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==", + "dev": true, + "dependencies": { + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", + "integrity": "sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", + "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^2.42.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", + "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", + "dev": true + }, + "node_modules/@types/jquery": { + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", + "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", + "dev": true, + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz", + "integrity": "sha512-Sr7BhXEAer9xyGuCN3Ek9eg9xPviCF2gfu9kTfuU2HkTVAMYSDeX40fvpmo72n5nansg3nsBjuQBrsS28r+NUw==" + }, + "node_modules/@types/node14": { + "name": "@types/node", + "version": "14.18.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.23.tgz", + "integrity": "sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA==", + "dev": true + }, + "node_modules/@types/node16": { + "name": "@types/node", + "version": "16.11.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.47.tgz", + "integrity": "sha512-fpP+jk2zJ4VW66+wAMFoBJlx1bxmBKx4DUFf68UHgdGCOuyUTDlLWqsaNPJh7xhNDykyJ9eIzAygilP/4WoN8g==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, + "node_modules/@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, + "dependencies": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "node_modules/@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, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@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, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@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" + } + }, + "node_modules/@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, + "dependencies": { + "@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" + } + }, + "node_modules/@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, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "node_modules/@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, + "dependencies": { + "@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" + } + }, + "node_modules/@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, + "dependencies": { + "@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" + } + }, + "node_modules/@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, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "deprecated": "This is probably built in to whatever tool you're using. If you still need it... idk", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "deprecated": "package has been renamed to acorn-import-attributes", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn5-object-spread": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn5-object-spread/-/acorn5-object-spread-4.0.0.tgz", + "integrity": "sha512-l+UYpDk+mjQoTXUHtSyUUb6glz9sSnl283LYLKvZJ7Nxpn4taIdP6DAr+8GwQ8UyY95tLWoKIr4/P7OWcw6WWw==", + "deprecated": "acorn>=5.4.1 supports object-spread", + "dev": true, + "dependencies": { + "acorn": "^5.1.2" + } + }, + "node_modules/acorn5-object-spread/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/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, + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/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, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/anymatch/node_modules/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, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", + "dev": true, + "dependencies": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "dependencies": { + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha512-Ej9qjcXY+8Tuy1cNqiwNMwFRXOy9UwgTeMA8LxreodygIPV48lx8PU1ecFxb5ZeU1DpMKxiq6vGLTxcitWZPbA==", + "dev": true + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/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, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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 + }, + "node_modules/async-hook-domain": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.4.tgz", + "integrity": "sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", + "dev": true, + "dependencies": { + "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/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, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "dev": true + }, + "node_modules/bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", + "dev": true, + "dependencies": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/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, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bind-obj-methods": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", + "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/blink-diff": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/blink-diff/-/blink-diff-1.0.13.tgz", + "integrity": "sha512-2hIEnGq8wruXfje9GvDV41VXo+4YdjrjL5ZMlVJT3Wi5k1jjz20fCTlVejSXoERirhEVsFYz9NmgdUYgQ41Giw==", + "dev": true, + "dependencies": { + "pngjs-image": "~0.11.5", + "preceptor-core": "~0.10.0", + "promise": "6.0.0" + }, + "bin": { + "blink-diff": "bin/blink-diff" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/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, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/browserify-sign/node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/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, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserify-zlib/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buble": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/buble/-/buble-0.20.0.tgz", + "integrity": "sha512-/1gnaMQE8xvd5qsNBl+iTuyjJ9XxeaVxAMF86dQ4EyxFJOZtsgOS8Ra+7WHgZTam5IFDtt4BguN0sH0tVTKrOw==", + "dev": true, + "dependencies": { + "acorn": "^6.4.1", + "acorn-dynamic-import": "^4.0.0", + "acorn-jsx": "^5.2.0", + "chalk": "^2.4.2", + "magic-string": "^0.25.7", + "minimist": "^1.2.5", + "regexpu-core": "4.5.4" + }, + "bin": { + "buble": "bin/buble" + } + }, + "node_modules/buble/node_modules/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, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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 + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/cacache/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001617", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", + "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/canvas-renderer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/canvas-renderer/-/canvas-renderer-2.2.0.tgz", + "integrity": "sha512-Itdq9pwXcs4IbbkRCXc7reeGBk6i6tlDtZTjE1yc+KvYkx1Mt3WLf6tidZ/Ixbm7Vmi+jpWKG0dRBor67x9yGw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "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" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/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, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/cloneable-readable/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/cloneable-readable/node_modules/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 + }, + "node_modules/cloneable-readable/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", + "dev": true, + "dependencies": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/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, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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 + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/concat-stream/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/concat-stream/node_modules/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 + }, + "node_modules/concat-stream/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/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 + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/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 + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-props": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", + "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", + "dev": true, + "dependencies": { + "each-props": "^1.3.2", + "is-plain-object": "^5.0.0" + } + }, + "node_modules/copy-props/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css/node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-format": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-0.0.0.tgz", + "integrity": "sha512-kAmAdtsjW5nQ02FERwI1bP4xe6HQBPwy5kpAF4CRSLOMUs/vgMIEEwpy6JqUs7NitTyhZiImxwAjgPpnteycHg==", + "deprecated": "0.x is no longer supported. Please upgrade to 4.x or higher.", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "dependencies": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + } + }, + "node_modules/debug-fabulous/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/debug-fabulous/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-require-extensions/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/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, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/duplexify/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/duplexify/node_modules/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 + }, + "node_modules/duplexify/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.761", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.761.tgz", + "integrity": "sha512-PIbxpiJGx6Bb8dQaonNc6CGTRlVntdLg/2nMa1YhnrwYOORY9a3ZgGN0UQYE6lAcj/lkyduJN7BPt/JiY+jAQQ==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enhanced-resolve/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/enhanced-resolve/node_modules/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, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/enhanced-resolve/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/enhanced-resolve/node_modules/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 + }, + "node_modules/enhanced-resolve/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/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, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/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, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", + "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.3", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/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 + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/glob-parent/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/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 + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "dependencies": { + "type": "^2.0.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-banner": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/extract-banner/-/extract-banner-0.1.2.tgz", + "integrity": "sha512-hDIp0Av6KuUUWSGH/jwo1Nj8U70wBlCA8mv9WshUC5xl29dCRol6no+yyWAEX/OMi2Au5+NGP833TemuaEh02g==", + "dev": true, + "dependencies": { + "strip-bom-string": "^0.1.2", + "strip-use-strict": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-banner/node_modules/strip-bom-string": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-0.1.2.tgz", + "integrity": "sha512-3DgNqQFTfOwWgxn3cXsa6h/WRgFa7dVb6/7YqwfJlBpLSSQbiU1VhaBNRKmtLI59CHjc9awLp9yGJREu7AnaMQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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 + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/fast-glob/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/fast-glob/node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/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 + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "deprecated": "This module is no longer supported.", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", + "integrity": "sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg==", + "dev": true + }, + "node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/flush-write-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/flush-write-stream/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/flush-write-stream/node_modules/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 + }, + "node_modules/flush-write-stream/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "dev": true, + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/from2/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/from2/node_modules/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 + }, + "node_modules/from2/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-exists-cached": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", + "integrity": "sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg==", + "dev": true + }, + "node_modules/fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function-loop": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", + "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream/node_modules/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, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/glob-stream/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/glob-stream/node_modules/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 + }, + "node_modules/glob-stream/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "dependencies": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-buble": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/gulp-buble/-/gulp-buble-0.9.0.tgz", + "integrity": "sha512-98YOmUfd9VG4Inskae19sfUuAxHnmzIatEt0/X6qbHGhlr8NozDFDA6+vTQQGh6tOgRZ2simlVc7c23h4gr1gg==", + "dev": true, + "dependencies": { + "buble": "^0.18.0", + "plugin-error": "^0.1.2", + "readable-stream": "^2.1.0", + "vinyl": "^2.1.0", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "node_modules/gulp-buble/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/gulp-buble/node_modules/acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ==", + "dev": true, + "dependencies": { + "acorn": "^3.0.4" + } + }, + "node_modules/gulp-buble/node_modules/acorn-jsx/node_modules/acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/gulp-buble/node_modules/buble": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/buble/-/buble-0.18.0.tgz", + "integrity": "sha512-U3NJxUiSz0H1EB54PEHAuBTxdXgQH4DaQkvkINFXf9kEKCDWSn67EgQfFKbkTzsok4xRrIPsoxWDl2czCHR65g==", + "dev": true, + "dependencies": { + "acorn": "^5.1.2", + "acorn-jsx": "^3.0.1", + "acorn5-object-spread": "^4.0.0", + "chalk": "^2.1.0", + "magic-string": "^0.22.4", + "minimist": "^1.2.0", + "os-homedir": "^1.0.1", + "vlq": "^0.2.2" + }, + "bin": { + "buble": "bin/buble" + } + }, + "node_modules/gulp-buble/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/gulp-buble/node_modules/magic-string": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "dev": true, + "dependencies": { + "vlq": "^0.2.2" + } + }, + "node_modules/gulp-buble/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/gulp-buble/node_modules/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 + }, + "node_modules/gulp-buble/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-rename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "dev": true, + "dependencies": { + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-sourcemaps/node_modules/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, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-terser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-2.1.0.tgz", + "integrity": "sha512-lQ3+JUdHDVISAlUIUSZ/G9Dz/rBQHxOiYDQ70IVWFQeh4b33TC1MCIU+K18w07PS3rq/CVc34aQO4SUbdaNMPQ==", + "dev": true, + "dependencies": { + "plugin-error": "^1.0.1", + "terser": "^5.9.0", + "through2": "^4.0.2", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-terser/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-terser/node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-terser/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-terser/node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-terser/node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/gulp-zip": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.1.0.tgz", + "integrity": "sha512-XZr/y91IliK/SpR74g3TkZejGkGEmK7CSDjSghT1jXshgO+dFvpLIz9w9fpuwkew6i7k4F+G24TubNgq1ISzEw==", + "dev": true, + "dependencies": { + "get-stream": "^5.2.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "vinyl": "^2.1.0", + "yazl": "^2.5.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "gulp": ">=4" + }, + "peerDependenciesMeta": { + "gulp": { + "optional": true + } + } + }, + "node_modules/gulp-zip/node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-zip/node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-zip/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-zip/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", + "dev": true, + "dependencies": { + "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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=", + "deprecated": "Please upgrade to v0.1.7", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/is-builtin-module": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", + "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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=", + "deprecated": "Please upgrade to v0.1.5", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/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, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/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 + }, + "node_modules/istanbul-lib-source-maps/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.1.tgz", + "integrity": "sha512-npN8f+M4+IQ8xD3CcWi3U62VQwKlT3Tj4GxbdT/fYTmeogD9eBF9OFdpoFG/VPNoshRjPUijdkp/p2XrzUHaVg==", + "dev": true, + "dependencies": { + "cliui": "^7.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jackspeak/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/jackspeak/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jackspeak/node_modules/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 + }, + "node_modules/jackspeak/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/jszip/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/jszip/node_modules/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 + }, + "node_modules/jszip/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/just-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", + "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", + "dev": true, + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/lazystream/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/lazystream/node_modules/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 + }, + "node_modules/lazystream/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", + "dev": true, + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libtap": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.4.0.tgz", + "integrity": "sha512-STLFynswQ2A6W14JkabgGetBNk6INL1REgJ9UeNKw5llXroC2cGLgKTqavv0sl8OLVztLLipVKMcQ7yeUcqpmg==", + "dev": true, + "dependencies": { + "async-hook-domain": "^2.0.4", + "bind-obj-methods": "^3.0.0", + "diff": "^4.0.2", + "function-loop": "^2.0.1", + "minipass": "^3.1.5", + "own-or": "^1.0.0", + "own-or-env": "^1.0.2", + "signal-exit": "^3.0.4", + "stack-utils": "^2.0.4", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "tcompare": "^5.0.6", + "trivial-deferred": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/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, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/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, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-path/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log4js": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-1.1.1.tgz", + "integrity": "sha512-lYb14ZSs1M/CUFuvy7Zk3VZLDtqrqOaVql9CE0tv8g6/qE1Gfq97XKdltBsjSxxvcJ+t8fAXOnvFxSsms7gGVg==", + "deprecated": "1.x is no longer supported. Please upgrade to 6.x or higher.", + "dev": true, + "dependencies": { + "debug": "^2.2.0", + "semver": "^5.3.0", + "streamroller": "^0.4.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/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, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/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, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", + "dev": true, + "dependencies": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/matchdep/node_modules/findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/matchdep/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/memoizee/node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/memory-fs/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/memory-fs/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/memory-fs/node_modules/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 + }, + "node_modules/memory-fs/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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 + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mississippi/node_modules/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, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/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, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/module-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==", + "dev": true + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/node-libs-browser/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/node-libs-browser/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/node-libs-browser/node_modules/readable-stream/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/node-libs-browser/node_modules/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 + }, + "node_modules/node-libs-browser/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/node-libs-browser/node_modules/string_decoder/node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/nyc/node_modules/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 + }, + "node_modules/nyc/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/nyc/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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 + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/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, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.3.tgz", + "integrity": "sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "dev": true, + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/ordered-read-streams/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/ordered-read-streams/node_modules/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 + }, + "node_modules/ordered-read-streams/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/own-or": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", + "integrity": "sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA==", + "dev": true + }, + "node_modules/own-or-env": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.2.tgz", + "integrity": "sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw==", + "dev": true, + "dependencies": { + "own-or": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/parallel-transform/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/parallel-transform/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/parallel-transform/node_modules/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 + }, + "node_modules/parallel-transform/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/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, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "dev": true, + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", + "dev": true, + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", + "dev": true, + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/pngjs-image": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/pngjs-image/-/pngjs-image-0.11.7.tgz", + "integrity": "sha512-JRyrmT+HXa1/gvdHpebus8TGqKa8WRgcsHz/DDalxRsMhvu6AOA99/enBFjZIPvmXVAzwKR051s80TuE1IiCpg==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.4.8", + "pako": "^0.2.6", + "pngjs": "2.3.1", + "request": "^2.55.0", + "stream-buffers": "1.0.1", + "underscore": "1.7.0" + } + }, + "node_modules/pngjs-image/node_modules/pngjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-2.3.1.tgz", + "integrity": "sha512-ITNPqvx+SSssNFOgHQzGG87HrqQ0g2nMSHc1jjU5Piq9xJEJ40fiFEPz0S5HSSXxBHrTnhaBHIayTO5aRfk2vw==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/preceptor-core": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/preceptor-core/-/preceptor-core-0.10.1.tgz", + "integrity": "sha512-WLDk+UowEESixvlhiamGOj/iqWrp8IWeCCHvBZrLh0g4/A1Fa77fDQWqQUd5S5rScT+9u49aDfa45xYRkxqmiA==", + "dev": true, + "dependencies": { + "log4js": "1.1.1", + "underscore": "1.7.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/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 + }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/promise": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.0.0.tgz", + "integrity": "sha512-PjIqIEWR8EWwP5ml3Wf5KWIP3sIdXAew9vQ6vLOLV+z4LMa/8ZQyLd7sTWe2r8OuA8A9jsIYptDfbEn/L36ogw==", + "dev": true, + "dependencies": { + "asap": "~1.0.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/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, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/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, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/readdirp/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/readdirp/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/readdirp/node_modules/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 + }, + "node_modules/readdirp/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "regenerate": "^1.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", + "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.0.2", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "dev": true, + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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 + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", + "dev": true, + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rollup": { + "version": "2.77.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.2.tgz", + "integrity": "sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-strip-banner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-strip-banner/-/rollup-plugin-strip-banner-2.0.0.tgz", + "integrity": "sha512-9ipg2Wzl+6AZ+8PW65DrvuLzVrf9PjXZW39GeG9R0j0vm6DgxYli14wDpovRuKc+xEjKIE5DLAGwUem4Yvo+IA==", + "dev": true, + "dependencies": { + "extract-banner": "0.1.2", + "magic-string": "0.25.7", + "rollup-pluginutils": "2.8.2" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "rollup": "^1.0.0 || ^2.0.0" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/rollup/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "dev": true, + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/selenium-webdriver": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.20.0.tgz", + "integrity": "sha512-s/G44lGQ1xB3tmtX6NNPomlkpL6CxLdmAvp/AGWWwi4qv5Te1+qji7tPSyr6gyuoPpdYiof1rKnWe3luy0MrYA==", + "dev": true, + "dependencies": { + "jszip": "^3.10.1", + "tmp": "^0.2.3", + "ws": ">=8.16.0" + }, + "engines": { + "node": ">= 14.20.0" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==", + "dev": true, + "dependencies": { + "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.1.3.tgz", + "integrity": "sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader/node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/source-map-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/source-map-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/source-map-loader/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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=", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "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" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dev": true, + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-browserify/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/stream-browserify/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/stream-browserify/node_modules/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 + }, + "node_modules/stream-browserify/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/stream-buffers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-1.0.1.tgz", + "integrity": "sha512-t+8bSU8qPq7NnWHWAvikjcZf+biErLZzD15RroYft1IKQwYbkRyiwppT7kNqwdtYLS59YPxc4sTSvwbLSMaodw==", + "dev": true, + "engines": { + "node": ">= 0.3.0" + } + }, + "node_modules/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, + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-http/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/stream-http/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/stream-http/node_modules/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 + }, + "node_modules/stream-http/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/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 + }, + "node_modules/streamroller": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.4.1.tgz", + "integrity": "sha512-w0GGkMlWOiIBIYTmOWHTWKy9Y5hKxGKpQ5WpiHqwhvoSoMHXNTITrk6ZsR3fdgz3Bi/c+CXVHwmfPUQFkEPL+A==", + "deprecated": "0.x is no longer supported. Please upgrade to 3.x or higher.", + "dev": true, + "dependencies": { + "date-format": "^0.0.0", + "debug": "^0.7.2", + "mkdirp": "^0.5.1", + "readable-stream": "^1.1.7" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/streamroller/node_modules/debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha512-EohAb3+DSHSGx8carOSKJe8G0ayV5/i609OD0J2orCkuyae7SyZSz2aoLmQF2s0Pj5gITDebwPH7GFBlqOUQ1Q==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-use-strict": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-use-strict/-/strip-use-strict-0.1.0.tgz", + "integrity": "sha512-E7gSkFVwkg3jge5tUrBM6u9S1lfcao2qPjliJqDw2+nWLmtyS5amnSJqDaMk6kCYvBqU/eIG25pN78uMtaj/Ig==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==", + "dev": true, + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/tap": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.0.tgz", + "integrity": "sha512-J9GffPUAbX6FnWbQ/jj7ktzd9nnDFP1fH44OzidqOmxUfZ1hPLMOvpS99LnDiP0H2mO8GY3kGN5XoY0xIKbNFA==", + "bundleDependencies": [ + "ink", + "treport", + "@types/react", + "@isaacs/import-jsx", + "react" + ], + "dev": true, + "dependencies": { + "@isaacs/import-jsx": "^4.0.1", + "@types/react": "^17", + "chokidar": "^3.3.0", + "findit": "^2.0.0", + "foreground-child": "^2.0.0", + "fs-exists-cached": "^1.0.0", + "glob": "^7.1.6", + "ink": "^3.2.0", + "isexe": "^2.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "jackspeak": "^1.4.1", + "libtap": "^1.4.0", + "minipass": "^3.1.1", + "mkdirp": "^1.0.4", + "nyc": "^15.1.0", + "opener": "^1.5.1", + "react": "^17.0.2", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.6", + "source-map-support": "^0.5.16", + "tap-mocha-reporter": "^5.0.3", + "tap-parser": "^11.0.1", + "tap-yaml": "^1.0.0", + "tcompare": "^5.0.7", + "treport": "^3.0.3", + "which": "^2.0.2" + }, + "bin": { + "tap": "bin/run.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "coveralls": "^3.1.1", + "flow-remove-types": ">=2.112.0", + "ts-node": ">=8.5.2", + "typescript": ">=3.7.2" + }, + "peerDependenciesMeta": { + "coveralls": { + "optional": true + }, + "flow-remove-types": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tap-mocha-reporter": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.3.tgz", + "integrity": "sha512-6zlGkaV4J+XMRFkN0X+yuw6xHbE9jyCZ3WUKfw4KxMyRGOpYSRuuQTRJyWX88WWuLdVTuFbxzwXhXuS2XE6o0g==", + "dev": true, + "dependencies": { + "color-support": "^1.1.0", + "debug": "^4.1.1", + "diff": "^4.0.1", + "escape-string-regexp": "^2.0.0", + "glob": "^7.0.5", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "unicode-length": "^2.0.2" + }, + "bin": { + "tap-mocha-reporter": "index.js" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap-mocha-reporter/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/tap-mocha-reporter/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap-mocha-reporter/node_modules/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 + }, + "node_modules/tap-parser": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-11.0.1.tgz", + "integrity": "sha512-5ow0oyFOnXVSALYdidMX94u0GEjIlgc/BPFYLx0yRh9hb8+cFGNJqJzDJlUqbLOwx8+NBrIbxCWkIQi7555c0w==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "minipass": "^3.1.6", + "tap-yaml": "^1.0.0" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap-yaml": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.0.tgz", + "integrity": "sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==", + "dev": true, + "dependencies": { + "yaml": "^1.5.0" + } + }, + "node_modules/tap/node_modules/@ampproject/remapping": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tap/node_modules/@babel/code-frame": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/compat-data": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/core": { + "version": "7.17.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/tap/node_modules/@babel/generator": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helpers": { + "version": "7.17.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/highlight": { + "version": "7.16.10", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/parser": { + "version": "7.17.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.17.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.16.7", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/template": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/traverse": { + "version": "7.17.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/types": { + "version": "7.17.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@isaacs/import-jsx": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.5.5", + "@babel/plugin-proposal-object-rest-spread": "^7.5.5", + "@babel/plugin-transform-destructuring": "^7.5.0", + "@babel/plugin-transform-react-jsx": "^7.3.0", + "caller-path": "^3.0.1", + "find-cache-dir": "^3.2.0", + "make-dir": "^3.0.2", + "resolve-from": "^3.0.0", + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tap/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/tap/node_modules/@types/prop-types": { + "version": "15.7.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/@types/react": { + "version": "17.0.41", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/tap/node_modules/@types/scheduler": { + "version": "0.16.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/@types/yoga-layout": { + "version": "1.9.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "inBundle": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/ansicolors": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/auto-bind": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/tap/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/browserslist": { + "version": "4.20.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/tap/node_modules/caller-callsite": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/caller-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/caniuse-lite": { + "version": "1.0.30001319", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ], + "inBundle": true, + "license": "CC-BY-4.0" + }, + "node_modules/tap/node_modules/cardinal": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/tap/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tap/node_modules/ci-info": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/cli-boxes": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/cli-truncate": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/code-excerpt": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/tap/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/convert-source-map": { + "version": "1.8.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/tap/node_modules/convert-to-spaces": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/tap/node_modules/csstype": { + "version": "3.0.11", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/tap/node_modules/electron-to-chromium": { + "version": "1.4.89", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/tap/node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/events-to-array": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/find-cache-dir": { + "version": "3.3.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/tap/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/tap/node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/glob": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tap/node_modules/globals": { + "version": "11.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/tap/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/ink": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "auto-bind": "4.0.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.0", + "cli-cursor": "^3.1.0", + "cli-truncate": "^2.1.0", + "code-excerpt": "^3.0.0", + "indent-string": "^4.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.20", + "patch-console": "^1.0.0", + "react-devtools-core": "^4.19.1", + "react-reconciler": "^0.26.2", + "scheduler": "^0.20.2", + "signal-exit": "^3.0.2", + "slice-ansi": "^3.0.0", + "stack-utils": "^2.0.2", + "string-width": "^4.2.2", + "type-fest": "^0.12.0", + "widest-line": "^3.1.0", + "wrap-ansi": "^6.2.0", + "ws": "^7.5.5", + "yoga-layout-prebuilt": "^1.9.6" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": ">=16.8.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/tap/node_modules/ink/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/ink/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tap/node_modules/ink/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/ink/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/ink/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/ink/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/is-ci": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/tap/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/tap/node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/json5": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/loose-envify": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/tap/node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tap/node_modules/minipass": { + "version": "3.1.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/node-releases": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/once": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/tap/node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/patch-console": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/punycode": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/react": { + "version": "17.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/react-devtools-core": { + "version": "4.24.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/tap/node_modules/react-reconciler": { + "version": "0.26.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^17.0.2" + } + }, + "node_modules/tap/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/tap/node_modules/redeyed": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "esprima": "~4.0.0" + } + }, + "node_modules/tap/node_modules/resolve-from": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/restore-cursor": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tap/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/scheduler": { + "version": "0.20.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/tap/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/tap/node_modules/shell-quote": { + "version": "1.7.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/slice-ansi": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/stack-utils": { + "version": "2.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/tap-parser": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "events-to-array": "^1.0.1", + "minipass": "^3.1.6", + "tap-yaml": "^1.0.0" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap/node_modules/tap-yaml": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yaml": "^1.5.0" + } + }, + "node_modules/tap/node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tap/node_modules/treport": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/import-jsx": "^4.0.1", + "cardinal": "^2.1.1", + "chalk": "^3.0.0", + "ink": "^3.2.0", + "ms": "^2.1.2", + "tap-parser": "^11.0.0", + "unicode-length": "^2.0.2" + }, + "peerDependencies": { + "react": "^17.0.2" + } + }, + "node_modules/tap/node_modules/treport/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/treport/node_modules/chalk": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/treport/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/treport/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/treport/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/treport/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/type-fest": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/unicode-length": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/tap/node_modules/unicode-length/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/unicode-length/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/widest-line": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/ws": { + "version": "7.5.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/tap/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/tap/node_modules/yoga-layout-prebuilt": { + "version": "1.10.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@types/yoga-layout": "1.9.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tcompare": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz", + "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==", + "dev": true, + "dependencies": { + "diff": "^4.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/through2/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/through2/node_modules/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 + }, + "node_modules/through2/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", + "dev": true, + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/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, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/trivial-deferred": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.0.1.tgz", + "integrity": "sha512-dagAKX7vaesNNAwOc9Np9C2mJ+7YopF4lk+jE2JML9ta4kZ91Y6UruJNH65bLRYoUROD8EY+Pmi44qQWwXR7sw==", + "dev": true + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript3": { + "name": "typescript", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true + }, + "node_modules/typescript4": { + "name": "typescript", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==", + "dev": true + }, + "node_modules/undertaker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-length": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.0.2.tgz", + "integrity": "sha512-Ph/j1VbS3/r77nhoY2WU0GWGjVYOHL3xpKp0y/Eq2e5r0mT/6b649vm7KFO6RdAdrZkYLdxphYVgvODxPB+Ebg==", + "dev": true, + "dependencies": { + "punycode": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/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, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/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, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", + "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-browserslist-db/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/vinyl-fs/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/vinyl-fs/node_modules/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 + }, + "node_modules/vinyl-fs/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", + "dev": true, + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "dev": true, + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/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, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/watchpack/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/watchpack/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "optional": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/watchpack/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/watchpack/node_modules/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, + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/watchpack/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/watchpack/node_modules/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, + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/watchpack/node_modules/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, + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/watchpack/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/watchpack/node_modules/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, + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/webpack": { + "version": "4.47.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz", + "integrity": "sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==", + "dev": true, + "peer": true, + "dependencies": { + "@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.5.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" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + }, + "webpack-command": { + "optional": true + } + } + }, + "node_modules/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, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/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, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack4": { + "name": "webpack", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "dev": true, + "dependencies": { + "@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.5.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" + } + }, + "node_modules/webpack4/node_modules/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, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack5": { + "name": "webpack", + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + } + }, + "node_modules/webpack5/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/webpack5/node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "peer": true + }, + "node_modules/webpack5/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/webpack5/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/webpack5/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/webpack5/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/webpack5/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/webpack5/node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/webpack5/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack5/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack5/node_modules/enhanced-resolve": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack5/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack5/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack5/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack5/node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/webpack5/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack5/node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/webpack5/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/webpack5/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack5/node_modules/terser": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/webpack5/node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/webpack5/node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack5/node_modules/webpack": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack5/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "peer": true + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "peer": true + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true, + "peer": true + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "peer": true + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/webpack5/node_modules/webpack/node_modules/es-module-lexer": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", + "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "dev": true, + "peer": true + }, + "node_modules/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 + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "node_modules/yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/jdenticon-js/package.json b/jdenticon-js/package.json new file mode 100644 index 0000000..9f07efc --- /dev/null +++ b/jdenticon-js/package.json @@ -0,0 +1,126 @@ +{ + "name": "jdenticon", + "version": "3.3.0", + "description": "Javascript identicon generator", + "main": "dist/jdenticon-node.js", + "exports": { + ".": { + "types": "./types/module.d.ts", + "node": { + "require": "./dist/jdenticon-node.js", + "import": "./dist/jdenticon-node.mjs" + }, + "browser": { + "require": "./dist/jdenticon-module.js", + "import": "./dist/jdenticon-module.mjs" + }, + "default": "./dist/jdenticon-node.js" + }, + "./browser": { + "types": "./types/module.d.ts", + "import": "./dist/jdenticon-module.mjs", + "default": "./dist/jdenticon-module.js" + }, + "./node": { + "types": "./types/module.d.ts", + "import": "./dist/jdenticon-node.mjs", + "default": "./dist/jdenticon-node.js" + }, + "./standalone": { + "types": "./types/umd.d.ts", + "default": "./dist/jdenticon.min.js" + } + }, + "browser": "dist/jdenticon-module", + "jsdelivr": "dist/jdenticon.min.js", + "unpkg": "dist/jdenticon.min.js", + "types": "types/module.d.ts", + "bin": { + "jdenticon": "bin/jdenticon.js" + }, + "engines": { + "node": ">=6.4.0" + }, + "dependencies": { + "canvas-renderer": "~2.2.0" + }, + "devDependencies": { + "@rollup/plugin-alias": "^3.1.9", + "@rollup/plugin-commonjs": "^22.0.2", + "@rollup/plugin-node-resolve": "^13.3.0", + "@types/jquery": "^3.5.14", + "@types/node14": "npm:@types/node@14", + "@types/node16": "npm:@types/node@16", + "acorn": "^8.8.0", + "blink-diff": "^1.0.13", + "buble": "^0.20.0", + "del": "^6.1.1", + "eslint": "^8.21.0", + "express": "^4.18.1", + "gulp": "^4.0.2", + "gulp-buble": "^0.9.0", + "gulp-rename": "^2.0.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-terser": "^2.1.0", + "gulp-zip": "^5.1.0", + "module-alias": "^2.2.2", + "pngjs": "^6.0.0", + "rollup": "^2.77.2", + "rollup-plugin-strip-banner": "^2.0.0", + "rollup-plugin-terser": "^7.0.2", + "selenium-webdriver": "^4.20.0", + "source-map-loader": "^1.1.3", + "tap": "^16.3.0", + "typescript3": "npm:typescript@^3.2.4", + "typescript4": "npm:typescript@^4.7.4", + "webpack4": "npm:webpack@4", + "webpack5": "npm:webpack@5" + }, + "tap": { + "check-coverage": false + }, + "scripts": { + "build": "npm run lint && npm run tsc3 -- -p src/tsconfig.json && gulp build", + "release": "gulp release", + "lint": "eslint ./src/**/*.js", + "test:unit": "gulp build-unit-tests && tap obj/test/unit/*.js", + "test:types": "npm run test:types:browser && npm run test:types:node14 && npm run test:types:node16 && npm run test:types:umd", + "test:types:browser": "npm run tsc3 -- -p ./test/types/module-browser/tsconfig.json && npm run tsc4 -- -p ./test/types/module-browser/tsconfig.json --moduleResolution node16", + "test:types:node14": "npm run tsc3 -- -p ./test/types/module-node14/tsconfig.json", + "test:types:node16": "npm run tsc4 -- -p ./test/types/module-node16/tsconfig.json && npm run tsc4 -- -p ./test/types/module-node16/tsconfig.json --moduleResolution node16", + "test:types:umd": "npm run tsc3 -- -p ./test/types/umd/tsconfig.json && npm run tsc4 -- -p ./test/types/umd/tsconfig.json --moduleResolution node16", + "test:browser-win": "tap ./test/e2e/browser/test.js --test-arg=win --test-arg=chrome,firefox,edge,ie11,ie10,ie9 --timeout=300", + "test:browser-macos": "tap ./test/e2e/browser/test.js --test-arg=macos --test-arg=chrome,firefox,safari --timeout=300", + "test:node-cjs": "tap ./test/e2e/node/test.js", + "test:node-esm": "tap ./test/e2e/node/test.mjs", + "test:webpack4": "cd ./test/e2e/webpack && node runner.js webpack4 && tap ./app.bundle.js --no-coverage", + "test:webpack5": "cd ./test/e2e/webpack && node runner.js webpack5 && tap ./app.bundle.js --no-coverage", + "test:rollup": "cd ./test/e2e/rollup && rollup --config && tap ./app.bundle.js --no-coverage", + "tsc3": "node ./node_modules/typescript3/bin/tsc", + "tsc4": "node ./node_modules/typescript4/bin/tsc" + }, + "files": [ + "bin/", + "dist/", + "standalone/", + "browser/", + "node/", + "types/*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/dmester/jdenticon" + }, + "bugs": { + "url": "https://github.com/dmester/jdenticon/issues" + }, + "keywords": [ + "javascript", + "identicon", + "jdenticon", + "avatar" + ], + "author": "Daniel Mester Pirttijärvi", + "license": "MIT", + "homepage": "https://jdenticon.com/" +} diff --git a/jdenticon-js/src/apis/configure.js b/jdenticon-js/src/apis/configure.js new file mode 100644 index 0000000..382d444 --- /dev/null +++ b/jdenticon-js/src/apis/configure.js @@ -0,0 +1,2 @@ + +export { configure } from "../common/configuration"; diff --git a/jdenticon-js/src/apis/drawIcon.js b/jdenticon-js/src/apis/drawIcon.js new file mode 100644 index 0000000..723fc9e --- /dev/null +++ b/jdenticon-js/src/apis/drawIcon.js @@ -0,0 +1,28 @@ +import { iconGenerator } from "../renderer/iconGenerator"; +import { isValidHash, computeHash } from "../common/hashUtils"; +import { CanvasRenderer } from "../renderer/canvas/canvasRenderer"; +import { IS_RENDERED_PROPERTY } from "../common/dom"; + +/** + * Draws an identicon to a context. + * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0). + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function drawIcon(ctx, hashOrValue, size, config) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + const canvas = ctx.canvas; + if (canvas) { + canvas[IS_RENDERED_PROPERTY] = true; + } +} diff --git a/jdenticon-js/src/apis/jquery.js b/jdenticon-js/src/apis/jquery.js new file mode 100644 index 0000000..9eb0bb8 --- /dev/null +++ b/jdenticon-js/src/apis/jquery.js @@ -0,0 +1,18 @@ +import { update } from "./update"; + +/** + * Renders an identicon for all matching supported elements. + * + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. If not + * specified the `data-jdenticon-hash` and `data-jdenticon-value` attributes of each element will be + * evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function jdenticonJqueryPlugin(hashOrValue, config) { + this["each"](function (index, el) { + update(el, hashOrValue, config); + }); + return this; +} \ No newline at end of file diff --git a/jdenticon-js/src/apis/toPng.js b/jdenticon-js/src/apis/toPng.js new file mode 100644 index 0000000..fe975af --- /dev/null +++ b/jdenticon-js/src/apis/toPng.js @@ -0,0 +1,24 @@ +import canvasRenderer from "canvas-renderer"; +import { iconGenerator } from "../renderer/iconGenerator"; +import { isValidHash, computeHash } from "../common/hashUtils"; +import { CanvasRenderer } from "../renderer/canvas/canvasRenderer"; + +/** + * Draws an identicon as PNG. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {Buffer} PNG data + */ +export function toPng(hashOrValue, size, config) { + const canvas = canvasRenderer.createCanvas(size, size); + const ctx = canvas.getContext("2d"); + + iconGenerator(new CanvasRenderer(ctx, size), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + + return canvas.toPng({ "Software": "Jdenticon" }); +} \ No newline at end of file diff --git a/jdenticon-js/src/apis/toSvg.js b/jdenticon-js/src/apis/toSvg.js new file mode 100644 index 0000000..8ac0fc4 --- /dev/null +++ b/jdenticon-js/src/apis/toSvg.js @@ -0,0 +1,21 @@ +import { iconGenerator } from "../renderer/iconGenerator"; +import { isValidHash, computeHash } from "../common/hashUtils"; +import { SvgRenderer } from "../renderer/svg/svgRenderer"; +import { SvgWriter } from "../renderer/svg/svgWriter"; + +/** + * Draws an identicon as an SVG string. + * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param {number} size - Icon size in pixels. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns {string} SVG string + */ +export function toSvg(hashOrValue, size, config) { + const writer = new SvgWriter(size); + iconGenerator(new SvgRenderer(writer), + isValidHash(hashOrValue) || computeHash(hashOrValue), + config); + return writer.toString(); +} diff --git a/jdenticon-js/src/apis/update.js b/jdenticon-js/src/apis/update.js new file mode 100644 index 0000000..eac2ab8 --- /dev/null +++ b/jdenticon-js/src/apis/update.js @@ -0,0 +1,149 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { iconGenerator } from "../renderer/iconGenerator"; +import { isValidHash, computeHash } from "../common/hashUtils"; +import { ATTRIBUTES, ICON_SELECTOR, IS_RENDERED_PROPERTY, documentQuerySelectorAll } from "../common/dom"; +import { SvgRenderer } from "../renderer/svg/svgRenderer"; +import { SvgElement } from "../renderer/svg/svgElement"; +import { CanvasRenderer } from "../renderer/canvas/canvasRenderer"; +import { ICON_TYPE_CANVAS, ICON_TYPE_SVG, getIdenticonType } from "../common/dom"; + + +/** + * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute. + */ +export function updateAll() { + if (documentQuerySelectorAll) { + update(ICON_SELECTOR); + } +} + +/** + * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute that have not already + * been rendered. + */ +export function updateAllConditional() { + if (documentQuerySelectorAll) { + /** @type {NodeListOf} */ + const elements = documentQuerySelectorAll(ICON_SELECTOR); + + for (let i = 0; i < elements.length; i++) { + const el = elements[i]; + if (!el[IS_RENDERED_PROPERTY]) { + update(el); + } + } + } +} + +/** + * Updates the identicon in the specified `` or `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function update(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType) { + return iconType == ICON_TYPE_SVG ? + new SvgRenderer(new SvgElement(el)) : + new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function updateCanvas(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType == ICON_TYPE_CANVAS) { + return new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d")); + } + }); +} + +/** + * Updates the identicon in the specified `` elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function updateSvg(el, hashOrValue, config) { + renderDomElement(el, hashOrValue, config, function (el, iconType) { + if (iconType == ICON_TYPE_SVG) { + return new SvgRenderer(new SvgElement(el)); + } + }); +} + +/** + * Updates the identicon in the specified canvas or svg elements. + * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param {Object|number|undefined} config + * @param {function(Element,number):import("../renderer/renderer").Renderer} rendererFactory - Factory function for creating an icon renderer. + */ +function renderDomElement(el, hashOrValue, config, rendererFactory) { + if (typeof el === "string") { + if (documentQuerySelectorAll) { + const elements = documentQuerySelectorAll(el); + for (let i = 0; i < elements.length; i++) { + renderDomElement(elements[i], hashOrValue, config, rendererFactory); + } + } + return; + } + + // Hash selection. The result from getValidHash or computeHash is + // accepted as a valid hash. + const hash = + // 1. Explicit valid hash + isValidHash(hashOrValue) || + + // 2. Explicit value (`!= null` catches both null and undefined) + hashOrValue != null && computeHash(hashOrValue) || + + // 3. `data-jdenticon-hash` attribute + isValidHash(el.getAttribute(ATTRIBUTES.HASH)) || + + // 4. `data-jdenticon-value` attribute. + // We want to treat an empty attribute as an empty value. + // Some browsers return empty string even if the attribute + // is not specified, so use hasAttribute to determine if + // the attribute is specified. + el.hasAttribute(ATTRIBUTES.VALUE) && computeHash(el.getAttribute(ATTRIBUTES.VALUE)); + + if (!hash) { + // No hash specified. Don't render an icon. + return; + } + + const renderer = rendererFactory(el, getIdenticonType(el)); + if (renderer) { + // Draw icon + iconGenerator(renderer, hash, config); + el[IS_RENDERED_PROPERTY] = true; + } +} diff --git a/jdenticon-js/src/browser-cjs.js b/jdenticon-js/src/browser-cjs.js new file mode 100644 index 0000000..833e91c --- /dev/null +++ b/jdenticon-js/src/browser-cjs.js @@ -0,0 +1,39 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// This file is compiled to dist/jdenticon-module.js + +import { defineConfigProperty } from "./common/configuration"; +import { configure } from "./apis/configure"; +import { drawIcon } from "./apis/drawIcon"; +import { toSvg } from "./apis/toSvg"; +import { update, updateAll, updateCanvas, updateSvg } from "./apis/update"; + +const jdenticon = updateAll; + +defineConfigProperty(jdenticon); + +// Export public API +jdenticon["configure"] = configure; +jdenticon["drawIcon"] = drawIcon; +jdenticon["toSvg"] = toSvg; +jdenticon["update"] = update; +jdenticon["updateCanvas"] = updateCanvas; +jdenticon["updateSvg"] = updateSvg; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +jdenticon["version"] = "#version#"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +jdenticon["bundle"] = "browser-cjs"; + +module.exports = jdenticon; \ No newline at end of file diff --git a/jdenticon-js/src/browser-esm.js b/jdenticon-js/src/browser-esm.js new file mode 100644 index 0000000..fbb7566 --- /dev/null +++ b/jdenticon-js/src/browser-esm.js @@ -0,0 +1,24 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// This file is compiled to dist/jdenticon-module.mjs + +export { configure } from "./apis/configure"; +export { drawIcon } from "./apis/drawIcon"; +export { toSvg } from "./apis/toSvg"; +export { update, updateCanvas, updateSvg } from "./apis/update"; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +export const version = "#version#"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +export const bundle = "browser-esm"; diff --git a/jdenticon-js/src/browser-umd.js b/jdenticon-js/src/browser-umd.js new file mode 100644 index 0000000..3513755 --- /dev/null +++ b/jdenticon-js/src/browser-umd.js @@ -0,0 +1,71 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// This file is compiled to dist/jdenticon.js and dist/jdenticon.min.js + +import { CONFIG_PROPERTIES, defineConfigProperty } from "./common/configuration"; +import { observer } from "./common/observer"; +import { configure } from "./apis/configure"; +import { drawIcon } from "./apis/drawIcon"; +import { toSvg } from "./apis/toSvg"; +import { update, updateAll, updateAllConditional } from "./apis/update"; +import { jdenticonJqueryPlugin } from "./apis/jquery"; +import { GLOBAL } from "./common/global"; +import { whenDocumentIsReady } from "./common/dom"; + +const jdenticon = updateAll; + +defineConfigProperty(jdenticon); + +// Export public API +jdenticon["configure"] = configure; +jdenticon["drawIcon"] = drawIcon; +jdenticon["toSvg"] = toSvg; +jdenticon["update"] = update; +jdenticon["updateCanvas"] = update; +jdenticon["updateSvg"] = update; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +jdenticon["version"] = "#version#"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +jdenticon["bundle"] = "browser-umd"; + +// Basic jQuery plugin +const jQuery = GLOBAL["jQuery"]; +if (jQuery) { + jQuery["fn"]["jdenticon"] = jdenticonJqueryPlugin; +} + +/** + * This function is called once upon page load. + */ +function jdenticonStartup() { + const replaceMode = ( + jdenticon[CONFIG_PROPERTIES.MODULE] || + GLOBAL[CONFIG_PROPERTIES.GLOBAL] || + { } + )["replaceMode"]; + + if (replaceMode != "never") { + updateAllConditional(); + + if (replaceMode == "observe") { + observer(update); + } + } +} + +// Schedule to render all identicons on the page once it has been loaded. +whenDocumentIsReady(jdenticonStartup); + +module.exports = jdenticon; diff --git a/jdenticon-js/src/common/configuration.js b/jdenticon-js/src/common/configuration.js new file mode 100644 index 0000000..ff81d92 --- /dev/null +++ b/jdenticon-js/src/common/configuration.js @@ -0,0 +1,152 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { parseColor } from "../renderer/color"; +import { GLOBAL } from "./global"; + +/** + * @typedef {Object} ParsedConfiguration + * @property {number} colorSaturation + * @property {number} grayscaleSaturation + * @property {string} backColor + * @property {number} iconPadding + * @property {function(number):number} hue + * @property {function(number):number} colorLightness + * @property {function(number):number} grayscaleLightness + */ + +export const CONFIG_PROPERTIES = { + GLOBAL: "jdenticon_config", + MODULE: "config", +}; + +var rootConfigurationHolder = {}; + +/** + * Defines the deprecated `config` property on the root Jdenticon object. When the property is set a warning is + * printed in the console. To minimize bundle size, this is only used in Node bundles. + * @param {!Object} rootObject + */ +export function defineConfigPropertyWithWarn(rootObject) { + Object.defineProperty(rootObject, CONFIG_PROPERTIES.MODULE, { + configurable: true, + get: () => rootConfigurationHolder[CONFIG_PROPERTIES.MODULE], + set: newConfiguration => { + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration; + console.warn("jdenticon.config is deprecated. Use jdenticon.configure() instead."); + }, + }); +} + +/** + * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console + * when it is being used. + * @param {!Object} rootObject + */ +export function defineConfigProperty(rootObject) { + rootConfigurationHolder = rootObject; +} + +/** + * Sets a new icon style configuration. The new configuration is not merged with the previous one. * + * @param {Object} newConfiguration - New configuration object. + */ +export function configure(newConfiguration) { + if (arguments.length) { + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] = newConfiguration; + } + return rootConfigurationHolder[CONFIG_PROPERTIES.MODULE]; +} + +/** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A + * local configuration overrides the global configuration in it entirety. This parameter can for backward + * compatibility also contain a padding value. A padding value only overrides the global padding, not the + * entire global configuration. + * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor + * explicitly to the API method. + * @returns {ParsedConfiguration} + */ +export function getConfiguration(paddingOrLocalConfig, defaultPadding) { + const configObject = + typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig || + rootConfigurationHolder[CONFIG_PROPERTIES.MODULE] || + GLOBAL[CONFIG_PROPERTIES.GLOBAL] || + { }, + + lightnessConfig = configObject["lightness"] || { }, + + // In versions < 2.1.0 there was no grayscale saturation - + // saturation was the color saturation. + saturation = configObject["saturation"] || { }, + colorSaturation = "color" in saturation ? saturation["color"] : saturation, + grayscaleSaturation = saturation["grayscale"], + + backColor = configObject["backColor"], + padding = configObject["padding"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultRange) { + let range = lightnessConfig[configName]; + + // Check if the lightness range is an array-like object. This way we ensure the + // array contain two values at the same time. + if (!(range && range.length > 1)) { + range = defaultRange; + } + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + /** + * Gets a hue allowed by the configured hue restriction, + * provided the originally computed hue. + */ + function hueFunction(originalHue) { + const hueConfig = configObject["hues"]; + let hue; + + // Check if 'hues' is an array-like object. This way we also ensure that + // the array is not empty, which would mean no hue restriction. + if (hueConfig && hueConfig.length > 0) { + // originalHue is in the range [0, 1] + // Multiply with 0.999 to change the range to [0, 1) and then truncate the index. + hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)]; + } + + return typeof hue == "number" ? + + // A hue was specified. We need to convert the hue from + // degrees on any turn - e.g. 746° is a perfectly valid hue - + // to turns in the range [0, 1). + ((((hue / 360) % 1) + 1) % 1) : + + // No hue configured => use original hue + originalHue; + } + + return { + hue: hueFunction, + colorSaturation: typeof colorSaturation == "number" ? colorSaturation : 0.5, + grayscaleSaturation: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0, + colorLightness: lightness("color", [0.4, 0.8]), + grayscaleLightness: lightness("grayscale", [0.3, 0.9]), + backColor: parseColor(backColor), + iconPadding: + typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : + typeof padding == "number" ? padding : + defaultPadding + } +} diff --git a/jdenticon-js/src/common/dom.js b/jdenticon-js/src/common/dom.js new file mode 100644 index 0000000..35dbf8b --- /dev/null +++ b/jdenticon-js/src/common/dom.js @@ -0,0 +1,56 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +export const ICON_TYPE_SVG = 1; + +export const ICON_TYPE_CANVAS = 2; + +export const ATTRIBUTES = { + HASH: "data-jdenticon-hash", + VALUE: "data-jdenticon-value" +}; + +export const IS_RENDERED_PROPERTY = "jdenticonRendered"; + +export const ICON_SELECTOR = "[" + ATTRIBUTES.HASH +"],[" + ATTRIBUTES.VALUE +"]"; + +export const documentQuerySelectorAll = /** @type {!Function} */ ( + typeof document !== "undefined" && document.querySelectorAll.bind(document)); + +export function getIdenticonType(el) { + if (el) { + const tagName = el["tagName"]; + + if (/^svg$/i.test(tagName)) { + return ICON_TYPE_SVG; + } + + if (/^canvas$/i.test(tagName) && "getContext" in el) { + return ICON_TYPE_CANVAS; + } + } +} + +export function whenDocumentIsReady(/** @type {Function} */ callback) { + function loadedHandler() { + document.removeEventListener("DOMContentLoaded", loadedHandler); + window.removeEventListener("load", loadedHandler); + setTimeout(callback, 0); // Give scripts a chance to run + } + + if (typeof document !== "undefined" && + typeof window !== "undefined" && + typeof setTimeout !== "undefined" + ) { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", loadedHandler); + window.addEventListener("load", loadedHandler); + } else { + // Document already loaded. The load events above likely won't be raised + setTimeout(callback, 0); + } + } +} diff --git a/jdenticon-js/src/common/global.js b/jdenticon-js/src/common/global.js new file mode 100644 index 0000000..a19f517 --- /dev/null +++ b/jdenticon-js/src/common/global.js @@ -0,0 +1,14 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. + +export const GLOBAL = + typeof window !== "undefined" ? window : + typeof self !== "undefined" ? self : + typeof global !== "undefined" ? global : + {}; diff --git a/jdenticon-js/src/common/global.umd.js b/jdenticon-js/src/common/global.umd.js new file mode 100644 index 0000000..dcac4e7 --- /dev/null +++ b/jdenticon-js/src/common/global.umd.js @@ -0,0 +1,10 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ +/* global umdGlobal */ + +// In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for +// backward compatibility. +export const GLOBAL = umdGlobal; diff --git a/jdenticon-js/src/common/hashUtils.js b/jdenticon-js/src/common/hashUtils.js new file mode 100644 index 0000000..f8d98dc --- /dev/null +++ b/jdenticon-js/src/common/hashUtils.js @@ -0,0 +1,23 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { sha1 } from "./sha1"; + +/** + * Inputs a value that might be a valid hash string for Jdenticon and returns it + * if it is determined valid, otherwise a falsy value is returned. + */ +export function isValidHash(hashCandidate) { + return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate; +} + +/** + * Computes a hash for the specified value. Currently SHA1 is used. This function + * always returns a valid hash. + */ +export function computeHash(value) { + return sha1(value == null ? "" : "" + value); +} diff --git a/jdenticon-js/src/common/observer.js b/jdenticon-js/src/common/observer.js new file mode 100644 index 0000000..26bcc6a --- /dev/null +++ b/jdenticon-js/src/common/observer.js @@ -0,0 +1,47 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { ATTRIBUTES, ICON_SELECTOR, getIdenticonType } from "./dom"; + +export function observer(updateCallback) { + if (typeof MutationObserver != "undefined") { + const mutationObserver = new MutationObserver(function onmutation(mutations) { + for (let mutationIndex = 0; mutationIndex < mutations.length; mutationIndex++) { + const mutation = mutations[mutationIndex]; + const addedNodes = mutation.addedNodes; + + for (let addedNodeIndex = 0; addedNodes && addedNodeIndex < addedNodes.length; addedNodeIndex++) { + const addedNode = addedNodes[addedNodeIndex]; + + // Skip other types of nodes than element nodes, since they might not support + // the querySelectorAll method => runtime error. + if (addedNode.nodeType == Node.ELEMENT_NODE) { + if (getIdenticonType(addedNode)) { + updateCallback(addedNode); + } + else { + const icons = /** @type {Element} */(addedNode).querySelectorAll(ICON_SELECTOR); + for (let iconIndex = 0; iconIndex < icons.length; iconIndex++) { + updateCallback(icons[iconIndex]); + } + } + } + } + + if (mutation.type == "attributes" && getIdenticonType(mutation.target)) { + updateCallback(mutation.target); + } + } + }); + + mutationObserver.observe(document.body, { + "childList": true, + "attributes": true, + "attributeFilter": [ATTRIBUTES.VALUE, ATTRIBUTES.HASH, "width", "height"], + "subtree": true, + }); + } +} diff --git a/jdenticon-js/src/common/parseHex.js b/jdenticon-js/src/common/parseHex.js new file mode 100644 index 0000000..2ef8ca3 --- /dev/null +++ b/jdenticon-js/src/common/parseHex.js @@ -0,0 +1,14 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +/** + * Parses a substring of the hash as a number. + * @param {number} startPosition + * @param {number=} octets + */ +export function parseHex(hash, startPosition, octets) { + return parseInt(hash.substr(startPosition, octets), 16); +} diff --git a/jdenticon-js/src/common/sha1.js b/jdenticon-js/src/common/sha1.js new file mode 100644 index 0000000..85dcc05 --- /dev/null +++ b/jdenticon-js/src/common/sha1.js @@ -0,0 +1,132 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +/** + * Computes a SHA1 hash for any value and returns it as a hexadecimal string. + * + * This function is optimized for minimal code size and rather short messages. + * + * @param {string} message + */ +export function sha1(message) { + const HASH_SIZE_HALF_BYTES = 40; + const BLOCK_SIZE_WORDS = 16; + + // Variables + // `var` is used to be able to minimize the number of `var` keywords. + var i = 0, + f = 0, + + // Use `encodeURI` to UTF8 encode the message without any additional libraries + // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky + // since `unescape` is deprecated. + urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding + + // This can be changed to a preallocated Uint32Array array for greater performance and larger code size + data = [], + dataSize, + + hashBuffer = [], + + a = 0x67452301, + b = 0xefcdab89, + c = ~a, + d = ~b, + e = 0xc3d2e1f0, + hash = [a, b, c, d, e], + + blockStartIndex = 0, + hexHash = ""; + + /** + * Rotates the value a specified number of bits to the left. + * @param {number} value Value to rotate + * @param {number} shift Bit count to shift. + */ + function rotl(value, shift) { + return (value << shift) | (value >>> (32 - shift)); + } + + // Message data + for ( ; i < urlEncodedMessage.length; f++) { + data[f >> 2] = data[f >> 2] | + ( + ( + urlEncodedMessage[i] == "%" + // Percent encoded byte + ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16) + // Unencoded byte + : urlEncodedMessage.charCodeAt(i++) + ) + + // Read bytes in reverse order (big endian words) + << ((3 - (f & 3)) * 8) + ); + } + + // f is now the length of the utf8 encoded message + // 7 = 8 bytes (64 bit) for message size, -1 to round down + // >> 6 = integer division with block size + dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS; + + // Message size in bits. + // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least + // significant 32 bits are set. -8 is for the '1' bit padding byte. + data[dataSize - 1] = f * 8 - 8; + + // Compute hash + for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) { + for (i = 0; i < 80; i++) { + f = rotl(a, 5) + e + ( + // Ch + i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 : + + // Parity + i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 : + + // Maj + i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc : + + // Parity + (b ^ c ^ d) + 0xca62c1d6 + ) + ( + hashBuffer[i] = i < BLOCK_SIZE_WORDS + // Bitwise OR is used to coerse `undefined` to 0 + ? (data[blockStartIndex + i] | 0) + : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1) + ); + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = f; + } + + hash[0] = a = ((hash[0] + a) | 0); + hash[1] = b = ((hash[1] + b) | 0); + hash[2] = c = ((hash[2] + c) | 0); + hash[3] = d = ((hash[3] + d) | 0); + hash[4] = e = ((hash[4] + e) | 0); + } + + // Format hex hash + for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) { + hexHash += ( + ( + // Get word (2^3 half-bytes per word) + hash[i >> 3] >>> + + // Append half-bytes in reverse order + ((7 - (i & 7)) * 4) + ) + // Clamp to half-byte + & 0xf + ).toString(16); + } + + return hexHash; +} diff --git a/jdenticon-js/src/node-cjs.js b/jdenticon-js/src/node-cjs.js new file mode 100644 index 0000000..b4171bf --- /dev/null +++ b/jdenticon-js/src/node-cjs.js @@ -0,0 +1,72 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// This file is compiled to dist/jdenticon-node.js + +if (typeof process === "undefined" && + typeof window !== "undefined" && + typeof document !== "undefined" +) { + console.warn( + "Jdenticon: 'dist/jdenticon-node.js' is only intended for Node.js environments and will increase your " + + "bundle size when included in browser bundles. If you want to run Jdenticon in the browser, please add a " + + "reference to 'dist/jdenticon.js' or 'dist/jdenticon.min.js' instead."); +} + +import { defineConfigPropertyWithWarn } from "./common/configuration"; +import { configure } from "./apis/configure"; +import { drawIcon } from "./apis/drawIcon"; +import { toPng } from "./apis/toPng"; +import { toSvg } from "./apis/toSvg"; + +/** + * @throws {Error} + */ +function jdenticon() { + throw new Error("jdenticon() is not supported on Node.js."); +} + +defineConfigPropertyWithWarn(jdenticon); + +jdenticon.configure = configure; +jdenticon.drawIcon = drawIcon; +jdenticon.toPng = toPng; +jdenticon.toSvg = toSvg; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +jdenticon.version = "#version#"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +jdenticon.bundle = "node-cjs"; + +/** + * @throws {Error} + */ +jdenticon.update = function update() { + throw new Error("jdenticon.update() is not supported on Node.js."); +}; + +/** + * @throws {Error} + */ +jdenticon.updateCanvas = function updateCanvas() { + throw new Error("jdenticon.updateCanvas() is not supported on Node.js."); +}; + +/** + * @throws {Error} + */ +jdenticon.updateSvg = function updateSvg() { + throw new Error("jdenticon.updateSvg() is not supported on Node.js."); +}; + +module.exports = jdenticon; \ No newline at end of file diff --git a/jdenticon-js/src/node-esm.js b/jdenticon-js/src/node-esm.js new file mode 100644 index 0000000..e4ee9e3 --- /dev/null +++ b/jdenticon-js/src/node-esm.js @@ -0,0 +1,55 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// This file is compiled to dist/jdenticon-node.mjs + +if (typeof process === "undefined" && + typeof window !== "undefined" && + typeof document !== "undefined" +) { + console.warn( + "Jdenticon: 'dist/jdenticon-node.mjs' is only intended for Node.js environments and will increase your " + + "bundle size when included in browser bundles. If you want to run Jdenticon in the browser, please add a " + + "reference to 'dist/jdenticon.js' or 'dist/jdenticon.min.js' instead."); +} + +export { configure } from "./apis/configure"; +export { drawIcon } from "./apis/drawIcon"; +export { toPng } from "./apis/toPng"; +export { toSvg } from "./apis/toSvg"; + +/** + * Specifies the version of the Jdenticon package in use. + * @type {string} + */ +export const version = "#version#"; + +/** + * Specifies which bundle of Jdenticon that is used. + * @type {string} + */ +export const bundle = "node-esm"; + +/** + * @throws {Error} + */ +export function update() { + throw new Error("jdenticon.update() is not supported on Node.js."); +} + +/** + * @throws {Error} + */ +export function updateCanvas() { + throw new Error("jdenticon.updateCanvas() is not supported on Node.js."); +} + +/** + * @throws {Error} + */ +export function updateSvg() { + throw new Error("jdenticon.updateSvg() is not supported on Node.js."); +} diff --git a/jdenticon-js/src/package.json b/jdenticon-js/src/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/jdenticon-js/src/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/jdenticon-js/src/renderer/canvas/canvasRenderer.js b/jdenticon-js/src/renderer/canvas/canvasRenderer.js new file mode 100644 index 0000000..5dd5de5 --- /dev/null +++ b/jdenticon-js/src/renderer/canvas/canvasRenderer.js @@ -0,0 +1,108 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { toCss3Color } from "../color"; + +/** + * @typedef {import("../renderer").Renderer} Renderer + * @typedef {import('../point').Point} Point + */ + +/** + * Renderer redirecting drawing commands to a canvas context. + * @implements {Renderer} + */ +export class CanvasRenderer { + /** + * @param {number=} iconSize + */ + constructor(ctx, iconSize) { + const canvas = ctx.canvas; + const width = canvas.width; + const height = canvas.height; + + ctx.save(); + + if (!iconSize) { + iconSize = Math.min(width, height); + + ctx.translate( + ((width - iconSize) / 2) | 0, + ((height - iconSize) / 2) | 0); + } + + /** + * @private + */ + this._ctx = ctx; + this.iconSize = iconSize; + + ctx.clearRect(0, 0, iconSize, iconSize); + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + setBackground(fillColor) { + const ctx = this._ctx; + const iconSize = this.iconSize; + + ctx.fillStyle = toCss3Color(fillColor); + ctx.fillRect(0, 0, iconSize, iconSize); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} fillColor Fill color on format #rrggbb[aa]. + */ + beginShape(fillColor) { + const ctx = this._ctx; + ctx.fillStyle = toCss3Color(fillColor); + ctx.beginPath(); + } + + /** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ + endShape() { + this._ctx.fill(); + } + + /** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ + addPolygon(points) { + const ctx = this._ctx; + ctx.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); + } + + /** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + const ctx = this._ctx, + radius = diameter / 2; + ctx.moveTo(point.x + radius, point.y + radius); + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + this._ctx.restore(); + } +} diff --git a/jdenticon-js/src/renderer/color.js b/jdenticon-js/src/renderer/color.js new file mode 100644 index 0000000..ce66238 --- /dev/null +++ b/jdenticon-js/src/renderer/color.js @@ -0,0 +1,123 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { parseHex } from "../common/parseHex"; + +function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; +} + +function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); +} + +/** + * @param {number} r Red channel [0, 255] + * @param {number} g Green channel [0, 255] + * @param {number} b Blue channel [0, 255] + */ +export function rgb(r, g, b) { + return "#" + decToHex(r) + decToHex(g) + decToHex(b); +} + +/** + * @param {string} color Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported. + * @returns {string} + */ +export function parseColor(color) { + if (/^#[0-9a-f]{3,8}$/i.test(color)) { + let result; + const colorLength = color.length; + + if (colorLength < 6) { + const r = color[1], + g = color[2], + b = color[3], + a = color[4] || ""; + result = "#" + r + r + g + g + b + b + a + a; + } + if (colorLength == 7 || colorLength > 8) { + result = color; + } + + return result; + } +} + +/** + * Converts a hexadecimal color to a CSS3 compatible color. + * @param {string} hexColor Color on the format "#RRGGBB" or "#RRGGBBAA" + * @returns {string} + */ +export function toCss3Color(hexColor) { + const a = parseHex(hexColor, 7, 2); + let result; + + if (isNaN(a)) { + result = hexColor; + } else { + const r = parseHex(hexColor, 1, 2), + g = parseHex(hexColor, 3, 2), + b = parseHex(hexColor, 5, 2); + result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")"; + } + + return result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +export function hsl(hue, saturation, lightness) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + let result; + + if (saturation == 0) { + const partialHex = decToHex(lightness * 255); + result = partialHex + partialHex + partialHex; + } + else { + const m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation, + m1 = lightness * 2 - m2; + result = + hueToRgb(m1, m2, hue * 6 + 2) + + hueToRgb(m1, m2, hue * 6) + + hueToRgb(m1, m2, hue * 6 - 2); + } + + return "#" + result; +} + +/** + * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues + * @param {number} hue Hue in range [0, 1] + * @param {number} saturation Saturation in range [0, 1] + * @param {number} lightness Lightness in range [0, 1] + * @returns {string} + */ +export function correctedHsl(hue, saturation, lightness) { + // The corrector specifies the perceived middle lightness for each hue + const correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(hue * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2; + + return hsl(hue, saturation, lightness); +} diff --git a/jdenticon-js/src/renderer/colorTheme.js b/jdenticon-js/src/renderer/colorTheme.js new file mode 100644 index 0000000..b941790 --- /dev/null +++ b/jdenticon-js/src/renderer/colorTheme.js @@ -0,0 +1,28 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { correctedHsl } from "./color"; + +/** + * Gets a set of identicon color candidates for a specified hue and config. + * @param {number} hue + * @param {import("../common/configuration").ParsedConfiguration} config + */ +export function colorTheme(hue, config) { + hue = config.hue(hue); + return [ + // Dark gray + correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(0)), + // Mid color + correctedHsl(hue, config.colorSaturation, config.colorLightness(0.5)), + // Light gray + correctedHsl(hue, config.grayscaleSaturation, config.grayscaleLightness(1)), + // Light color + correctedHsl(hue, config.colorSaturation, config.colorLightness(1)), + // Dark color + correctedHsl(hue, config.colorSaturation, config.colorLightness(0)) + ]; +} diff --git a/jdenticon-js/src/renderer/graphics.js b/jdenticon-js/src/renderer/graphics.js new file mode 100644 index 0000000..9a72cee --- /dev/null +++ b/jdenticon-js/src/renderer/graphics.js @@ -0,0 +1,116 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { NO_TRANSFORM } from "./transform"; + +/** + * @typedef {import("./renderer").Renderer} Renderer + * @typedef {import("./transform").Transform} Transform + */ + +/** + * Provides helper functions for rendering common basic shapes. + */ +export class Graphics { + /** + * @param {Renderer} renderer + */ + constructor(renderer) { + /** + * @type {Renderer} + * @private + */ + this._renderer = renderer; + + /** + * @type {Transform} + */ + this.currentTransform = NO_TRANSFORM; + } + + /** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ + addPolygon(points, invert) { + const di = invert ? -2 : 2, + transformedPoints = []; + + for (let i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(this.currentTransform.transformIconPoint(points[i], points[i + 1])); + } + + this._renderer.addPolygon(transformedPoints); + } + + /** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ + addCircle(x, y, size, invert) { + const p = this.currentTransform.transformIconPoint(x, y, size, size); + this._renderer.addCircle(p, size, invert); + } + + /** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ + addRectangle(x, y, w, h, invert) { + this.addPolygon([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); + } + + /** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ + addTriangle(x, y, w, h, r, invert) { + const points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.addPolygon(points, invert); + } + + /** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ + addRhombus(x, y, w, h, invert) { + this.addPolygon([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); + } +} \ No newline at end of file diff --git a/jdenticon-js/src/renderer/iconGenerator.js b/jdenticon-js/src/renderer/iconGenerator.js new file mode 100644 index 0000000..aaafd87 --- /dev/null +++ b/jdenticon-js/src/renderer/iconGenerator.js @@ -0,0 +1,95 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { Transform } from "./transform"; +import { Graphics } from "./graphics"; +import { centerShape, outerShape } from "./shapes"; +import { colorTheme } from "./colorTheme"; +import { parseHex } from "../common/parseHex"; +import { getConfiguration } from "../common/configuration"; + +/** + * Draws an identicon to a specified renderer. + * @param {import('./renderer').Renderer} renderer + * @param {string} hash + * @param {Object|number=} config + */ +export function iconGenerator(renderer, hash, config) { + const parsedConfig = getConfiguration(config, 0.08); + + // Set background color + if (parsedConfig.backColor) { + renderer.setBackground(parsedConfig.backColor); + } + + // Calculate padding and round to nearest integer + let size = renderer.iconSize; + const padding = (0.5 + size * parsedConfig.iconPadding) | 0; + size -= padding * 2; + + const graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + const cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + const x = 0 | (padding + size / 2 - cell * 2); + const y = 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + const shapeIndex = parseHex(hash, index, 1); + let r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0; + + renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]); + + for (let i = 0; i < positions.length; i++) { + graphics.currentTransform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shapes(shapeIndex, graphics, cell, i); + } + + renderer.endShape(); + } + + // AVAILABLE COLORS + const hue = parseHex(hash, -7) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, parsedConfig), + + // The index of the selected colors + selectedColorIndexes = []; + + let index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (let i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (let i = 0; i < 3; i++) { + index = parseHex(hash, 8 + i, 1) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); +} diff --git a/jdenticon-js/src/renderer/point.js b/jdenticon-js/src/renderer/point.js new file mode 100644 index 0000000..650202c --- /dev/null +++ b/jdenticon-js/src/renderer/point.js @@ -0,0 +1,19 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +/** + * Represents a point. + */ +export class Point { + /** + * @param {number} x + * @param {number} y + */ + constructor(x, y) { + this.x = x; + this.y = y; + } +} diff --git a/jdenticon-js/src/renderer/renderer.js b/jdenticon-js/src/renderer/renderer.js new file mode 100644 index 0000000..be0edbe --- /dev/null +++ b/jdenticon-js/src/renderer/renderer.js @@ -0,0 +1,18 @@ +/** + * @typedef {import('./point').Point} Point + */ + +/** + * @typedef {Object} Renderer + * @property {number} iconSize + * @property {function(string):void} setBackground Fills the background with the specified color. + * @property {function(string):void} beginShape Marks the beginning of a new shape of the specified color. Should be + * ended with a call to endShape. + * @property {function():void} endShape Marks the end of the currently drawn shape. This causes the queued paths to + * be rendered on the canvas. + * @property {function(Point[]):void} addPolygon Adds a polygon to the rendering queue. + * @property {function(Point,number,boolean):void} addCircle Adds a circle to the rendering queue. + * @property {function():void} finish Called when the icon has been completely drawn. + */ + +export default class {} diff --git a/jdenticon-js/src/renderer/shapes.js b/jdenticon-js/src/renderer/shapes.js new file mode 100644 index 0000000..9fb92ea --- /dev/null +++ b/jdenticon-js/src/renderer/shapes.js @@ -0,0 +1,154 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + * @param {number} positionIndex + * @typedef {import('./graphics').Graphics} Graphics + */ +export function centerShape(index, g, cell, positionIndex) { + index = index % 14; + + let k, m, w, h, inner, outer; + + !index ? ( + k = cell * 0.42, + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ])) : + + index == 1 ? ( + w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8), + + g.addTriangle(cell - w, 0, w, h, 2)) : + + index == 2 ? ( + w = 0 | (cell / 3), + g.addRectangle(w, w, cell - w, cell - w)) : + + index == 3 ? ( + inner = cell * 0.1, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)), + + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer)) : + + index == 4 ? ( + m = 0 | (cell * 0.15), + w = 0 | (cell * 0.5), + g.addCircle(cell - w - m, cell - w - m, w)) : + + index == 5 ? ( + inner = cell * 0.1, + outer = inner * 4, + + // Align edge to nearest pixel in large icons + outer > 3 && (outer = 0 | outer), + + g.addRectangle(0, 0, cell, cell), + g.addPolygon([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true)) : + + index == 6 ? + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]) : + + index == 7 ? + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 8 ? ( + g.addRectangle(0, 0, cell, cell / 2), + g.addRectangle(0, cell / 2, cell / 2, cell / 2), + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1)) : + + index == 9 ? ( + inner = cell * 0.14, + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)), + + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + g.addRectangle(0, 0, cell, cell), + g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true)) : + + index == 10 ? ( + inner = cell * 0.12, + outer = inner * 3, + + g.addRectangle(0, 0, cell, cell), + g.addCircle(outer, outer, cell - inner - outer, true)) : + + index == 11 ? + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3) : + + index == 12 ? ( + m = cell * 0.25, + g.addRectangle(0, 0, cell, cell), + g.addRhombus(m, m, cell - m, cell - m, true)) : + + // 13 + ( + !positionIndex && ( + m = cell * 0.4, w = cell * 1.2, + g.addCircle(m, m, w) + ) + ); +} + +/** + * @param {number} index + * @param {Graphics} g + * @param {number} cell + */ +export function outerShape(index, g, cell) { + index = index % 4; + + let m; + + !index ? + g.addTriangle(0, 0, cell, cell, 0) : + + index == 1 ? + g.addTriangle(0, cell / 2, cell, cell / 2, 0) : + + index == 2 ? + g.addRhombus(0, 0, cell, cell) : + + // 3 + ( + m = cell / 6, + g.addCircle(m, m, cell - 2 * m) + ); +} diff --git a/jdenticon-js/src/renderer/svg/constants.js b/jdenticon-js/src/renderer/svg/constants.js new file mode 100644 index 0000000..1c28499 --- /dev/null +++ b/jdenticon-js/src/renderer/svg/constants.js @@ -0,0 +1,11 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +export const SVG_CONSTANTS = { + XMLNS: "http://www.w3.org/2000/svg", + WIDTH: "width", + HEIGHT: "height", +} \ No newline at end of file diff --git a/jdenticon-js/src/renderer/svg/svgElement.js b/jdenticon-js/src/renderer/svg/svgElement.js new file mode 100644 index 0000000..5608738 --- /dev/null +++ b/jdenticon-js/src/renderer/svg/svgElement.js @@ -0,0 +1,88 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { SVG_CONSTANTS } from "./constants"; + +/** + * Creates a new element and adds it to the specified parent. + * @param {Element} parentNode + * @param {string} name + * @param {...(string|number)} keyValuePairs + */ +function SvgElement_append(parentNode, name, ...keyValuePairs) { + const el = document.createElementNS(SVG_CONSTANTS.XMLNS, name); + + for (let i = 0; i + 1 < keyValuePairs.length; i += 2) { + el.setAttribute( + /** @type {string} */(keyValuePairs[i]), + /** @type {string} */(keyValuePairs[i + 1]), + ); + } + + parentNode.appendChild(el); +} + + +/** + * Renderer producing SVG output. + */ +export class SvgElement { + /** + * @param {Element} element - Target element + */ + constructor(element) { + // Don't use the clientWidth and clientHeight properties on SVG elements + // since Firefox won't serve a proper value of these properties on SVG + // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811) + // Instead use 100px as a hardcoded size (the svg viewBox will rescale + // the icon to the correct dimensions) + const iconSize = this.iconSize = Math.min( + (Number(element.getAttribute(SVG_CONSTANTS.WIDTH)) || 100), + (Number(element.getAttribute(SVG_CONSTANTS.HEIGHT)) || 100) + ); + + /** + * @type {Element} + * @private + */ + this._el = element; + + // Clear current SVG child elements + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + // Set viewBox attribute to ensure the svg scales nicely. + element.setAttribute("viewBox", "0 0 " + iconSize + " " + iconSize); + element.setAttribute("preserveAspectRatio", "xMidYMid meet"); + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ + setBackground(fillColor, opacity) { + if (opacity) { + SvgElement_append(this._el, "rect", + SVG_CONSTANTS.WIDTH, "100%", + SVG_CONSTANTS.HEIGHT, "100%", + "fill", fillColor, + "opacity", opacity); + } + } + + /** + * Appends a path to the SVG element. + * @param {string} color Fill color on format #xxxxxx. + * @param {string} dataString The SVG path data string. + */ + appendPath(color, dataString) { + SvgElement_append(this._el, "path", + "fill", color, + "d", dataString); + } +} diff --git a/jdenticon-js/src/renderer/svg/svgPath.js b/jdenticon-js/src/renderer/svg/svgPath.js new file mode 100644 index 0000000..e2b01fe --- /dev/null +++ b/jdenticon-js/src/renderer/svg/svgPath.js @@ -0,0 +1,58 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +/** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ +function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; +} + +/** + * Represents an SVG path element. + */ +export class SvgPath { + constructor() { + /** + * This property holds the data string (path.d) of the SVG path. + * @type {string} + */ + this.dataString = ""; + } + + /** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ + addPolygon(points) { + let dataString = ""; + for (let i = 0; i < points.length; i++) { + dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.dataString += dataString + "Z"; + } + + /** + * Adds a circle with the current fill color to the SVG path. + * @param {import('../point').Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + const sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter), + svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " "; + + this.dataString += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + svgArc + svgDiameter + ",0" + + svgArc + (-svgDiameter) + ",0"; + } +} + diff --git a/jdenticon-js/src/renderer/svg/svgRenderer.js b/jdenticon-js/src/renderer/svg/svgRenderer.js new file mode 100644 index 0000000..c6e6f0e --- /dev/null +++ b/jdenticon-js/src/renderer/svg/svgRenderer.js @@ -0,0 +1,104 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { SvgPath } from "./svgPath"; +import { parseHex } from "../../common/parseHex"; + +/** + * @typedef {import("../point").Point} Point + * @typedef {import("../renderer").Renderer} Renderer + * @typedef {import("./svgElement").SvgElement} SvgElement + * @typedef {import("./svgWriter").SvgWriter} SvgWriter + */ + +/** + * Renderer producing SVG output. + * @implements {Renderer} + */ +export class SvgRenderer { + /** + * @param {SvgElement|SvgWriter} target + */ + constructor(target) { + /** + * @type {SvgPath} + * @private + */ + this._path; + + /** + * @type {Object.} + * @private + */ + this._pathsByColor = { }; + + /** + * @type {SvgElement|SvgWriter} + * @private + */ + this._target = target; + + /** + * @type {number} + */ + this.iconSize = target.iconSize; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb[aa]. + */ + setBackground(fillColor) { + const match = /^(#......)(..)?/.exec(fillColor), + opacity = match[2] ? parseHex(match[2], 0) / 255 : 1; + this._target.setBackground(match[1], opacity); + } + + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ + beginShape(color) { + this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath()); + } + + /** + * Marks the end of the currently drawn shape. + */ + endShape() { } + + /** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ + addPolygon(points) { + this._path.addPolygon(points); + } + + /** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle(point, diameter, counterClockwise) { + this._path.addCircle(point, diameter, counterClockwise); + } + + /** + * Called when the icon has been completely drawn. + */ + finish() { + const pathsByColor = this._pathsByColor; + for (let color in pathsByColor) { + // hasOwnProperty cannot be shadowed in pathsByColor + // eslint-disable-next-line no-prototype-builtins + if (pathsByColor.hasOwnProperty(color)) { + this._target.appendPath(color, pathsByColor[color].dataString); + } + } + } +} diff --git a/jdenticon-js/src/renderer/svg/svgWriter.js b/jdenticon-js/src/renderer/svg/svgWriter.js new file mode 100644 index 0000000..c9c93ba --- /dev/null +++ b/jdenticon-js/src/renderer/svg/svgWriter.js @@ -0,0 +1,59 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { SVG_CONSTANTS } from "./constants"; + +/** + * Renderer producing SVG output. + */ +export class SvgWriter { + /** + * @param {number} iconSize - Icon width and height in pixels. + */ + constructor(iconSize) { + /** + * @type {number} + */ + this.iconSize = iconSize; + + /** + * @type {string} + * @private + */ + this._s = + ''; + } + + /** + * Fills the background with the specified color. + * @param {string} fillColor Fill color on the format #rrggbb. + * @param {number} opacity Opacity in the range [0.0, 1.0]. + */ + setBackground(fillColor, opacity) { + if (opacity) { + this._s += ''; + } + } + + /** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #rrggbb. + * @param {string} dataString The SVG path data string. + */ + appendPath(color, dataString) { + this._s += ''; + } + + /** + * Gets the rendered image as an SVG string. + */ + toString() { + return this._s + ""; + } +} diff --git a/jdenticon-js/src/renderer/transform.js b/jdenticon-js/src/renderer/transform.js new file mode 100644 index 0000000..5c3624a --- /dev/null +++ b/jdenticon-js/src/renderer/transform.js @@ -0,0 +1,45 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +import { Point } from "./point"; + +/** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + */ +export class Transform { + /** + * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle. + * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle. + * @param {number} size The size of the transformed rectangle. + * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad + */ + constructor(x, y, size, rotation) { + this._x = x; + this._y = y; + this._size = size; + this._rotation = rotation; + } + + /** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ + transformIconPoint(x, y, w, h) { + const right = this._x + this._size, + bottom = this._y + this._size, + rotation = this._rotation; + return rotation === 1 ? new Point(right - y - (h || 0), this._y + x) : + rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) : + new Point(this._x + x, this._y + y); + } +} + +export const NO_TRANSFORM = new Transform(0, 0, 0, 0); diff --git a/jdenticon-js/src/tsconfig.json b/jdenticon-js/src/tsconfig.json new file mode 100644 index 0000000..e3f6609 --- /dev/null +++ b/jdenticon-js/src/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "checkJs": true, + "noEmit": true, + "types": ["node14"], + "lib": ["es6", "dom"] + }, + "include": ["*.js"] +} \ No newline at end of file diff --git a/jdenticon-js/standalone/package.json b/jdenticon-js/standalone/package.json new file mode 100644 index 0000000..2ff678b --- /dev/null +++ b/jdenticon-js/standalone/package.json @@ -0,0 +1,4 @@ +{ + "main": "../dist/jdenticon.min.js", + "types": "../types/umd.d.ts" +} diff --git a/jdenticon-js/test/e2e/.gitignore b/jdenticon-js/test/e2e/.gitignore new file mode 100644 index 0000000..5e181c2 --- /dev/null +++ b/jdenticon-js/test/e2e/.gitignore @@ -0,0 +1,2 @@ +*.bundle.js +*.bundle.js.map diff --git a/jdenticon-js/test/e2e/base-browser-test.js b/jdenticon-js/test/e2e/base-browser-test.js new file mode 100644 index 0000000..d6f92e6 --- /dev/null +++ b/jdenticon-js/test/e2e/base-browser-test.js @@ -0,0 +1,40 @@ +const tap = require("tap"); +const canvasRenderer = require("canvas-renderer"); + +export function testBrowser(jdenticon, bundle) { + tap.test(bundle, bundleTest => { + bundleTest.test("jdenticon.bundle", t => { + t.equal(jdenticon.bundle, bundle); + t.end(); + }); + + bundleTest.test("jdenticon.version", t => { + t.match(jdenticon.version, /^\d+(\.\d+)*$/); + t.end(); + }); + + bundleTest.test("jdenticon.configure", t => { + t.doesNotThrow(() => jdenticon.configure({ backColor: "#fff" })); + t.end(); + }); + + bundleTest.test("jdenticon.drawIcon", t => { + t.doesNotThrow(() => jdenticon.drawIcon(canvasRenderer.createCanvas(100, 100).getContext("2d"), "Icon1", 100)); + t.end(); + }); + + bundleTest.test("jdenticon.toSvg", t => { + t.match(jdenticon.toSvg("Icon1", 100), /^ { + t.type(jdenticon.update, Function); + t.type(jdenticon.updateCanvas, Function); + t.type(jdenticon.updateSvg, Function); + t.end(); + }); + + bundleTest.end(); + }); +} diff --git a/jdenticon-js/test/e2e/browser/assets/amd.html b/jdenticon-js/test/e2e/browser/assets/amd.html new file mode 100644 index 0000000..5384814 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/amd.html @@ -0,0 +1,35 @@ + + + + Jdenticon + + + +

"Icon0" - AMD - Should be equal

+
+ +
IMG static image
+
+ +
+ +
Canvas update(value) AMD
+
+ + + + + + + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/center.html b/jdenticon-js/test/e2e/browser/assets/center.html new file mode 100644 index 0000000..550f115 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/center.html @@ -0,0 +1,63 @@ + + + + Jdenticon + + + + +

"Icon2" - Should be centered vertically and horizontally

+
+ +
Canvas data-jdenticon-value
+
+ +
+ +
Canvas update(value)
+
+ +
+ +
SVG data-jdenticon-value
+
+ +
+ +
SVG update(value)
+
+ +
+ +
Canvas data-jdenticon-value
+
+ +
+ +
Canvas update(value)
+
+ +
+ +
SVG data-jdenticon-value
+
+ +
+ +
SVG update(value)
+
+ + + + + + + + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/common.js b/jdenticon-js/test/e2e/browser/assets/common.js new file mode 100644 index 0000000..3549c17 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/common.js @@ -0,0 +1,27 @@ + +addEventListener("message", function (ev) { + var data = JSON.parse(ev.data); + + if ("scrollHeight" in data) { + var iframe = document.getElementsByName(data.name); + if (iframe && iframe.length) { + iframe[0].style.height = data.scrollHeight + "px"; + } + } +}); + +function postHeight(timeout) { + setTimeout(function () { + // IE9 does not support passing objects through postMessage + window.parent.postMessage(JSON.stringify({ + scrollHeight: document.body.scrollHeight, + name: window.name + }), "*"); + }, timeout); +} + +if (window.parent) { + postHeight(0); + postHeight(1000); + addEventListener("resize", postHeight); +} \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/index.html b/jdenticon-js/test/e2e/browser/assets/index.html new file mode 100644 index 0000000..f2a6abe --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/index.html @@ -0,0 +1,66 @@ + + + Jdenticon browser test + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/normal.html b/jdenticon-js/test/e2e/browser/assets/normal.html new file mode 100644 index 0000000..b9cde9f --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/normal.html @@ -0,0 +1,224 @@ + + + + Jdenticon + + + +

"Icon0" - Should be equal

+
+ +
IMG static image
+
+ +
+ +
Canvas data-jdenticon-value
+
+ +

"Icon0" - Should be equal to above, but black and no background

+
+ +
IMG static image
+
+ +
+ +
Canvas update()
+
+ +
+ +
Canvas drawIcon()
+
+ +
+ +
Canvas jQuery(value)
+
+ +
+ +
SVG toSvg(value)
+
+ +

"Icon04" - Should be equal but different to the icon above

+
+ +
IMG static image
+
+ +
+ +
Canvas data-jdenticon-value
+
+ +
+ +
Canvas data-jdenticon-hash
+
+ +
+ +
Canvas update(hash)
+
+ +
+ +
Canvas update(value)
+
+ +
+ +
Canvas jQuery(hash)
+
+ +
+ +
Canvas jQuery(value)
+
+ +
+ +
Canvas drawIcon(hash)
+
+ +
+ +
Canvas drawIcon(value)
+
+ +
+ +
Canvas dynamic
+
+ +
+ +
Canvas resize
+
+ + +
+ +
SVG data-jdenticon-value
+
+ +
+ +
SVG data-jdenticon-hash
+
+ +
+ +
SVG update(hash)
+
+ +
+ +
SVG update(value)
+
+ +
+ +
SVG jQuery(hash)
+
+ +
+ +
SVG jQuery(value)
+
+ +
+ +
SVG toSvg(hash)
+
+ +
+ +
SVG toSvg(value)
+
+ + +
+ +
SVG dynamic
+
+ +
+ +
SVG resize
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/padding.html b/jdenticon-js/test/e2e/browser/assets/padding.html new file mode 100644 index 0000000..cf1dc38 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/padding.html @@ -0,0 +1,97 @@ + + + + Jdenticon + + + +

"Icon04" - Should be equal - % padding in style

+
+ +
IMG static image
+
+ +
+ +
IMG static image
+
+ + +
+ +
Canvas data-jdenticon-value
+
+ +
+ +
Canvas update(value)
+
+ +
+ +
Canvas jQuery(value)
+
+ +
+ +
Canvas drawIcon(value)
+
+ + +
+ +
SVG data-jdenticon-value
+
+ +
+ +
SVG update(value)
+
+ +
+ +
SVG jQuery(value)
+
+ +
+ +
SVG toSvg(value)
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/styles.css b/jdenticon-js/test/e2e/browser/assets/styles.css new file mode 100644 index 0000000..3c9b7d8 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/styles.css @@ -0,0 +1,64 @@ +html { + padding: 0; + margin: 0; +} + +body { + margin: 0; + padding: 20px; +} + +h1 { + font: bold 16px Arial; + margin: 30px 0 15px; +} + +h1:first-child { + margin-top: 0; +} + +.test-metadata { + font: 14px Arial; + margin: 0 0 1em; + min-height: 4em; + background: #C1D1EA; + padding: 10px; + line-height: 1.4; +} + +.jdenticon-info { + font-weight: bold; +} + +canvas, +svg, +img { + border: 6px solid #444; + margin-bottom: 5px; +} + +figure { + width: 116px; + font: 10px Arial; + margin: 0 10px 16px 0; + display: inline-block; + vertical-align: top; +} + +figure strong { + display: block; +} + + +.padding-0 .padding-30-only, +.padding-30 .padding-0-only { + display: none; +} + + +iframe { + border: none; + width: 100%; + height: 100px; + margin: 0 -20px; +} \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/assets/umd-in-head.html b/jdenticon-js/test/e2e/browser/assets/umd-in-head.html new file mode 100644 index 0000000..c811e6a --- /dev/null +++ b/jdenticon-js/test/e2e/browser/assets/umd-in-head.html @@ -0,0 +1,26 @@ + + + + Jdenticon + + + + + +

"Icon0" - UMD in <head> - Should be equal

+
+ +
IMG static image
+
+ +
+ +
Canvas UMD in <head>
+
+ + + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/browser/expected/macos-chrome.png b/jdenticon-js/test/e2e/browser/expected/macos-chrome.png new file mode 100644 index 0000000..ef4eddf Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/macos-chrome.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/macos-firefox.png b/jdenticon-js/test/e2e/browser/expected/macos-firefox.png new file mode 100644 index 0000000..5db6b57 Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/macos-firefox.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/macos-safari.png b/jdenticon-js/test/e2e/browser/expected/macos-safari.png new file mode 100644 index 0000000..934fa82 Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/macos-safari.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-bs1.png b/jdenticon-js/test/e2e/browser/expected/win-bs1.png new file mode 100644 index 0000000..d59cd2a Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-bs1.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-bs2.png b/jdenticon-js/test/e2e/browser/expected/win-bs2.png new file mode 100644 index 0000000..d59cd2a Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-bs2.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-bs3.png b/jdenticon-js/test/e2e/browser/expected/win-bs3.png new file mode 100644 index 0000000..d59cd2a Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-bs3.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-chrome.png b/jdenticon-js/test/e2e/browser/expected/win-chrome.png new file mode 100644 index 0000000..6f635ee Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-chrome.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-edge.png b/jdenticon-js/test/e2e/browser/expected/win-edge.png new file mode 100644 index 0000000..31e8e8d Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-edge.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-firefox.png b/jdenticon-js/test/e2e/browser/expected/win-firefox.png new file mode 100644 index 0000000..38ff358 Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-firefox.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-ie10.png b/jdenticon-js/test/e2e/browser/expected/win-ie10.png new file mode 100644 index 0000000..89b69c6 Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-ie10.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-ie11.png b/jdenticon-js/test/e2e/browser/expected/win-ie11.png new file mode 100644 index 0000000..803c93e Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-ie11.png differ diff --git a/jdenticon-js/test/e2e/browser/expected/win-ie9.png b/jdenticon-js/test/e2e/browser/expected/win-ie9.png new file mode 100644 index 0000000..a85673c Binary files /dev/null and b/jdenticon-js/test/e2e/browser/expected/win-ie9.png differ diff --git a/jdenticon-js/test/e2e/browser/screenshooter.js b/jdenticon-js/test/e2e/browser/screenshooter.js new file mode 100644 index 0000000..f5f1886 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/screenshooter.js @@ -0,0 +1,45 @@ +const { PNG } = require("pngjs"); + +async function screenshot(driver) { + var dimensions = await driver.executeScript(`return { + scrollWidth: document.body.offsetWidth, + scrollHeight: document.body.offsetHeight, + innerWidth: window.innerWidth || document.documentElement.clientWidth, + innerHeight: window.innerHeight || document.documentElement.clientHeight + }`); + + const combinedImage = new PNG({ + width: dimensions.scrollWidth, + height: dimensions.scrollHeight + }); + + const xnum = Math.ceil(dimensions.scrollWidth / dimensions.innerWidth); + const ynum = Math.ceil(dimensions.scrollHeight / dimensions.innerHeight); + + for (let x = 0; x < xnum; x++) { + for (let y = 0; y < ynum; y++) { + + var scrollpos = await driver.executeScript(` + window.scrollTo(${x * dimensions.innerWidth}, ${y * dimensions.innerHeight}); + return { x: window.scrollX || window.pageXOffset, y: window.scrollY || window.pageYOffset }`) + + // Delay for Safari + await driver.sleep(500); + + const datauri = await driver.takeScreenshot(); + const image = PNG.sync.read(Buffer.from(datauri, "base64")); + + PNG.bitblt(image, combinedImage, + 0, 0, + Math.min(image.width, combinedImage.width - scrollpos.x), + Math.min(image.height, combinedImage.height - scrollpos.y), + scrollpos.x, + scrollpos.y); + + } + } + + return PNG.sync.write(combinedImage); +} + +module.exports = screenshot; diff --git a/jdenticon-js/test/e2e/browser/test.js b/jdenticon-js/test/e2e/browser/test.js new file mode 100644 index 0000000..007bc04 --- /dev/null +++ b/jdenticon-js/test/e2e/browser/test.js @@ -0,0 +1,187 @@ +const express = require("express"); +const fs = require("fs"); +const tap = require("tap"); +const webdriver = require("selenium-webdriver"); +const screenshot = require("./screenshooter"); +const BlinkDiff = require("blink-diff"); +const path = require("path"); + +// Command line arguments examples: +// node test.js win ie11,chrome +// node test.js macos safari,chrome,firefox + +const environmentId = process.argv[2] || ""; +const enabledBrowsers = (process.argv[3] || "").split(/[,;]/g).filter(name => name); + +if (!enabledBrowsers.length) { + throw new Error("Expected browser names"); +} + +const screenshotDir = process.env.BROWSER_SCREENSHOT_DIR || path.join(__dirname, "artifacts/screenshots"); +const diffDir = process.env.BROWSER_DIFF_DIR || path.join(__dirname, "artifacts/diffs"); +const expectedDir = process.env.BROWSER_EXPECTED_DIR || path.join(__dirname, "expected"); + +// fs.mkdirSync(_ , { recursive: true }) did not work on GitHub Actions using Node v12.18.2 (Windows and macOS). +// Worked fine locally however. Replacing it with a custom recursive implementation. +// Ignored GitHub issue for tracking any status: +// https://github.com/nodejs/node/issues/27293 +function mkdirRecursive(dirPath) { + if (!fs.existsSync(dirPath)) { + const parent = path.dirname(dirPath) + if (parent && parent !== dirPath) { + mkdirRecursive(parent); + } + fs.mkdirSync(dirPath); + } +} + +mkdirRecursive(screenshotDir); +mkdirRecursive(diffDir); + +const BROWSER_DEFINITIONS = [ + { + name: "ie11", + uaCompatible: "IE=Edge", + capabilities: { + "browserName": webdriver.Browser.INTERNET_EXPLORER, + "ie.ensureCleanSession": true, + }, + }, + { + name: "ie10", + uaCompatible: "IE=10", + capabilities: { + "browserName": webdriver.Browser.INTERNET_EXPLORER, + "ie.ensureCleanSession": true, + }, + }, + { + name: "ie9", + uaCompatible: "IE=9", + capabilities: { + "browserName": webdriver.Browser.INTERNET_EXPLORER, + "ie.ensureCleanSession": true, + }, + }, + { + name: "firefox", + capabilities: { + "browserName": webdriver.Browser.FIREFOX, + }, + }, + { + name: "chrome", + capabilities: { + "browserName": webdriver.Browser.CHROME, + }, + }, + { + name: "edge", + capabilities: { + "browserName": webdriver.Browser.EDGE, + "ms:edgeChromium": true, + }, + }, + { + name: "safari", + capabilities: { + "browserName": webdriver.Browser.SAFARI, + }, + }, +] + +async function serve(root, options, asyncCallback) { + const app = express(); + + app.use(express.static(root, options)); + + await new Promise((resolve, reject) => { + const listener = app.listen(async () => { + try { + await asyncCallback(listener); + resolve(); + + } catch (e) { + reject(e); + + } finally { + listener.close(); + } + }); + }); +} + +async function testBrowser(browserName) { + const browser = BROWSER_DEFINITIONS.find(x => x.name === browserName); + await tap.test(browserName, async t => { + if (!browser) { + t.fail(`Could not find a browser with the name ${browserName}.`); + return; + } + + await serve( + path.join(__dirname, "../../"), + { + "index": ["index.html"], + setHeaders: resp => { + // Prevent stale files + resp.setHeader("Cache-Control", "no-store"); + + if (browser.uaCompatible) { + resp.setHeader("X-UA-Compatible", browser.uaCompatible); + } + } + }, + async listener => { + const url = "http://localhost:" + listener.address().port + "/e2e/browser/assets/"; + + console.log(`Screenshot in ${browserName}`); + console.log(url); + + const driver = await new webdriver.Builder() + .withCapabilities(browser.capabilities) + .build(); + + await driver.manage().window().setRect({ width: 1000, height: 2000 }); + + const documentInitialised = () => driver.executeScript("return true"); + + try { + await driver.get(url); + await driver.wait(() => documentInitialised(), 10000); + await driver.sleep(2500); + + const screenshotBuffer = await screenshot(driver); + fs.writeFileSync(path.join(screenshotDir, `${environmentId}-${browserName}.png`), screenshotBuffer); + + } finally { + await driver.quit(); + } + + var diff = new BlinkDiff({ + imageAPath: path.join(expectedDir, `${environmentId}-${browserName}.png`), + imageBPath: path.join(screenshotDir, `${environmentId}-${browserName}.png`), + + thresholdType: BlinkDiff.THRESHOLD_PIXEL, + threshold: 1000, + + imageOutputPath: path.join(diffDir, `${environmentId}-${browserName}.png`), + + // Ignore test metadata area containing browser versions etc. + blockOut: [{ x: 0, y: 0, width: 20000, height: 100 }], + }); + + const diffResult = await diff.runWithPromise(); + t.ok(diff.hasPassed(diffResult.code), `Found ${diffResult.differences} differences.`); + }, + ); + }); +} + +async function testBrowsers(enabledBrowsers) { + for (var i = 0; i < enabledBrowsers.length; i++) { + await testBrowser(enabledBrowsers[i]); + } +} + +testBrowsers(enabledBrowsers); \ No newline at end of file diff --git a/jdenticon-js/test/e2e/node/base.js b/jdenticon-js/test/e2e/node/base.js new file mode 100644 index 0000000..a42b2ac --- /dev/null +++ b/jdenticon-js/test/e2e/node/base.js @@ -0,0 +1,46 @@ +const tap = require("tap"); +const canvasRenderer = require("canvas-renderer"); +const iconTest = require("./icons"); + +// The user might have modified the native object prototypes. +// It should not break Jdenticon. +Object.prototype.somethingOdd = function() {}; +Object.prototype.nothing = null; +Object.prototype.anEmptyObject = {}; + +function testNode(jdenticon) { + tap.test("jdenticon.version", t => { + t.match(jdenticon.version, /^\d+(\.\d+)*$/); + t.end(); + }); + + tap.test("jdenticon.configure", t => { + t.doesNotThrow(() => jdenticon.configure({ backColor: "#fff" })); + t.end(); + }); + + tap.test("jdenticon.drawIcon", t => { + t.doesNotThrow(() => jdenticon.drawIcon(canvasRenderer.createCanvas(100, 100).getContext("2d"), "Icon1", 100)); + t.end(); + }); + + tap.test("jdenticon.toPng", t => { + t.type(jdenticon.toPng("Icon1", 100), Buffer); + iconTest(jdenticon); + t.end(); + }); + + tap.test("jdenticon.toSvg", t => { + t.match(jdenticon.toSvg("Icon1", 100), /^ { + t.throws(() => jdenticon.update(), "jdenticon.update() is not supported on Node.js."); + t.throws(() => jdenticon.updateCanvas(), "jdenticon.updateCanvas() is not supported on Node.js."); + t.throws(() => jdenticon.updateSvg(), "jdenticon.updateSvg() is not supported on Node.js."); + t.end(); + }); +} + +module.exports = testNode; diff --git a/jdenticon-js/test/e2e/node/expected/39.png b/jdenticon-js/test/e2e/node/expected/39.png new file mode 100644 index 0000000..e9fd062 Binary files /dev/null and b/jdenticon-js/test/e2e/node/expected/39.png differ diff --git a/jdenticon-js/test/e2e/node/expected/39.svg b/jdenticon-js/test/e2e/node/expected/39.svg new file mode 100644 index 0000000..4a17252 --- /dev/null +++ b/jdenticon-js/test/e2e/node/expected/39.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/node/expected/50.png b/jdenticon-js/test/e2e/node/expected/50.png new file mode 100644 index 0000000..b9f5aef Binary files /dev/null and b/jdenticon-js/test/e2e/node/expected/50.png differ diff --git a/jdenticon-js/test/e2e/node/expected/50.svg b/jdenticon-js/test/e2e/node/expected/50.svg new file mode 100644 index 0000000..3ca5730 --- /dev/null +++ b/jdenticon-js/test/e2e/node/expected/50.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/node/expected/73.png b/jdenticon-js/test/e2e/node/expected/73.png new file mode 100644 index 0000000..5d675b9 Binary files /dev/null and b/jdenticon-js/test/e2e/node/expected/73.png differ diff --git a/jdenticon-js/test/e2e/node/expected/73.svg b/jdenticon-js/test/e2e/node/expected/73.svg new file mode 100644 index 0000000..8aafe70 --- /dev/null +++ b/jdenticon-js/test/e2e/node/expected/73.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/node/expected/76.png b/jdenticon-js/test/e2e/node/expected/76.png new file mode 100644 index 0000000..29373fe Binary files /dev/null and b/jdenticon-js/test/e2e/node/expected/76.png differ diff --git a/jdenticon-js/test/e2e/node/expected/76.svg b/jdenticon-js/test/e2e/node/expected/76.svg new file mode 100644 index 0000000..0619190 --- /dev/null +++ b/jdenticon-js/test/e2e/node/expected/76.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jdenticon-js/test/e2e/node/icons.js b/jdenticon-js/test/e2e/node/icons.js new file mode 100644 index 0000000..47e2eb2 --- /dev/null +++ b/jdenticon-js/test/e2e/node/icons.js @@ -0,0 +1,97 @@ +"use strict"; + +const assert = require("assert"); +const tap = require("tap"); +const fs = require("fs"); +const path = require("path"); +const { PNG } = require("pngjs"); + +const expectedDir = path.join(__dirname, "expected"); + +function equal(obj1, obj2) { + try { + assert.deepStrictEqual(obj1, obj2); + return true; + } catch (e) { + return false; + } +} + +function test(jdenticon, icon, style) { + jdenticon.configure(style); + + // PNG + { + const actual = jdenticon.toPng(icon, 50); + const expected = fs.readFileSync(path.join(expectedDir, icon +".png")); + + const actualDecoded = PNG.sync.read(actual); + const expectedDecoded = PNG.sync.read(expected); + + if (!equal(actualDecoded, expectedDecoded)) { + actualDecoded.data = "..."; + expectedDecoded.data = "..."; + fs.writeFileSync(path.join(expectedDir, icon +".metadata.json"), JSON.stringify(expectedDecoded, undefined, 2)); + fs.writeFileSync(path.join(expectedDir, icon +"-actual.metadata.json"), JSON.stringify(actualDecoded, undefined, 2)); + fs.writeFileSync(path.join(expectedDir, icon +"-actual.png"), actual); + tap.ok(false, "Icon '" + icon + "' failed PNG test."); + } + else { + tap.ok(true); + } + } + + // SVG + { + const actual = jdenticon.toSvg(icon, 50); + const expected = fs.readFileSync(path.join(expectedDir, icon +".svg")); + + if (actual !== expected.toString()) { + fs.writeFileSync(path.join(expectedDir, icon + "-actual.svg"), actual); + tap.ok(false, "Icon '" + icon + "' failed SVG test."); + console.log(expected.toString()); + console.log(actual); + } + else { + tap.ok(true); + } + } +} + +function testIcons(jdenticon) { + test(jdenticon, 73, { + backColor: "#fff" + }); + + test(jdenticon, 76, { + hues: [ 134 /*green*/, 0 /*red*/, 60 /*yellow*/ ], + lightness: { + color: [0.29, 0.53], + grayscale: [0.19, 0.40] + }, + saturation: { + color: 0.45, + grayscale: 0.72 + }, + backColor: "#0000002a" + }); + + test(jdenticon, 39, { + hues: [ 134 /*green*/, 0 /*red*/, 60 /*yellow*/ ], + lightness: { + color: [0.65, 0.86], + grayscale: [0.00, 1.00] + }, + saturation: { + color: 0.34, + grayscale: 0.10 + }, + backColor: "#ffffffff" + }); + + test(jdenticon, 50, { + backColor: "#fff" + }); +} + +module.exports = testIcons; \ No newline at end of file diff --git a/jdenticon-js/test/e2e/node/test.js b/jdenticon-js/test/e2e/node/test.js new file mode 100644 index 0000000..590f377 --- /dev/null +++ b/jdenticon-js/test/e2e/node/test.js @@ -0,0 +1,30 @@ +const tap = require("tap"); +const jdenticon = require("jdenticon"); +const baseNode = require("./base"); + +tap.test("jdenticon.bundle", t => { + t.equal(jdenticon.bundle, "node-cjs"); + t.end(); +}); + +tap.test("jdenticon.config", t => { + const originalConsoleWarn = console.warn; + const warn = []; + + console.warn = function () { + warn.push(Array.prototype.join.call(arguments, "")); + } + + try { + jdenticon.config = {}; + } finally { + console.warn = originalConsoleWarn; + } + + t.same(warn, ["jdenticon.config is deprecated. Use jdenticon.configure() instead."]); + t.end(); +}); + +baseNode(jdenticon); + + diff --git a/jdenticon-js/test/e2e/node/test.mjs b/jdenticon-js/test/e2e/node/test.mjs new file mode 100644 index 0000000..85d8e89 --- /dev/null +++ b/jdenticon-js/test/e2e/node/test.mjs @@ -0,0 +1,11 @@ +import tap from "tap"; +import { bundle } from "jdenticon"; +import * as jdenticon from "jdenticon"; +import baseNode from "./base.js"; + +tap.test("jdenticon.bundle", t => { + t.equal(bundle, "node-esm"); + t.end(); +}); + +baseNode(jdenticon); diff --git a/jdenticon-js/test/e2e/rollup/app.js b/jdenticon-js/test/e2e/rollup/app.js new file mode 100644 index 0000000..ef4ee1f --- /dev/null +++ b/jdenticon-js/test/e2e/rollup/app.js @@ -0,0 +1,13 @@ +import { testBrowser } from "../base-browser-test"; + +import * as jdenticonEsm from "jdenticon"; +testBrowser(jdenticonEsm, "browser-esm"); + +import * as jdenticonEsmBrowser from "jdenticon/browser"; +testBrowser(jdenticonEsmBrowser, "browser-esm"); + +import * as jdenticonEsmNode from "jdenticon/node"; +testBrowser(jdenticonEsmNode, "node-esm"); + +import * as jdenticonUmd from "jdenticon/standalone"; +testBrowser(jdenticonUmd, "browser-umd"); \ No newline at end of file diff --git a/jdenticon-js/test/e2e/rollup/rollup.config.js b/jdenticon-js/test/e2e/rollup/rollup.config.js new file mode 100644 index 0000000..582b8d8 --- /dev/null +++ b/jdenticon-js/test/e2e/rollup/rollup.config.js @@ -0,0 +1,22 @@ +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import { terser } from "rollup-plugin-terser"; + +export default { + input: "./app.js", + output: { + file: "./app.bundle.js", + format: "iife", + globals: { + "canvas-renderer": "{}", + }, + }, + external: ["canvas-renderer"], + plugins: [ + commonjs(), + nodeResolve({ + browser: true, + }), + terser(), + ], +}; \ No newline at end of file diff --git a/jdenticon-js/test/e2e/webpack/app.js b/jdenticon-js/test/e2e/webpack/app.js new file mode 100644 index 0000000..40afab6 --- /dev/null +++ b/jdenticon-js/test/e2e/webpack/app.js @@ -0,0 +1,13 @@ +import { testBrowser } from "../base-browser-test"; + +import * as jdenticonEsm from "jdenticon"; +testBrowser(jdenticonEsm, "browser-esm"); + +import * as jdenticonEsmBrowser from "jdenticon/browser"; +testBrowser(jdenticonEsmBrowser, "browser-esm"); + +import * as jdenticonEsmNode from "jdenticon/node"; +testBrowser(jdenticonEsmNode, "node-esm"); + +import * as jdenticonUmd from "jdenticon/standalone"; +testBrowser(jdenticonUmd, "browser-umd"); diff --git a/jdenticon-js/test/e2e/webpack/runner.js b/jdenticon-js/test/e2e/webpack/runner.js new file mode 100644 index 0000000..fa7b197 --- /dev/null +++ b/jdenticon-js/test/e2e/webpack/runner.js @@ -0,0 +1,40 @@ +const process = require("process"); +const config = require("./webpack.config"); +const moduleAlias = require("module-alias"); +const webpackPackageName = process.argv[2]; + +// This file is used instead of webpack-cli to allow testing with multiple webpack versions + +if (!webpackPackageName) { + console.error("Usage: node runner.js (webpack4|webpack5)"); + process.exit(1); +} + +if (!/^webpack\d+$/.test(webpackPackageName)) { + console.error("Invalid webpack package name specified"); + process.exit(2); +} + +// The terser plugin in webpack4 imports "webpack/lib/RequestShortener", so we can't require the right webpack module +// name and use it straight away. By using module-alias we make "webpack" requirable. +moduleAlias.addAlias("webpack", webpackPackageName); + +const webpack = require("webpack"); + +webpack(config, (err, stats) => { + if (err) { + console.error(err); + process.exit(3); + return; + } + + console.log(stats.toString({ + colors: true + })); + + console.log("---"); + + if (stats.hasErrors()) { + process.exit(4); + } +}); \ No newline at end of file diff --git a/jdenticon-js/test/e2e/webpack/webpack.config.js b/jdenticon-js/test/e2e/webpack/webpack.config.js new file mode 100644 index 0000000..7cbafb6 --- /dev/null +++ b/jdenticon-js/test/e2e/webpack/webpack.config.js @@ -0,0 +1,33 @@ +const path = require("path"); + +module.exports = { + mode: "production", + entry: path.join(__dirname, "app.js"), + externals: { + "tap": "commonjs tap", + "canvas-renderer": "commonjs canvas-renderer" + }, + module: { + rules: [ + { + test: /\.mjs$/, + enforce: 'pre', + use: ['source-map-loader'], + }, + { + test: /\.js$/, + enforce: 'pre', + use: ['source-map-loader'], + }, + ], + }, + stats: { + warningsFilter: [/Failed to parse source map/], + }, + devtool: "source-map", + + output: { + path: __dirname, + filename: "app.bundle.js", + }, +} diff --git a/jdenticon-js/test/package.json b/jdenticon-js/test/package.json new file mode 100644 index 0000000..ee4e271 --- /dev/null +++ b/jdenticon-js/test/package.json @@ -0,0 +1,5 @@ +{ + "name": "jdenticon-test", + "version": "1.0.0", + "private": true +} diff --git a/jdenticon-js/test/types/module-browser/module-test-explicit.ts b/jdenticon-js/test/types/module-browser/module-test-explicit.ts new file mode 100644 index 0000000..ba3ad45 --- /dev/null +++ b/jdenticon-js/test/types/module-browser/module-test-explicit.ts @@ -0,0 +1,59 @@ +import { configure, drawIcon, update, updateSvg, updateCanvas, toPng, toSvg, JdenticonConfig } from "jdenticon/browser"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +window.jdenticon_config = oldConfig; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +toPng("value to hash", 100, newConfig); +toSvg("value to hash", 100, newConfig); + +var el = document.createElement("canvas"); +update(el, "value"); +update(el, "value", 0.08); +update(el, "value", newConfig); +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + +var ctx = el.getContext("2d"); +if (ctx) { + drawIcon(ctx, "value", 100); + drawIcon(ctx, "value", 100, 0.08); + drawIcon(ctx, "value", 100, newConfig); +} + +// Ensure Jdenticon dodn't leak Node typings. +// setTimeout returns a NodeJS.Timeout when the Node typings are loaded. +const timeoutRef: number = setTimeout(() => { }, 100); diff --git a/jdenticon-js/test/types/module-browser/module-test.ts b/jdenticon-js/test/types/module-browser/module-test.ts new file mode 100644 index 0000000..8574e53 --- /dev/null +++ b/jdenticon-js/test/types/module-browser/module-test.ts @@ -0,0 +1,59 @@ +import { configure, drawIcon, update, updateSvg, updateCanvas, toPng, toSvg, JdenticonConfig } from "jdenticon"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +window.jdenticon_config = oldConfig; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +toPng("value to hash", 100, newConfig); +toSvg("value to hash", 100, newConfig); + +var el = document.createElement("canvas"); +update(el, "value"); +update(el, "value", 0.08); +update(el, "value", newConfig); +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + +var ctx = el.getContext("2d"); +if (ctx) { + drawIcon(ctx, "value", 100); + drawIcon(ctx, "value", 100, 0.08); + drawIcon(ctx, "value", 100, newConfig); +} + +// Ensure Jdenticon dodn't leak Node typings. +// setTimeout returns a NodeJS.Timeout when the Node typings are loaded. +const timeoutRef: number = setTimeout(() => { }, 100); diff --git a/jdenticon-js/test/types/module-browser/tsconfig.json b/jdenticon-js/test/types/module-browser/tsconfig.json new file mode 100644 index 0000000..5da897e --- /dev/null +++ b/jdenticon-js/test/types/module-browser/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": [], + "lib": ["es6", "dom"], + }, + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/jdenticon-js/test/types/module-node14/module-test-explicit.ts b/jdenticon-js/test/types/module-node14/module-test-explicit.ts new file mode 100644 index 0000000..7eeb204 --- /dev/null +++ b/jdenticon-js/test/types/module-node14/module-test-explicit.ts @@ -0,0 +1,47 @@ +import { configure, update, updateSvg, updateCanvas, toPng, toSvg, JdenticonConfig } from "jdenticon/node"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +const buffer: Buffer = toPng("value to hash", 100, newConfig); + +toSvg("value to hash", 100, newConfig); + +// Check that Node typings are loaded +buffer.swap64(); + +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + diff --git a/jdenticon-js/test/types/module-node14/module-test.ts b/jdenticon-js/test/types/module-node14/module-test.ts new file mode 100644 index 0000000..c15a66b --- /dev/null +++ b/jdenticon-js/test/types/module-node14/module-test.ts @@ -0,0 +1,47 @@ +import { configure, update, updateSvg, updateCanvas, toPng, toSvg, JdenticonConfig } from "jdenticon"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +const buffer: Buffer = toPng("value to hash", 100, newConfig); + +toSvg("value to hash", 100, newConfig); + +// Check that Node typings are loaded +buffer.swap64(); + +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + diff --git a/jdenticon-js/test/types/module-node14/tsconfig.json b/jdenticon-js/test/types/module-node14/tsconfig.json new file mode 100644 index 0000000..48545ce --- /dev/null +++ b/jdenticon-js/test/types/module-node14/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": [ "node14" ], + "lib": [ "es6" ], + }, + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/jdenticon-js/test/types/module-node16/module-test-explicit.ts b/jdenticon-js/test/types/module-node16/module-test-explicit.ts new file mode 100644 index 0000000..7eeb204 --- /dev/null +++ b/jdenticon-js/test/types/module-node16/module-test-explicit.ts @@ -0,0 +1,47 @@ +import { configure, update, updateSvg, updateCanvas, toPng, toSvg, JdenticonConfig } from "jdenticon/node"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +const buffer: Buffer = toPng("value to hash", 100, newConfig); + +toSvg("value to hash", 100, newConfig); + +// Check that Node typings are loaded +buffer.swap64(); + +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + diff --git a/jdenticon-js/test/types/module-node16/module-test.ts b/jdenticon-js/test/types/module-node16/module-test.ts new file mode 100644 index 0000000..c15a66b --- /dev/null +++ b/jdenticon-js/test/types/module-node16/module-test.ts @@ -0,0 +1,47 @@ +import { configure, update, updateSvg, updateCanvas, toPng, toSvg, JdenticonConfig } from "jdenticon"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +const buffer: Buffer = toPng("value to hash", 100, newConfig); + +toSvg("value to hash", 100, newConfig); + +// Check that Node typings are loaded +buffer.swap64(); + +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + diff --git a/jdenticon-js/test/types/module-node16/tsconfig.json b/jdenticon-js/test/types/module-node16/tsconfig.json new file mode 100644 index 0000000..006d21f --- /dev/null +++ b/jdenticon-js/test/types/module-node16/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": [ "node16" ], + "lib": [ "es6" ], + }, + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/jdenticon-js/test/types/tsconfig.json b/jdenticon-js/test/types/tsconfig.json new file mode 100644 index 0000000..20cf2a3 --- /dev/null +++ b/jdenticon-js/test/types/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "typeRoots": [ + "../../node_modules/@types" + ], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "compileOnSave": false +} \ No newline at end of file diff --git a/jdenticon-js/test/types/umd/global-test.ts b/jdenticon-js/test/types/umd/global-test.ts new file mode 100644 index 0000000..37adfe0 --- /dev/null +++ b/jdenticon-js/test/types/umd/global-test.ts @@ -0,0 +1,60 @@ + +const config: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +window.jdenticon_config = config; + +jdenticon.configure(config); + +jdenticon.toPng("value to hash", 100); +jdenticon.toSvg("value to hash", 100); + +jdenticon.toPng("value to hash", 100, 0.08); +jdenticon.toSvg("value to hash", 100, 0.08); + +jdenticon.toPng("value to hash", 100, config); +jdenticon.toSvg("value to hash", 100, config); + +var el = document.createElement("canvas"); +jdenticon.update(el, "value"); +jdenticon.update(el, "value", 0.08); +jdenticon.update(el, "value", config); +jdenticon.update("#selector", "value"); +jdenticon.update("#selector", "value", 0.08); +jdenticon.update("#selector", "value", config); + +jdenticon.updateSvg("#selector", "value", config); +jdenticon.updateCanvas("#selector", "value", config); + +jdenticon(); + +var ctx = el.getContext("2d"); +if (ctx) { + jdenticon.drawIcon(ctx, "value", 100); + jdenticon.drawIcon(ctx, "value", 100, 0.08); + jdenticon.drawIcon(ctx, "value", 100, config); +} + +// Ensure Jdenticon dodn't leak Node typings. +// setTimeout returns a NodeJS.Timeout when the Node typings are loaded. +const timeoutRef1: number = setTimeout(() => { }, 100); \ No newline at end of file diff --git a/jdenticon-js/test/types/umd/jquery-test.ts b/jdenticon-js/test/types/umd/jquery-test.ts new file mode 100644 index 0000000..368b052 --- /dev/null +++ b/jdenticon-js/test/types/umd/jquery-test.ts @@ -0,0 +1,24 @@ +/// + +const jqueryConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +$("canvas").jdenticon("value"); +$("canvas").jdenticon("value", 0.08); +$("canvas").jdenticon("value", jqueryConfig); + +// Ensure Jdenticon dodn't leak Node typings. +// setTimeout returns a NodeJS.Timeout when the Node typings are loaded. +const timeoutRef2: number = setTimeout(() => { }, 100); diff --git a/jdenticon-js/test/types/umd/module-test.ts b/jdenticon-js/test/types/umd/module-test.ts new file mode 100644 index 0000000..790cf77 --- /dev/null +++ b/jdenticon-js/test/types/umd/module-test.ts @@ -0,0 +1,59 @@ +import { configure, drawIcon, update, updateSvg, updateCanvas, toPng, toSvg } from "jdenticon/standalone"; + +const newConfig: JdenticonConfig = { + lightness: { + color: [0.40, 0.80], + grayscale: [0.30, 0.90] + }, + saturation: { + color: 0.50, + grayscale: 0.00 + }, + hues: [45, 677], + padding: 0.3, + replaceMode: "observe", + backColor: "#86444400" +}; + +const oldConfig: JdenticonConfig = { + lightness: { + color: [0.4, 0.8], + grayscale: [0.3, 0.9] + }, + saturation: 0.5 +}; + +window.jdenticon_config = oldConfig; + +configure(oldConfig); + +toPng("value to hash", 100); +toSvg("value to hash", 100); + +toPng("value to hash", 100, 0.08); +toSvg("value to hash", 100, 0.08); + +toPng("value to hash", 100, newConfig); +toSvg("value to hash", 100, newConfig); + +var el = document.createElement("canvas"); +update(el, "value"); +update(el, "value", 0.08); +update(el, "value", newConfig); +update("#selector", "value"); +update("#selector", "value", 0.08); +update("#selector", "value", newConfig); + +updateSvg("#selector", "value", newConfig); +updateCanvas("#selector", "value", newConfig); + +var ctx = el.getContext("2d"); +if (ctx) { + drawIcon(ctx, "value", 100); + drawIcon(ctx, "value", 100, 0.08); + drawIcon(ctx, "value", 100, newConfig); +} + +// Ensure Jdenticon dodn't leak Node typings. +// setTimeout returns a NodeJS.Timeout when the Node typings are loaded. +const timeoutRef3: number = setTimeout(() => { }, 100); diff --git a/jdenticon-js/test/types/umd/tsconfig.json b/jdenticon-js/test/types/umd/tsconfig.json new file mode 100644 index 0000000..63a81cd --- /dev/null +++ b/jdenticon-js/test/types/umd/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": [ + "jdenticon/standalone" + ], + "lib": ["es6", "dom"], + }, + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/jdenticon-js/test/unit/sha1.js b/jdenticon-js/test/unit/sha1.js new file mode 100644 index 0000000..005a67e --- /dev/null +++ b/jdenticon-js/test/unit/sha1.js @@ -0,0 +1,56 @@ +"use strict"; + +import tap from "tap"; +import { sha1 } from "../../src/common/sha1.js"; + +tap.equal("92cfceb39d57d914ed8b14d0e37643de0797ae56", sha1(42)); +tap.equal("21d90aad4d34f48f4aad9b5fa3c37c118af16df9", sha1("Value to be hashed")); +tap.equal("d5d4cd07616a542891b7ec2d0257b3a24b69856e", sha1()); // undefined +tap.equal("2be88ca4242c76e8253ac62474851065032d6833", sha1(null)); +tap.equal("08a73daac75982601504c4ba956f49e73ee52667", sha1("abcåäö")); // non-ASCII chars +tap.equal("da39a3ee5e6b4b0d3255bfef95601890afd80709", sha1("")); +tap.equal("11f6ad8ec52a2984abaafd7c3b516503785c2072", sha1("x")); + +// The message is broken down to 64 byte blocks. Test the region around 64 bytes. + +// 54 chars +tap.equal("6d9fbf872b4e22afee77d8c9e95c10ec03bc731d", sha1("012345678901234567890123456789012345678901234567890123")); + +// 55 chars +tap.equal("9f3a4ce7f66b1b74c34da2c5d732c39f81e0f8df", sha1("0123456789012345678901234567890123456789012345678901234")); + +// 56 chars +tap.equal("0a40b8fbdaafb7c29651618ac15d27e772287130", sha1("01234567890123456789012345678901234567890123456789012345")); + +// 57 chars +tap.equal("46cc79601f8c6b81a4180774ce08465987a225a7", sha1("012345678901234567890123456789012345678901234567890123456")); + +// 58 chars +tap.equal("b2aac732d817277777547d2f067df99bb1b5c5ee", sha1("0123456789012345678901234567890123456789012345678901234567")); + +// 59 chars +tap.equal("beb7f3acc8e5c80ad813fb013406b58b0dc821ee", sha1("01234567890123456789012345678901234567890123456789012345678")); + +// 60 chars +tap.equal("f52e3c2732de7bea28f216d877d78dae1aa1ac6a", sha1("012345678901234567890123456789012345678901234567890123456789")); + +// 61 chars +tap.equal("2a1fc3a0fb3d5a6aac17068f5e12e3989269d221", sha1("0123456789012345678901234567890123456789012345678901234567890")); + +// 62 chars +tap.equal("bfbe32d71cb46704d9e185cb6b1e42e1b0965635", sha1("01234567890123456789012345678901234567890123456789012345678901")); + +// 63 chars +tap.equal("984b0f2f6d78c24020f5a79d409f67ab99302891", sha1("012345678901234567890123456789012345678901234567890123456789012")); + +// 64 chars +tap.equal("cf0800f7644ace3cb4c3fa33388d3ba0ea3c8b6e", sha1("0123456789012345678901234567890123456789012345678901234567890123")); + +// 65 chars +tap.equal("92de3a8444fe6d15268f0ba810aa43bc8b3a4ffe", sha1("01234567890123456789012345678901234567890123456789012345678901234")); + +// 66 chars +tap.equal("54af28647b3c9f53d5c20b2b7877062eb69a4675", sha1("012345678901234567890123456789012345678901234567890123456789012345")); + +// 130 chars (spans three blocks) +tap.equal("1f548f0569669daed4fee89712d9019e3d276b55", sha1("0123456789012345678901234567890123456789012345678901234567890123012345678901234567890123456789012345678901234567890123456789012345")); diff --git a/jdenticon-js/test/unit/toPng.js b/jdenticon-js/test/unit/toPng.js new file mode 100644 index 0000000..b3c5de2 --- /dev/null +++ b/jdenticon-js/test/unit/toPng.js @@ -0,0 +1,17 @@ +"use strict"; + +import tap from "tap"; +import { toPng } from "../../src/node-esm.js"; + +const pngHash = toPng("Icon1", 100); +const pngValue = toPng("9faff4f3d6d7d75577ce810ec6d6a06be49c3a5a", 100); + +tap.ok(pngHash); +tap.ok(pngHash instanceof Buffer); +tap.ok(pngHash.length > 500); + +tap.ok(pngValue); +tap.ok(pngValue instanceof Buffer); +tap.ok(pngValue.length > 500); + +tap.ok(pngHash.equals(pngValue)); diff --git a/jdenticon-js/test/unit/toSvg.js b/jdenticon-js/test/unit/toSvg.js new file mode 100644 index 0000000..d45d2bf --- /dev/null +++ b/jdenticon-js/test/unit/toSvg.js @@ -0,0 +1,9 @@ +"use strict"; + +import tap from "tap"; +import { toSvg } from "../../src/node-esm.js"; + +const expectedSvg = ''; + +tap.equal(expectedSvg, toSvg("Icon1", 100)); +tap.equal(expectedSvg, toSvg("9faff4f3d6d7d75577ce810ec6d6a06be49c3a5a", 100)); diff --git a/jdenticon-js/types/env.d.ts b/jdenticon-js/types/env.d.ts new file mode 100644 index 0000000..45b638a --- /dev/null +++ b/jdenticon-js/types/env.d.ts @@ -0,0 +1,19 @@ +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +// Allow Jdenticon to be used in Node environments without referencing the "dom" lib. +// An alternative is to use , but it leaks to the user code base, so this is probably a +// safer option. +interface Element { } + +// By declaring Buffer without including the Node typings, we can avoid type issues related to differences +// between Node and browser typings, e.g. the return type of setTimeout. The user can import the Node typings +// if desired. +declare module "buffer" { + global { + interface Buffer { } + } +} diff --git a/jdenticon-js/types/module.d.ts b/jdenticon-js/types/module.d.ts new file mode 100644 index 0000000..d6b2721 --- /dev/null +++ b/jdenticon-js/types/module.d.ts @@ -0,0 +1,241 @@ +/// +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +export interface JdenticonConfig { + /** + * Limits the possible hues in generated icons. The hues are specified as an array of hues in degrees. If the + * option is omitted or an empty array is specified, all hues are allowed. + */ + hues?: number[], + /** + * Specifies the lightness of the generated icon. + */ + lightness?: { + /** + * Specifies the lightness range of colored shapes of an icon. The range is expressed as an array + * containing two numbers, representing the minimum and maximum lightness in the range [0.0, 1.0]. + */ + color?: number[], + /** + * Specifies the lightness range of grayscale shapes of an icon. The range is expressed as an array + * containing two numbers, representing the minimum and maximum lightness in the range [0.0, 1.0]. + */ + grayscale?: number[] + }, + /** + * Specifies the saturation of the generated icon. + * + * For backward compatibility a single number can be specified instead of a `{ color, grayscale }` + * object. This single number refers to the saturation of colored shapes. + */ + saturation?: { + /** + * Specifies the saturation of originally colored shapes of an icon. The saturation is expressed as a + * number in the range [0.0, 1.0]. + */ + color?: number, + /** + * Specifies the saturation of originally grayscale shapes of an icon. The saturation is expressed as a + * number in the range [0.0, 1.0]. + */ + grayscale?: number + } | number, + /** + * Specifies the padding surrounding the icon in percents in the range [0.0, 0.5). + */ + padding?: number; + /** + * Specifies the background color to be rendered behind the icon. + * + * Supported syntaxes are: + * * `"#rgb"` + * * `"#rgba"` + * * `"#rrggbb"` + * * `"#rrggbbaa"` + */ + backColor?: string, + /** + * Specifies when icons will be rendered. + * + * * `"never"` – icons are never rendered automatically. You need to call `jdenticon.update()` manually to + * render identicons. + * + * * `"once"` – icons are rendered once the page has loaded. Any dynamically inserted or modified icons will + * not be rendered unless `jdenticon.update()` is manually called. + * + * * `"observe"` – icons are rendered upon page load, and the DOM is monitored for new icons using a + * `MutationObserver`. Use this if icons are inserted dynamically, e.g. by using Angular, React or + * VanillaJS. This option behaves as `"once"` in IE<11. + * + * @remarks + * This option has no effect in Node environments. + */ + replaceMode?: "never" | "once" | "observe" +} + +/** + * Updates the identicon in the specified `` or `` elements. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + * + * @param elementOrSelector Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param hashOrValue Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function update(elementOrSelector: Element | string, hashOrValue?: any, config?: JdenticonConfig | number): void; + +/** + * Updates the identicon in the specified `` elements. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + * + * @param elementOrSelector Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param hashOrValue Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function updateCanvas(elementOrSelector: Element | string, hashOrValue?: any, config?: JdenticonConfig | number): void; + +/** + * Updates the identicon in the specified `` elements. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + * + * @param elementOrSelector Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param hashOrValue Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function updateSvg(elementOrSelector: Element | string, hashOrValue?: any, config?: JdenticonConfig | number): void; + +/** + * Draws an identicon to a context. + * @param ctx Canvas context on which the icon will be drawn at location (0, 0). + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param size Icon size in pixels. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ +export function drawIcon( + ctx: JdenticonCompatibleCanvasRenderingContext2D, + hashOrValue: any, + size: number, + config?: JdenticonConfig | number): void; + +/** + * Draws an identicon as an SVG string. + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param size Icon size in pixels. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns SVG string + */ +export function toSvg(hashOrValue: any, size: number, config?: JdenticonConfig | number): string; + +/** + * Draws an identicon as PNG. + * + * @remarks + * This method is not available in the browser. + * + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param size Icon size in pixels. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns PNG data + */ +export function toPng(hashOrValue: any, size: number, config?: JdenticonConfig | number): Buffer; + +/** + * Gets the current global style configuration. + */ +export function configure(): JdenticonConfig; + +/** + * Specifies the color options for the generated icons. This is the only supported method of setting identicon + * styles when used in a Node environment. + * + * In browsers {@link jdenticon_config} is the prefered way of setting an identicon style to avoid a race + * condition where the style is set before the Jdenticon lib has loaded, leading to an unhandled error. + */ +export function configure(newConfig: JdenticonConfig): JdenticonConfig; + +/** + * Specifies the version of the Jdenticon package in use. + */ +export const version: string; + +/** + * This is a subset of `HTMLCanvasElement` to allow using incomplete canvas implementations, + * like `canvas-renderer`. + */ +export interface JdenticonCompatibleCanvas { + // HTMLCanvasElement + readonly height: number; + readonly width: number; + getContext(contextId: "2d"): JdenticonCompatibleCanvasRenderingContext2D | null; +} + +/** + * This is a subset of `CanvasRenderingContext2D` to allow using incomplete canvas implementations, + * like `canvas-renderer`. + */ +export interface JdenticonCompatibleCanvasRenderingContext2D { + // CanvasRenderingContext2D + readonly canvas: JdenticonCompatibleCanvas; + + // CanvasDrawPath + beginPath(): void; + fill(): void; + + // CanvasFillStrokeStyles + fillStyle: any; + + // CanvasPath + arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void; + closePath(): void; + lineTo(x: number, y: number): void; + moveTo(x: number, y: number): void; + + // CanvasRect + clearRect(x: number, y: number, w: number, h: number): void; + fillRect(x: number, y: number, w: number, h: number): void; + + // CanvasState + restore(): void; + save(): void; + + // CanvasTransform + translate(x: number, y: number): void; +} + +declare global { + interface Window { + /** + * Specifies options for generated identicons. + * + * See also {@link jdenticon.config} for Node usage. + */ + jdenticon_config?: JdenticonConfig; + } +} diff --git a/jdenticon-js/types/umd.d.ts b/jdenticon-js/types/umd.d.ts new file mode 100644 index 0000000..5f09097 --- /dev/null +++ b/jdenticon-js/types/umd.d.ts @@ -0,0 +1,269 @@ +/// +/** + * Jdenticon + * https://github.com/dmester/jdenticon + * Copyright © Daniel Mester Pirttijärvi + */ + +declare global { + interface JdenticonConfig { + /** + * Limits the possible hues in generated icons. The hues are specified as an array of hues in degrees. If the + * option is omitted or an empty array is specified, all hues are allowed. + */ + hues?: number[], + /** + * Specifies the lightness of the generated icon. + */ + lightness?: { + /** + * Specifies the lightness range of colored shapes of an icon. The range is expressed as an array + * containing two numbers, representing the minimum and maximum lightness in the range [0.0, 1.0]. + */ + color?: number[], + /** + * Specifies the lightness range of grayscale shapes of an icon. The range is expressed as an array + * containing two numbers, representing the minimum and maximum lightness in the range [0.0, 1.0]. + */ + grayscale?: number[] + }, + /** + * Specifies the saturation of the generated icon. + * + * For backward compatibility a single number can be specified instead of a `{ color, grayscale }` + * object. This single number refers to the saturation of colored shapes. + */ + saturation?: { + /** + * Specifies the saturation of originally colored shapes of an icon. The saturation is expressed as a + * number in the range [0.0, 1.0]. + */ + color?: number, + /** + * Specifies the saturation of originally grayscale shapes of an icon. The saturation is expressed as a + * number in the range [0.0, 1.0]. + */ + grayscale?: number + } | number, + /** + * Specifies the padding surrounding the icon in percents in the range [0.0, 0.5). + */ + padding?: number; + /** + * Specifies the background color to be rendered behind the icon. + * + * Supported syntaxes are: + * * `"#rgb"` + * * `"#rgba"` + * * `"#rrggbb"` + * * `"#rrggbbaa"` + */ + backColor?: string, + /** + * Specifies when icons will be rendered. + * + * * `"never"` – icons are never rendered automatically. You need to call `jdenticon.update()` manually to + * render identicons. + * + * * `"once"` – icons are rendered once the page has loaded. Any dynamically inserted or modified icons will + * not be rendered unless `jdenticon.update()` is manually called. + * + * * `"observe"` – icons are rendered upon page load, and the DOM is monitored for new icons using a + * `MutationObserver`. Use this if icons are inserted dynamically, e.g. by using Angular, React or + * VanillaJS. This option behaves as `"once"` in IE<11. + * + * @remarks + * This option has no effect in Node environments. + */ + replaceMode?: "never" | "once" | "observe" + } + + interface Jdenticon { + /** + * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + */ + (): void; + + /** + * Updates the identicon in the specified canvas or svg elements. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + * + * @param elementOrSelector Specifies the container in which the icon is rendered as a DOM element of the type + * `` or ``, or a CSS selector to such an element. + * @param hash Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ + update(elementOrSelector: Element | string, hashOrValue?: any, config?: JdenticonConfig | number): void; + + /** + * Updates the identicon in the specified `` elements. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + * + * @param elementOrSelector Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param hashOrValue Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ + updateCanvas(elementOrSelector: Element | string, hashOrValue?: any, config?: JdenticonConfig | number): void; + + /** + * Updates the identicon in the specified `` elements. + * + * @remarks + * This method is only available in the browser. Calling this method on Node.js will throw an error. + * + * @param elementOrSelector Specifies the container in which the icon is rendered as a DOM element of the type + * ``, or a CSS selector to such an element. + * @param hashOrValue Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or + * `data-jdenticon-value` attribute will be evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any + * global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ + updateSvg(elementOrSelector: Element | string, hashOrValue?: any, config?: JdenticonConfig | number): void; + + /** + * Draws an identicon to a context. + * @param ctx Canvas context on which the icon will be drawn at location (0, 0). + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param size Icon size in pixels. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ + drawIcon( + ctx: JdenticonCompatibleCanvasRenderingContext2D, + hashOrValue: any, + size: number, + config?: JdenticonConfig | number): void; + + /** + * Draws an identicon as an SVG string. + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param size Icon size in pixels. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns SVG string + */ + toSvg(hashOrValue: any, size: number, config?: JdenticonConfig | number): string; + + /** + * Draws an identicon as PNG. + * + * @remarks + * This method is not available in the browser. + * + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. + * @param size Icon size in pixels. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + * @returns PNG data + */ + toPng(hashOrValue: any, size: number, config?: JdenticonConfig | number): Buffer; + + /** + * Gets the current global style configuration. + */ + configure(): JdenticonConfig; + + /** + * Specifies the color options for the generated icons. This is the only supported method of setting identicon + * styles when used in a Node environment. + * + * In browsers {@link jdenticon_config} is the prefered way of setting an identicon style to avoid a race + * condition where the style is set before the Jdenticon lib has loaded, leading to an unhandled error. + */ + configure(newConfig: JdenticonConfig): JdenticonConfig; + + /** + * Specifies the version of the Jdenticon package in use. + */ + readonly version: string; + } + + /** + * This is a subset of `HTMLCanvasElement` to allow using incomplete canvas implementations, + * like `canvas-renderer`. + */ + interface JdenticonCompatibleCanvas { + // HTMLCanvasElement + readonly height: number; + readonly width: number; + getContext(contextId: "2d"): JdenticonCompatibleCanvasRenderingContext2D | null; + } + + /** + * This is a subset of `CanvasRenderingContext2D` to allow using incomplete canvas implementations, + * like `canvas-renderer`. + */ + interface JdenticonCompatibleCanvasRenderingContext2D { + // CanvasRenderingContext2D + readonly canvas: JdenticonCompatibleCanvas; + + // CanvasDrawPath + beginPath(): void; + fill(): void; + + // CanvasFillStrokeStyles + fillStyle: any; + + // CanvasPath + arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void; + closePath(): void; + lineTo(x: number, y: number): void; + moveTo(x: number, y: number): void; + + // CanvasRect + clearRect(x: number, y: number, w: number, h: number): void; + fillRect(x: number, y: number, w: number, h: number): void; + + // CanvasState + restore(): void; + save(): void; + + // CanvasTransform + translate(x: number, y: number): void; + } + + interface JQuery { + /** + * Renders an indenticon for all matching supported elements. + * + * @param hashOrValue A hexadecimal hash string or any value that will be hashed by Jdenticon. If not + * specified the `data-jdenticon-hash` and `data-jdenticon-value` attributes of each element will be + * evaluated. + * @param config Optional configuration. If specified, this configuration object overrides any global + * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be + * specified in place of a configuration object. + */ + jdenticon(hashOrValue?: any, config?: JdenticonConfig | number): void; + } + + interface Window { + /** + * Specifies options for generated identicons. + * + * See also {@link jdenticon.config} for Node usage. + */ + jdenticon_config?: JdenticonConfig; + } +} + +declare var jdenticon: Jdenticon; +export = jdenticon; +export as namespace jdenticon; diff --git a/jdenticon/config.go b/jdenticon/config.go new file mode 100644 index 0000000..0e282a7 --- /dev/null +++ b/jdenticon/config.go @@ -0,0 +1,235 @@ +package jdenticon + +import ( + "fmt" + "regexp" +) + +// Config holds configuration options for identicon generation. +type Config struct { + // HueRestrictions specifies the hues (in degrees) that are allowed for the identicon. + // If empty, any hue can be used. Values should be between 0 and 360. + HueRestrictions []float64 + + // ColorLightnessRange specifies the lightness range for colored shapes [min, max]. + // Values should be between 0.0 and 1.0. Default: [0.4, 0.8] + ColorLightnessRange [2]float64 + + // GrayscaleLightnessRange specifies the lightness range for grayscale shapes [min, max]. + // Values should be between 0.0 and 1.0. Default: [0.3, 0.9] + GrayscaleLightnessRange [2]float64 + + // ColorSaturation controls the saturation of colored shapes. + // Values should be between 0.0 and 1.0. Default: 0.5 + ColorSaturation float64 + + // GrayscaleSaturation controls the saturation of grayscale shapes. + // Values should be between 0.0 and 1.0. Default: 0.0 + GrayscaleSaturation float64 + + // BackgroundColor sets the background color for the identicon. + // Accepts hex colors like "#fff", "#ffffff", "#ffffff80" (with alpha). + // If empty, the background will be transparent. + BackgroundColor string + + // Padding controls the padding around the identicon as a percentage of the size. + // Values should be between 0.0 and 0.5. Default: 0.08 + Padding float64 +} + +// DefaultConfig returns a default configuration that matches jdenticon-js defaults. +func DefaultConfig() Config { + return Config{ + HueRestrictions: nil, // No restrictions + ColorLightnessRange: [2]float64{0.4, 0.8}, + GrayscaleLightnessRange: [2]float64{0.3, 0.9}, + ColorSaturation: 0.5, + GrayscaleSaturation: 0.0, + BackgroundColor: "", // Transparent + Padding: 0.08, + } +} + +// ConfigOption represents a configuration option function. +type ConfigOption func(*Config) error + +// WithHueRestrictions sets the allowed hues in degrees (0-360). +func WithHueRestrictions(hues []float64) ConfigOption { + return func(c *Config) error { + for _, hue := range hues { + if hue < 0 || hue >= 360 { + return fmt.Errorf("hue must be between 0 and 360, got %f", hue) + } + } + c.HueRestrictions = make([]float64, len(hues)) + copy(c.HueRestrictions, hues) + return nil + } +} + +// WithColorLightnessRange sets the lightness range for colored shapes. +func WithColorLightnessRange(min, max float64) ConfigOption { + return func(c *Config) error { + if min < 0 || min > 1 || max < 0 || max > 1 { + return fmt.Errorf("lightness values must be between 0 and 1, got min=%f max=%f", min, max) + } + if min > max { + return fmt.Errorf("minimum lightness cannot be greater than maximum, got min=%f max=%f", min, max) + } + c.ColorLightnessRange = [2]float64{min, max} + return nil + } +} + +// WithGrayscaleLightnessRange sets the lightness range for grayscale shapes. +func WithGrayscaleLightnessRange(min, max float64) ConfigOption { + return func(c *Config) error { + if min < 0 || min > 1 || max < 0 || max > 1 { + return fmt.Errorf("lightness values must be between 0 and 1, got min=%f max=%f", min, max) + } + if min > max { + return fmt.Errorf("minimum lightness cannot be greater than maximum, got min=%f max=%f", min, max) + } + c.GrayscaleLightnessRange = [2]float64{min, max} + return nil + } +} + +// WithColorSaturation sets the saturation for colored shapes. +func WithColorSaturation(saturation float64) ConfigOption { + return func(c *Config) error { + if saturation < 0 || saturation > 1 { + return fmt.Errorf("saturation must be between 0 and 1, got %f", saturation) + } + c.ColorSaturation = saturation + return nil + } +} + +// WithGrayscaleSaturation sets the saturation for grayscale shapes. +func WithGrayscaleSaturation(saturation float64) ConfigOption { + return func(c *Config) error { + if saturation < 0 || saturation > 1 { + return fmt.Errorf("saturation must be between 0 and 1, got %f", saturation) + } + c.GrayscaleSaturation = saturation + return nil + } +} + +// WithBackgroundColor sets the background color. +func WithBackgroundColor(color string) ConfigOption { + return func(c *Config) error { + if color != "" { + if err := validateHexColor(color); err != nil { + return fmt.Errorf("invalid background color: %w", err) + } + } + c.BackgroundColor = color + return nil + } +} + +// WithPadding sets the padding around the identicon. +func WithPadding(padding float64) ConfigOption { + return func(c *Config) error { + if padding < 0 || padding > 0.5 { + return fmt.Errorf("padding must be between 0 and 0.5, got %f", padding) + } + c.Padding = padding + return nil + } +} + +// Configure creates a new configuration with the given options. +func Configure(options ...ConfigOption) (Config, error) { + config := DefaultConfig() + + for _, option := range options { + if err := option(&config); err != nil { + return Config{}, err + } + } + + return config, nil +} + +// validateHexColor validates that a color string is a valid hex color. +func validateHexColor(color string) error { + hexColorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3,8}$`) + if !hexColorPattern.MatchString(color) { + return fmt.Errorf("color must be a hex color like #fff, #ffffff, or #ffffff80") + } + + // Validate length - must be 3, 4, 6, or 8 characters after # + length := len(color) - 1 + if length != 3 && length != 4 && length != 6 && length != 8 { + return fmt.Errorf("hex color must be 3, 4, 6, or 8 characters after #") + } + + return nil +} + +// ParseColor normalizes a hex color string to the full #RRGGBB or #RRGGBBAA format. +func ParseColor(color string) (string, error) { + if color == "" { + return "", nil + } + + if err := validateHexColor(color); err != nil { + return "", err + } + + // Normalize short forms to full forms + switch len(color) { + case 4: // #RGB -> #RRGGBB + r, g, b := color[1], color[2], color[3] + return fmt.Sprintf("#%c%c%c%c%c%c", r, r, g, g, b, b), nil + case 5: // #RGBA -> #RRGGBBAA + r, g, b, a := color[1], color[2], color[3], color[4] + return fmt.Sprintf("#%c%c%c%c%c%c%c%c", r, r, g, g, b, b, a, a), nil + case 7, 9: // #RRGGBB or #RRGGBBAA - already normalized + return color, nil + default: + return "", fmt.Errorf("unsupported color format") + } +} + +// GetHue returns the hue for the given original hue value, taking into account hue restrictions. +func (c *Config) GetHue(originalHue float64) float64 { + if len(c.HueRestrictions) == 0 { + return originalHue + } + + // Map originalHue [0,1] to one of the restricted hues + index := int(originalHue * 0.999 * float64(len(c.HueRestrictions))) + if index >= len(c.HueRestrictions) { + index = len(c.HueRestrictions) - 1 + } + + hue := c.HueRestrictions[index] + // Convert degrees to [0,1] range and normalize + return ((hue/360.0)+1.0) - float64(int((hue/360.0)+1.0)) +} + +// GetColorLightness returns a lightness value within the color lightness range. +func (c *Config) GetColorLightness(value float64) float64 { + return c.getLightness(value, c.ColorLightnessRange) +} + +// GetGrayscaleLightness returns a lightness value within the grayscale lightness range. +func (c *Config) GetGrayscaleLightness(value float64) float64 { + return c.getLightness(value, c.GrayscaleLightnessRange) +} + +// getLightness maps a value [0,1] to the specified lightness range. +func (c *Config) getLightness(value float64, lightnessRange [2]float64) float64 { + result := lightnessRange[0] + value*(lightnessRange[1]-lightnessRange[0]) + if result < 0 { + return 0 + } + if result > 1 { + return 1 + } + return result +} \ No newline at end of file diff --git a/jdenticon/config_test.go b/jdenticon/config_test.go new file mode 100644 index 0000000..ad00633 --- /dev/null +++ b/jdenticon/config_test.go @@ -0,0 +1,437 @@ +package jdenticon + +import ( + "testing" +) + +func TestDefaultConfig(t *testing.T) { + config := DefaultConfig() + + // Test default values match jdenticon-js + if len(config.HueRestrictions) != 0 { + t.Errorf("Expected no hue restrictions, got %v", config.HueRestrictions) + } + + expectedColorRange := [2]float64{0.4, 0.8} + if config.ColorLightnessRange != expectedColorRange { + t.Errorf("Expected color lightness range %v, got %v", expectedColorRange, config.ColorLightnessRange) + } + + expectedGrayscaleRange := [2]float64{0.3, 0.9} + if config.GrayscaleLightnessRange != expectedGrayscaleRange { + t.Errorf("Expected grayscale lightness range %v, got %v", expectedGrayscaleRange, config.GrayscaleLightnessRange) + } + + if config.ColorSaturation != 0.5 { + t.Errorf("Expected color saturation 0.5, got %f", config.ColorSaturation) + } + + if config.GrayscaleSaturation != 0.0 { + t.Errorf("Expected grayscale saturation 0.0, got %f", config.GrayscaleSaturation) + } + + if config.BackgroundColor != "" { + t.Errorf("Expected empty background color, got %s", config.BackgroundColor) + } + + if config.Padding != 0.08 { + t.Errorf("Expected padding 0.08, got %f", config.Padding) + } +} + +func TestWithHueRestrictions(t *testing.T) { + tests := []struct { + name string + hues []float64 + wantErr bool + }{ + {"valid hues", []float64{0, 90, 180, 270}, false}, + {"single hue", []float64{120}, false}, + {"empty slice", []float64{}, false}, + {"negative hue", []float64{-10}, true}, + {"hue too large", []float64{360}, true}, + {"mixed valid/invalid", []float64{120, 400}, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := Configure(WithHueRestrictions(tt.hues)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for hues %v, got none", tt.hues) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if len(config.HueRestrictions) != len(tt.hues) { + t.Errorf("Expected %d hue restrictions, got %d", len(tt.hues), len(config.HueRestrictions)) + } + + for i, hue := range tt.hues { + if config.HueRestrictions[i] != hue { + t.Errorf("Expected hue %f at index %d, got %f", hue, i, config.HueRestrictions[i]) + } + } + }) + } +} + +func TestWithLightnessRanges(t *testing.T) { + tests := []struct { + name string + min float64 + max float64 + wantErr bool + }{ + {"valid range", 0.2, 0.8, false}, + {"full range", 0.0, 1.0, false}, + {"equal values", 0.5, 0.5, false}, + {"min > max", 0.8, 0.2, true}, + {"negative min", -0.1, 0.5, true}, + {"max > 1", 0.5, 1.1, true}, + {"both invalid", -0.1, 1.1, true}, + } + + for _, tt := range tests { + t.Run("color_"+tt.name, func(t *testing.T) { + config, err := Configure(WithColorLightnessRange(tt.min, tt.max)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for range [%f, %f], got none", tt.min, tt.max) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + expected := [2]float64{tt.min, tt.max} + if config.ColorLightnessRange != expected { + t.Errorf("Expected range %v, got %v", expected, config.ColorLightnessRange) + } + }) + + t.Run("grayscale_"+tt.name, func(t *testing.T) { + config, err := Configure(WithGrayscaleLightnessRange(tt.min, tt.max)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for range [%f, %f], got none", tt.min, tt.max) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + expected := [2]float64{tt.min, tt.max} + if config.GrayscaleLightnessRange != expected { + t.Errorf("Expected range %v, got %v", expected, config.GrayscaleLightnessRange) + } + }) + } +} + +func TestWithSaturation(t *testing.T) { + tests := []struct { + name string + saturation float64 + wantErr bool + }{ + {"valid saturation", 0.5, false}, + {"zero saturation", 0.0, false}, + {"max saturation", 1.0, false}, + {"negative saturation", -0.1, true}, + {"saturation > 1", 1.1, true}, + } + + for _, tt := range tests { + t.Run("color_"+tt.name, func(t *testing.T) { + config, err := Configure(WithColorSaturation(tt.saturation)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for saturation %f, got none", tt.saturation) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if config.ColorSaturation != tt.saturation { + t.Errorf("Expected saturation %f, got %f", tt.saturation, config.ColorSaturation) + } + }) + + t.Run("grayscale_"+tt.name, func(t *testing.T) { + config, err := Configure(WithGrayscaleSaturation(tt.saturation)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for saturation %f, got none", tt.saturation) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if config.GrayscaleSaturation != tt.saturation { + t.Errorf("Expected saturation %f, got %f", tt.saturation, config.GrayscaleSaturation) + } + }) + } +} + +func TestWithBackgroundColor(t *testing.T) { + tests := []struct { + name string + color string + wantErr bool + expected string + }{ + {"empty color", "", false, ""}, + {"3-char hex", "#fff", false, "#fff"}, + {"4-char hex with alpha", "#ffff", false, "#ffff"}, + {"6-char hex", "#ffffff", false, "#ffffff"}, + {"8-char hex with alpha", "#ffffff80", false, "#ffffff80"}, + {"lowercase", "#abc123", false, "#abc123"}, + {"uppercase", "#ABC123", false, "#ABC123"}, + {"invalid format", "ffffff", true, ""}, + {"invalid chars", "#gggggg", true, ""}, + {"too short", "#ff", true, ""}, + {"5-char hex", "#fffff", true, ""}, + {"7-char hex", "#fffffff", true, ""}, + {"9-char hex", "#fffffffff", true, ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := Configure(WithBackgroundColor(tt.color)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for color %s, got none", tt.color) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if config.BackgroundColor != tt.expected { + t.Errorf("Expected color %s, got %s", tt.expected, config.BackgroundColor) + } + }) + } +} + +func TestWithPadding(t *testing.T) { + tests := []struct { + name string + padding float64 + wantErr bool + }{ + {"valid padding", 0.08, false}, + {"zero padding", 0.0, false}, + {"max padding", 0.5, false}, + {"negative padding", -0.1, true}, + {"padding > 0.5", 0.6, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := Configure(WithPadding(tt.padding)) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for padding %f, got none", tt.padding) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if config.Padding != tt.padding { + t.Errorf("Expected padding %f, got %f", tt.padding, config.Padding) + } + }) + } +} + +func TestConfigureMultipleOptions(t *testing.T) { + config, err := Configure( + WithHueRestrictions([]float64{120, 240}), + WithColorSaturation(0.7), + WithPadding(0.1), + WithBackgroundColor("#ff0000"), + ) + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if len(config.HueRestrictions) != 2 || config.HueRestrictions[0] != 120 || config.HueRestrictions[1] != 240 { + t.Errorf("Expected hue restrictions [120, 240], got %v", config.HueRestrictions) + } + + if config.ColorSaturation != 0.7 { + t.Errorf("Expected color saturation 0.7, got %f", config.ColorSaturation) + } + + if config.Padding != 0.1 { + t.Errorf("Expected padding 0.1, got %f", config.Padding) + } + + if config.BackgroundColor != "#ff0000" { + t.Errorf("Expected background color #ff0000, got %s", config.BackgroundColor) + } + + // Check that other values are still default + expectedColorRange := [2]float64{0.4, 0.8} + if config.ColorLightnessRange != expectedColorRange { + t.Errorf("Expected default color lightness range %v, got %v", expectedColorRange, config.ColorLightnessRange) + } +} + +func TestParseColor(t *testing.T) { + tests := []struct { + name string + input string + expected string + wantErr bool + }{ + {"empty", "", "", false}, + {"3-char to 6-char", "#abc", "#aabbcc", false}, + {"4-char to 8-char", "#abcd", "#aabbccdd", false}, + {"6-char unchanged", "#abcdef", "#abcdef", false}, + {"8-char unchanged", "#abcdef80", "#abcdef80", false}, + {"invalid format", "abcdef", "", true}, + {"invalid length", "#abcde", "", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := ParseColor(tt.input) + + if tt.wantErr { + if err == nil { + t.Errorf("Expected error for input %s, got none", tt.input) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if result != tt.expected { + t.Errorf("Expected %s, got %s", tt.expected, result) + } + }) + } +} + +func TestConfigGetHue(t *testing.T) { + tests := []struct { + name string + restrictions []float64 + input float64 + expected float64 + }{ + {"no restrictions", nil, 0.5, 0.5}, + {"single restriction", []float64{180}, 0.5, 0.5}, + {"multiple restrictions", []float64{0, 120, 240}, 0.0, 0.0}, + {"multiple restrictions mid", []float64{0, 120, 240}, 0.5, 120.0 / 360.0}, + {"multiple restrictions high", []float64{0, 120, 240}, 0.99, 240.0 / 360.0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := DefaultConfig() + config.HueRestrictions = tt.restrictions + + result := config.GetHue(tt.input) + + // Allow small floating point differences + if abs(result-tt.expected) > 0.001 { + t.Errorf("Expected hue %f, got %f", tt.expected, result) + } + }) + } +} + +func TestConfigGetLightness(t *testing.T) { + config := DefaultConfig() + + // Test color lightness + colorLight := config.GetColorLightness(0.0) + if colorLight != 0.4 { + t.Errorf("Expected min color lightness 0.4, got %f", colorLight) + } + + colorLight = config.GetColorLightness(1.0) + if colorLight != 0.8 { + t.Errorf("Expected max color lightness 0.8, got %f", colorLight) + } + + colorLight = config.GetColorLightness(0.5) + expected := 0.4 + 0.5*(0.8-0.4) + if abs(colorLight-expected) > 0.001 { + t.Errorf("Expected mid color lightness %f, got %f", expected, colorLight) + } + + // Test grayscale lightness + grayLight := config.GetGrayscaleLightness(0.0) + if grayLight != 0.3 { + t.Errorf("Expected min grayscale lightness 0.3, got %f", grayLight) + } + + grayLight = config.GetGrayscaleLightness(1.0) + if abs(grayLight-0.9) > 0.001 { + t.Errorf("Expected max grayscale lightness 0.9, got %f", grayLight) + } +} + +func TestConfigureFailing(t *testing.T) { + // Test that Configure fails on invalid options + _, err := Configure( + WithHueRestrictions([]float64{400}), // Invalid hue + WithColorSaturation(0.5), // Valid option after invalid + ) + + if err == nil { + t.Error("Expected Configure to fail with invalid hue restriction") + } +} + +// Helper function for floating point comparison +func abs(x float64) float64 { + if x < 0 { + return -x + } + return x +} \ No newline at end of file diff --git a/jdenticon/doc.go b/jdenticon/doc.go new file mode 100644 index 0000000..8ea9968 --- /dev/null +++ b/jdenticon/doc.go @@ -0,0 +1,14 @@ +// Package jdenticon provides highly recognizable identicon generation. +// +// This package is a Go port of the JavaScript library Jdenticon, +// offering the same visual quality and recognizability in Go applications. +// +// Basic usage: +// +// icon := jdenticon.Generate("user@example.com", 200) +// svg := icon.ToSVG() +// png := icon.ToPNG() +// +// The library supports both SVG and PNG output formats, with configurable +// styling options including color themes, saturation, and brightness. +package jdenticon \ No newline at end of file diff --git a/jdenticon/generate.go b/jdenticon/generate.go new file mode 100644 index 0000000..84db194 --- /dev/null +++ b/jdenticon/generate.go @@ -0,0 +1,344 @@ +package jdenticon + +import ( + "fmt" + "reflect" + "strconv" + + "github.com/kevin/go-jdenticon/internal/engine" + "github.com/kevin/go-jdenticon/internal/renderer" +) + +// iconRenderer defines the common interface for rendering identicons to different formats +type iconRenderer interface { + SetBackground(fillColor string, opacity float64) + BeginShape(color string) + AddPolygon(points []engine.Point) + AddCircle(topLeft engine.Point, size float64, invert bool) + EndShape() +} + +// Icon represents a generated identicon that can be rendered in various formats. +type Icon struct { + icon *engine.Icon +} + +// renderTo renders the icon to the given renderer, handling all common rendering logic +func (i *Icon) renderTo(r iconRenderer) { + if i.icon == nil { + return + } + + // Set background color if configured + if i.icon.Config.BackColor != nil { + r.SetBackground(i.icon.Config.BackColor.String(), 1.0) + } + + // Render each shape group + for _, group := range i.icon.Shapes { + r.BeginShape(group.Color.String()) + + for _, shape := range group.Shapes { + // Skip empty shapes + if shape.Type == "empty" { + continue + } + + switch shape.Type { + case "polygon": + // Transform points + transformedPoints := make([]engine.Point, len(shape.Points)) + for j, point := range shape.Points { + transformedPoints[j] = shape.Transform.TransformIconPoint(point.X, point.Y, 0, 0) + } + r.AddPolygon(transformedPoints) + + case "circle": + // Use dedicated circle fields - CircleX, CircleY represent top-left corner + topLeft := shape.Transform.TransformIconPoint(shape.CircleX, shape.CircleY, 0, 0) + r.AddCircle(topLeft, shape.CircleSize, shape.Invert) + } + } + + r.EndShape() + } +} + +// Generate creates an identicon for the given input value and size. +// The input value is typically an email address, username, or any string +// that should produce a consistent visual representation. +func Generate(value string, size int) (*Icon, error) { + // Compute hash from the input value + hash := ComputeHash(value) + + // Create generator with default configuration + generator := engine.NewDefaultGenerator() + + // Generate the icon + engineIcon, err := generator.Generate(hash, float64(size)) + if err != nil { + return nil, err + } + + return &Icon{icon: engineIcon}, nil +} + +// ToSVG renders the icon as an SVG string. +func (i *Icon) ToSVG() (string, error) { + if i.icon == nil { + return "", nil + } + + svgRenderer := renderer.NewSVGRenderer(int(i.icon.Size)) + i.renderTo(svgRenderer) + return svgRenderer.ToSVG(), nil +} + +// ToPNG renders the icon as PNG image data. +func (i *Icon) ToPNG() ([]byte, error) { + if i.icon == nil { + return nil, nil + } + + pngRenderer := renderer.NewPNGRenderer(int(i.icon.Size)) + i.renderTo(pngRenderer) + return pngRenderer.ToPNG(), nil +} + +// ToSVG generates an identicon as an SVG string for the given input value. +// The value can be any type - it will be converted to a string and hashed. +// Size specifies the icon size in pixels. +// Optional config parameters can be provided to customize the appearance. +func ToSVG(value interface{}, size int, config ...Config) (string, error) { + if size <= 0 { + return "", fmt.Errorf("size must be positive, got %d", size) + } + + // Generate icon with the provided configuration + icon, err := generateWithConfig(value, size, config...) + if err != nil { + return "", fmt.Errorf("failed to generate icon: %w", err) + } + + // Render as SVG + svg, err := icon.ToSVG() + if err != nil { + return "", fmt.Errorf("failed to render SVG: %w", err) + } + + return svg, nil +} + +// ToPNG generates an identicon as PNG image data for the given input value. +// The value can be any type - it will be converted to a string and hashed. +// Size specifies the icon size in pixels. +// Optional config parameters can be provided to customize the appearance. +func ToPNG(value interface{}, size int, config ...Config) ([]byte, error) { + if size <= 0 { + return nil, fmt.Errorf("size must be positive, got %d", size) + } + + // Generate icon with the provided configuration + icon, err := generateWithConfig(value, size, config...) + if err != nil { + return nil, fmt.Errorf("failed to generate icon: %w", err) + } + + // Render as PNG + png, err := icon.ToPNG() + if err != nil { + return nil, fmt.Errorf("failed to render PNG: %w", err) + } + + return png, nil +} + +// ToHash generates a hash string for the given input value. +// This is a convenience function that wraps ComputeHash with better type handling. +// The hash can be used with other functions or stored for consistent icon generation. +func ToHash(value interface{}) string { + return ComputeHash(value) +} + +// generateWithConfig is a helper function that creates an icon with optional configuration. +func generateWithConfig(value interface{}, size int, configs ...Config) (*Icon, error) { + // Convert value to string representation + stringValue := convertToString(value) + + // Compute hash from the input value + hash := ComputeHash(stringValue) + + // Create generator with configuration + var generator *engine.Generator + if len(configs) > 0 { + // Use the provided configuration + engineConfig := convertToEngineConfig(configs[0]) + generator = engine.NewGenerator(engineConfig) + } else { + // Use default configuration + generator = engine.NewDefaultGenerator() + } + + // Generate the icon + engineIcon, err := generator.Generate(hash, float64(size)) + if err != nil { + return nil, err + } + + return &Icon{icon: engineIcon}, nil +} + +// convertToString converts any value to its string representation using reflection. +// This handles various types similar to how JavaScript would convert them. +func convertToString(value interface{}) string { + if value == nil { + return "" + } + + // Use reflection to handle different types + v := reflect.ValueOf(value) + + switch v.Kind() { + case reflect.String: + return v.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return fmt.Sprintf("%d", v.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return fmt.Sprintf("%d", v.Uint()) + case reflect.Float32, reflect.Float64: + return fmt.Sprintf("%g", v.Float()) + case reflect.Bool: + if v.Bool() { + return "true" + } + return "false" + case reflect.Slice: + // Handle byte slices specially + if v.Type().Elem().Kind() == reflect.Uint8 { + return string(v.Bytes()) + } + fallthrough + default: + // For all other types, use fmt.Sprintf + return fmt.Sprintf("%v", value) + } +} + +// convertToEngineConfig converts a public Config to an internal engine.ColorConfig. +func convertToEngineConfig(config Config) engine.ColorConfig { + engineConfig := engine.DefaultColorConfig() + + // Convert hue restrictions + if len(config.HueRestrictions) > 0 { + engineConfig.Hues = make([]float64, len(config.HueRestrictions)) + copy(engineConfig.Hues, config.HueRestrictions) + } + + // Convert lightness ranges + engineConfig.ColorLightness = engine.LightnessRange{ + Min: config.ColorLightnessRange[0], + Max: config.ColorLightnessRange[1], + } + engineConfig.GrayscaleLightness = engine.LightnessRange{ + Min: config.GrayscaleLightnessRange[0], + Max: config.GrayscaleLightnessRange[1], + } + + // Convert saturation values + engineConfig.ColorSaturation = config.ColorSaturation + engineConfig.GrayscaleSaturation = config.GrayscaleSaturation + + // Convert background color + if config.BackgroundColor != "" { + normalizedColor, err := ParseColor(config.BackgroundColor) + if err == nil { + // Parse the normalized hex color into an engine.Color + color, parseErr := parseHexToEngineColor(normalizedColor) + if parseErr == nil { + engineConfig.BackColor = &color + } + } + } + + // Convert padding + engineConfig.IconPadding = config.Padding + + return engineConfig +} + +// parseHexToEngineColor converts a hex color string to an engine.Color. +func parseHexToEngineColor(hexColor string) (engine.Color, error) { + if hexColor == "" { + return engine.Color{}, fmt.Errorf("empty color string") + } + + // Remove # if present + if len(hexColor) > 0 && hexColor[0] == '#' { + hexColor = hexColor[1:] + } + + var r, g, b, a uint8 = 0, 0, 0, 255 + + switch len(hexColor) { + case 6: // RRGGBB + rgb, err := parseHexComponent(hexColor[0:2]) + if err != nil { + return engine.Color{}, err + } + r = rgb + + rgb, err = parseHexComponent(hexColor[2:4]) + if err != nil { + return engine.Color{}, err + } + g = rgb + + rgb, err = parseHexComponent(hexColor[4:6]) + if err != nil { + return engine.Color{}, err + } + b = rgb + + case 8: // RRGGBBAA + rgb, err := parseHexComponent(hexColor[0:2]) + if err != nil { + return engine.Color{}, err + } + r = rgb + + rgb, err = parseHexComponent(hexColor[2:4]) + if err != nil { + return engine.Color{}, err + } + g = rgb + + rgb, err = parseHexComponent(hexColor[4:6]) + if err != nil { + return engine.Color{}, err + } + b = rgb + + rgb, err = parseHexComponent(hexColor[6:8]) + if err != nil { + return engine.Color{}, err + } + a = rgb + + default: + return engine.Color{}, fmt.Errorf("invalid hex color length: %d", len(hexColor)) + } + + return engine.NewColorRGBA(r, g, b, a), nil +} + +// parseHexComponent parses a 2-character hex string to a uint8 value. +func parseHexComponent(hex string) (uint8, error) { + if len(hex) != 2 { + return 0, fmt.Errorf("hex component must be 2 characters, got %d", len(hex)) + } + value, err := strconv.ParseUint(hex, 16, 8) + if err != nil { + return 0, fmt.Errorf("invalid hex component '%s': %w", hex, err) + } + return uint8(value), nil +} \ No newline at end of file diff --git a/jdenticon/generate_test.go b/jdenticon/generate_test.go new file mode 100644 index 0000000..59da6df --- /dev/null +++ b/jdenticon/generate_test.go @@ -0,0 +1,719 @@ +package jdenticon + +import ( + "bytes" + "fmt" + "strings" + "testing" +) + +func TestGenerate(t *testing.T) { + tests := []struct { + name string + value string + size int + }{ + { + name: "email address", + value: "test@example.com", + size: 64, + }, + { + name: "username", + value: "johndoe", + size: 32, + }, + { + name: "large icon", + value: "large-icon-test", + size: 256, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + icon, err := Generate(tt.value, tt.size) + if err != nil { + t.Fatalf("Generate failed: %v", err) + } + + if icon == nil { + t.Fatal("Generate returned nil icon") + } + + // Test SVG generation + svg, err := icon.ToSVG() + if err != nil { + t.Fatalf("ToSVG failed: %v", err) + } + + if svg == "" { + t.Error("ToSVG returned empty string") + } + + // Basic SVG validation + if !strings.Contains(svg, "") { + t.Error("SVG output does not contain closing svg tag") + } + + // Test PNG generation + png, err := icon.ToPNG() + if err != nil { + t.Fatalf("ToPNG failed: %v", err) + } + + if len(png) == 0 { + t.Error("ToPNG returned empty data") + } + + // Basic PNG validation (check PNG signature) + if len(png) < 8 || string(png[1:4]) != "PNG" { + t.Error("PNG output does not have valid PNG signature") + } + }) + } +} + +func TestGenerateConsistency(t *testing.T) { + value := "consistency-test" + size := 64 + + // Generate the same icon multiple times + icon1, err := Generate(value, size) + if err != nil { + t.Fatalf("First generate failed: %v", err) + } + + icon2, err := Generate(value, size) + if err != nil { + t.Fatalf("Second generate failed: %v", err) + } + + // SVG should be identical + svg1, err := icon1.ToSVG() + if err != nil { + t.Fatalf("First ToSVG failed: %v", err) + } + + svg2, err := icon2.ToSVG() + if err != nil { + t.Fatalf("Second ToSVG failed: %v", err) + } + + if svg1 != svg2 { + t.Error("SVG outputs are not consistent for same input") + } + + // PNG should be identical + png1, err := icon1.ToPNG() + if err != nil { + t.Fatalf("First ToPNG failed: %v", err) + } + + png2, err := icon2.ToPNG() + if err != nil { + t.Fatalf("Second ToPNG failed: %v", err) + } + + if !bytes.Equal(png1, png2) { + t.Error("PNG outputs are not consistent for same input") + } +} + +func TestGenerateInvalidInputs(t *testing.T) { + tests := []struct { + name string + value string + size int + }{ + { + name: "zero size", + value: "test", + size: 0, + }, + { + name: "negative size", + value: "test", + size: -10, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := Generate(tt.value, tt.size) + if err == nil { + t.Error("Expected error for invalid input") + } + }) + } +} + +func TestGenerateVariety(t *testing.T) { + // Test that different inputs produce different outputs + values := []string{"value1", "value2", "value3", "test@example.com", "another-test"} + size := 64 + + svgs := make([]string, len(values)) + + for i, value := range values { + icon, err := Generate(value, size) + if err != nil { + t.Fatalf("Generate failed for %s: %v", value, err) + } + + svg, err := icon.ToSVG() + if err != nil { + t.Fatalf("ToSVG failed for %s: %v", value, err) + } + + svgs[i] = svg + } + + // Check that all SVGs are different + for i := 0; i < len(svgs); i++ { + for j := i + 1; j < len(svgs); j++ { + if svgs[i] == svgs[j] { + t.Errorf("SVG outputs are identical for different inputs: %s and %s", values[i], values[j]) + } + } + } +} + +func BenchmarkGenerate(b *testing.B) { + value := "benchmark-test" + size := 64 + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := Generate(value, size) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + } +} + +// BenchmarkGenerateVariousSizes tests generation performance across different icon sizes +func BenchmarkGenerateVariousSizes(b *testing.B) { + sizes := []int{64, 128, 256, 512, 1024} + + for _, size := range sizes { + b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _, err := Generate("benchmark@test.com", size) + if err != nil { + b.Fatalf("Generate failed for size %d: %v", size, err) + } + } + }) + } +} + +// BenchmarkGenerateVariousInputs tests generation performance with different input types +func BenchmarkGenerateVariousInputs(b *testing.B) { + inputs := []struct { + name string + value string + }{ + {"email", "user@example.com"}, + {"username", "john_doe_123"}, + {"uuid", "550e8400-e29b-41d4-a716-446655440000"}, + {"short", "abc"}, + {"long", "this_is_a_very_long_identifier_that_might_be_used_for_generating_identicons_in_some_applications"}, + {"special_chars", "user+test@domain.co.uk"}, + {"numbers", "12345678901234567890"}, + } + + for _, input := range inputs { + b.Run(input.name, func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _, err := Generate(input.value, 128) + if err != nil { + b.Fatalf("Generate failed for input %s: %v", input.name, err) + } + } + }) + } +} + +func BenchmarkToSVG(b *testing.B) { + icon, err := Generate("benchmark-test", 64) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := icon.ToSVG() + if err != nil { + b.Fatalf("ToSVG failed: %v", err) + } + } +} + +func BenchmarkToPNG(b *testing.B) { + icon, err := Generate("benchmark-test", 64) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := icon.ToPNG() + if err != nil { + b.Fatalf("ToPNG failed: %v", err) + } + } +} + +// BenchmarkHashGeneration benchmarks just hash computation performance +func BenchmarkHashGeneration(b *testing.B) { + inputs := []string{ + "user@example.com", + "john_doe_123", + "550e8400-e29b-41d4-a716-446655440000", + "abc", + "this_is_a_very_long_identifier_that_might_be_used_for_generating_identicons", + } + + for _, input := range inputs { + b.Run(fmt.Sprintf("len_%d", len(input)), func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _ = ComputeHash(input) + } + }) + } +} + +// BenchmarkSVGRenderingVariousSizes benchmarks SVG rendering across different sizes +func BenchmarkSVGRenderingVariousSizes(b *testing.B) { + sizes := []int{64, 128, 256, 512} + icons := make(map[int]*Icon) + + // Pre-generate icons + for _, size := range sizes { + icon, err := Generate("benchmark@test.com", size) + if err != nil { + b.Fatalf("Failed to generate icon for size %d: %v", size, err) + } + icons[size] = icon + } + + for _, size := range sizes { + b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) { + icon := icons[size] + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _, err := icon.ToSVG() + if err != nil { + b.Fatalf("ToSVG failed for size %d: %v", size, err) + } + } + }) + } +} + +// BenchmarkPNGRenderingVariousSizes benchmarks PNG rendering across different sizes +func BenchmarkPNGRenderingVariousSizes(b *testing.B) { + sizes := []int{64, 128, 256} // Smaller range for PNG due to higher memory usage + icons := make(map[int]*Icon) + + // Pre-generate icons + for _, size := range sizes { + icon, err := Generate("benchmark@test.com", size) + if err != nil { + b.Fatalf("Failed to generate icon for size %d: %v", size, err) + } + icons[size] = icon + } + + for _, size := range sizes { + b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) { + icon := icons[size] + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _, err := icon.ToPNG() + if err != nil { + b.Fatalf("ToPNG failed for size %d: %v", size, err) + } + } + }) + } +} + +// BenchmarkWithCustomConfig benchmarks generation with custom configuration +func BenchmarkWithCustomConfig(b *testing.B) { + config, err := Configure( + WithHueRestrictions([]float64{0.0, 0.33, 0.66}), + WithColorSaturation(0.6), + WithBackgroundColor("#ffffff"), + WithPadding(0.1), + ) + if err != nil { + b.Fatalf("Configure failed: %v", err) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _, err := ToSVG("benchmark@test.com", 128, config) + if err != nil { + b.Fatalf("ToSVG with config failed: %v", err) + } + } +} + +// BenchmarkWithCustomConfigPNG benchmarks PNG generation with custom configuration +func BenchmarkWithCustomConfigPNG(b *testing.B) { + config, err := Configure( + WithColorSaturation(0.6), + WithBackgroundColor("#123456"), + WithPadding(0.1), + ) + if err != nil { + b.Fatalf("Configure failed: %v", err) + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + _, err := ToPNG("benchmark-png-config@test.com", 128, config) + if err != nil { + b.Fatalf("ToPNG with config failed: %v", err) + } + } +} + +// BenchmarkConcurrentGeneration tests performance under concurrent load +func BenchmarkConcurrentGeneration(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + value := fmt.Sprintf("concurrent_user_%d@example.com", i) + _, err := Generate(value, 128) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + i++ + } + }) +} + +// BenchmarkBatchGeneration tests memory allocation patterns for batch generation +func BenchmarkBatchGeneration(b *testing.B) { + const batchSize = 100 + + b.SetBytes(batchSize) // Report throughput as items/sec + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for j := 0; j < batchSize; j++ { + value := fmt.Sprintf("batch_user_%d@example.com", j) + _, err := Generate(value, 64) + if err != nil { + b.Fatalf("Generate failed: %v", err) + } + } + } +} + +// Tests for new public API functions + +func TestToSVG(t *testing.T) { + tests := []struct { + name string + value interface{} + size int + valid bool + }{ + {"string input", "test@example.com", 64, true}, + {"int input", 12345, 64, true}, + {"float input", 123.45, 64, true}, + {"bool input", true, 64, true}, + {"nil input", nil, 64, true}, + {"byte slice", []byte("hello"), 64, true}, + {"zero size", "test", 0, false}, + {"negative size", "test", -10, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + svg, err := ToSVG(tt.value, tt.size) + + if tt.valid { + if err != nil { + t.Fatalf("ToSVG failed: %v", err) + } + + if svg == "" { + t.Error("ToSVG returned empty string") + } + + // Basic SVG validation + if !strings.Contains(svg, "") { + t.Error("SVG output does not contain closing svg tag") + } + } else { + if err == nil { + t.Error("Expected error for invalid input") + } + } + }) + } +} + +func TestToPNG(t *testing.T) { + tests := []struct { + name string + value interface{} + size int + valid bool + }{ + {"string input", "test@example.com", 64, true}, + {"int input", 12345, 64, true}, + {"float input", 123.45, 64, true}, + {"bool input", false, 64, true}, + {"nil input", nil, 64, true}, + {"byte slice", []byte("hello"), 64, true}, + {"zero size", "test", 0, false}, + {"negative size", "test", -10, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + png, err := ToPNG(tt.value, tt.size) + + if tt.valid { + if err != nil { + t.Fatalf("ToPNG failed: %v", err) + } + + if len(png) == 0 { + t.Error("ToPNG returned empty data") + } + + // Basic PNG validation (check PNG signature) + if len(png) < 8 || string(png[1:4]) != "PNG" { + t.Error("PNG output does not have valid PNG signature") + } + } else { + if err == nil { + t.Error("Expected error for invalid input") + } + } + }) + } +} + +func TestToHash(t *testing.T) { + tests := []struct { + name string + value interface{} + expected string + }{ + {"string", "test", ComputeHash("test")}, + {"int", 123, ComputeHash("123")}, + {"float", 123.45, ComputeHash("123.45")}, + {"bool true", true, ComputeHash("true")}, + {"bool false", false, ComputeHash("false")}, + {"nil", nil, ComputeHash("")}, + {"byte slice", []byte("hello"), ComputeHash("hello")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hash := ToHash(tt.value) + + if hash != tt.expected { + t.Errorf("ToHash(%v) = %s, expected %s", tt.value, hash, tt.expected) + } + + // Hash should be non-empty and valid hex + if hash == "" { + t.Error("ToHash returned empty string") + } + + // Should be valid hex characters + for _, c := range hash { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) { + t.Errorf("Hash contains invalid character: %c", c) + break + } + } + }) + } +} + +func TestToSVGWithConfig(t *testing.T) { + value := "config-test" + size := 64 + + // Test with custom configuration + config, err := Configure( + WithHueRestrictions([]float64{120, 240}), // Blue/green hues + WithColorSaturation(0.8), + WithBackgroundColor("#ffffff"), + WithPadding(0.1), + ) + if err != nil { + t.Fatalf("Configure failed: %v", err) + } + + svg, err := ToSVG(value, size, config) + if err != nil { + t.Fatalf("ToSVG with config failed: %v", err) + } + + if svg == "" { + t.Error("ToSVG with config returned empty string") + } + + // Test that it's different from default + defaultSvg, err := ToSVG(value, size) + if err != nil { + t.Fatalf("ToSVG default failed: %v", err) + } + + if svg == defaultSvg { + t.Error("SVG with config is identical to default SVG") + } +} + +func TestToPNGWithConfig(t *testing.T) { + value := "config-test" + size := 64 + + // Test with custom configuration + config, err := Configure( + WithHueRestrictions([]float64{60, 180}), // Yellow/cyan hues + WithColorSaturation(0.9), + WithBackgroundColor("#000000"), + WithPadding(0.05), + ) + if err != nil { + t.Fatalf("Configure failed: %v", err) + } + + png, err := ToPNG(value, size, config) + if err != nil { + t.Fatalf("ToPNG with config failed: %v", err) + } + + if len(png) == 0 { + t.Error("ToPNG with config returned empty data") + } + + // Test that it's different from default + defaultPng, err := ToPNG(value, size) + if err != nil { + t.Fatalf("ToPNG default failed: %v", err) + } + + if bytes.Equal(png, defaultPng) { + t.Error("PNG with config is identical to default PNG") + } +} + +func TestPublicAPIConsistency(t *testing.T) { + value := "consistency-test" + size := 64 + + // Generate with both old and new APIs + icon, err := Generate(value, size) + if err != nil { + t.Fatalf("Generate failed: %v", err) + } + + oldSvg, err := icon.ToSVG() + if err != nil { + t.Fatalf("Icon.ToSVG failed: %v", err) + } + + oldPng, err := icon.ToPNG() + if err != nil { + t.Fatalf("Icon.ToPNG failed: %v", err) + } + + newSvg, err := ToSVG(value, size) + if err != nil { + t.Fatalf("ToSVG failed: %v", err) + } + + newPng, err := ToPNG(value, size) + if err != nil { + t.Fatalf("ToPNG failed: %v", err) + } + + // Results should be identical + if oldSvg != newSvg { + t.Error("SVG output differs between old and new APIs") + } + + if !bytes.Equal(oldPng, newPng) { + t.Error("PNG output differs between old and new APIs") + } +} + +func TestTypeConversion(t *testing.T) { + tests := []struct { + name string + input interface{} + expected string + }{ + {"string", "hello", "hello"}, + {"int", 42, "42"}, + {"int64", int64(42), "42"}, + {"float64", 3.14, "3.14"}, + {"bool true", true, "true"}, + {"bool false", false, "false"}, + {"nil", nil, ""}, + {"byte slice", []byte("test"), "test"}, + {"struct", struct{ Name string }{"test"}, "{test}"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := convertToString(tt.input) + if result != tt.expected { + t.Errorf("convertToString(%v) = %s, expected %s", tt.input, result, tt.expected) + } + }) + } +} + +// Helper function for min +func min(a, b int) int { + if a < b { + return a + } + return b +} \ No newline at end of file diff --git a/jdenticon/hash.go b/jdenticon/hash.go new file mode 100644 index 0000000..c893245 --- /dev/null +++ b/jdenticon/hash.go @@ -0,0 +1,226 @@ +package jdenticon + +import ( + "crypto/sha1" + "encoding/hex" + "errors" + "fmt" + "strconv" + "strings" + + "github.com/kevin/go-jdenticon/internal/util" +) + +// ComputeHash computes a SHA-1 hash for any value and returns it as a hexadecimal string. +// This function mimics the JavaScript version's behavior for compatibility. +func ComputeHash(value interface{}) string { + var input string + + // Handle different input types, converting to string like JavaScript version + switch v := value.(type) { + case nil: + input = "" + case string: + input = v + case []byte: + input = string(v) + case int: + input = strconv.Itoa(v) + case int64: + input = strconv.FormatInt(v, 10) + case float64: + input = fmt.Sprintf("%g", v) + default: + // Convert to string using fmt.Sprintf for other types + input = fmt.Sprintf("%v", v) + } + + // Compute SHA-1 hash using Go's crypto/sha1 package + h := sha1.New() + h.Write([]byte(input)) + hash := h.Sum(nil) + + // Convert to hexadecimal string (lowercase to match JavaScript) + return fmt.Sprintf("%x", hash) +} + +// HashValue is a convenience function that wraps ComputeHash for string inputs. +// Kept for backward compatibility. +func HashValue(value string) string { + return ComputeHash(value) +} + +// IsValidHash checks if a string is a valid hash for Jdenticon. +// It must be a hexadecimal string with at least 11 characters. +func IsValidHash(hashCandidate string) bool { + return util.IsValidHash(hashCandidate) +} + +// isValidHash is a private wrapper for backward compatibility with existing tests +func isValidHash(hashCandidate string) bool { + return IsValidHash(hashCandidate) +} + +// parseHex extracts a value from a hex string at a specific position. +// This function is used to deterministically extract shape and color information +// from the hash string, matching the JavaScript implementation. +// When octets is 0 or negative, it reads from startPosition to the end of the string. +func parseHex(hash string, startPosition int, octets int) (int, error) { + return util.ParseHex(hash, startPosition, octets) +} + +// ParseHex provides a public API that matches the JavaScript parseHex function exactly. +// It extracts a hexadecimal value from the hash string at the specified position. +// If octets is not provided or is <= 0, it reads from the position to the end of the string. +// Returns 0 on error to maintain compatibility with the JavaScript implementation. +func ParseHex(hash string, startPosition int, octets ...int) int { + octetCount := 0 + if len(octets) > 0 { + octetCount = octets[0] + } + result, err := parseHex(hash, startPosition, octetCount) + if err != nil { + return 0 // Maintain JavaScript compatibility: return 0 on error + } + return result +} + +// ParseHash converts a hexadecimal hash string into a byte array for further processing. +// It validates the input hash string and handles common prefixes like "0x". +// Returns an error if the hash contains invalid hexadecimal characters. +func ParseHash(hash string) ([]byte, error) { + if hash == "" { + return nil, errors.New("hash string cannot be empty") + } + + // Remove "0x" prefix if present + cleanHash := strings.TrimPrefix(hash, "0x") + cleanHash = strings.TrimPrefix(cleanHash, "0X") + + // Validate hash length (must be even for proper byte conversion) + if len(cleanHash)%2 != 0 { + return nil, errors.New("hash string must have even length") + } + + // Decode hex string to bytes + bytes, err := hex.DecodeString(cleanHash) + if err != nil { + return nil, fmt.Errorf("invalid hexadecimal string: %w", err) + } + + return bytes, nil +} + +// ExtractInt extracts a specific number of bits from a hash byte array and converts them to an integer. +// The index parameter specifies the starting position (negative values count from the end). +// The bits parameter specifies how many bits to extract. +func ExtractInt(hash []byte, index, bits int) int { + if len(hash) == 0 || bits <= 0 { + return 0 + } + + // Handle negative indices (count from end) + if index < 0 { + index = len(hash) + index + } + + // Ensure index is within bounds + if index < 0 || index >= len(hash) { + return 0 + } + + // Calculate how many bytes we need to read + bytesNeeded := (bits + 7) / 8 // Round up to nearest byte + + // Ensure we don't read past the end of the array + if index+bytesNeeded > len(hash) { + bytesNeeded = len(hash) - index + } + + if bytesNeeded <= 0 { + return 0 + } + + // Extract bytes and convert to integer + var result int + for i := 0; i < bytesNeeded; i++ { + if index+i < len(hash) { + result = (result << 8) | int(hash[index+i]) + } + } + + // Mask to only include the requested number of bits + if bits < 64 { + mask := (1 << bits) - 1 + result &= mask + } + + return result +} + +// ExtractFloat extracts a specific number of bits from a hash byte array and converts them to a float64 value between 0 and 1. +// The value is normalized by dividing by the maximum possible value for the given number of bits. +func ExtractFloat(hash []byte, index, bits int) float64 { + if bits <= 0 { + return 0.0 + } + + // Extract integer value + intValue := ExtractInt(hash, index, bits) + + // Calculate maximum possible value for the given number of bits + maxValue := (1 << bits) - 1 + if maxValue == 0 { + return 0.0 + } + + // Normalize to [0,1] range + return float64(intValue) / float64(maxValue) +} + +// ExtractHue extracts the hue value from a hash string using the same algorithm as the JavaScript version. +// This is a convenience function that extracts the last 7 characters and normalizes to [0,1] range. +// Returns 0.0 on error to maintain compatibility with the JavaScript implementation. +func ExtractHue(hash string) float64 { + hueValue, err := parseHex(hash, -7, 0) // Read from -7 to end + if err != nil { + return 0.0 // Maintain JavaScript compatibility: return 0.0 on error + } + return float64(hueValue) / 0xfffffff +} + +// ExtractShapeIndex extracts a shape index from the hash at the specified position. +// This is a convenience function that matches the JavaScript shape selection logic. +// Returns 0 on error to maintain compatibility with the JavaScript implementation. +func ExtractShapeIndex(hash string, position int) int { + result, err := parseHex(hash, position, 1) + if err != nil { + return 0 // Maintain JavaScript compatibility: return 0 on error + } + return result +} + +// ExtractRotation extracts a rotation value from the hash at the specified position. +// This is a convenience function that matches the JavaScript rotation logic. +// Returns 0 on error to maintain compatibility with the JavaScript implementation. +func ExtractRotation(hash string, position int) int { + result, err := parseHex(hash, position, 1) + if err != nil { + return 0 // Maintain JavaScript compatibility: return 0 on error + } + return result +} + +// ExtractColorIndex extracts a color index from the hash at the specified position. +// This is a convenience function that matches the JavaScript color selection logic. +// Returns 0 on error to maintain compatibility with the JavaScript implementation. +func ExtractColorIndex(hash string, position int, availableColors int) int { + value, err := parseHex(hash, position, 1) + if err != nil { + return 0 // Maintain JavaScript compatibility: return 0 on error + } + if availableColors > 0 { + return value % availableColors + } + return value +} \ No newline at end of file diff --git a/jdenticon/hash_test.go b/jdenticon/hash_test.go new file mode 100644 index 0000000..1c33635 --- /dev/null +++ b/jdenticon/hash_test.go @@ -0,0 +1,481 @@ +package jdenticon + +import ( + "strings" + "testing" +) + +func TestComputeHash(t *testing.T) { + tests := []struct { + name string + input interface{} + expected string // Known SHA-1 hash values + }{ + { + name: "empty string", + input: "", + expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709", + }, + { + name: "simple string", + input: "hello", + expected: "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", + }, + { + name: "email address", + input: "user@example.com", + expected: "63a710569261a24b3766275b7000ce8d7b32e2f7", + }, + { + name: "nil input", + input: nil, + expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709", // Same as empty string + }, + { + name: "integer input", + input: 123, + expected: "40bd001563085fc35165329ea1ff5c5ecbdbbeef", // SHA-1 of "123" + }, + { + name: "byte slice input", + input: []byte("test"), + expected: "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ComputeHash(tt.input) + if result != tt.expected { + t.Errorf("ComputeHash(%v) = %s, want %s", tt.input, result, tt.expected) + } + }) + } +} + +func TestHashValue(t *testing.T) { + // Test that HashValue produces the same result as ComputeHash for strings + testStrings := []string{"", "hello", "user@example.com", "test123"} + + for _, str := range testStrings { + hashValue := HashValue(str) + computeHash := ComputeHash(str) + + if hashValue != computeHash { + t.Errorf("HashValue(%s) = %s, but ComputeHash(%s) = %s", str, hashValue, str, computeHash) + } + } +} + +func TestIsValidHash(t *testing.T) { + tests := []struct { + name string + input string + expected bool + }{ + { + name: "valid long hash", + input: "da39a3ee5e6b4b0d3255bfef95601890afd80709", + expected: true, + }, + { + name: "valid minimum length", + input: "da39a3ee5e6", + expected: true, + }, + { + name: "too short", + input: "da39a3ee5e", + expected: false, + }, + { + name: "invalid character", + input: "da39a3ee5e6g4b0d3255bfef95601890afd80709", + expected: false, + }, + { + name: "uppercase valid", + input: "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", + expected: true, + }, + { + name: "empty string", + input: "", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := isValidHash(tt.input) + if result != tt.expected { + t.Errorf("isValidHash(%s) = %v, want %v", tt.input, result, tt.expected) + } + }) + } +} + +func TestParseHex(t *testing.T) { + hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" + + tests := []struct { + name string + startPosition int + octets int + expected int + }{ + { + name: "first character", + startPosition: 0, + octets: 1, + expected: 0xd, // 'd' in hex + }, + { + name: "two characters", + startPosition: 0, + octets: 2, + expected: 0xda, + }, + { + name: "middle position", + startPosition: 10, + octets: 1, + expected: 0x6, // '6' at position 10 + }, + { + name: "negative index", + startPosition: -1, + octets: 1, + expected: 0x9, // last character '9' + }, + { + name: "negative index multiple chars", + startPosition: -2, + octets: 2, + expected: 0x09, // last two characters "09" + }, + { + name: "out of bounds", + startPosition: 100, + octets: 1, + expected: 0, + }, + { + name: "zero octets reads to end", + startPosition: -7, + octets: 0, + expected: 0xfd80709, // Last 7 characters "fd80709" + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := parseHex(hash, tt.startPosition, tt.octets) + if err != nil { + // For out of bounds case, we expect an error but return 0 + if tt.expected == 0 && tt.name == "out of bounds" { + // This is expected + return + } + t.Errorf("parseHex(%s, %d, %d) unexpected error: %v", hash, tt.startPosition, tt.octets, err) + return + } + if result != tt.expected { + t.Errorf("parseHex(%s, %d, %d) = %d, want %d", hash, tt.startPosition, tt.octets, result, tt.expected) + } + }) + } +} + +func TestParseHexErrors(t *testing.T) { + tests := []struct { + name string + hash string + startPosition int + octets int + expectError bool + errorContains string + }{ + { + name: "invalid hex character", + hash: "da39g3ee5e6b4b0d3255bfef95601890afd80709", + startPosition: 4, + octets: 1, + expectError: true, + errorContains: "failed to parse hex", + }, + { + name: "out of bounds positive", + hash: "da39a3ee", + startPosition: 10, + octets: 1, + expectError: true, + errorContains: "out of bounds", + }, + { + name: "out of bounds negative", + hash: "da39a3ee", + startPosition: -20, + octets: 1, + expectError: true, + errorContains: "out of bounds", + }, + { + name: "valid hex", + hash: "da39a3ee5e6b4b0d3255bfef95601890afd80709", + startPosition: 0, + octets: 2, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := parseHex(tt.hash, tt.startPosition, tt.octets) + + if tt.expectError { + if err == nil { + t.Errorf("parseHex(%s, %d, %d) expected error, got nil", tt.hash, tt.startPosition, tt.octets) + return + } + if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) { + t.Errorf("parseHex(%s, %d, %d) error = %v, want error containing %s", tt.hash, tt.startPosition, tt.octets, err, tt.errorContains) + } + } else { + if err != nil { + t.Errorf("parseHex(%s, %d, %d) unexpected error: %v", tt.hash, tt.startPosition, tt.octets, err) + } + // For valid case, just ensure no error and result >= 0 + if result < 0 { + t.Errorf("parseHex(%s, %d, %d) = %d, want >= 0", tt.hash, tt.startPosition, tt.octets, result) + } + } + }) + } +} + +func TestParseHash(t *testing.T) { + tests := []struct { + name string + input string + expectError bool + expected []byte + }{ + { + name: "valid hex string", + input: "da39a3ee", + expectError: false, + expected: []byte{0xda, 0x39, 0xa3, 0xee}, + }, + { + name: "with 0x prefix", + input: "0xda39a3ee", + expectError: false, + expected: []byte{0xda, 0x39, 0xa3, 0xee}, + }, + { + name: "with 0X prefix", + input: "0Xda39a3ee", + expectError: false, + expected: []byte{0xda, 0x39, 0xa3, 0xee}, + }, + { + name: "uppercase hex", + input: "DA39A3EE", + expectError: false, + expected: []byte{0xda, 0x39, 0xa3, 0xee}, + }, + { + name: "empty string", + input: "", + expectError: true, + expected: nil, + }, + { + name: "odd length", + input: "da39a3e", + expectError: true, + expected: nil, + }, + { + name: "invalid character", + input: "da39g3ee", + expectError: true, + expected: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := ParseHash(tt.input) + + if tt.expectError { + if err == nil { + t.Errorf("ParseHash(%s) expected error, got nil", tt.input) + } + } else { + if err != nil { + t.Errorf("ParseHash(%s) unexpected error: %v", tt.input, err) + } + if len(result) != len(tt.expected) { + t.Errorf("ParseHash(%s) length = %d, want %d", tt.input, len(result), len(tt.expected)) + } + for i, b := range result { + if i < len(tt.expected) && b != tt.expected[i] { + t.Errorf("ParseHash(%s)[%d] = %x, want %x", tt.input, i, b, tt.expected[i]) + } + } + } + }) + } +} + +func TestExtractHue(t *testing.T) { + // Test with a known hash to ensure consistent behavior with JavaScript + hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" + hue := ExtractHue(hash) + + // Verify it's in [0,1] range + if hue < 0.0 || hue > 1.0 { + t.Errorf("ExtractHue(%s) = %f, want value in [0,1] range", hash, hue) + } + + // Verify it matches manual calculation + expectedValue, err := parseHex(hash, -7, 0) + if err != nil { + t.Fatalf("parseHex failed: %v", err) + } + expectedHue := float64(expectedValue) / 0xfffffff + if hue != expectedHue { + t.Errorf("ExtractHue(%s) = %f, want %f", hash, hue, expectedHue) + } + + // Test error cases - should return 0.0 for JavaScript compatibility + t.Run("invalid hash", func(t *testing.T) { + invalidHash := "invalid-hash-with-non-hex-chars" + hue := ExtractHue(invalidHash) + if hue != 0.0 { + t.Errorf("ExtractHue with invalid hash should return 0.0, got %f", hue) + } + }) + + t.Run("short hash", func(t *testing.T) { + shortHash := "short" + hue := ExtractHue(shortHash) + if hue != 0.0 { + t.Errorf("ExtractHue with short hash should return 0.0, got %f", hue) + } + }) +} + +func TestExtractShapeIndex(t *testing.T) { + hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" + + tests := []struct { + position int + expected int + }{ + {1, 0xa}, // 'a' at position 1 + {2, 0x3}, // '3' at position 2 + {4, 0xa}, // 'a' at position 4 + } + + for _, tt := range tests { + result := ExtractShapeIndex(hash, tt.position) + if result != tt.expected { + t.Errorf("ExtractShapeIndex(%s, %d) = %d, want %d", hash, tt.position, result, tt.expected) + } + } + + // Test error cases - should return 0 for JavaScript compatibility + t.Run("invalid hash", func(t *testing.T) { + invalidHash := "invalid-hash-with-non-hex-chars" + result := ExtractShapeIndex(invalidHash, 0) + if result != 0 { + t.Errorf("ExtractShapeIndex with invalid hash should return 0, got %d", result) + } + }) + + t.Run("out of bounds position", func(t *testing.T) { + result := ExtractShapeIndex(hash, 100) + if result != 0 { + t.Errorf("ExtractShapeIndex with out of bounds position should return 0, got %d", result) + } + }) +} + +func TestExtractColorIndex(t *testing.T) { + hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" + + // Test modulo behavior + availableColors := 5 + position := 8 + rawValue, err := parseHex(hash, position, 1) + if err != nil { + t.Fatalf("parseHex failed: %v", err) + } + expected := rawValue % availableColors + + result := ExtractColorIndex(hash, position, availableColors) + if result != expected { + t.Errorf("ExtractColorIndex(%s, %d, %d) = %d, want %d", hash, position, availableColors, result, expected) + } + + // Test with zero availableColors + result = ExtractColorIndex(hash, position, 0) + if result != rawValue { + t.Errorf("ExtractColorIndex(%s, %d, 0) = %d, want %d", hash, position, result, rawValue) + } + + // Test error cases - should return 0 for JavaScript compatibility + t.Run("invalid hash", func(t *testing.T) { + invalidHash := "invalid-hash-with-non-hex-chars" + result := ExtractColorIndex(invalidHash, 0, 5) + if result != 0 { + t.Errorf("ExtractColorIndex with invalid hash should return 0, got %d", result) + } + }) + + t.Run("out of bounds position", func(t *testing.T) { + result := ExtractColorIndex(hash, 100, 5) + if result != 0 { + t.Errorf("ExtractColorIndex with out of bounds position should return 0, got %d", result) + } + }) +} + +func TestExtractRotation(t *testing.T) { + hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" + + tests := []struct { + position int + expected int + }{ + {0, 0xd}, // 'd' at position 0 + {1, 0xa}, // 'a' at position 1 + {5, 0x3}, // '3' at position 5 + } + + for _, tt := range tests { + result := ExtractRotation(hash, tt.position) + if result != tt.expected { + t.Errorf("ExtractRotation(%s, %d) = %d, want %d", hash, tt.position, result, tt.expected) + } + } + + // Test error cases - should return 0 for JavaScript compatibility + t.Run("invalid hash", func(t *testing.T) { + invalidHash := "invalid-hash-with-non-hex-chars" + result := ExtractRotation(invalidHash, 0) + if result != 0 { + t.Errorf("ExtractRotation with invalid hash should return 0, got %d", result) + } + }) + + t.Run("out of bounds position", func(t *testing.T) { + result := ExtractRotation(hash, 100) + if result != 0 { + t.Errorf("ExtractRotation with out of bounds position should return 0, got %d", result) + } + }) +} \ No newline at end of file diff --git a/jdenticon/main b/jdenticon/main new file mode 100755 index 0000000..15d9d53 Binary files /dev/null and b/jdenticon/main differ diff --git a/jdenticon/reference_test.go b/jdenticon/reference_test.go new file mode 100644 index 0000000..915e2b5 --- /dev/null +++ b/jdenticon/reference_test.go @@ -0,0 +1,58 @@ +package jdenticon + +import ( + "os" + "path/filepath" + "testing" +) + +func TestJavaScriptReferenceCompatibility(t *testing.T) { + testCases := []struct { + input string + size int + }{ + {"test-hash", 64}, + {"example1@gmail.com", 64}, + {"example2@yahoo.com", 64}, + } + + for _, tc := range testCases { + t.Run(tc.input+"_"+string(rune(tc.size)), func(t *testing.T) { + // Generate Go SVG + goSvg, err := ToSVG(tc.input, tc.size) + if err != nil { + t.Fatalf("Failed to generate Go SVG: %v", err) + } + + // Read reference JavaScript SVG + var refFilename string + if tc.input == "test-hash" { + refFilename = "test-hash_64.svg" + } else if tc.input == "example1@gmail.com" { + refFilename = "example1_at_gmail_com_64.svg" + } else if tc.input == "example2@yahoo.com" { + refFilename = "example2_at_yahoo_com_64.svg" + } + + refPath := filepath.Join("../reference", refFilename) + refData, err := os.ReadFile(refPath) + if err != nil { + t.Skipf("Reference file not found: %s", refPath) + return + } + refSvg := string(refData) + + // Compare + if goSvg != refSvg { + t.Errorf("SVG output differs from JavaScript reference") + t.Logf("Go output:\n%s", goSvg) + t.Logf("JS reference:\n%s", refSvg) + + // Save Go output for manual inspection + goPath := filepath.Join("../go-output", refFilename) + os.WriteFile(goPath, []byte(goSvg), 0644) + t.Logf("Go output saved to: %s", goPath) + } + }) + } +} \ No newline at end of file diff --git a/prd.txt b/prd.txt new file mode 100644 index 0000000..5c61e64 --- /dev/null +++ b/prd.txt @@ -0,0 +1,269 @@ +# Product Requirements Document: Go Jdenticon Code Quality Improvements + +## Executive Summary + +This PRD outlines a comprehensive code quality improvement initiative for the Go Jdenticon library. The goal is to enhance reliability, performance, and maintainability while preserving the critical JavaScript reference compatibility that makes this library unique. + +## Background + +Go Jdenticon is a Go port of the JavaScript Jdenticon library that generates deterministic identicons. The library has achieved a significant milestone: **byte-for-byte identical SVG output** with the JavaScript reference implementation. While functionally acceptable, a comprehensive code review has identified opportunities to improve code quality without compromising this critical compatibility. + +## Problem Statement + +The current codebase, while functional, has several quality issues: + +1. **Silent Error Handling**: Critical parsing functions fail silently, potentially masking bugs +2. **Performance Inefficiencies**: Unnecessary memory allocations during icon generation +3. **Maintainability Issues**: Extensive use of magic numbers makes code hard to understand +4. **Non-idiomatic Go**: Some patterns don't follow Go best practices +5. **Data Structure Concerns**: Confusing abstractions that make the code harder to reason about + +## Success Criteria + +### Primary Goals +- ✅ Maintain 100% JavaScript reference compatibility (all reference tests pass) +- ✅ Eliminate silent error conditions that could mask bugs +- ✅ Improve code readability and maintainability +- ✅ Optimize performance without breaking compatibility +- ✅ Apply Go idioms and best practices consistently + +### Key Performance Indicators (KPIs) +- Zero breaking changes to public API +- 100% pass rate on JavaScript reference compatibility tests +- Measurable reduction in memory allocations during icon generation +- Improved code review scores and maintainability metrics +- No performance regressions in icon generation speed + +## Requirements + +### Functional Requirements + +#### FR1: Error Handling Improvements +- **FR1.1**: Color parsing functions must return explicit errors for invalid input +- **FR1.2**: Hash parsing functions must propagate parsing errors to callers +- **FR1.3**: All silent failures must be converted to explicit error returns +- **FR1.4**: Error messages must be descriptive and actionable + +#### FR2: Performance Optimizations +- **FR2.1**: Eliminate repeated slice allocations in color correction +- **FR2.2**: Optimize string building operations for cache keys +- **FR2.3**: Improve polygon rendering efficiency +- **FR2.4**: Maintain or improve current generation speed benchmarks + +#### FR3: Code Readability Enhancements +- **FR3.1**: Replace all magic numbers with named constants +- **FR3.2**: Add clear documentation for hash position mappings +- **FR3.3**: Simplify complex nested logic patterns +- **FR3.4**: Improve variable and function naming clarity + +#### FR4: Data Structure Improvements +- **FR4.1**: Fix circle geometry storage in Shape struct +- **FR4.2**: Clean up renderer interface inconsistencies +- **FR4.3**: Improve type safety in shape handling + +### Non-Functional Requirements + +#### NFR1: Compatibility +- **NFR1.1**: Must maintain byte-for-byte identical SVG output with JavaScript reference +- **NFR1.2**: All existing reference compatibility tests must pass +- **NFR1.3**: No breaking changes to public API +- **NFR1.4**: Backward compatibility with existing configuration options + +#### NFR2: Performance +- **NFR2.1**: Icon generation time must not increase by more than 5% +- **NFR2.2**: Memory allocations should decrease by at least 10% +- **NFR2.3**: No regression in concurrent generation performance + +#### NFR3: Maintainability +- **NFR3.1**: Code must follow standard Go conventions and idioms +- **NFR3.2**: All functions should have clear, single responsibilities +- **NFR3.3**: Complex algorithms must be well-documented +- **NFR3.4**: Test coverage must remain at current levels or improve + +## Technical Specifications + +### High-Priority Issues (Critical & High) + +#### Issue #1: Silent Color Parsing Failures +**Location**: `internal/engine/color.go:274` +**Current**: `hexToByte` returns 0 for invalid input +**Required**: Return error for invalid hex strings +**Impact**: Prevents incorrect color output without warning + +#### Issue #2: Hash Parsing Error Propagation +**Location**: `internal/engine/generator.go:296` +**Current**: `parseHex` returns 0 on failure +**Required**: Propagate parsing errors to prevent malformed icons +**Impact**: Ensures generation fails fast on invalid data + +#### Issue #3: Performance - Color Correction Allocations +**Location**: `internal/engine/color.go:183` +**Current**: `correctors` slice allocated on every call +**Required**: Move to package-level variable +**Impact**: Reduces GC pressure during generation + +#### Issue #4: Magic Numbers in Generator +**Location**: `internal/engine/generator.go` (multiple locations) +**Current**: Literal integers for hash positions and logic +**Required**: Named constants with clear documentation +**Impact**: Dramatically improves code readability + +#### Issue #5: Circle Storage Data Structure +**Location**: `internal/engine/generator.go:260` +**Current**: Circle size stored in Point struct X/Y fields +**Required**: Dedicated circle geometry fields in Shape struct +**Impact**: Eliminates confusing abstraction leakage + +### Medium-Priority Improvements + +#### Issue #6: String Building Optimization +**Location**: `internal/engine/generator.go:322` +**Current**: `fmt.Sprintf` for cache key generation +**Required**: `strings.Builder` for efficiency +**Impact**: Reduces allocation overhead + +#### Issue #7: Complex Logic Simplification +**Location**: `internal/engine/generator.go:187` +**Current**: Nested loops in `isDuplicateColor` +**Required**: Helper functions for clarity +**Impact**: Improves code readability + +#### Issue #8: Go Idiom Application +**Location**: `internal/engine/color.go:123` +**Current**: Anonymous function emulating ternary operator +**Required**: Standard Go `if` statement +**Impact**: More idiomatic Go code + +## Implementation Plan + +### Phase 1: Critical Fixes (Week 1) +**Objective**: Eliminate silent failures and improve reliability + +**Tasks:** +1. Implement error returns in `hexToByte` and `ParseHexColor` +2. Add error handling to `parseHex` function +3. Update all callers to handle new error returns +4. Verify reference compatibility tests still pass + +**Acceptance Criteria:** +- All parsing functions return explicit errors +- No silent failures in color or hash parsing +- 100% reference test compatibility maintained + +### Phase 2: Performance & Structure (Week 2) +**Objective**: Optimize performance and fix data structures + +**Tasks:** +1. Move `correctors` slice to package level +2. Replace magic numbers with named constants +3. Fix circle geometry storage in Shape struct +4. Benchmark performance improvements + +**Acceptance Criteria:** +- Measurable reduction in allocations +- All magic numbers replaced with constants +- Clean circle data structure +- No performance regressions + +### Phase 3: Code Quality Polish (Week 3) +**Objective**: Apply Go idioms and improve readability + +**Tasks:** +1. Optimize string building operations +2. Simplify complex logic patterns +3. Apply consistent Go idioms +4. Complete final testing and documentation + +**Acceptance Criteria:** +- More efficient string operations +- Simplified, readable code patterns +- Idiomatic Go throughout codebase +- Comprehensive test validation + +## Risk Assessment + +### High Risk: Compatibility Breaking +**Risk**: Changes could break JavaScript reference compatibility +**Mitigation**: Run reference tests after every change, incremental development + +### Medium Risk: Performance Regression +**Risk**: Optimizations could inadvertently slow down generation +**Mitigation**: Benchmark before/after, maintain performance test suite + +### Low Risk: API Changes +**Risk**: Internal changes could affect public API +**Mitigation**: Careful interface design, comprehensive testing + +## Testing Strategy + +### Compatibility Testing +- Run `go test ./jdenticon -run TestJavaScriptReferenceCompatibility` after every change +- Validate byte-for-byte SVG output matches JavaScript reference +- Test all supported input types and edge cases + +### Performance Testing +- Benchmark icon generation speed before and after changes +- Measure memory allocation patterns +- Test concurrent generation performance + +### Regression Testing +- Run full test suite after each phase +- Validate all existing functionality works as expected +- Test edge cases and error conditions + +## Dependencies + +### Internal Dependencies +- JavaScript reference implementation in `jdenticon-js/` +- Reference test cases and expected outputs +- Existing test suite and benchmarks + +### External Dependencies +- Go standard library (no changes required) +- Development tools (testing, benchmarking) + +## Timeline + +### Week 1: Critical Fixes +- Days 1-2: Implement error handling in color parsing +- Days 3-4: Add error handling to hash parsing +- Day 5: Testing and validation + +### Week 2: Performance & Structure +- Days 1-2: Performance optimizations +- Days 3-4: Data structure improvements +- Day 5: Benchmarking and validation + +### Week 3: Quality Polish +- Days 1-3: Code quality improvements +- Days 4-5: Final testing and documentation + +## Success Metrics + +### Quantitative Metrics +- 100% pass rate on reference compatibility tests +- 10%+ reduction in memory allocations +- 0% performance regression in generation speed +- 0 breaking changes to public API + +### Qualitative Metrics +- Improved code readability scores +- Reduced cognitive complexity in key functions +- Better error messaging and debugging experience +- More maintainable and idiomatic Go codebase + +## Post-Implementation + +### Monitoring +- Continuous integration with reference compatibility tests +- Performance monitoring in production usage +- Code quality metrics tracking + +### Maintenance +- Documentation updates reflecting improvements +- Developer guide updates for new patterns +- Ongoing code review standards enforcement + +## Conclusion + +This code quality improvement initiative will transform the Go Jdenticon library into a more reliable, performant, and maintainable codebase while preserving its unique JavaScript compatibility advantage. The phased approach ensures safety while delivering meaningful improvements to the developer experience and library quality. \ No newline at end of file diff --git a/quick_test.go b/quick_test.go new file mode 100644 index 0000000..10ae53b --- /dev/null +++ b/quick_test.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "os" + + "github.com/kevin/go-jdenticon/jdenticon" +) + +func main() { + // Quick test - generate an avatar for your GitHub username or email + + // Change this to your email or username! + userInput := "your-email@example.com" + + fmt.Printf("🎨 Generating avatar for: %s\n", userInput) + + // Generate SVG (good for web use) + svg, err := jdenticon.ToSVG(userInput, 100) + if err != nil { + panic(err) + } + + err = os.WriteFile("my_avatar.svg", []byte(svg), 0644) + if err != nil { + panic(err) + } + + // Generate PNG (good for Discord, Slack, etc.) + png, err := jdenticon.ToPNG(userInput, 100) + if err != nil { + panic(err) + } + + err = os.WriteFile("my_avatar.png", png, 0644) + if err != nil { + panic(err) + } + + fmt.Println("✅ Generated my_avatar.svg and my_avatar.png") + fmt.Printf("📊 SVG: %d chars, PNG: %d bytes\n", len(svg), len(png)) + + // Show that the same input always produces the same avatar + svg2, _ := jdenticon.ToSVG(userInput, 100) + fmt.Printf("🔒 Deterministic: %v\n", svg == svg2) + + // Show the hash that determines the avatar + hash := jdenticon.ToHash(userInput) + fmt.Printf("🔢 Hash: %s\n", hash) +} \ No newline at end of file diff --git a/reference/example1_at_gmail_com_128.png b/reference/example1_at_gmail_com_128.png new file mode 100644 index 0000000..49633e9 Binary files /dev/null and b/reference/example1_at_gmail_com_128.png differ diff --git a/reference/example1_at_gmail_com_128.svg b/reference/example1_at_gmail_com_128.svg new file mode 100644 index 0000000..333938e --- /dev/null +++ b/reference/example1_at_gmail_com_128.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/reference/example1_at_gmail_com_64.png b/reference/example1_at_gmail_com_64.png new file mode 100644 index 0000000..837ccdf Binary files /dev/null and b/reference/example1_at_gmail_com_64.png differ diff --git a/reference/example1_at_gmail_com_64.svg b/reference/example1_at_gmail_com_64.svg new file mode 100644 index 0000000..b5d7a2e --- /dev/null +++ b/reference/example1_at_gmail_com_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/reference/example2_at_yahoo_com_128.png b/reference/example2_at_yahoo_com_128.png new file mode 100644 index 0000000..088cafb Binary files /dev/null and b/reference/example2_at_yahoo_com_128.png differ diff --git a/reference/example2_at_yahoo_com_128.svg b/reference/example2_at_yahoo_com_128.svg new file mode 100644 index 0000000..9250d51 --- /dev/null +++ b/reference/example2_at_yahoo_com_128.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/reference/example2_at_yahoo_com_64.png b/reference/example2_at_yahoo_com_64.png new file mode 100644 index 0000000..d108715 Binary files /dev/null and b/reference/example2_at_yahoo_com_64.png differ diff --git a/reference/example2_at_yahoo_com_64.svg b/reference/example2_at_yahoo_com_64.svg new file mode 100644 index 0000000..165a737 --- /dev/null +++ b/reference/example2_at_yahoo_com_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/reference/test-hash_64.svg b/reference/test-hash_64.svg new file mode 100644 index 0000000..5c246ca --- /dev/null +++ b/reference/test-hash_64.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/user_alice.png b/user_alice.png new file mode 100644 index 0000000..fd46f94 Binary files /dev/null and b/user_alice.png differ diff --git a/user_bob@c.png b/user_bob@c.png new file mode 100644 index 0000000..3925a63 Binary files /dev/null and b/user_bob@c.png differ diff --git a/user_charl.png b/user_charl.png new file mode 100644 index 0000000..bf144b8 Binary files /dev/null and b/user_charl.png differ diff --git a/user_diana.png b/user_diana.png new file mode 100644 index 0000000..4b5e5e8 Binary files /dev/null and b/user_diana.png differ