pax_global_header00006660000000000000000000000064135744116330014521gustar00rootroot0000000000000052 comment=104a0ae91ff816fe6dc8914c1fb90fe32321ae9f gabs-2.3.0/000077500000000000000000000000001357441163300124375ustar00rootroot00000000000000gabs-2.3.0/.github/000077500000000000000000000000001357441163300137775ustar00rootroot00000000000000gabs-2.3.0/.github/FUNDING.yml000066400000000000000000000000771357441163300156200ustar00rootroot00000000000000# These are supported funding model platforms github: Jeffail gabs-2.3.0/LICENSE000066400000000000000000000020401357441163300134400ustar00rootroot00000000000000Copyright (c) 2019 Ashley Jeffs 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. gabs-2.3.0/README.md000066400000000000000000000137121357441163300137220ustar00rootroot00000000000000![Gabs](gabs_logo.png "Gabs") [![godoc for Jeffail/gabs][godoc-badge]][godoc-url] Gabs is a small utility for dealing with dynamic or unknown JSON structures in Go. It's pretty much just a helpful wrapper for navigating hierarchies of `map[string]interface{}` objects provided by the `encoding/json` package. It does nothing spectacular apart from being fabulous. If you're migrating from version 1 check out [`migration.md`][migration-doc] for guidance. ## Use ### Import Using modules: ```go import ( "github.com/Jeffail/gabs/v2" ) ``` Without modules: ```go import ( "github.com/Jeffail/gabs" ) ``` ### Parsing and searching JSON ```go jsonParsed, err := gabs.ParseJSON([]byte(`{ "outter":{ "inner":{ "value1":10, "value2":22 }, "alsoInner":{ "value1":20, "array1":[ 30, 40 ] } } }`)) if err != nil { panic(err) } var value float64 var ok bool value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64) // value == 10.0, ok == true value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64) // value == 10.0, ok == true value, ok = jsonParsed.Search("outter", "alsoInner", "array1", "1").Data().(float64) // value == 40.0, ok == true gObj, err := jsonParsed.JSONPointer("/outter/alsoInner/array1/1") if err != nil { panic(err) } value, ok = gObj.Data().(float64) // value == 40.0, ok == true value, ok = jsonParsed.Path("does.not.exist").Data().(float64) // value == 0.0, ok == false exists := jsonParsed.Exists("outter", "inner", "value1") // exists == true exists = jsonParsed.ExistsP("does.not.exist") // exists == false ``` ### Iterating objects ```go jsonParsed, err := gabs.ParseJSON([]byte(`{"object":{"first":1,"second":2,"third":3}}`)) if err != nil { panic(err) } // S is shorthand for Search for key, child := range jsonParsed.S("object").ChildrenMap() { fmt.Printf("key: %v, value: %v\n", key, child.Data().(string)) } ``` ### Iterating arrays ```go jsonParsed, err := gabs.ParseJSON([]byte(`{"array":["first","second","third"]}`)) if err != nil { panic(err) } for _, child := range jsonParsed.S("array").Children() { fmt.Println(child.Data().(string)) } ``` Will print: ``` first second third ``` Children() will return all children of an array in order. This also works on objects, however, the children will be returned in a random order. ### Searching through arrays If your structure contains arrays you must target an index in your search. ```go jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[{"value":1},{"value":2},{"value":3}]}`)) if err != nil { panic(err) } fmt.Println(jsonParsed.Path("array.1.value").String()) ``` Will print `2`. ### Generating JSON ```go jsonObj := gabs.New() // or gabs.Wrap(jsonObject) to work on an existing map[string]interface{} jsonObj.Set(10, "outter", "inner", "value") jsonObj.SetP(20, "outter.inner.value2") jsonObj.Set(30, "outter", "inner2", "value3") fmt.Println(jsonObj.String()) ``` Will print: ``` {"outter":{"inner":{"value":10,"value2":20},"inner2":{"value3":30}}} ``` To pretty-print: ```go fmt.Println(jsonObj.StringIndent("", " ")) ``` Will print: ``` { "outter": { "inner": { "value": 10, "value2": 20 }, "inner2": { "value3": 30 } } } ``` ### Generating Arrays ```go jsonObj := gabs.New() jsonObj.Array("foo", "array") // Or .ArrayP("foo.array") jsonObj.ArrayAppend(10, "foo", "array") jsonObj.ArrayAppend(20, "foo", "array") jsonObj.ArrayAppend(30, "foo", "array") fmt.Println(jsonObj.String()) ``` Will print: ``` {"foo":{"array":[10,20,30]}} ``` Working with arrays by index: ```go jsonObj := gabs.New() // Create an array with the length of 3 jsonObj.ArrayOfSize(3, "foo") jsonObj.S("foo").SetIndex("test1", 0) jsonObj.S("foo").SetIndex("test2", 1) // Create an embedded array with the length of 3 jsonObj.S("foo").ArrayOfSizeI(3, 2) jsonObj.S("foo").Index(2).SetIndex(1, 0) jsonObj.S("foo").Index(2).SetIndex(2, 1) jsonObj.S("foo").Index(2).SetIndex(3, 2) fmt.Println(jsonObj.String()) ``` Will print: ``` {"foo":["test1","test2",[1,2,3]]} ``` ### Converting back to JSON This is the easiest part: ```go jsonParsedObj, _ := gabs.ParseJSON([]byte(`{ "outter":{ "values":{ "first":10, "second":11 } }, "outter2":"hello world" }`)) jsonOutput := jsonParsedObj.String() // Becomes `{"outter":{"values":{"first":10,"second":11}},"outter2":"hello world"}` ``` And to serialize a specific segment is as simple as: ```go jsonParsedObj := gabs.ParseJSON([]byte(`{ "outter":{ "values":{ "first":10, "second":11 } }, "outter2":"hello world" }`)) jsonOutput := jsonParsedObj.Search("outter").String() // Becomes `{"values":{"first":10,"second":11}}` ``` ### Merge two containers You can merge a JSON structure into an existing one, where collisions will be converted into a JSON array. ```go jsonParsed1, _ := ParseJSON([]byte(`{"outter":{"value1":"one"}}`)) jsonParsed2, _ := ParseJSON([]byte(`{"outter":{"inner":{"value3":"three"}},"outter2":{"value2":"two"}}`)) jsonParsed1.Merge(jsonParsed2) // Becomes `{"outter":{"inner":{"value3":"three"},"value1":"one"},"outter2":{"value2":"two"}}` ``` Arrays are merged: ```go jsonParsed1, _ := ParseJSON([]byte(`{"array":["one"]}`)) jsonParsed2, _ := ParseJSON([]byte(`{"array":["two"]}`)) jsonParsed1.Merge(jsonParsed2) // Becomes `{"array":["one", "two"]}` ``` ### Parsing Numbers Gabs uses the `json` package under the bonnet, which by default will parse all number values into `float64`. If you need to parse `Int` values then you should use a [`json.Decoder`](https://golang.org/pkg/encoding/json/#Decoder): ```go sample := []byte(`{"test":{"int":10,"float":6.66}}`) dec := json.NewDecoder(bytes.NewReader(sample)) dec.UseNumber() val, err := gabs.ParseJSONDecoder(dec) if err != nil { t.Errorf("Failed to parse: %v", err) return } intValue, err := val.Path("test.int").Data().(json.Number).Int64() ``` [godoc-badge]: https://godoc.org/github.com/Jeffail/gabs?status.svg [godoc-url]: https://godoc.org/github.com/Jeffail/gabs [migration-doc]: ./migration.md gabs-2.3.0/gabs.go000066400000000000000000000640641357441163300137140ustar00rootroot00000000000000// Copyright (c) 2019 Ashley Jeffs // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Package gabs implements a wrapper around creating and parsing unknown or // dynamic map structures resulting from JSON parsing. package gabs import ( "bytes" "encoding/json" "errors" "fmt" "io" "io/ioutil" "strconv" "strings" ) //------------------------------------------------------------------------------ var ( // ErrOutOfBounds indicates an index was out of bounds. ErrOutOfBounds = errors.New("out of bounds") // ErrNotObjOrArray is returned when a target is not an object or array type // but needs to be for the intended operation. ErrNotObjOrArray = errors.New("not an object or array") // ErrNotObj is returned when a target is not an object but needs to be for // the intended operation. ErrNotObj = errors.New("not an object") // ErrInvalidQuery is returned when a seach query was not valid. ErrInvalidQuery = errors.New("invalid search query") // ErrNotArray is returned when a target is not an array but needs to be for // the intended operation. ErrNotArray = errors.New("not an array") // ErrPathCollision is returned when creating a path failed because an // element collided with an existing value. ErrPathCollision = errors.New("encountered value collision whilst building path") // ErrInvalidInputObj is returned when the input value was not a // map[string]interface{}. ErrInvalidInputObj = errors.New("invalid input object") // ErrInvalidInputText is returned when the input data could not be parsed. ErrInvalidInputText = errors.New("input text could not be parsed") // ErrNotFound is returned when a query leaf is not found. ErrNotFound = errors.New("field not found") // ErrInvalidPath is returned when the filepath was not valid. ErrInvalidPath = errors.New("invalid file path") // ErrInvalidBuffer is returned when the input buffer contained an invalid // JSON string. ErrInvalidBuffer = errors.New("input buffer contained invalid JSON") ) //------------------------------------------------------------------------------ // JSONPointerToSlice parses a JSON pointer path // (https://tools.ietf.org/html/rfc6901) and returns the path segments as a // slice. // // Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in // gabs paths, '~' needs to be encoded as '~0' and '/' needs to be encoded as // '~1' when these characters appear in a reference key. func JSONPointerToSlice(path string) ([]string, error) { if len(path) < 1 { return nil, errors.New("failed to resolve JSON pointer: path must not be empty") } if path[0] != '/' { return nil, errors.New("failed to resolve JSON pointer: path must begin with '/'") } hierarchy := strings.Split(path, "/")[1:] for i, v := range hierarchy { v = strings.Replace(v, "~1", "/", -1) v = strings.Replace(v, "~0", "~", -1) hierarchy[i] = v } return hierarchy, nil } // DotPathToSlice returns a slice of path segments parsed out of a dot path. // // Because the characters '~' (%x7E) and '.' (%x2E) have special meanings in // gabs paths, '~' needs to be encoded as '~0' and '.' needs to be encoded as // '~1' when these characters appear in a reference key. func DotPathToSlice(path string) []string { hierarchy := strings.Split(path, ".") for i, v := range hierarchy { v = strings.Replace(v, "~1", ".", -1) v = strings.Replace(v, "~0", "~", -1) hierarchy[i] = v } return hierarchy } //------------------------------------------------------------------------------ // Container references a specific element within a wrapped structure. type Container struct { object interface{} } // Data returns the underlying value of the target element in the wrapped // structure. func (g *Container) Data() interface{} { if g == nil { return nil } return g.object } //------------------------------------------------------------------------------ func (g *Container) searchStrict(allowWildcard bool, hierarchy ...string) (*Container, error) { object := g.Data() for target := 0; target < len(hierarchy); target++ { pathSeg := hierarchy[target] if mmap, ok := object.(map[string]interface{}); ok { object, ok = mmap[pathSeg] if !ok { return nil, fmt.Errorf("failed to resolve path segment '%v': key '%v' was not found", target, pathSeg) } } else if marray, ok := object.([]interface{}); ok { if allowWildcard && pathSeg == "*" { tmpArray := []interface{}{} for _, val := range marray { if (target + 1) >= len(hierarchy) { tmpArray = append(tmpArray, val) } else if res := Wrap(val).Search(hierarchy[target+1:]...); res != nil { tmpArray = append(tmpArray, res.Data()) } } if len(tmpArray) == 0 { return nil, nil } return &Container{tmpArray}, nil } index, err := strconv.Atoi(pathSeg) if err != nil { return nil, fmt.Errorf("failed to resolve path segment '%v': found array but segment value '%v' could not be parsed into array index: %v", target, pathSeg, err) } if len(marray) <= index { return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(marray)) } object = marray[index] } else { return nil, fmt.Errorf("failed to resolve path segment '%v': field '%v' was not found", target, pathSeg) } } return &Container{object}, nil } // Search attempts to find and return an object within the wrapped structure by // following a provided hierarchy of field names to locate the target. // // If the search encounters an array then the next hierarchy field name must be // either a an integer which is interpreted as the index of the target, or the // character '*', in which case all elements are searched with the remaining // search hierarchy and the results returned within an array. func (g *Container) Search(hierarchy ...string) *Container { c, _ := g.searchStrict(true, hierarchy...) return c } // Path searches the wrapped structure following a path in dot notation, // segments of this path are searched according to the same rules as Search. // // Because the characters '~' (%x7E) and '.' (%x2E) have special meanings in // gabs paths, '~' needs to be encoded as '~0' and '.' needs to be encoded as // '~1' when these characters appear in a reference key. func (g *Container) Path(path string) *Container { return g.Search(DotPathToSlice(path)...) } // JSONPointer parses a JSON pointer path (https://tools.ietf.org/html/rfc6901) // and either returns a *gabs.Container containing the result or an error if the // referenced item could not be found. // // Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in // gabs paths, '~' needs to be encoded as '~0' and '/' needs to be encoded as // '~1' when these characters appear in a reference key. func (g *Container) JSONPointer(path string) (*Container, error) { hierarchy, err := JSONPointerToSlice(path) if err != nil { return nil, err } return g.searchStrict(false, hierarchy...) } // S is a shorthand alias for Search. func (g *Container) S(hierarchy ...string) *Container { return g.Search(hierarchy...) } // Exists checks whether a field exists within the hierarchy. func (g *Container) Exists(hierarchy ...string) bool { return g.Search(hierarchy...) != nil } // ExistsP checks whether a dot notation path exists. func (g *Container) ExistsP(path string) bool { return g.Exists(DotPathToSlice(path)...) } // Index attempts to find and return an element within a JSON array by an index. func (g *Container) Index(index int) *Container { if array, ok := g.Data().([]interface{}); ok { if index >= len(array) { return nil } return &Container{array[index]} } return nil } // Children returns a slice of all children of an array element. This also works // for objects, however, the children returned for an object will be in a random // order and you lose the names of the returned objects this way. If the // underlying container value isn't an array or map nil is returned. func (g *Container) Children() []*Container { if array, ok := g.Data().([]interface{}); ok { children := make([]*Container, len(array)) for i := 0; i < len(array); i++ { children[i] = &Container{array[i]} } return children } if mmap, ok := g.Data().(map[string]interface{}); ok { children := []*Container{} for _, obj := range mmap { children = append(children, &Container{obj}) } return children } return nil } // ChildrenMap returns a map of all the children of an object element. IF the // underlying value isn't a object then an empty map is returned. func (g *Container) ChildrenMap() map[string]*Container { if mmap, ok := g.Data().(map[string]interface{}); ok { children := make(map[string]*Container, len(mmap)) for name, obj := range mmap { children[name] = &Container{obj} } return children } return map[string]*Container{} } //------------------------------------------------------------------------------ // Set attempts to set the value of a field located by a hierarchy of field // names. If the search encounters an array then the next hierarchy field name // is interpreted as an integer index of an existing element, or the character // '-', which indicates a new element appended to the end of the array. // // Any parts of the hierarchy that do not exist will be constructed as objects. // This includes parts that could be interpreted as array indexes. // // Returns a container of the new value or an error. func (g *Container) Set(value interface{}, hierarchy ...string) (*Container, error) { if g == nil { return nil, errors.New("failed to resolve path, container is nil") } if len(hierarchy) == 0 { g.object = value return g, nil } if g.object == nil { g.object = map[string]interface{}{} } object := g.object for target := 0; target < len(hierarchy); target++ { pathSeg := hierarchy[target] if mmap, ok := object.(map[string]interface{}); ok { if target == len(hierarchy)-1 { object = value mmap[pathSeg] = object } else if object = mmap[pathSeg]; object == nil { mmap[pathSeg] = map[string]interface{}{} object = mmap[pathSeg] } } else if marray, ok := object.([]interface{}); ok { if pathSeg == "-" { if target < 1 { return nil, errors.New("unable to append new array index at root of path") } if target == len(hierarchy)-1 { object = value } else { object = map[string]interface{}{} } marray = append(marray, object) if _, err := g.Set(marray, hierarchy[:target]...); err != nil { return nil, err } } else { index, err := strconv.Atoi(pathSeg) if err != nil { return nil, fmt.Errorf("failed to resolve path segment '%v': found array but segment value '%v' could not be parsed into array index: %v", target, pathSeg, err) } if len(marray) <= index { return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(marray)) } if target == len(hierarchy)-1 { object = value marray[index] = object } else if object = marray[index]; object == nil { return nil, fmt.Errorf("failed to resolve path segment '%v': field '%v' was not found", target, pathSeg) } } } else { return nil, ErrPathCollision } } return &Container{object}, nil } // SetP sets the value of a field at a path using dot notation, any parts // of the path that do not exist will be constructed, and if a collision occurs // with a non object type whilst iterating the path an error is returned. func (g *Container) SetP(value interface{}, path string) (*Container, error) { return g.Set(value, DotPathToSlice(path)...) } // SetIndex attempts to set a value of an array element based on an index. func (g *Container) SetIndex(value interface{}, index int) (*Container, error) { if array, ok := g.Data().([]interface{}); ok { if index >= len(array) { return nil, ErrOutOfBounds } array[index] = value return &Container{array[index]}, nil } return nil, ErrNotArray } // SetJSONPointer parses a JSON pointer path // (https://tools.ietf.org/html/rfc6901) and sets the leaf to a value. Returns // an error if the pointer could not be resolved due to missing fields. func (g *Container) SetJSONPointer(value interface{}, path string) (*Container, error) { hierarchy, err := JSONPointerToSlice(path) if err != nil { return nil, err } return g.Set(value, hierarchy...) } // Object creates a new JSON object at a target path. Returns an error if the // path contains a collision with a non object type. func (g *Container) Object(hierarchy ...string) (*Container, error) { return g.Set(map[string]interface{}{}, hierarchy...) } // ObjectP creates a new JSON object at a target path using dot notation. // Returns an error if the path contains a collision with a non object type. func (g *Container) ObjectP(path string) (*Container, error) { return g.Object(DotPathToSlice(path)...) } // ObjectI creates a new JSON object at an array index. Returns an error if the // object is not an array or the index is out of bounds. func (g *Container) ObjectI(index int) (*Container, error) { return g.SetIndex(map[string]interface{}{}, index) } // Array creates a new JSON array at a path. Returns an error if the path // contains a collision with a non object type. func (g *Container) Array(hierarchy ...string) (*Container, error) { return g.Set([]interface{}{}, hierarchy...) } // ArrayP creates a new JSON array at a path using dot notation. Returns an // error if the path contains a collision with a non object type. func (g *Container) ArrayP(path string) (*Container, error) { return g.Array(DotPathToSlice(path)...) } // ArrayI creates a new JSON array within an array at an index. Returns an error // if the element is not an array or the index is out of bounds. func (g *Container) ArrayI(index int) (*Container, error) { return g.SetIndex([]interface{}{}, index) } // ArrayOfSize creates a new JSON array of a particular size at a path. Returns // an error if the path contains a collision with a non object type. func (g *Container) ArrayOfSize(size int, hierarchy ...string) (*Container, error) { a := make([]interface{}, size) return g.Set(a, hierarchy...) } // ArrayOfSizeP creates a new JSON array of a particular size at a path using // dot notation. Returns an error if the path contains a collision with a non // object type. func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) { return g.ArrayOfSize(size, DotPathToSlice(path)...) } // ArrayOfSizeI create a new JSON array of a particular size within an array at // an index. Returns an error if the element is not an array or the index is out // of bounds. func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) { a := make([]interface{}, size) return g.SetIndex(a, index) } // Delete an element at a path, an error is returned if the element does not // exist or is not an object. In order to remove an array element please use // ArrayRemove. func (g *Container) Delete(hierarchy ...string) error { if g == nil || g.object == nil { return ErrNotObj } if len(hierarchy) == 0 { return ErrInvalidQuery } object := g.object target := hierarchy[len(hierarchy)-1] if len(hierarchy) > 1 { object = g.Search(hierarchy[:len(hierarchy)-1]...).Data() } if obj, ok := object.(map[string]interface{}); ok { if _, ok = obj[target]; !ok { return ErrNotFound } delete(obj, target) return nil } if array, ok := object.([]interface{}); ok { if len(hierarchy) < 2 { return errors.New("unable to delete array index at root of path") } index, err := strconv.Atoi(target) if err != nil { return fmt.Errorf("failed to parse array index '%v': %v", target, err) } if index >= len(array) { return ErrOutOfBounds } array = append(array[:index], array[index+1:]...) g.Set(array, hierarchy[:len(hierarchy)-1]...) return nil } return ErrNotObjOrArray } // DeleteP deletes an element at a path using dot notation, an error is returned // if the element does not exist. func (g *Container) DeleteP(path string) error { return g.Delete(DotPathToSlice(path)...) } // MergeFn merges two objects using a provided function to resolve collisions. // // The collision function receives two interface{} arguments, destination (the // original object) and source (the object being merged into the destination). // Which ever value is returned becomes the new value in the destination object // at the location of the collision. func (g *Container) MergeFn(source *Container, collisionFn func(destination, source interface{}) interface{}) error { var recursiveFnc func(map[string]interface{}, []string) error recursiveFnc = func(mmap map[string]interface{}, path []string) error { for key, value := range mmap { newPath := append(path, key) if g.Exists(newPath...) { existingData := g.Search(newPath...).Data() switch t := value.(type) { case map[string]interface{}: switch existingVal := existingData.(type) { case map[string]interface{}: if err := recursiveFnc(t, newPath); err != nil { return err } default: if _, err := g.Set(collisionFn(existingVal, t), newPath...); err != nil { return err } } default: if _, err := g.Set(collisionFn(existingData, t), newPath...); err != nil { return err } } } else { // path doesn't exist. So set the value if _, err := g.Set(value, newPath...); err != nil { return err } } } return nil } if mmap, ok := source.Data().(map[string]interface{}); ok { return recursiveFnc(mmap, []string{}) } return nil } // Merge a source object into an existing destination object. When a collision // is found within the merged structures (both a source and destination object // contain the same non-object keys) the result will be an array containing both // values, where values that are already arrays will be expanded into the // resulting array. // // It is possible to merge structures will different collision behaviours with // MergeFn. func (g *Container) Merge(source *Container) error { return g.MergeFn(source, func(dest, source interface{}) interface{} { destArr, destIsArray := dest.([]interface{}) sourceArr, sourceIsArray := source.([]interface{}) if destIsArray { if sourceIsArray { return append(destArr, sourceArr...) } return append(destArr, source) } if sourceIsArray { return append(append([]interface{}{}, dest), sourceArr...) } return []interface{}{dest, source} }) } //------------------------------------------------------------------------------ /* Array modification/search - Keeping these options simple right now, no need for anything more complicated since you can just cast to []interface{}, modify and then reassign with Set. */ // ArrayAppend attempts to append a value onto a JSON array at a path. If the // target is not a JSON array then it will be converted into one, with its // original contents set to the first element of the array. func (g *Container) ArrayAppend(value interface{}, hierarchy ...string) error { if array, ok := g.Search(hierarchy...).Data().([]interface{}); ok { array = append(array, value) _, err := g.Set(array, hierarchy...) return err } newArray := []interface{}{} if d := g.Search(hierarchy...).Data(); d != nil { newArray = append(newArray, d) } newArray = append(newArray, value) _, err := g.Set(newArray, hierarchy...) return err } // ArrayAppendP attempts to append a value onto a JSON array at a path using dot // notation. If the target is not a JSON array then it will be converted into // one, with its original contents set to the first element of the array. func (g *Container) ArrayAppendP(value interface{}, path string) error { return g.ArrayAppend(value, DotPathToSlice(path)...) } // ArrayRemove attempts to remove an element identified by an index from a JSON // array at a path. func (g *Container) ArrayRemove(index int, hierarchy ...string) error { if index < 0 { return ErrOutOfBounds } array, ok := g.Search(hierarchy...).Data().([]interface{}) if !ok { return ErrNotArray } if index < len(array) { array = append(array[:index], array[index+1:]...) } else { return ErrOutOfBounds } _, err := g.Set(array, hierarchy...) return err } // ArrayRemoveP attempts to remove an element identified by an index from a JSON // array at a path using dot notation. func (g *Container) ArrayRemoveP(index int, path string) error { return g.ArrayRemove(index, DotPathToSlice(path)...) } // ArrayElement attempts to access an element by an index from a JSON array at a // path. func (g *Container) ArrayElement(index int, hierarchy ...string) (*Container, error) { if index < 0 { return nil, ErrOutOfBounds } array, ok := g.Search(hierarchy...).Data().([]interface{}) if !ok { return nil, ErrNotArray } if index < len(array) { return &Container{array[index]}, nil } return nil, ErrOutOfBounds } // ArrayElementP attempts to access an element by an index from a JSON array at // a path using dot notation. func (g *Container) ArrayElementP(index int, path string) (*Container, error) { return g.ArrayElement(index, DotPathToSlice(path)...) } // ArrayCount counts the number of elements in a JSON array at a path. func (g *Container) ArrayCount(hierarchy ...string) (int, error) { if array, ok := g.Search(hierarchy...).Data().([]interface{}); ok { return len(array), nil } return 0, ErrNotArray } // ArrayCountP counts the number of elements in a JSON array at a path using dot // notation. func (g *Container) ArrayCountP(path string) (int, error) { return g.ArrayCount(DotPathToSlice(path)...) } //------------------------------------------------------------------------------ // Bytes marshals an element to a JSON []byte blob. func (g *Container) Bytes() []byte { if bytes, err := json.Marshal(g.Data()); err == nil { return bytes } return []byte("null") } // BytesIndent marshals an element to a JSON []byte blob formatted with a prefix // and indent string. func (g *Container) BytesIndent(prefix string, indent string) []byte { if g.object != nil { if bytes, err := json.MarshalIndent(g.Data(), prefix, indent); err == nil { return bytes } } return []byte("null") } // String marshals an element to a JSON formatted string. func (g *Container) String() string { return string(g.Bytes()) } // StringIndent marshals an element to a JSON string formatted with a prefix and // indent string. func (g *Container) StringIndent(prefix string, indent string) string { return string(g.BytesIndent(prefix, indent)) } // EncodeOpt is a functional option for the EncodeJSON method. type EncodeOpt func(e *json.Encoder) // EncodeOptHTMLEscape sets the encoder to escape the JSON for html. func EncodeOptHTMLEscape(doEscape bool) EncodeOpt { return func(e *json.Encoder) { e.SetEscapeHTML(doEscape) } } // EncodeOptIndent sets the encoder to indent the JSON output. func EncodeOptIndent(prefix string, indent string) EncodeOpt { return func(e *json.Encoder) { e.SetIndent(prefix, indent) } } // EncodeJSON marshals an element to a JSON formatted []byte using a variant // list of modifier functions for the encoder being used. Functions for // modifying the output are prefixed with EncodeOpt, e.g. EncodeOptHTMLEscape. func (g *Container) EncodeJSON(encodeOpts ...EncodeOpt) []byte { var b bytes.Buffer encoder := json.NewEncoder(&b) encoder.SetEscapeHTML(false) // Do not escape by default. for _, opt := range encodeOpts { opt(encoder) } if err := encoder.Encode(g.object); err != nil { return []byte("null") } result := b.Bytes() if len(result) > 0 { result = result[:len(result)-1] } return result } // New creates a new gabs JSON object. func New() *Container { return &Container{map[string]interface{}{}} } // Wrap an already unmarshalled JSON object (or a new map[string]interface{}) // into a *Container. func Wrap(root interface{}) *Container { return &Container{root} } // ParseJSON unmarshals a JSON byte slice into a *Container. func ParseJSON(sample []byte) (*Container, error) { var gabs Container if err := json.Unmarshal(sample, &gabs.object); err != nil { return nil, err } return &gabs, nil } // ParseJSONDecoder applies a json.Decoder to a *Container. func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) { var gabs Container if err := decoder.Decode(&gabs.object); err != nil { return nil, err } return &gabs, nil } // ParseJSONFile reads a file and unmarshals the contents into a *Container. func ParseJSONFile(path string) (*Container, error) { if len(path) > 0 { cBytes, err := ioutil.ReadFile(path) if err != nil { return nil, err } container, err := ParseJSON(cBytes) if err != nil { return nil, err } return container, nil } return nil, ErrInvalidPath } // ParseJSONBuffer reads a buffer and unmarshals the contents into a *Container. func ParseJSONBuffer(buffer io.Reader) (*Container, error) { var gabs Container jsonDecoder := json.NewDecoder(buffer) if err := jsonDecoder.Decode(&gabs.object); err != nil { return nil, err } return &gabs, nil } // MarshalJSON returns the JSON encoding of this container. This allows // structs which contain Container instances to be marshaled using // json.Marshal(). func (g *Container) MarshalJSON() ([]byte, error) { return json.Marshal(g.Data()) } //------------------------------------------------------------------------------ gabs-2.3.0/gabs_logo.png000066400000000000000000005075331357441163300151160ustar00rootroot00000000000000PNG  IHDR:GsBIT|d pHYs+tEXtSoftwarewww.inkscape.org< IDATxye}}37BYwTDEJ (J.m]jujmjՊ@2QvA I,e$lY {2vɜ庮s#93s[Ե` Z>i@=5HR^ AA\IFT8(I}F)~~+cA'#7S0{@S0 MIBCiI&HH.ibK/KnIiR=TRN(^v4I/^[vԸN!1 UZ2[ K(7G lIGV8"19([@-RasA,pӀ7B Thq.]м_MH'J5gޡlo\&ڣNVQ&)('ߧ̦F@5+X7nֲi}8=TDI'H:Q 4s/cg塂C'%=.鉂'^;W@@jF̵qSɡSLI'KZ zvpv3 GwIcP9I ^5\ Y`̻VW5NSs/ .XҩV3/ل^hGB=}/׸PSpA9t͗IAKy;EYK5. B+{`p}(_^[N5%=z}M8!9^#\IejB=K5jmY"mօ +\'TI۵qUP lIm*/]e8~QcQֿ[ ޓS'5}-@ `m.o'܌Q}cq|`MN_;_59bcԩ _J%[ƻ.ج@%{لIT);cZµRnMšKusޓ(T׮J6wHr)xf} 4`&|=%PwKuf `o׹ưmi)is$W'LlfyO.r٤go+={ @:v.hާ7 #"IMmW&q"Yƞ}IzjTe-τ瀤{vU NR*|o2IތMl z5sh= 'MztG+t#-@"cv.ݤtcgn2ll: MAƭS ~2ݾ-^u02PVje$=PUJj,o㳥jmꇚf&f&g3MmYuyift£ ](x(U~gf5$Qj wtnX>vYP}|!FIPzөe)(egz3p3`4k{wڭz4g3lY_^ A'm]Ҫ]+SN+?ꅛɃl.ҏbޮZ- k2]>!.r*8JK1IWHT~>jO׮6Ӯ̬g@"12h?Lems =6W6x-װƐN-= K`{,WoVff`3~e1kz7fd =Mچ Re@&.[[C3RpNC*oMpZY>[&R95ݰBk<TPUw¶.寔ǒ 8ZYp9ԬE\\4fssγ~cD6Q<O ֤O9Q$R/׬ƘN-(@L(.?n{nmڎIg~ƭ)NU|g XWWh͞ r `.WӇ*ijz 8ms{ft*l;V$[xox7 =xI_ͫWhNxdVN'Ò:W;ps.jT-j&YN= <% ZR@*7kٴ OHP~>yY՛مffqc0ۀ4 HG~uhuOkϮ@(#rM nӒ 0PYIϻzYiъ,CM.JC}b W{Π^_kReuP^tjyǀz?(3IϏd5mp3UYջln|tgeYгګQ# `!ygguCs/I]5PcֲwH%-4i,ݚX4z|~}d^Ya/<]R+ .u/Iz|UάVoV#ܬm<ǯՌƈn=B <|2)S\ Jΐt|=>7nׯG.b:w茚'Iu>Y63PPO_[qL`kW#f-֧_ >")_~>Τ7s3N&ج[Gkګ,?CܖC ^zny1qdVԁkuU$>'iRI~Q5I6Vw_IEL~#iUг~Okݡ/Z80Vlx:tίH:tōD|{[Vgfvf5֒n4?٦6nfxgX ~$; ';S:qj/"ͦWq&8eff6e>.N3njgU3]PޥlpL`׶:u@} ~F Z'٦D>L5m@UҋBM.*% ,ݯgWy& <ܿp@(}&}Ak}qdM Y,uŤpfn[i}.WS;FKz,ś1JY>-cֻSC:q]*Nmj)Mwf Cj3DY4w^QsGYlGsWE:1S[CuӒr6(]9g-WoVf6fբS58c)jg=Ghug([ uGehp*]JUygmj]1+L=Hq/ƚzwbZ`t}_X0 5++ZڴgeS6qVqgi 7G~j}ַW_V!ks$Zmg@ܻ-0ݤ%#W{gmjIǤ77Y\em& ?3~3=.Kό|UR(uk_`Y$}IRM8mS'smO5m5ʹH\U{,-̗6vz&<2R:?W߬К4 A'0Jt£n t~Vq #pfz,}>%t3k?._l[Owf4F(ݗV莍)ԑr kl^+i|3*lsͤeVk[M¿x#mYUqWw`u)%cAxF邉 joU볊3-o#g6fMh6*i+<6jxgzf3YYog[՝4KW-ӭ-Sa# SK*H$-0UI׀sͤc 4GbuSF2g;]#TiuS T?`-mױJK/LMaW8Ƶ7̐+Ps<^e&?YiWyV#v>;S=>snex#d<^uVIZnkv8ۦ6iY՛ه#lfwWėFImuBLx&g*ne{.pe 5VWs, [-ilU-+7n 6]D?XLc&e,L3}$<ܯWwZ?#.D72<kW`Y$KVi|{&z&:W'Q™j5Y-sEF[.iW=/U}ج?]=uz^Hn& 6]ŽkؑtS׬{m9zjogguglO\2<׷@]hiK֦[4'iN״מg.TА/ ڲ>ycI 7lfwO [3*nm{ڭUwxC W];-e2}ҩ%Yi6!gghyjլֺ'wj5T+IiJӲVu#u97{ӆZYXk K]7*\/i8ӭlت<_>Fu7]t)=okCTf}uYPQluT#pL:^,q׆"nmoe7.n2<*U`\Ye[\eҐi-[t.P«,S=ۥ0LEmiq$X_wbcڸ3d|VY^i;km!V^[6Bd`W@]֦[Z脿x{g`z^ګBoMjڡ\Kc1ݼV}74ݣZ7 6GNY^d(ٶYzfs3};ugznݽ2< t5p.(MmmB8+59{8ЫmwZ/!'սEsxB~+uޠ|{׸af5*Uvt5^sl޽z}߰nfuN^,3zL fݝh*&i? x'Ծz+/ݮ tX(=Z?/5#W^ LjG.LlG,օ3K3ӄ#x&ߦE= ;-{~nwcR#Sz |7Lz?WӾ|ri{EyRCQӹg܇^AC>r'J{jIMߖZօ3Բp&ŨpЋ7J_KƉ߲;ukzSo;ۨr苨5Jv/Oʂ4?]w:C齶6+ WA>ӿ>rW_/쪨RPV_iq⑚pywP}ҚU5|$0^qg}2=/Ss52|fOV%Y?׾uŤOhm9CJ/iqzNq0STa;E+?#{/nomٷ|Z__ۛ;K **wvŪeN([F3>!9|XbDi$̛5KCϩoqfу 8mf^t~-/VS_Z쵛ޝ%͗_̧֒A J7TpI6>7}n u IDATǚn?֓q⪜aCQG8iNܕ_U#^(g${-naߺ cE|Qs ~/?}F7^s5_5Oo#*2<{.{3:h]b> A'*-#)$czgh?-Or3}Kvz{'kk#3&ƪu :t2q8ψ`ӳ?Yl[+IntΓxFJϥ[ٚ]/j:xA'S;pg୶69:m̈le'Y"BXxA~h}Y; J.6[u,^KQt|ašI~VmY3{xh5;r> LOvZG .)nd c)gkSs_Q=zhp Ůq͓<ഩ 7}BIM`3ʄ=ZE۷vc&dA_Y{"\iOۈk{hZy>xSҹ jl4b@dV^M?tM!)5m:׆wbT;(5s+41*ýkv$*p>nlHx11{__Ag"x툽OS=M7Cq cssip+1{(\ԫtr?B%ac?u͈.ÁvhwfGãƉ:wǮwv cM8]}Ms@f͒(/Ҥӎ1 /}ԟl?5UAO-ztI۵B`#՝KgUvVOyhgˑSt'/tTv{ c{~Qs:@Ǯqވ xE4=I7L19]fZ? *BO3 *ι!*tE:z 9%\\[Jͻe5>࣮'%Www_&*^ZYH#8AZQ]Ǣ^㸄}~UUG@4^<{FG<˿rg;[X;]GLy2 ;5v#ԒcCRӄ-3'i寳۳_/z#|4t,jG{>_#kٺG\5=wG hl"!߀HTU &,k.}6"00C eL,ղ`?6}juE%,~-GI 6MGc~#>Cϼ9|[1Lg*ۙ=]y_:0ݤR#B xض|~J]mTP*u?Y9b#9n& 6MG;Z=Kڍlg1GaJ``ZOT˂j^8C ی}T&,I =m9-mjxZClK#NӨ؞oYlAedyh䲰oNIK_{ndknԒY`97,?6䔤睪!e}Ί҆v5{8 p& 7}[FJ6$ubZm#>zrZPj7MA{;Z,!z"Ox!cOFWPvupUQaPqZWi3T!snҒ]7cA'ƭN-QPO$:?:򝯎.S}GX[iLFـ3hȫvj=v\swPyv۬N84J.P$j:*;hi8s~~eЁ'5ΘSiD;ZTP=::Sw|Y&c'T|<+Cjxa f[PylOnԒ]۷cA'ƥN-TPݒNv+jr4{bWylR@dm|I`'^?qZ}/;S^y͎*}}鹿]p}k ='Wz,5LmO=ׯ7)( G+- gh{_\'ϖڷmv!x/Ե~S:L)m+2;w9j3:t<ֵ[َr?Ao=].s`"]}@}wҙv#r*{lUdmǢBΑ_ř4:~\ 4mq45ͧjʛNs[0ԄE beF܆g4/_Io8nk5Q3f\!m}>쥸b5Κ 0>لQyyEfl}WwVFYle;3jSߴBkcA'ƕtA^!q34Jo~ gmǢVMF7* 8_zf}Wp)4ުԱh~1%_;tFò?g:j;NSv&sZ  +G:F3<7f[זlCr!d3*x) 4Mcf3yH8T,ut+ 껥SߺBkzM1(7R+s{|jW!$|2c'?7?'?됳rۼCfy:XMYd_.QLBU|>=:vZk:dz )Vk%}&ɇ9%i+(p_3*\w'Ojo]35ˠ*{Pq|#61ܑgU}FIWP&ָC7i_ZNyC[$㣠ߔxgGd? QM0ڷ-Z3~Y22{:d6I چ[v0eke/mLiu6,{7=;:^CMq?wDX2~a}.~:`,M¯|^ !g imƶg},Usqz}JfO)?No~ծ~OlU`gO;UG~5N$ZS%I]6DdW/fɛ"Z KD.1y BWr[u=4/jep]阥>hc'p8A8ԢtL)<=MP>0j[y@R8.WM^2_^ϽSjfVR?䵓IRgˍ-kZ1e>sWeֻaJNOԺpOa-_=տ@LyotO?#'>u?BQdHP^TׯQ/i`gFM8MS[+@-'Ƕ8Z=oդ N8޷m|G:1ih)E3(jzK_Wn#04lqgi0h^Ea)ɰ0KʇҍZ>s`t#t޾0jWC)Im̲ہ ی52:ojk 8]#ۚt-WiGGU8ا'-GM۵toH)+:4LP˱33[ҷ~]?z78ERkq?~14It*O6^u?Py.\ֵ?gQgtY}uHveÁi-ge^=WY:Z^g=gւ;U+tMыcZ6Ww3 9Gr6͘zjwi |Ccǚ㥂#0wwU7g =5uܵWh⫏5+(wv#^AΛS tf3)Vzmruuү|9\Uyist̍4 j}ѼX594ntǚqCϨsxyuPٚlڂ@Sm2/Bc3 Y \}lً;Ke*}ZmB[oi{dk7j󴟵@QGLұxͻ%* ew N.h5ut8Y*sh,IÍm00NX(6EINV;ݣBkt`XNWBX{Jn 55=p>Ly) f9ƼNUC![hGgֿB'^&7U寷7zF}x~7̱Xiaoi<j>޽JKE:0&ҷҟv-BΊbО΂]ë zMycs %stԧ.PGAߦOR%s9CZO|߳_? ~6Bօ0#ݿyVAa( $iuj9~{>c+ۄ}pɷԽh >`-톣ߋCO;W4)~*3j|sW2]=ayǔNRИOT|lӜw\MYg;2x=c/*Dk79ϗHjԪ#mj!j\qw޿RCi%mx!;~^b<3!7/Me%gMTϩs5Wk^m]\8ا]=wZ_(i`W6+:.!t V}e>o94iҾAIG}C}URHP1[_M70zWh#kMA HZ9St~_wրHBN疁k; $ tz'S1lUoeM{bYl"CNIZSnRSe?:t.ެgARi_D+jSZ8*vo^.LmoИשNGLt۶W/N\qM춮Oi}y =+[W#>}LQ)v*ZCE{|вaWܰ/>{t5[ IDAT`TZ ˣEQ!yꅜ4Нšܡmp*my4}˕oOlkF|T~&J9CVs3ע\+NJZr&(_(ג~_TDF=/ 9g5ߙֵkK_jLH?m^Ql}l?y 0]Yse3\~B\$cWkهME QS^%v!_z[GmM#gi;o[{m۱d\?|c9m/1qGYm[nSьpwݟ%¼\Tmҏ_V ؾp4|Gqݡ3`u=뽭QEkKi#};lTZ~6TϝZiB QF-UHjr 9BNI*dumC{njIBNsiSYdߺ՜xqjf\qP៬Ҳ&@ @ԒcCQCM~}JCΨpl7VD۱G8}@ACNG[Rչ7*5XǴ^j!xHI!Yݟg1hv߸< ݷ5]؇~\ {5@go.٧2}E(֪iFQsZsp7y}k)oi4|uEKTC`$tQ?I\ߗ߇>Qm=k I:0`4487rM e?y@RIvʎ1 9.*Fsle9j?3ֵ=mƙbES߿K=CL:\{sy{}T:oX n]u E^ψaCkXKUaRP>Fˢj(ު_r6FA'*-[(PT1æ^%}}>wձ뾧U%1ͧ*(YjqCNI:OmQ*{U)o{Y9$i/p`X?K0,9G GWll5I^vt|>!) 9a:vlwn<7\zI'zZ;,mqդR9#Iz34? SwhnS>}[[UdV՝lag=)\$!({]}B7 TA'=z}CA㾜p=`=v5WG>!gŸi=gKb%#w8t,(94uuW*ڔ}k7u{!fcE럱4<)j]< c8Y9k7Ιđ\E@-k,/Hk,icc\~ضt|zfv1bd ?IK_ Puh@~=|kFc?pwP;~[j;v ߐsƲ3u7Ȭar9)f@tn(țF9M>WvOY'VrtVR/hl[\$?ն%Ac㤨νYz`WA\AY\q7Fc{Fh45P܍}W@Aցff}?fST30uγj~TوɆ'+ɲ W:_{F)cQtI(<}{g@BRVٛ[\dDO[ۦGˋǓCyr88߉#[ `w*vIGڟ:P AAATR5d  H˱*vvl{W >-W(=SCq? U\O҄ c.:i_U]otiZrw`h#RK"a|L]vf.BijoC`}Ц;P3Vd6uηGcpMz:: (!p,msFr5f:cb#]^ʬ˱K“ReAAD?BAA!gC[CNѬ0&=U G/nCU'ÕaKQdcͿ:VW"*"{1@93UDK%{89E5:9[{'CF_"<G:)q҅m!򕳻鈴$%iVqÑ<>c!5a+]vI jSwM`gJMCٓ Bg\H )ÕeDO(SK]oʊǖFb塼-Kvb2/OpMtU'voak˱{˻AADz[AAX0~/c˖+bnY3`К_߈;^I)F] @Ϛ{ƝR>]!h+ƼF(`W_ΌcodO,=лBYGg-gP>s<T•! אG/[ڊڝD`}'d%^r }`|IO+pAADZ  8$܂[\Q Bp"!p )j{(To=F}Ue{$G1:d>rOZV7%wջ-YR'Y9[Z/\hMcAftutzƾawM%O9Kʋڿb'xWJU^gm[3'[p ]_"   Q  0_^)J99> ox />ke=t!wbu n.x>z4M;젮 4a;ߐB;XzVq/rs$}"O( Xs*߮EWuЃK|L©pe)K`msgMCSvk#h1"L2>^W2\>s\+Ɇj'﵍I|G^پ)s=Z>Tc7pA BAAIAAt܈=oggYu;(#r,X ݉ޕ>WWbiG`G=ٽƎLa_|JpzZZWeoSӒ?Miʿp/؉fds"Q}?Zvzyld^dKaā"*ĈX-w*<N/ةN궉lΔW  p AAAe9_`~q[$p ~*~$bU's`uS|8To[+iK'끧(U,23FЎ)(.@ #5]M`jabUa 5bAC/vG?puq0u5<ݝgF: b131{(OvwfAn8XLXlf=&INFʹ-;t`r,֐   HzCAA Jt+ݷź.6>5{ m;d4N~tݍ7Fw0ûc­<~82#֍/mݝ5Wp璳[Jޟ&CglF4s#TfƴF7W[͕Cgx#Z$۱{4@ӭu#cȫCZMt>T fth~_wXAAA(AB'AAqpW~j"\WI5qUaNh"PYE3Q8w4%hdj7_Z̚ەE1CSѱ2^m)I&͜cw tȜ8(m5!oT䜒LƐ۬=< ש:o λQDbl2T.抧*:C6tďNdG1[$O/w@1XLLbz()SH.v jc k[A].W(   C%   ˰<؊>Vo#ܲV$r*SWpjam/QMDT2~ 6;4)rмo2kC :U ggfwN€|eR<_nIiƀ\ Wm#MΜL/M,G45*4&ʖ13a՚vw_5kjmlY6ū!YؾS:#ώ&,[~ [+q3e˱{3  H$  =2aPem)Kx1\11s{n}{n}.iÑ=~2 s.̆ tss-m "k=qʌt}Q dISʕL:Tf2Q-πEox^gD#eѼ?-x7I3l-Ww_)? ]ef@WxD2t}[o^fhw[[%vݝok1|p:;9uinj9L-v8` [Ժ:{-3  H:  ~L5jwd:XUɎ#LEѾ+됩%w(<*-cj4vڑQϥa?|/" B3?AySUPpC9g!6jx omy4w Y[ۊ&Di1ɕUE,[⑼z֌i~j<'(\L$ʎ~x<ѮZl`FwO8UZybTü_VOAA*8CAA,qV,ډ;0DôusFuQV_' "'4 ӫDƀ<;>k5(8X-ϮCF#)[0,#Ep>T] B;,sS`ȃ#~+FtoއXC\yMg0P茎]3?89ӄ]/bA2z.@l"Ё7A뗕ҥ(W79b7ӚW+y'if?zk8P IDAT].b~]>ݹMbp}V   R::  ~EiSVSBU䌏[=f@J"'tŬWW;.tΟj: V7v@ʹ}Ga_ͭg`O)|ΤAsR4d<'NO<Pw٣R"'^|>HCNsP|초*LS=?cwS2azw'߷iZetv7a"Xjy6o_.:GrGu!z/y.AAutAA2,ПUCШwSOUI$~95W1\È G GBZԠ;wޞ>lC~xH\-Ӄs'vD:画(y[{hhs}/nDz0FƐdㆥc Ѓp+"u=vgYgvwsgg\S?sgNSqgK)NQ~zSҥxqAAA8::  ~Ip⛾Mot""Aָ2x pf"DuH=1Ov*kA[؁.5ͨQVӄ}?K3D)z!F]0%>%>;Wnk`L;4xy(3ݝLvДfMU[]WK)uwuj0Rܽ~1iuXAAAp!  < `-:*"JR& ;.sFԡ2ܖ(z8/q`jcD:8[ `TnkmE#Rs-yTA ‡=ЁEpvDtѽN~-OB9CxǿsmS܉Ж}8 j/~zWX+@n&1#b54 19pfd2,O9gW!3AW{@sT1C~1#  eH$  N {ɀVzΦ DY6";ˇQǭ )W˜˜_BGΈ4w!Um<6;k}cU?xі.UЃT-}n7ҶsA_t7wϟCp yj܋ua%a]cZFi5\ӬC8-} RϡTK"){In\^@S  PN  ",¹Α`+7~![-ZV{:_d`=X{_kѾl-O mOIK,bT?:^̵Q=y|\3[ʼg,yP#~{z2Bk^@h޴ѽj7vϸwduyv5̭lߙ,dNS0jZP=,Oq>"R,fAAD:  $[/;8m"ϟ9S_޲2P9:\.OR趷s*"g-/G 7bc[sѽF mkַjiCQu}oXv괿 Ty m5uPuڸ(?nԞs??ݯk6wG+gj2̱J+κ<#xy47-vkR-C씩n\>xjl+d?9*ioFAAr~   EE1w(,'_t^U|6zwfYZ]^9z(Hs' )C0u͎c6a1d wF_π\ %(l:dC"\ۂ}x-+Ac>+}?ء(* p;--~R,/L|d+AmF}!Z_ӉugsIzhmyΫf?'T顺(Sgu08y}XkڈcZt{vNj`TjSSExx5=&  hH$  2,تt8ENUoYkױ4EO֔^hk }wOȿ]8^_o*r&ut9d.W]keu$>_: =Ryc!tW?B[ Crl~Vg`.=K度Rus&kqs|!vx:E^gAAD$tAAсdmU.\(e_v+X귬ӆKT',4z(+D`f< H }ۉO]KHNN*Ԥ?_t)jUY>⿢X2\ߜxf;# ;[P$tٜcNN"gs`[Dcimur{t~   IAA8b9.N|.β/짳SGL=q=W=Y^̉kDGQU.λ}Pf"]h^W$wb#d78X$Zɉh wbI0coߏgnֈGYSŋ.WŶ(H೫MN?7b2/!=r7*Wn Vw#  :  耦C=_\ ^[]WwaZm[4n&zxQ#>3[)RmSAAqCB'AAr,"{<*qZt;c/2^N˔ݭxr"٢&#h=fL^VɒlTuZƙc:[b̗վ}x̿ nbEc͸ux%'~v;[Q$3=U5+-sR=N^wWV`Bu7  HN  B[p o__h+r -ʞv {Wdpy9m5,S`(YDM|'CVQFKfd` Qlݸ`5Ǭ`Qn. /TW-'`h$W9hv?US%*/︖]4fxKY8GWL$9qO[g 3PBݮՁzAAA  %LIG,EarjHo:/@3(S iV_~?k <ywǿLq%,X|>OeqY~bqMQ湑x֕EJ/- ՝ DtkQWǨr㯍qJwm&vu߳ƭQy[2^*$ɟTv>˖/fWEnAAđP@AA|}X1ws_9s#-Znj>x 0 %V3^el4xin=6d7asv R>a-`0h4@>&u`Rg!FsuӾ%_. b'{Y'ݸX]ȳ`d5tV گt`fJAA IAAH e/6x-ui7QJ,8s Z-co'i%ӑ?m3ڈglSyڸydRh43aS/ĔPnN:]9o B?eVcGf[Jꂦi4wm&pkvcz{ uqݍGyg-;OBbbVGN;5v\",vYu\^%Ͻ*cLAAِIAAHCo‡H9LC&E jXFŏoZ/'?K+k/8M/~ª-!y`_*TCU{ m'$JY7r7S2tWn/\NO5Bm}222QT2%PT2~ .@fv߇sazG)Nk)]ŽlgoKg'vb2yAEs&v[ت~Y_{!'jcU֖N  –   H2*"Sd'^϶FD^iaGߊHczV~OA6&cd36~2z$wꓳ 9ebS9zMV/pcx<|RqDBH+`eNTo77o ʇ0xTC4[o{k7c#;/JZP;mC[  k, 0e7F^4[[gX1X󖒟msl'}DXZ,sn[*&rqTl9YĠqG”  8!  i,zON,'fDH3v *9KV i±ovT-陙Z}#K0v"'Tin͖HTU8%%W<7amBf+xp=be1vw ԏ>nz -oLz]kx<|ּ_ZQSb vp}1NoōQI_Sg|x߱R.De{]ljWS%/3`˩SNԀw. gHAAG0$tAABat Z^ecmUsʉ*"'"1^p>P{~-۝E'EpCȣ7/c439G}b{JiS n:AO0)\OqP t^PK+ bۑn)&&ANKix?IRbT}GŖw]<K#7Q6KC  #:  .+`d 3!%"&k+VlUUf^"gC~8#3z$+Km\#K<|N\<,-i8ْ^@ ܆s[sk;piy$F.5{S>Nmi57UaeD1yPʱ ;5xb1nI[{]_թcUDp++M  #.  8|opSQEłSՕQyJ)ӽ;߇֗L95'+rjv1d1hIhEUe>?ۧJ}7]vܰ'`9ވ n-^Vjok|9gc֘|sr.iln^d206aN8wDrt{?MevDs\o5hbm5Z,Mj傮?N=AAM$ $  EE1EtQesn7*Frݛ~Al-\G/nޞݫX21aY(,{]XoԮbF]wq,xgVȜ .ܸ_ePE}F*u|}Dr]9AAq$AA8V"n$.$3eR""+NJqq4mjN+ODSz4Uh^OB!qKQZFXl~\'+a[V *3QBMf'Atb(V gaxw.wcBÂ2Iygwzc.vwb'+gR'vYVh̭, ^$c5O\Ct)^\.aJAAAIAAXXE%k/RGpLA$r_nXYNDNdyxdAQ$( gr=\u`2۰ׁ.zBҵDQrFffe.'A;b)™ߌӍ wϿ9 .&Ζo*m.V>~+(,&MBqآ 7#숏t 7N~;bk&bpcWs*4ۛ4\A唷ٶjjjB'/ ʙAAG $tAA4֩؊z;-_p,V^p뵶骑WމȪ&r:AY?o؁/ #nwː|=x$'_f.4`]hkA[S "0K0}.nA?⾑Tڰ! ۲Z+36dffC,t)0]G2_ʊO?3dxIcxϋ0r _?$PV4~'^Zt7$tQh&D(8QBy?(jNROIՄ=)8 [g[+v_JAAGC]AAqxC/5GӒ$+YYXVB(|'%o[U4nȬP<5ϗsŻ-*#GcPR:C轋סŷ{tM 1=W5;>]k wr4'y8 W4p4kdž&liWYMnI^x2|u kce# "{L`wB.C[6gŷ!Ûŵ[e'ʹ9+ɽ+n|͵8KH$M$5q:ƅW/<}ZkzF5དྷzۉh4 ȉe''DqH`,`$pH$bBm'ŬWwAUz4yE4 `r%+?+o ynQw {7K0vTfg` a1z:#T".`uk^z=I_zlu Oa硫[V-G!h{,eJfVJAIhF1(*t Z,p$Ҟ]c虬)f/3^_Æu!`pҜ1h'oo&6XxVUAm{vz5aS9@VN!NKg;Xon<7K.+}Ztjrrr]gnOv{ ݅< cL$By)Wwsuv4j2wchj!χ߼2(YYmU28Ec bpJHAAސIAAn-. 뿧×ģNEG{o9[&8 x9l+p k9G읊YoG'd4 1zdeXMK &/D,AŚ u17/СP>X q, bf#VLƗGwЀ@W ^\#t7`”s0kz]\q:ֲeC'),}')J~}ݝXj9\+QPTAPPT0btDOW݉O\Pv1tPx^ٳpADӂ O ])Ĥ`&1%TL;MZh'mۋNrs#iKbiljQ#ޓF?|rZtLAAIAA3ro["?9e99b)k6qU%]t{Ӹa'rWb/rc892pvg`pBIbWNjlbQTo֬B&x1b q?EÒ(pxsc]R/9a9YR<"GV."(U!U_NeEΠ+? `9Yh6tZ\4 x7I4p[<͟>ۓQ'b0j,dxeEN^ٶ^_6  ISMx_av 5g,)~Z+liE~`[eDȱ')&Wfc.f y=;r{ @JԁaOĩno)>sù瞋Çcى躎Zر۶mÎ;el۶MbXۏվ+4L ]\q;pieÛs"{ɋUYTo4(f"ym.ľ N  N  '8j" NEtLxDA4L*B`h'oY S4i0IbAcزY[sK1ey:uxh7f~xZbe/ aޓA4'qmb7 q/^g8DB5`\nVD͛7{Ƨs-܂? nK(ݎkKG|tS[/ou\Q(U|V\W'y 샇  AB'AA2,: Wt3Qf^e.kf[^dfO5WW(2jj,ͨ\βcqW',yeyӐצ7F :*7g}6c}1cOeW܏. Ǟc".r3P7Ն\)Z˥!3+.[/jhoۏFQc'& oJߍ?:*xǿfc_2>O>$>ձQ,\t; :*!Z% IS@ӒnjbcqKI 8 k+Pǟ| <j7&uk;SdQWCFLsúOU=ߺ'z2O)SR3a&0xᇕggo,Xx)ݬX!zrӶf3Ϝ;?9XTVgruqU2 ׹>Ġ_r  k AAG0c~,Qf?^/:VS8ּ¥M^q;Ӊ 9厾M<&q,X&us=5DcX#gફsKFXb[T k7]39冎5hjĈ'SAِI[*'dX,X (vhn[߻heC'' r_m-׊_a1g;,=v8a%ludΘ:u*^z%\wuW,;2<ޱӍڹJtS;G:d-sp:1pzϑ8y̕  &@B'AA 2ˬbOpȂ2SmEq䲛sgZDN[֪?SFb0^)BȕPӐV+U0d95mr̬\twq>ͻܘ{-rMA8]ldbhS$fƅމUQX:YfW4¾ {KeY}]k?gB'x<??x+_}*OG|L-y5#[ʷ:E8U?g^_)_  z~  8Q{Q炫q2BNh 6yEcEև5pEFaӥ1s$aOKۋUL9:+E,5= sFa'\"f㈜_S^ӎa7^|&C88dR0#3Vzx(07UW0nk[[%}P//b棯cdWs2`+jBѿcX_QS]*#=}ni1:]$kl*"'/#rDNO IƱ|NdmŞqӟIK5}7;X2Yc *ڊn[QXy:w/Vs;V-GTWWfn/8܎-k_*ѓd֥XXޜlb0ݻ7/^0Oik5 (>j^_]ӍEl|>k-  m]KA#LLix. WtF;Mx37ek=bED qTHh=1#ڶ#5yTs\qdWqm6Ǝ /鈜0Si8m[wEC} JJrS]u<3QDD%BH:v˅?#jV<#&|DŶAyi~od_Il1mÌ_Fr۞ YS(->߶oQW[ᝋ$a0&^"BcԟV4k;wO >N;|ihm)?Q_(pgr.9o1rÈNj e4*84e*OZikR҆Ȏnݪp-ȑ#K/ɶ>4K# s?Zsa2^5L4u) u +8+i{WLX  \N  ?$쪀uxc7sY_ ƴ8#W\TFPQ硌l$*wDz2Վt@mfL&zz*,PYQ;diiCqI!F6",r(EN=̼uĵvsš=_Dh .o4dm+ۺc4oӹg[F"1bq{w~" gZ`;} \ur|L+- _.*5<ϓy:`ԩemK_:F]z3wyTkL3w,DOtAA!  2\>@z{%Nf*IdcLf^j?ok,ZiNqBɯll.32Ƭ˟EhhPR,SBEΦ6.Cqp*Æ`̂)j̳K1,+hNUC"U JstDM~p*oUw}ffo|$IĵfňI)1K On[i&9"4ZlT58ySؼI8jeFʻ>FѷjoBi ̜~aO30=b'cG%ן2Bc1 RRz˱o>N,21 x0ڲjR/r4vHn63o:l w~R}Yhm5]OV8≑եy8s6,"c._ WD{^.' "*As^zgZqJLխTY hŪEaa`ѣ8?G,49kJY+ȩwzA$x/lPm{N=3}W8b`L|{DF'dv 3'98}roDê~jkʐ2\ASne7U<4%[Sk㓺#1oE](,,ԟ\i׮~iY8`?c/,f]E]{}l-|;RLPAA AA $ |˜mn~ݢݧgԚZBbyhRUt_QRg֬x20ѳ%ތDNgi09A?EN_;&l@|r7vϓ+㇥mhM.CIfcP^rR} hGs]/ 'k'vOog(0$*X/**… 1|pw}gm[ɓ'c֬Yc7$5ii(n=Q\{0sE{ߧ澓- xH  ⼁N  ?-[Ƭo#DWј*o,&TDk57+t:mMd++%ɍD_d#W=E#7m9uPDNf(r2328BP]]ŷe >C@YIn83!k7p:T(>_25U@ņ/{&*'{&]Տɯ>UUXdl6 GD}nЀ+V`ʔ)3gvO$<HII۪NC?{| Asl[;?h hAA$tAAAH ۻfĶ#*-CհFe\Acfor6wL?Ӗx./q1m0`%{>O5`ӆ:Yo0oWhZٸzFLw._~}Joa  s:  XjCො m37S7PiT| + m~,mUi_aG9i#4A-m>vBok1lu͊lY뛷n 4wBHh$ z M$qאoqsQYp|DC#=NïWfP_WGuEf%_B<"[HsS' m; 4><"8,kkepLDא8~7nČ3L#z` :7x mXǏ{-`^"k4)wSb߁cݬ e!  KH$ nüȋm6/U#-4Ho >iFPVQu$N5g؀-|{tQ#o+%[ "'0S7v5`IQTp~]-]P^iG\kT;5~Jqq2gfvg x\mP\zÿ:m,6]QwPQ6: qIǟV(*Szv^&[+%1r'jE0LAP8,[$a?~<6lO$@^$$x=`?BV-Yթ}}cF Sv#[ /S}_fߠ`<AA AA>d $F08jQ97+ũ̬vgZN Z:yb'$ǦC aqt%$Qɸh\|9TWѭ#h_PSU_}W;[شp]n;׊Y0zSHG=&\@"6* ӄi\i(xrPp`jd$rBvmx79YFTTz!YQd;B1N2 l4R0sF|+v'  H$ `e:ҿnZu$qX^8>-Z jB++jJ3:\\RTsêOUIJJOjkk? `"W8Smv$ CQQ1XɓxqԂEcԔe+(=s#E )|l!#h$¼pKBblWI'=cSn'.x6u޴f"d>ySg=l bjTz8n7z),X@D\~HO>B߅)W)~1\0Bw,$[]hPAAĹ AAXPo\i|6xblU]g3/m`U#mY zYKTT"^Omm9VdfG0P5|W%fENL}] 08Kfg&~Y6͋s片î@۴Xa.0ݏû֡cm5l6WĔt: PUQhoV*$z+xW~)Gh xM4쌡oX&FE!j[xIb}'k[G uEE7EߍN+RO+30NKCAAIAq 1'Zox QZb0'^edp_A/$lcj?+ аs+vU64 W^+ EJON?_Ï89U6&ENڿΆtɸh(ئ\6~kX e7cx(>u[xY6 _$q|z uuuh:M}Pw وdtC;>7Crsx)|jjE);k]{!s?C0!:6^âEĉeUGe8`?ֶ;k;+q|Q˖ ]  8W!  Dž/cMU+kD \L450oܭojN8b" \ {@ܴpvofeS R՞CC?t7E3/9PZt\+EŶŔ¡+ibKp:g7Q[[Z>y@PVt~?~<.,{2؍]_w7}wFEi$vG$j9LDH\iVv XCd6nYOvcbC=֮]NOc馛dmk#N4Gջ2C mp ]g߿DG`AA؄  8a2 rAQM]sE̚nF5_%VfAHH܆1%vBa\]HK9v5іFj`%(81Ԯ77O¼C9z޻n6TM,=!|;"+suiֻMnHHM{6aK3a.?2nn\.0ЪU+!/G'. \ ua%ef#gd&+_'EdP].tdg͕(h*v{EJJ 222tř9s&yTVV~tINY'yR㟍/}Zul5r1Ӌx Z sf<A:E'M;f2J` >7qqLf2[KY-EQo7a98c:"EVֽ͙ 'c2 '7UcTUoǫ9yH$IHizDNXlWض_xN#r6W,u#.xV=^)kWlv5{Ǚ,$뎆3(;g]%0h |Xd ك۷_FEE9,X_Jv;:\:DZ#ħ)zƕV|Z0&TΩ2ёgvVWW[oӧ Axx8.q;C S3sԏw%oO0o#H/{AA'Vc  |e)g`7'21nZܢ1XbX=TRK䥶=7J[Tx|[1 ZWx$n ,av*8 x%'\YYߋ w۶0f7ɨR95Iw r9ZUU=!*N]bk?? ҇#2&QWS6t8lX8* y0 'v^a>Zzv]`ǜ9s0k,9 il֭, qCRR*+*Pp|N؀v[\T|BThuk~"8Wez{k˱g2O[ cHjQNڅx}=R٫O?3gdW2 IDATf |(ՊիW{c] ORJ_@1V:ѯL΍oiuDY 06%  GH$ glY?COfǷҎK/d"hR S^w1[?Oj+N*p8| 0 cp:q'FTd%8B4kJnNzUo:&X!{,Vt} O5!4"gpm=M9$w]M릷v?~<*K!I\.B"`ڶœO?{h߾\k1d; Ŝ9s`Hݻw8}%B¢^0^”׭NQj-k*`8gh&4bA͆<簠ɓaQTRR-ZIHe~z_0;Qo#[\'ZO<s`8F+d+pp!  s:  .`Dχ4珘&*)sc*(jmBS܁'/=NtyiŴzjL8ѵ"c :b1`,=c[=>6[%ķjq7@D v5'SWz9媴cV L@dlk|iٍԮQ8ѭRa9uB9}C}}=!Y\1b-ZdY9r$233łbͨAbb"%n't}~ԑ4 gP#UWa/x[1DM1~3jEՊl`"!!!/P\XM\nÕ`+p ~ *y#~ˍY T[AA#N   ``t4qsRGDaҿzv-u3we>-fΏVV3=f+[>bĀ}{ľ_l&M|jf9UckF0)rrAkL\w ŻOB*Okk[=s߄}"<<6 {4i^{5kv#77;wķ~]v!??S5KHHyj_oW<eray*秚OYP r[l?o |iN2dm~!|A\. /1=zk%Q~7;E+q,DLj0N nJAAc@  d9f`6x!sG0Cƭ_M*!37oWs dnOHH-(*:_<*>0qJH#C-kY gTq*a۾^?_7!K߉矪?9TAa.0PWWŊ“{1zh<쳰Z/Xf 6oތURR2331}t oƊ+'`ߏˑ1'4cYE+/浑U@ʖWbCoP_-K^~$}G‹/P3xHLLYvx<M[9(ΧHxx,DXy  ں  ez\ `Y?xrK+kTvf俠e"CrSjiDŗayZF'$$G~bM6=1uc՞B&AC߄)Pk*WXѾPz9*J u+ؽTWd0f㓦Y-kɬnGdT4: á5++ _8x j}8_o>\YYYի=3f fϞ7 kFtwl0gf2Ւi2~gW2qNűxl23S婤ݎr e>UG_Ç#..϶m۰}v%Ml_@;fKIc\Z"[dL22/ . Y_7--$ @]M9Jp5n#,, V<ܳWwFee|T$_?`Ȑ!8<~ؾe\0m4N2?5UgdٹC0 "I!! >aʕ_pBO7FYYv9䌁Cj~ KC[\G;Ki,Ьc׾vAAlAA Qy4g@+bHg*HTGiɘFq^n$IزeNNsc)}DRj`Wtl9棶 @iaכ1\vTjJSWW~YHOODŽ #<-kOMMŨQpbFՔ~OY'Es 4*4Eaߟ1Ҹ8+OF3ŋt:=j}+<+^VKP//Q;ѫXVj݂2  8N   8x$/_Dy9qVL-ʕ֋^eiǼ֌aٲex/6[\02MNMHH!0NKlY@29yv^Wm$ ӦMS~Wd ,=}] *#G>S7uTlڴ 97s44ebMkTw2@ISylSQv&VuqN'JN8U8NXx1RRRгgOt0`N'|M 5xSSejXe(X$VE?/ZpȉO?  s:  .0$}\h`T۵xzv*TW[5*KP2gpZBs:,py%ITqZV . ihfz&Lp|_jNp3 Q1"'k+q7VՆ0Gfvp2T3i$ 4 7x#;l۶MedyEj!?'t0QT́erfaTLT InTaP#"hrZGO($!''999Xnfɪ9Ԥ EH-%iՊ.}hD@bbھTJ-*  s:  .0cz p%_ *KȠY@Ehu#_jy(ϙڡ?B0!?L)2oSlfo/[ˈLrnEп?X,'ٳFYzm۷/WǑi@nݐsrOSZ/اT ,G(68% jkqg7P=alm8ۜP)&?W0eb`QUM 3:_O  8  CelN覫2[W 2?̦yoF6^5-5X/CVg*N:!44999hLa0f}2ٶ֔X(GDjB(r|ՀΝ;ݺuMlnѣG5}n*:SN \ X>KJؚ ^u_bˊ^PЀC55.I1#==IIIHJJBhhѣGQZZGx#z%`Tm; m7օagF| T> \R[v`#6͆F  N  7k$-4gH?y?L}LȊ+y(!8͡'bڷ;t &`ܸqׯv؁Kf3q=Jq(WG(4WS<䈔""6~خZe9vŠ ɪḐ~ɓ'cРA|OAAM6.U",:Q-j >^7UU/gNHƹsǧf,  /C A߾}iݻcUv8 QעwcP]22kap]2B$gF3'&NkGp8@,)AAAN  zp 5c?"@/?9NRu֘5kMnݺyڋCBw:w#̍xoy_JI!w}E`ppxR!f@Xfرc .nfdff"-- –-[d%OV5>鲾aHTM&xlg+,vUCl]Z(:c,ݭkE>}ꫯĉ HHH1w\_~-[vZde 54n;׶FfM 2a JhORp,꠨l)h9=  N  pe ]#38brWߜ(ü^.&aSh>>#Fo74js ,VU<՝<jSClKUBOMhSRtL!r8gtt4f̘ٳgG\@q8>|8x5kĉ**Kև`}x\aȬi5)hgݾ֌&&:1TGmI66K1%:|^RAAg:  . $>;uz&htD8sBl`E )P˜28VDAkӟɒ%G֧6[M~ 6Mm[HmM9JKrPYY 5qDf  Gh4"[!"2`xꊤbX"")8uljEb*HE믿0w\̞=Szٳ'zx;wĪUfnoGŊȣ^Kj:`dM[Hր5'#jA(ll0$  H$ `*{`p6s FͧX٧ҀU$(-j|͸ꪫ {nꫲ!)vmZTF8qg<UۮcGBt\;wD\|*Z%uBr 8">z*YUVʒ\O[RjD7ڹ*:f]VVv<ڷojɁ@hd=4!^ŦRT{-G4na[Uqu5(#л$ .ֽ^PU٦_~ׯ}Qlٲ~)+-8R!%XuVbRu*Z/p_h_z'Z-Z5`IAq@B'AA4%@c2+3#o|0Se*Z'k*ᓈXj׭[7̝;]vl6ᡇ9m(nیL0X2$T~9DzzM$7*PUY] q ihӾڦ@/b{)$*g\S-NY֞qBB#GF^z1p, .XG 橲Es9OեVbԭ'v*+?e(n9뱵 pw?'N%6 GѣQ]]O?ŷ~ڸR"UWW3\H#^I$De`D27bp׋ 4aNAAIAqAEVPC phM?V\[jK#bUD6ZhCbܹ"vD^^-:* ]p atI8|9 NwlXn)™,u%,ڤGZs*I՝b'ߪM-l}Pߖ"V344sܹs=9Dxx8Oӧ+Vld6\L++ ީq8}Qߣ҈48A  s:  .)m%4$.f^l߱1\ֆûQPjS1c޼y߿q-Z${.j̩O ,,FgsF?t!8DzxK؈HCh!VW7FM u(CAI-NdQ5: N~"h0m;i9E97 E'x6SzZw{7~1cd1Νx@<@nVUƍ!IZw&_&xTw*N%^/d;>}J2T74ìYpuA}1o<̛7۶mŋ~zYIX~N.UVI쉒Kk-.*W5fhEkp4.f& +O  8W  _՜JZʒظM5149[!Xle>Պ)S;DnLeO?7xC6vhۦ2USZEn܃hhPWbY0s`=;5 zq[v   AA~301|Oo[z^1uM׎S{5RN9?K IDAT0M?'};~xNfU3^v'EPbXp㮻BǎU:u gFIIGL̘'3# {'ygIVVyD͓QXvaXd\2kp{@ !@g] `Ӟش4rE\b' }+:;XGU xޔY۰; IN'D\ Db?-r}˗sβxQ\\9Qڶm_mq-7{g^K!ygt޹=Gկx_ȄNJ[;͛;!!!.,ꫯG}} ++(4ɖ4zvfRIf-Esҷssxvckg {|ҴAAqAB'AA2X`?-&*2"]/'vZc鈏STm8ciĂ?ҶroH1ݎ"k 6 гgOUN'nܹ*.7]:SiJ.Y4:QQQbZԴSHWN3@N\Ogw#  5H$ @Xt7pgN*v*! : ╳hcǑGN R555<&Ng}˖-ikHHHd't$yC[>1Ca=Zaˮx: O#S.z;]ذZùjfà1#cl0&"PȔ3X߯Xcnpjb_ "Q blW^U:qϟlUݺu?OmVշl2<=n.2T5Q+vġoa}o&[QX~=}Q>-uqxd Ϳ࿰fJ %SWU*ki9  \N  [kQQ)fDDE[ ⣨k~,}X L|u27†2{Պn χ@0Xn|AYԉwKeYtE8Up dYgj\ cHk]ѿK"uKBȦ^v SRE%Hn`Sx#8SO.Ñy8BNۯ舠A$a~%ܝp54VG'aSl{6K?@JJjNXt)z-TVVছnµ^}#!ͦ:p:v8yG Z/}ǮcqJnŹQ]6`Ugi6SZZ7oP\\ I`X}"33Í7ބJ_$yOG;ƉrbrSO޿ycڳctR-AEEKlٲ N:=8"55zBFFzꅄGۍ[}, e hs CP:EU_58' t."Hƪm&  s :  .a_⏯?bDT*QѨz9E^?NXBg=so~Į"qK/!&& ӟiKNYSDީۋy{p0 S[!#-;%"#-͹s$;:(Bcmӷ/L5\ a0񺅈oӭE!"h}]%ּ~ *J0m%w¾!CYdcT^=;!!!k֬A֭aDEEaZ srrpǼ;7,D# N׮Aψ֭[aXxlkP[[kڿcǎ?~<ƏAf5?Q-[Gynz1=(m'(`IN{NgemWc&  s :  .bح#v pd@NgOl ɟ{0GufFvfO 2?xDŽ%Q$I}݇7zڬV;bQQYbaHk$dtJDNI֡lV$o^kT99spXt) bAݎPNIBۍZ .T#/X|9/_={Ì3܍둝~F~dn(mܶQ\̑%,ۉ  hI  `fǷŪ'IN^N-^=Dsߎ?_CS|WrNY.(n{?~{쁤#b0ꔌ]Zc`ݹ5|RS}vV6IWRnGˋx'I} x'W{\v;p7m˩|ڕ}Z՜<+]<}7>'Ŀ"C|j n t<>Ə+oFrZ?T)DEE;_$}v,[ vB.]s.T} 08"Zq+6eӗǾ6HUn_{&|~Owߍ9s 9vR-kDmDQCpy\:66r n馀kQN'fΜ={xFִ=%}=&v˖x)Wzuf$f'@54ѼAAq.AB'AAyRLk`?pags@T:5.,*G|ԴJ%a3jzUW矇TWW_[\/",}Anm1[+n{K.I. txgIE-Y6SJp+?{.ۦ iagFT⽿OĠ sힾ*4V4z3g*=;gd&B 5PEtaFQQTl.ޫͲ]We]/]Ŏ H  !O2GfΜ>%|Ɯ}33Q7{pky4f&f.zRu_GK\#`'Ƕ7@łٸ馛_mmmhllO<>)))p\0L<c. YæIN@WvNV.;u۵D'-;DopP׿]tb8xv%m,Dw:abຽOR]jqj+Q XQ f0 V] ]  ^N  >\1iHu):^+b-:_!ڽ3sכ+"ɄnMxܛBWN2֭$a֭㏱m6LQj6bʨ8`22w4mnYjYZSC`JjKl;PɈ%WǼhK8a۾9\%o_΂Q0}1VR6.7ʒݸÆ--sGBkK3\-Xpk`SHr#|f g=N-+p(ܭ58U V+L&|>:::`2`xގ$CQ31h< ?Ƽx3>Zz3Uz8-7ވirw}ؾ}Պ E.w^ñ6jmEǣꪫw>,}Ybg٧ڣSoME&{^t*O;ۢFAA"AA}G7sOsRq}ܦRLJJs=8æMr퓛iICq޸#^ c!4U~Vs3u gr5nZ۽X4lJFv'@y2@Xжǿ;^c~o%WUCT^巭HǶh+ { ~> Ѥx||p/&^kԝ«\ =aTu5'\[#?<m{CF”+ C%g!B+\x =2ZOKnjr/'D[[j0'))f uM5fcNTy< e.UWW{+?;vlsE¯~+lٲEEEZC6:NeHv%GLĘg@  C  ÃkްG\wA323r= x7Z76iy8|/CFJ[VtgRStS")gP. ,$@?| >$%XpyR%CD `Tv^?s0]ݯ*`1'I6c5'Wχy8˘:g($&ȷ{0U`{"cx~=x#$qzy\|]j\!<#1M`l[SPq.]_HKK ;FYY~,A6fƲ1 MiY .Dۍ]*s׮]+qmjz-zL&<(,,$ێv"o\jG|8yؘ  $ 0˹7hUuz> 4b3F*4/8gV~.6,3f+8|7x_8d.:Oǔa`OwP:j O Ch0+3X(C%RY `t }!IU܎g^ۅу0(+I$5cHv.^8{JQY.kŅ?{ Q {I0 |>~|Ops3cL0YP0VͷØ7hPu볲ЀZ-9Knhŕ88e68 O=&N0yd̙37o3>w/H2E9ьa8^Օ?#  znAAqU X2ʏE^^Q>ScMZqqʙ=z85>WL.8hnK5XkQnhO00L&|GWޡv0}7?A|*Ǿ3} }yjF.^F[wM,V6y/?13T*GK?)СCؼy3֭[ɵZqaZFEShF2hk+ ,Y˖->y$fϞ O,f:zmGH^&^\ZEx{t  etAAa^5f^Q3&9+S{0As>NK$33nt\x7[s{Zepbf.:L OYzU޳ƚR<=@}v3?Q}؜i2rJŜ# gMQ]y4%'Lh)61Exg-GX%Zj%eL :t/®08/5Z!4#w80nǶzji.ǪUbʕׯh1p@r-uG1mXc BG=;3=134֮ˑ  1HtAAaٙ-vS='~c>|l;Pnl !V+~_ e׋ 6_DCCjd~vdΞ@"e1yZSMz~nG!9oP<[ڭ&x#wtm+|~_}_w.? V NQTKOT,3s඗1|ҕ8jRaC;q>UFev50 *9&ŤŞC^E[ڿª߰s9| G/m}X )Qsb/rF] ZbS"  ȥCN7QJ ;%&'#!Ȯ͈qffd`ÁOkj ]vaܹxg0sHF wߍ 6 -OS0z`>!   z5$:  0zrэ5rbڽZX6:J~:ٛFcǎň#0d 6 iii0L|ǫ-[}jylٲVBEE+E玆hZ}4ob'5< yDX!Shvݘ*IUY6bbqFcwA WK }0-HHʂlG\g&ϧ9Rrpˑ=< *Kv4M+=%ٙUapljAӄA38qtג9đ+ IDATًآĜGSn?kgY,3].lh@,`ݎIIIH35?[B3\6nuu(m[__n <nHf%117x#y2ޘPۚz~wΌ})AA}AA}>;T_f5vȑ9z8>$%%a٘5kf̘D~??H/^ÇU;n~^8 Ԉh]+z[aUmP>$gę ֫Nbxd&`Cv~oò?#'flLoAsj0y<Ù/IW5Z[7 IYޒI/ tdsj5x&ŞϢ$D.+Ce0(bU߆M4sz=mrpCv@˅6шLvX)βl0`vf&l8?0qwG86r ֬Y#w1_wI+A[鐊lEαrm6lB{_jckF;ȲX3hz~(,,t&U222`_@- 'Q24Lg2[0&  AA}^Cސcp268^uΣh0Ò%K2>pl2PYYW^yE7jH]<SL(3R@,7 zʷ1Y{ c1:jKѹi>GS8Q?Y]}h.^NHŢ٣T.$dI5SG@T{-5^L63>#{T|oX:h5P>lC7̖8 M5%9eFLY:!F:SE]ffe$դnR 4Uy"<~cgQ =mT`VRŒ= }Ahj _?UYʀtXt)}XXf 6 3f֭[uԣ\z ;3zFFJgy!`pO/  AA}ܵ=z<50:%mjXXm3_~9;fO?J% 2d TlOeeOV")d`%Txˠ͈ڃU$ 3l_b)]Z!<mv#lz3,]>y k`IHBG[c8gZ.ƤD dͫx& 9#/ɒ =hmSB,=0R (ndYSn---Ŋ+D;BAt uⴱ 1PepЁV?əY@ώ?L"|CXcd Znȟ+Lnhm0?:vZ+H/ Y苗0?v\(Vi]G/V.y^<35n7hĥYYxBk֬ 0|XG_D47wn;Hhd`7 ܩܑ^~\C l伀;M߽  F  ZaAHTs =wg~7377V믿ڊeee0 ͟ ރ:3$/C5*?Y(F%A0X*Wʊw1WK$)=|]Qy,~3O*v~o9Eθ oF>ݸcgL.֗ uBBocHUӝe0ZpkQu|r3e{d=m^}#/UT`&*c0cEW6 1*1Q|ȚX,\tEBc% ?㨹o8KPv,o8KPkh9F?DTew}  '/  \1cyXo|JꕷJI=NZ/!~QΤ5|푬G&y" ֗Խ(CƺnҥKbшŸ;w^I}VZ},;1$(2+/%̆E&te,cɚ D5)22YJ1:,ہע z\0Y9p?r޵` FQ$L#_ fK1jM0̊uWLS8?1tU0meu W(ʼnyqwr~&VhffԩS;v, ̪p͛7s8Drr2 O_深>4CG9L-Hې٢0_CQEODAAĝAAـ N)5ыNy': zy %'a`BꫯMR9{!a+ *u> \H:EX#kt%kupswdq`ҷaMH;x{i9.zm⾧}M+o%wƠqD_"׍ a.ԫӎMOha=Lٜ,k!l*(yXqDm XB@3-gtC!By?rZ]- ?E ---?~<83b.CAAF0՟ ֆR;v {Ŏ;pa<5F1u&;E+Nj m,NIwjzы:v ]  2:  (ȿe6ѩrgEztwD౴8hWܨ8q"V^o-o/_-Ա,_.X-fgsdfpkY d}Y,+f$s ՒvM99]Uj/T+*Zp3J*RlSGyӟGI~J'KN^D&].َƚRߦV+= 4G)4Gh_ɄuLuχr̟??UJ)--_|!OaÆ4DL6 /FVV?&IL[8mlC/vN}L(z7AA N  >J!F`Hjq!:CtOtnJ8)z%#FڵkQXXs|xyfI9bͣ@vFHD'Du e +(8)/H<]Q'H j骰J%CN;;:v~# EU€1 j鹣1`6ێo< a&CbV)(acK+0OX,H0/4#KMfF+4G0N? 'ǏGff&ƍǻĉ1iҤh4b1|p;v rS>W1ēJ >#Ia@ӎ !$\H\?.N^˫zcEbU}d}0־ͫUK {|vv}:$u]2#bDSH? X'eye04BrGfI8B>rΔ\IS+pfe8M&Lm/#Y))˷'ǖ-[c)269pؚpg# Nxo*  > N  > @w!xmg|x2u7bx衇`6GH8?0>cI 'odf"#3(ϴ\P_Xr$ԯ2UVv51+?G<)e}lגzRκ2s,]vy@&;qҠ1 =TAAD$ 軤"=)hY?Kۍe1c6mڄSv˺x眇?/AHlɶ0M KHMZj󆓞*dOBkQapSeqNZd^HݏZc q7AADD'AAD\߇7J)Qln~x饗msX7n-=c^-7 bɩٙjS%S>q$>7We>FCr?; &ͅY$˱h"DtUUU|;ٳAvv6~i;8sjPv<uq6uKg'pAAAD!|AAXyK2g Q hE2z 5̣(kdq Ϧ~K}aSdē?x%uO\ frt32Hש$SI@qNm)EVr,NݦMkq3 pSk{-H6~ZЎqZ`C-PS`G+P+#3܊vhWv>%`Gq *=Ec1 0SXѓ^/*59OKiJ2i*VLTI \g2XYVuGPWY$H0Tef Mf 4c&m6inޱ|;w.l6yؿP;ۧµ^#FGssgxY ~;|EHuk8>  MPF'AADI3WnE^K<i˱dɒnw֭xꩧ$u7s~PݎV^eJJ?3S^|MsrPԨ㴲JesF@Ɠ3m Y֔/mP?I֫%vFI$epU{d9ߔgŦVјjٜxHvLn eG$aWyQ4h\_U}y>R)_ Q_2Fi&4u8+Q|hjjzOnu`Μ9O*ҭ1v"X_ѝmEAAt$:  xz*hZjDRl2q][xGl2"6e 7WeDT;Va^תՋŪ^Ye[pbYޫA7O[T޵ Ó ;75o6>)j[ƫL榘_d[ؾ4NFv~4dT`b >—VgtS9"&gN 2rJ꫽^yՄf$2R_4FZcap:1''G"8Ei 6H=a2tl6㗿%>\y u,܁}:1.ҮÚٳw}7?|kx`1"ɥMgoV"S,~cs>ǭ,ν _FVEH}-VS(q5d♪YS%3>?IzmغD)%۾d\U2|x58Qe9`  ?|'  N/te F`sq3:=bߥ8FF/V)7j"8p_g$GN럼 ' Tp7yYҦWW HvU@Ik0\ D`k binNV6ʼa ph N>QIN 6ѩ"}ł7 lV}'lD26"Ta쭯Gqxg0e|뮻P_ziӰn:8p~);\u<ݕ1HtjIt4Oi;& o6ƌ>G+駟| lQkZ%"e\0ih2WW);bP̫ՋjS"آ-HP<.H VLO, 6`KPgm@  NXPpp3|RN73}q0MMDD$;a$|#Y>Sk60Rr@͇q pjJp'ԥ>#] &OkśnoW8p^=zTR7޺z$:C=bNAA^AA=?C 0+cԈ< zw+DH!ە1HtjuQt27gP,+h6,[ k׮Ud Z141բzՄ"#u9)+hg3(65ՐЫ )/̞y L::m,FxU0[$}!*Ah1a^^_)/91e"W;Srqh\g^ߏl&4#1NBS<#z N-ʒ4pNz:iiȶa6$ej 4wxqq1֯_1i$ s&''3g|si?cg!E譞^AAm!  z @㔤|]wh4<^YYJ4a0TLJJB7,g@r9J\06җh>K+V/x@ '*/3nx*[_|F然z\8m 1[ Iְ2+D;=uɇfdg#ldx.!p`Q n< ̙;wF4v,5 *Ӆzz>Ξ^AA=$:  <zV/bZ(`1WVV XR?jłLX,H<%S*BӯQM*HEuzTW|)BEe>WW2 ũ#qd,:ϭ%(g,ٛ'88:eT$ޥi>~|]4haBh7¼<:|8F''Ցzٝ,ZpW~>.Ycpu駟Oex0d 0B]ov" ݩKςwAAD!IAzpsu~(,,.// 7܀J091SSabz&)٩2Qjdmƚ"*5''jSr<g{VLCY9ٛZR)DRy+ͺ ب2tG)85)t FrJ߂p[֊y`jOZ::?j)Qs3ݡl?1 t*! %Fatr$8Z _<vV̤ VAA}AA}oaWvFR5 .DEEPg`NK àG(e3h7JvZRK|ń2VN5)Vm q@[loh_b? #T{ﺞ.Iyyۻa`^-mye\ /)FNh $ԚOk0aظ˅2(v~BS  m$ 0 li˾1O+sh{\7.q~?~ߠ\30 .NKàw7cyr]^sFdiHHY]\ǩ"+ÊH;5"yfgp=r7OLQY,=kagIHt:S#%sLޅPdA6m9fSk]Sfsɟt$]]fF+4rEqs3j::rՊ቉qKLGؔILCCn+O,_B҈Cn/НDFAAt$:  $e2Ι3wY;w,٩ȵ~v-~_]5A$J}<):BTW[, TsZ5@Mc+ʫaca5C3;pkN@?S*=UŧKV '7Un:ЌHex!H/yhc΄dc#w>0*'cɌǐm<8G]SjQ.k@U -ntx|hm탘a9aFQKd($YʌQ҆7w5 eтs E<3ѣ\ À@iefd[F$9U3JW2"V ѥ(pȾ*b/&+!PqłP[ ȵ۱D5k֠+Vdjmz,\+WDUUUF|6I\mAAAHtAAMz\tv0"SH 1֯_жVLH<p/3KO Lר{cZWErÃ-8U݊6nVnAUc;8k7Ëmy, TƄ6c110 'cF8Y# 3Y+`9DL qd5m!?o8]F"+"Buɩ6( Vg ɂ# `#e*7~' }(V,P<4'qq"ԌKQZfWf؈0c0F#n>Nľ:m֭Xx1^|E8ٹ1h"$t8Ql+,&0fHTGOJ8 gk GAAD?;60.="sNcl߾]RγyqC1$p\5[qhy#O7q1!Fg TtTU#44l(s>2^wռw@ׁ6]<[qk*Rq##QK 9*Q F f^3:dxlM+􅧤].!IlHƊ83ǩINEL=+w,~Ty<kBS 'ZZtn7\X_VV ۷oǒ%Kf]s7 vXe3'  N  þ$m6Ǝ8{쑔2vKB[F<@LLv{{7QT34#fg.LaN̅)q LI0:1kOB/oƣ,F5 `Џ[{x9Z׈&rॺx4X0cr L$!kIO|honĪkP*5[!sx0|cD-#'òe˰b  ]8a 8'O6߅;U#  $ ^/)]ߚQƇ&P3fLL5%%%1 l0(zqIn~e\7=ՃCUhw/5š8ymO?S# B  m*0?̭DF bT8v0,<=n$WsG"/|HV۵Mm4>4+CI6GɃaO [ؒ8aɉEgw!Aɖ=bk;ڣXp(fb>gG$2wMva 3$&K)JUNRY;^*|n`TPhÄ6d'u=͇].|x[ZUy^%b̹ 1`02ն f>x(>.1 @X[Ն*#*3 qHBSg΀eN̄ES"Ec-]zgv3;_|EL2]vYJNaQIOF$ 蛐$  AWq$ieڎ9sF('nno q-kj9_ƻ+jx|j2UCԻ0dd;ef@nAeYwvaN` k0YPo·Ń(v(s1_^8K t}+ܮ͜ZeC=DU;8Y~ߟjڅ4ò,0cpI&;H4aܪ3b@ۏ67v/Su^ypƍ='qڭlIvH7@=}IdP6Sz*Zٖ NiHNQs;I١ym~{F1@hvpcyfŔ묥 ߏ:ǏG~| >ǎ77i ?" @  ucs2,夹U6nܸ+++'IF#}>mYk6x`G<[ol++t}nak6#hgUUpȲF ̿JvUM>ء͆CyO `$W{ǣqUy2{'T.bUt20|ęSuZN  YN  YDSײgzeό{LaK[`iw#ln3! /)kc3P߽af1lيѐ%UrF׋9n7$*9n <|ġ%cSY3CT&IY:x}Eh-&2/2=-%gdvs>RGEa=?qs2M8gY YV@`L8bx*U.ܞ4f2~N+fy J2x(1yMMcb` M!~Lu%ªӱ{qp?cr"1cz8:øic- $7QXよY^eX 0}[G>c=d$cBSpgU%jٝ'ߓX!+Ӳ'6&KNrJ|ә*s*2Ӵ}q-4bՅw!Q^t3ZO4P* 1<>3 m/?8MudRNAA8HtAAN&edy:!E1,kι>ؽuvc!vIi#8~Z~'38<,P@V MdC*nT5o"7)4C N-䔡=7 L S?V7F"uH1#i7ϘhCe!D4uażWE&h g(I Nbd

Z :ėy\aǼР,K_"#  AAfuٍٙmhhȨ׾MxI }8K)k-mhxpJ 9n"{# Mj}N*xE`'7%nZC&'AbČ% dr,W_W uUc z-DQ0,]by?-s*M|G WSWN^,ZK?觡*;41oi1eqLIrciSSt>Ewv"ʭj|m1V].V^&gvZUt!fk&>&  f$  ȳ6 ٽ-h}.ܹs3eOAvA ,{L-cc~|W$EEX_Pi9es@.i%2cS<<T4\ >>o 3= W |DY^l1ArΩZ?w|~%+P^ؐhO(bJ ٭]''`NK4۩;.fr,,INY%1$?t~F3.&b1e˒UUښE?O0O>5W*+۶ms:*S֬Y#ls:jw&$7  etAAJr;q>C4uj$qעFW%Kul)掅c[]ƌz8-@Q5EMn SP9B^_ YE9LXM;=xj>#N%1S۫zd`*%M*pu:MIHf'5,3#7pb24Pvuw#!^QZR]6Y\JSN#Ny <'N>p:Ee4KB$uVxg>)  bBAAљE=jhR7)/*ӡ@@(cUX^HiرWp/~ZAr\%YIT(CWHKz).[ ~% qkqJ21NJ 2Drv%`ʛ|!qgxisqcSXXZu=q,kįA $AAAAq3.ۑ'F윏?CgF_.H!4S3777nB½3A\V\ Ǒl0ylL8F!!5b1RJLI(72_d.fXupj8~QNb> Bi~d:#9LL' qTgkZHN P 啫p/y{M$O16LN 14_?}+ls$`*1 -D<߬ۗZ_ʺ3_WQ\H?S够Vo0 rVl} yI[N:"s2"~ #˖+>1ݬI?uSFq%~a]uuS E1$q ۋ^{ 6lH{<PWW>g]aTF9΢j % $ 0`}e|8n&&&p e77+,M{ ums̸Z˅QNgC‰N^9,!2 `dW29U 1ӓP\<߃]o|CG^׏0Ʌ(KFqзj+(BXJdQiM2/qNbj z|u8]N"תMC.FGq]dhkqjsZe iDVfT%8owK;O[_[ɘR CKZ4I<O(_x.r$ϕ^I&OٝYdya83MX)dolXyf&ٜ NKZhZyƒ_Dgg'I ??_ɩ/Y1VSJCAA%hN  Y=x r tAƂHԄFGmك[۲$ởϝGq͸^xpyI N˃_WPеD_b4E:ϴ L(HdiiV]|N> s’F3>}qG&$gâɩ 3nHX5 * |{>.Kp8_30:,:0Fω,ih}w0f _X&150Si[j"$rN$Ì`2VABRu  HtAAJh:ce[d U>`ʘk~bj(Gp/[p7"I+(EE(r$4y +WXEKmM=L^H&bRL%9Ƨ,Q +O{Ȳ6M(=ƣu8=jP ܄wD{%nϱ8 զ' g|B g磊S9##4o>.B-q$JB$$UzəC24fNQl).9;xcpM{WYonBȤPzݭX}m5)̄ZΔ(Lj mJOz']h)KZnM{MsZcDg 惛gL"LԖ).[_Sc$NZgÑ0J{^O±t*{1\{⺮~MYJ3o+  )3/U  i8yٕ/Lɑ }vl߾. KfZy^AtLCʒSឪ,p;>uO IΣВn?ԬK$e'^Ar2S)7eY(eky8:_5⟮SY7wt?|am}=I3}ĕ\OMv&I OUNxjZOkr:&sA(P;.)sS_wQ-;o3;Bs*BTc?'&`KJJYm1sUbΛ'V˾CUUhiicSuZ;N  YM]KA1+a'lZ;weFu 7Ӱ,\b044$IgN,qeI Ns,9)]?QmS'^o1i('t$hSnEZi/9TmY$)?Å/^x|O:kxiO}s 9]n/.XCt2 -Ŝ唶W~&Yn׬Z#9~v觸”V:n<>N9k欭5Nk׷jؔ qRaJ+о144o|@c ] w81mhZ  N  Y "nVoN;2+{UbĽdK< 6EvUB׋di)2;gs.\ix|)"1O򑗓P'YvtmNg YSnK֦ǁ߽Ԇ]z̵rǏ%YNɆ4qhhme~?ZYV1\ M%Ae\x1 ͩo.sV\^HZf JY5;(@+ 5@C+<]}>tt7} 9VS7%s|%괪z2 ,2kr %dOZ$cn*HV^:c`8X"pJL6lLݶ̕ Q|[^j԰ngb;Uށw[^_Rx8|0_Q/33;!Qa=NXBl_rkYQMH ` PavOiie{ڵL7'\qֵ# dYBƗ3`m3a+a?|.1  iD'AA,Df`@KZp4T yF\qߍ3i<5⌡'FO8CC2ഒҟ:5ë==BٲU+ 'Dx+(8撓Nd)-:X2$߈R9 ^׏#cny 8v@ʩٔNsFAl+8"fH?(/;=q (Lzp?F'Bk/ņ, IDATJj# azZ8l;JjC 𗆽Ht*7][3߿{PwYAAo'Nh鞘jQƙڧl-B8x> 7 .ĊPI\fB"%I]  $ "h.[N_XųJ/!8Em~~>.r[k׮EII^E0|Cv܉{,ǣQ<ڊkjpRAi\oqC͞prb9\`{Ze @Rv*88S\ Iy S jj]NUrvH #v{ȰZ֡!z:,k)Q<7f/=b\pv7&1Md'"LZewq-“XL]\cy"NK묍N]h>Zp Vtv.W[ Y6g63Md$$2Lf~ lsOx4NZIf7322:  f)$:  f)Б^f~e.bMA t[ng>X.S6n܈7ƦM_p@b?ut*~ n+5a']y,b$9&$HLौ"6BKNTFi*?):uCT^QQ 9:5 :8:o;3/Boco|0_=w Cbyig^<6p޲ݙ|c:)t&*BM1٦ =q:YQX,i־?~rj8Bߺ4~E "TޠU  N  Y 됦t :Q4^5ZO=&|>q;*;k_*|ѣG8wvST腎Ĺy.Ŝf ZLD(9)j)"4q2>!LP:v b~Z0I_uk`_ęN.d~81쫨ZK6Ke4N?z NXQYOÆEI6`O33Snl$^R֗;NдRk+\6juQ|ą!&&˜XB7Գ,V&2D'AA,D'AA%'SfN.2-өr[ ⧥1&cYs0xb<# <:LdAq7}b<@ kM3-3.4L- ik5 Nm[$䫔 .,]x*眂lQx8M:{pjpe>peccc7ƝiSWļE.>3P= cHNW+d_&˙i&"0S O^ZʌkKem]N k$@be+0z5Kt?:oX/,Ba0߱H4 h893"}?I L/'H\f&ٝZ4YHO3K.bSxb]lMe_,W!&>kNEh:>i M3FQe$$ K_,~1FE"H}7gJhD'AA,D'AA,E=3 YNUT{n_<^pDUWW|/B_Ae,]o g=^?Feg5Z3hTC&72U>uSnZY槅4[W`aĨ%EJj.$%a~^%6M ܊^h0`$w,'p˃Ng 6(+e(; uT[IO~l3J:,HݧREWG+ee1՗6^mmd\En64հն 4?wZղ@܅e)bUV/CmAAD IA1K[G7~iRѮ}jj/?˺)kW(,gq3[%d.]PO⚹s׎^BDŽ`խJxY_h'm^r&2*q]NC!bT; ܕ'qR0npC/nAw!1@E5^J8_MNC䴦M;Vxj-QH>>j)4Je,7j1w k{A UW?"48Jp,;:݉9SŘMBS!pdh]`vs[[ ŝ5; R(Adez13#'zAAD$ NaR1UZx@ǦM$K(Kbx/M @m0qqӃ++z<W([sW+ ŤKg*%͞Nk%srK6'Tш%'/6-Df'/S˩p|ర]YR; "j)`)q<& 011իכo)4TYc&;\Y|5iv'>)7ILGzt4d*妱0=x㕟hq$ "h6emX#ivLD9sP2:n, _{8[ZVg'B1iʤA j&X;QucU-Lt֗~̐ $ Ő$ 0H-;1|M=0/h֯__|S=p]]]FGGzzzׇ.GIe,*,ee ;oũ%%CCxU| ],$%0]UxWnS+23\ksOWLdϚ>O)Nik.'Oװ&}Wq*!yӤ&KN@19,^0I)oeFv&IVN5k=NExr{nMO=ݡZ N8Sx {+/%4K{PvUֺ0LƓQ bX֖$0cǰ E A0922QLNNcfVѹȋ;]48Ք qAAAAф5II}ѓ2)Q=I 6^oFѭ8tyػw/3$bTPАZB[J^Ӧ$|;:;ΩX5ySRLĦ2Mmb] pJEhHJumNi\S>+z풒jaBb%'4GF3^6 2c ,m Xu43Y7Si~A#>O$NŦZAu;)8ixҢ"S^n߇3@hjaֻ:;1]^V|ݿ8:8hbŊX`Ν:ףEEE(,,L9A8Fgg':C {ASSqzꕜ4tki SNS ?g*'\xmvYmd{j3c[}LD/j5u*8ݿn_v'Lo]zyіE9_.SYCsWg'B!u{QI ;8φд⯝h.2|[Bmm>27x#nFZ!͈ԒQr؏p v>ANߏOϛ|zfTb+/n+&^}qa_:}f%K?!nvvء K1T8f#4ud\}csalݺuYYsn݊x_^x!lb>;tVSJ@z><.MwˋV$Z"9|e*- LLD䄱amNs0exdTJtIy ٙȄ䶑s~1qKcQ1rLM^e}QWƾ"xd)ýX}L*DZ$ܼݸŽe,XM6; z+կbhhHؗv㲺:zIXZ\ Is*4 8 ᒋ.Ц'MছYKN,[_nZ9le>;4#!f>jGG; #/"զe'c* 6k JKKS馺?0/ s]d~48yNjP忡  釦% LM:Fa%P;FMoT+:>VII |A>L?Wb$a9X[^|I p$lXK ±//ƥ?.0IsIy–V$6fXSH23a.4M锴ϵqC8M).^miL$W$UX&M5eb[ J +|'|v$il uLeTӌxn }(S2S 6m*»ZY3)-&}Z$<{rlL4is.Δ;cDŽ74)gԕkY _hoow˴#2?Xp!.1W>n_!wj6Dtt!QF'AA,2:  f91fD6BSe#uf+؃=j/~ ̛7b1G?mfuyyu"lJ)92ҙ.{*`[K >o>.PR2QlnMf/D AX*Ll%ɗ3 G5Av% J7~Zb&?O.h uSLe NƼD*|IʜM3|VE&~$'pAU䜡 IDATL7A_Z!+򰲼ܘi7tAmqM>6,'|DO|<JJ#R6b_`/k6e9 $:  f9$:  f9- f+٘ΞW/jʾ/a͚5;22o{nYƅ(ӭKi㍞,ذ(+]`ӓH5幾'nF1iV?PQnX Ÿ́/:=hIF2m?am08j*La%;51CRꄧ]tg3ΐ+3uM h/xѯbtGS㖅 k88%mes^X_/"4BnLDB#G{'[s AAKhZ  Yuی m_#u{Zk>ZԌqY|i;tGOOOn&ByχOף<h |mtLLൎa٫o57ML%$d$=^vJR?W3=yj)9!iVۯV̔HTˊuPU$Me(T)O2:c8[wܴZTEv7\W_uI;AJqͯ8Ni;&&ݷ[?Hd\'X[Q*+ꔱ4e[qq˚/\Dfڿ8iRR=~{DLw:,{fhkkC{{;zQ\\%K`ŊS]aزe >ϩ%J?D$k=e_#_)AA1 IA@N;Gz Hz%{;ЫA?۝GM7݄ceŸ^(f1&q=Vى6j0PG:jjNIr";fqv'0%Nd"] Iie=> ]ƥݲqnEVb-CiW6f1lkJ `ZTbMuu3]B,N1AA$H{s?#byϭ14Jvmi7x 9]+Uɩffmp} Iܯ+J:u-Lu_2s|ivPr:)ilqsu7ʙr1(D9S$qP^t@.SO`8?W{f0Y7Ӭ>4iON_][d:.{j[)6-4-On\RT/*+X33Ϩttm{uuLႆxdK 9ZCSa V^*cj֦r|_'ϙ˅ϟv ^!d4UԳsN\~7(Ԋl޼ .T`maN35=ٝlϴtCAA$ 0 yI+h&CuWx^ v7Fofa?'oFssZe|j 9\pIbwu\N'ψj5]c;egxZWvW$gpɑ!qֽx >؁wv/;?4mQZϟxfXf:mFGqWv<921%ct \xq%ǗW> 8$T:? J~(10 ehtvbBm#7OSX"WQUUGy7p$>n&2۷[5GCrN$FAAL$:  >-бWW5GJ2Q:v#PZ$wMrߏna,mYz[əkir8>v75ɋ chSic+Ӷ1 E^*prSSBx_}j;wP>\{8v1^'I$ƭƙ[Vl dtruZSV e 1cl6v& mv62a“k Omotvm& Xzq߳ؽk :L|8+JKmq"lƩO"N;>k&cJ׆$f[yypa,,.VE'j~g'E&0gBɬL#0^:46vR&2Sokp{y{EUUaeiLI7SiFtkvc]]/hi~gk+If8*UucY6gm׍ ,zşQmrrڊ[sʜ9s#3_˰(\ 23{s"SAAIA񑁽获p/uW\pD ooBJZZvV vaʩ(]E .8RڔPgj&mN@rSvY?%''~ڗߊ- 9*,xAMxX\=RL6XU) H uS9Z|dr%4pm%[o^7 $eö-kWƈ8#oǾ7݀wj*9K}>\Y_;-饥p)qNo>cl"Y>О_ue^ f륪pӃG?`]ZV],#5Wuk\.\d f J~C=;FPdI'E%3nLYZ  N  vc2cX7-Uumۍk:6ƹ̺yy8i qưKw7rNQq>U] *JUxq>Rk&8 RךLCPrJar2-~W>)\_A\7⚵_?~*caT<x)ItzL;`<JJJU~Qr CH#v2hpIA1K  bM|ֵNL0"!E-[\=5xWrۍkjy`?S>+b_85R,vU덼W@Tm.) 8 Ř9Ǻq_gd]h1n⿰lYywxeF:EasIUS#*3S q4Xguw +4%(x}dS\ӹ$R : ƞp?_9wWUU@E-bXдD|!> 9\ k$:FGq"gYV>v-nxET 4|nz\4 /஻B8QYY((n?~]|aՇ)k9  H$tAA> 8M)n#K,q`0o}[RݥzMдk%6듪_~D̨8)qD\yqkrOhߘ:8NO( ƜZ_^hr-w#/t%UX[N&cl #$Nթ++^Ƣ&9z|>ʒ[IKqw2?@Sx^ώt|nz䥧SAqݸpem4'׾5D}}=^ aggKugF"RӉl$gɊoID 2QPXefuĚus^\7gC9Ԍ4Ӓqse% 224-T*F~t> &gx#R*1R,hZ?5< 4-y'\.X#;O: He8 9Yx8}MvIGd=[HT4S3%˴rH'}< Y =QF?c♇Q!ˣ8{٩PUq&xjqSDZmR4K %u9ŸO*EሪF,"@q#2\sڮ]욱\]+E3_@1);p?:MK*/Y+/GTHbfaQίނ=G3{pi蠯>+102Dǹvbc5P\X]h @iz:Ap L/"Dgtnㅕxt3K}󯸟 R)Q#]kO!3W\;/| ]ò|ͼϘI7ĉ7v{s3&<l@QFcRs2Z:Fs72b \ubˡrUp@$w܁gy7kx.Ώ3R$ikp:  f$ Ev55R\!hv 䀹\rD:5+bR#@YQ]cȅiDiuxr@JOW{{ &MbnlXs3>FAnZڥs{B*& sDDcT4tGnnVnG$er k̡;|<`8=L?7\Y>9Cq836}V_mom=.rඳ3Ϙd94UvBѪb ﮭMء5Mgr۷^}VիZ+;py:6qƻ].|jZefja/~\#33ַ ~1 <㦾spJz  AB'AA,x`Ɏ+ƦC0uyyyu(LO1р~^O2E]=FyyqJ&YJ(LHe!r=_~8uȴW .$B.tȱ7rIZ4r=y_^T+OBPsEi2KE`4 ѷgUƾNxsZ" N<8xyOc,PLTдICϘ7AS!< 冽j᱗JLi}=r8utH>l6WU͖bA3{ZϬ_K\/ycƍR x)S)rf<ږAAtCB'AAĬ6:sg&8w޻\.dee94*.?p>MUlTIq1Jd3V_1Iڗh .T8qe/;#)ǻ. ?Q_lEWq[*8tF6@zqCY²U@.8=#Ҥ( {@0JG'/*K-}gu5Q ^ɟUw}7Z'n˿ ҅Ϻ]=umF2AA)N  GʅNnSJddzۂL:< =p0`6jM+Ac_=)܎1M0_ C*[ݱ,ܛH'v?3SSSYM7\k\,(-67S)c?,h qg?$Ϲ``n&Zr-zVwL//3##RZ= ,ܝImI<.OI*tLp/Y}_,畔H"Ts)h*L&bs Bﮭ3AӉi5ٳRUu͚7M^Tb<\.|bZdxgcQaע@ ۞B>z~{bQA>nMp- "NWmKqf&޿t)ޯ⋑ P__%KSi#g!dpwIc}oMdAA@B'AA,po~.M<3yҾW|aeYabb‘Й\ Gҙ{/Mm|f+N+,Ab'xYK4^3cg̵3T{Dl|Pr˃y7aZ\kd6b[w}'Qֈ0̣.GW cv9dPVT:9hA^Zz"o|OA¥(O`<*z oqg})BݐJwVy *œy=;^t *ڇ{ieg#=~ VqwV _g'K.ф0h _E䦭eo JAAJ]KA1 q=68lTVVjN2ݬ9:܌ n+F@DlF\r:ZӚiN.:6OoS;{}49raO㚛[O̫ڠxo}5M1 @{LŠJl?\MT&&e0ʬڲby! !9ӎTUyh6EzZy,7u]{5D_ѿ}_U+?&YIa:CS:Yp",Wt잎tij (hՅw8q}>~g(7nDvz#1)gt( .n><)/~ ;Ab˖-R;:8!< {AA9:  f!!%wZ9쀌npp!PWW9K&a NL #YS➒ovw{|0ţ۾l"TCs3!,*ܚZ}̵)8!?Nc^FsǛ]EBբQR .ƤuXϲMW)B)t|E)7WH(èAM҈:9!m(!P`^J|jhK N.䕋3Z;"ytq]jYr/J98}zVxgeOFZla-kXs1lwvj;PP`vVMee%1== IAAH$ O=w\s*mR68:E|>,xh;݂r|Lܼt)8~cQgмk-{?2^vsqyGpz[wbl|v٥([t9*@Qsq9K*QBrҰP^{)j/V1Eވ5OLMu{*WxTƍ DLҁȧ<3Ӭ:=UM֎X`d3ϴ9B9H+ ,4͕mMMR7Tܙq;cl q{x-Z1~3۝pǣ>O| q\Xp!=8#k`%  :  f)x@9k#mGgooM,YD*(A.Nun.>r%8q¾{>7?P_NT[4:բ$Fđ.vGKh؏ rlR_+PP~\QiϩsH+gGUhm"+6BL$d*=md6YT9;U'U8,_x4(k+xG!uS{Pj+~jMŪqKߏSdAS._> }Ν@'98-LD2k>bf1v 4we /E7ܲi0;B≷YTm'4nf,y- (QO[}\E*sT6)M/PS 4E$zX_n}^*a,6:瘠i5欫sZ:RE?=gHuZ-X(l4EmAN DG@k 1Wi!wyւIA1K!  b |3'޷$$x&ۍjY":#s+6X6xOu5֕b{{;sqpHh F\ ^%נ<\LZLg,{R^\A ;}F̚3i9EH^^%23 >32l(tjsFZ*xo-DOsNMi\h[p+qm.hb~$+ W4Unha\x)l4XQZ pûb1W4Ia{S9AAq  br#׿dNmڊBp!=:V:'B!a'(ZLo|yV>Z_w͟=]]x@Bkc%_ļU'-[q_u% 37([,*LjbL[|NCXVVDc˫@P7 !DU2Y֢6oZ§vϹ{r yqEtɯx&|$/'/gbxܜ(hkK 8~\TUV\:Y hZQ[X(-zZ/\;&"AA1  bVö?%Zʌ7ʟ̈ݍӧOѪt^b zC}~>Bns|h0`8Js\x,x].wrM3Y$+15(9?^i'=Fg0G߈>dID =8r|LZKlfpnj1a^  1wTOXQDty3ۊ V*U*a"`dǴP(Bd-Ug n+->j^ܳw/Baݥ[[P_nq9(hZQ!H8eEdMAA9N  Y̍xdN<}UEoeZ >׋/O|eHMzO7c(Fyvq4 jy(y .J~ܱ99T9O흞gZҌ,tcli-9R9.΋"FIa-./BHFŰ tc c-]UЕ|N.pַhmCC8եsp… }f&,*Nbad<;ߴiR\!7- n,LҦ+2rxH*'   -dAAm1kNl ~Gu8Ҝ6Y`+N ۱D8 1vuvJbNn78d@Kebڮg ݘfZATڤ~9u9<:XL;v+[\-ꃡvC|\C|؎cC5vɓUrѢH:pg2UpBχ܉1aŒ ܵy3F0Ɛ/:Ϟ=p )~''9!   AAT%28efaa G+:t۷o%B[[vڥ ӑ z$ 6q GP[p0ݠGd1S90{kO)1hT]P-pŒbV|^(7+ڌ+^\K_sJ xv 13hVqͩ 0 1?77s Uv`L4g #~k'&u޹iF bpWWMO5`P*Вd4:AAq!  b@܎I'WE+wdto|###cp-`BxؽVl?bCM:;1.O-Yz5KVDŽ. j2jJ&0^=#"+0Eܚ 114JϢ*9SJHeMT8dp$;ty E|>UC3z9s!m=@kʜu 81QrYvi`S(h∌Ν~7ݸ ?3Ds||w /=p!5nN3z1a   AAu9u=+̦R3rSSNP(7x{ߋ&>%:?.9pTyp/GUUc:uJb 7#\_%}fx\bXͥիDIC@YsS'5 r?]9ȩ8ńDzqsLdCYq=IY9O)4rBf+zO(SۦZ~6qcR|Cڹ$h0CxU\2d1ӎc+eiZ >|[8.Zq|!t AA3 J]KA18ip:*ܗƾZTOǤ;=G9cbЏ[󍝁DPR( B ]w9xEqv< -[ШNo+G3[עQ<2]ݲ6/D~V:E)g'A|)Zy(̜l8(+JKu$0kQ{ll ;vcυs}  Y  bpk^xgsOu8<9W7* r,sO$,.o$A LLH"'LB[==xٷ>)дJ>>uJG9h<6G& IDATLJ4c"=ZStt2x5·]< h/;k z,I%[sZZya;4A`/ !.p H {݋y^[P;6lDΤB*?0_z%\Qwxr_ AA3rtAA?ŀOM|ͮ4ӚAod`_f/҆N a`TTZx]LCsC؛3o$HS9t;3L-3TD. 87Q1*~" ;:=y&.GˌE3C.}u6QJ ky@$ue~5#pSwyj}URTW`uOOg4UN=}i8rSB?ph&Ý375a,++1/'•sڇݻ/gg6!C@Rd{^^x>6n`0^xA+s7j'RqXJ?  +  bzLAj9Ȯֱɣ:E_BؓՃݙhLv` eeeɁֆ!Ӭ;;:oW{{ѥPT?1m¾zP}@T@D&G:͎?qmERFv|BR˓!6wPrjCycL'% ` 2rNU4λ/I^St#r,4ULx^ps2'qw_{M_ 33qׅJ{JSUT--Ql߾\sm۶[+/σ'?X$ CIA1F ye@if-v{Ʊ#;slWkbXd -Zdhhhm̙3zihG,ScMx9;ULk/x,OS x§4f:H*CRy~NsqqV҂qZB{ cԺu/> n ̟mߏ~7|W_7M*S=AA &!AA3r Ȁ随۔NeN 嶛DN׋xG _BDN Z a D8L/^q`K5&.rM*x%_6QLjP>P)o k3f7I \ %4 =\LȋG嬵% XƵLiq>Ϡ+> 9CcyI8ԥ_'5k✬iqAUT{0:8vm.ywk AA3rtAA1.Kq>9\T:fE'1d^NNnv|()Iɾ_^ .Dss3&n^eVt^ty*{ 1vvtipP+swb&GDt>Y1*4$Hz,Ӫ#vL^@xtN͹uv*ܛ.C$}홞csttDG"Fq4[fӮ]b&gdLa9Dֆ1KΑsÇUЊزp9YlF|>BHx4+ (BAF PWT}}6|EEE^u s(֦d xF~n  bAB'AA ”d'Fx5 6Iˆ7ވ ] ׿Ƒ3o⽋り H|OG ƨKH˭@8aP2JtTջ(ٌw k8\wu4"d˵Z9iN=Hާ M4 j3"ܪcj@yI-\.ϔDOc\a8}禥!x_Y1 MslmhwܜO8 isWۙq|4҂!-in7#/=]HꪫL"gev62=wOp_)oZP:0Ǒ^+=p99nΘ);yuX+F )Pn|=8K"E#8TB*\i~uaU3d+U_3U0G˙C ۃkVcqGι5y?#НmYό?3l58([dNnXQZO[$b[S~w@"S\ax֍U;2'?6v  bCN  9 -:Wy=Ŷ/jDXx~l2<(((ٳ򗿌Զr%F|Xsݸ˰yf\HOOz{{܌V477hllD(d-fq!qV qp9YI=I HAAm  bQGšCobϞ=hkkS0eb_V/'pp9' ${oMA  9o  b}o&tyԑEEE~W޽V-CYHa.¢!TX<;c`Oz0AA񶇄N  9L'EN`NFknUd@`vG?RT޽{_=BzD\z"\RUzyٸs:)lom3!bK N Qw=NMݣhRMfUd{t'VߧxVe'fSziBѽ\q΄SXD8MNZyrJ#:vhjMoHk"Dۧ5FX8߸,tfa3 Rzm>ɰߏ܉>OxMPs7::ޚiEyy9:\wuصkn݊'|Uw ?/=ucŸ~` 9B%eAAے AA1#<d 1 *ac=G NKu-[dM. 7p}Yȹq#޹`Rd{/g֬AAFԷeh޳;Z[-+DƔ/dW p{[3=*vliWo#ydW88!1-E66ΔW^Wkq9p-b$ih#-\oŅŚU-[q\|ku/Ǐ|qcwߏc==Z03-\8-sC!xnt nyX' 8akxQnƍ)`˖-sN|+_Mŷ!}HɳZ/AAc~%  0>\9uu>\x>;|3IFGGuV<سgrz޺:l7/ȢpV7'SxpKKxsq݂\xHbD@)(}6O3ש@w6BI\bfN*Q!m*j<(̙3 pDܐyMoh9_Ta^|8q{!t~ZQeK  B49gO8!O$B{QHb ZEE)6PW3 圜~O;;_6taTԬ  x@B'AA8G@ti<>ӭfee;IrS߿<n݊%d$%i|!5+V`UI :v `Pk;ۋoލ,[Uݗ}}*AXRt$ : zlUkEM}c(t>h"&ENXI[ګS;9TSrjB(+Z -)Ёh8q-xz=*G1g4QLPr&::,:QAepft\< nNKp˚5X7{œㅦ&B04 S?'կ`P,4kIt  !vǃwԤ|g\cTwyM Kܳz$hnܰbiu[o5Z ?8.b~gv7_|qAAČN  7-ع:=>S$\.}S7sկEtTVs^pAQFM8+AtBƍɑΎ{/MM+ǫ„W&tKaEp{3ft.FBqWz$`ZkYW:U;/(NcE0 ~MR?}yF{r LSFĄ8gTyX*9&t>swx'Z[ѣRݦ*|lRG eee:\j/c``|7u]'?׎}Y쥏  b1rAAJ8دpΩ"$lk׮t쑑|SΝ;l类Ɩ*6FBr!X?m܈?75fMTԄ+Q' ŗֲLd͢.!+A!PB.7\̍0}^L8g1, kcv{r6 ;i9#;zN1OCD1;gixRv'?“ǏFӓ+piuVy|TVdhf.\?ފÇKˋˑ&8  﫫B3ķwh|K9' n$E}F-R:T.bi+CUNDƣyipkή(hZU ys 3Ў`7Mk7fXa7#B̌\Xtj)^ 7wqpNOp}Zpp粣skCB{yM rlFL~b>iΚܱa)7`W-􎍙6TVW#7]Nի{|{=dffbʕXp!***PVV JqnG?G>2KTꄷ[+h$.p8͘_^[02O" hk] '\R= vMVbg$S7K(7 +2^k?.\8ev iueٸs&dLX =r/77 22pyay7TVbl<[>|>݋{Z/))AMM -Zz_W4++ w> 0Oq[o\`[v|AA _AABa>gi];r΢ۣ?m6[%W"AӎL^5ee#Zہ.4 +WbUieWTM ;zZ]\̊"d4B$ uqR}͔B7:.;$MNvNa*쮵S[';x;{OBglnGa&cyqaŵ>jG4u0,MSB[3W[ϐKop&>?ص c]/?]x!R4lbϙ3xA %ոqJGbը+*9]]ެAOO$ꫯm݆{Uy;EѨ7Xlɽ!` ưIPR`'fC ,@6 $$!b[Xƶlcؒ,Yڌf{Ϲe4Fz 9sϽHo}4xrrrnHQG~ 1MJAAH AAp3^ޝ-#x/I{Gǔm݆*"ImRs)I|+$F5sDBp<|Q!w{xAx~R֔M}.F'$0%yi$(?+(]q/X$uV&In8"&%xcg댯`hekbhحY6d*ܓm|Ƃ\Ivٯٳs&bWCҶJ4O549m6<~= LgxOۇgIY.a\*DlaJ \?Rܹj6Ι1 N_o|zXl }75  ,AAi]9MkǢʃK/6mz.χOؓS$ܺd ҙ$4dlӉ]N›55JIA`Ө=+W(5UW-\+c8aEBrT@SMeF?'aY1NiR\I5CìjUaҭ ܦ$ \$?M->qR93qC[H6UX0A*)Ets2K`Oʆ +VD,-,[DKG9uK@)3a3W?E]qip-Ml8=$;LHƐg&#>5j%()Sak-?Fci3C`څ Ot|vҶHcPI—VƂ̘_o:0WVxGܴ4ܵj榧VIBӉ)[?ch@mw7>imEekPu z+?`ŊQ]b'? )**1AA1àҵAA0뉾isZ㮻gŖ-[,yHNr\و09uNj*a.;WWf ogi2I+X-\ jSQ^IaYsyMES@j|2}Zsjp/ b7&  $:  V$@HC;zvZZx9x ~ }R힜q 4IBh"b͋ふk(ލBjBKU*qOkeČgdgkBzA*bhJJWgʟcKPsaɏӦUM3 ;Eݧ/QωccI 5^HwpWB14f2EhM7SßYPG`Oc٥Kq91L܉-G/UkDqӒ% Hn79-JWKAALwHtAA:n_<7Q,_Qс>ne99%KRh"B)K`en0n3gY\-(($"799zۧH>})q0ZjZҵ]!w -YurVssRg'"yձMm'2SF(k_.^Hs9N5lLpl3fwzɢs{}=qY~>fk/oUWZb\5q;^[ſ܉^Xݎ;W7/\)Zad,76l@rm__/ 6 ?过$IzQQQILAALKHtAAX)>cNٸۣ[ZZZvÁ;/J$΅f9Rpոk $lXm$e^[@T& LU 'J7%8iVNF"B~eҵ}cYAx4JFvx )b)&an|+g[TLMEK2F&>M߱6IZN6kОk}W8͹;&/,mQx6PֆGm;55B)r`\M2ܴ4]=(y`ڵ馛.c'Y)AA1]!IAAr38 Xϻ-Epw}7NgTsbV!HBh`i*|Λ=n܈4lrRH# Oa 3^0ө:("ٟӬ,00B OJ! $vy) o}C=13 \p$"H|Q~MB?Q/YRvWCz\pC--xRt.ʄtPYspP8{.[Ӂ٩oZ|^x!깾o#%%~WAAtD'AAa ClC?*]J;99wygTstuu--Ŝ7gBS;d,w:l,*S ꄝ"SN}: aigp>n~}B/d%<#KuBIP0Ƃ?ʁ$߲u%3Ë'ZN)} v'^5*%i >c!?#ԓYf~Z?cxZf˜_ݍg?~ ~ٺ;녿=Ms?.͆O %KGhhhjl|_c?]fdAA1á,  Snk{7Vvvç.|Hr'xJ8- (7mfX-%' Z0MRmAOgP2gj=f:AX5k}3tmRڥXn^0ƒaRrlxf~q{6Uf󺲴˹#D=Ͻދy)mIKLIAAL;HtAA!1KuH7xcTO<-[(mł[.Bs< )K\Z@)Y 2JiρqyZaNTiY~٧|l%I?zNvWIy?\u<9ƴ'7Qz W],|)Ef okҜ-Zr (}aô,kxpbUxlf,ΞN;w{Ul**BQJʘwi6V9Ŵ+ILSFVInrN ;E'coh*S{MD(@5ljNb c Msį nNYUy` kwwK8/Dž>kj Ǩŧf&Ec#Aמf_禥aiNNzrtqIQ͆֯GV1D={J{jNZq#>t)3oӉjJ>G^x!.+b<:AAӍAAD̸K~4y:z8AquE5ĉx7fKhF"# cꄦ(2vJ"aoG%%LcEAԦ8ӆ:ɧ7$Akq7B˷W]$Eq8BgssIYµz;{8w%½iyIS0P&Hp WANo55BߵE4ۭ%XxP6٧7~Y;55xt6|ʕ_F.ţ]Sbs֫ꫯF=? Xٳg/  N  ",=y@xӜpeE5^,Js g$3g4t_dkrbõ)pLZL1 cVo+ ']eMĨFxԘ!:&4y1=.Z=:]9oƩʱB[+Ry 'BCјtLJtVK''Y51<]Qڮ."I1sRۋ?[w fe{6R23$&M'Rr-|ƽ@  bZA  W? iiiXX/k .;7 M9b$4ƟSFEĊjUdO'=3`[fOu.4IF?' z͙5,4 etVs@CLwu)6cX6o+m4aRk[1ND5q̬D;9]h3FBU01;r(}Nǒvܹj l2̥,?j}}=n<=wsAAAAAAL{HtAA1Hdt2K.%z-)5HJHP*Nf/4[IB#l %g=2%@TҜ=4KkRP 2T1eu=׌2Hig8(ȘVB3*ABhDgJ"Xޥ$TAWӞqMғSՕebIP-9P5&BS~'' %E݃]'l<2r.ci4T24e#|<8ީ3N_T466O?+E+_6 _,/qwj*mIfzAAg$:  `I hfwBB <6xj^ =qI@R4ݧsHmrUee&'@ Tqu Čyy[}qi/|h6.xsEzɥ {!2HUEL݆=M{N 7V1~I HLKF_⇆^T_Sq{hvw/J_qz:Xqn0{֭8pFj6m’Y4 %1KԄG=OAA.+3_!AA=߁AA1N; hưq聆,[ `F MI2? ӖkGS yy `^x〈4fk+csS?zfhE#c skZ~qm*p\LpiWEh N9 X1HO~ cBOq^THCאܧo8!()A&?//K\xpz8ltz>0ك<<{QZ޸._ij虌}z1sm muc^AA1m&  fO9 #9!?LZ]]■ KRQrQ1tClw1$]18IV<++Y 9>ͩJRe'><hӣ3:ҵ{:k׆xm~~mv>n!e? a|`E2!,Yõ;~d9Sv K.G=O~^^AN;Ɔ HMLR,ݺ:`EpEI ,gYP_yn߾}Llܸ|`eQAAtAAD܃E3UK|% IDATնr m:1"U, MrQ݌ݚkk4˥*qIOHMO> ԕU)c O3>MJۑx/ū_7 0drOq ʴJS9G-*\d1HLtIy$9:eN.(>Ÿ}rW}~p&NqrD3)}8rT]%NjRv ZQ "45&5;g$NZ5Q<}pJay w V+<<lǖGÝ;Q+KqUƍKN6 ͿcMunڴIh3ƺ&  bz@  NHumpꣵ5"K.u==1F22C{0'kKRie'@5pekEYJ2af{cjڮ5*rӬPxjƉb޹qv<2hd3ZfD‰(Lp!=Y0ܮ҇d!g OWT+#n$|ij,̌Zx=*/]|16͝;#rܷoߘYdЖ$iɩAA D'AA1f$~h󬐐ƥ:?5֬Y#$*[[c"4CGB,c'WV,(Xt> 4E1ͳД&rl䄒Q=98-]|t % .+)x<CPť%wZ1\i182<'vV+ Hw8uںu8KN4))Hy<%%%30Ɩ8  $  _ɹ^hllBBdOBӌVtse9QM LA% *ԥ6~,"cbSZ?+I36vFeNS\$pQ/[kχ z,q"6Vt&:\wc| Z*٘gk^wοϽ6#Ix|i\8gNonwnMs7oFY^T-oF"(IOWzddq}OAA D'AA1.>W_py}+++#ƍ7(w54L4(4vОs\KSe*66e?JH. PqOG&HAMaL'Ip)L3>ǧ&R//uusW]4L/BnԯӮZ*<ʚJ.BY&uO`; 3 QOgSVtDGǃ(J.??o8wktb|f1SWr pתUQ%"TO4jKAA!$:  qÀ#B%:СC}\ i "Hj'~koWΔ<ν@w L""Ķ6 )id&TiS!RB#v+9&!ק"j^|5|_iٴ{tzE@dwޮxFqN^zU|]/@Ixysv4 _'/3YII!Fmlī~*+,+Vtݶ [ZcV0>iegOf XL_V  B  7ⵏꜢdX7ر#322pUW)|xfjhԜ7AXIOjD`Ś"%9IH&)I&mn:'3[*mKB2nP׬<5J_=[Kyk1O* $e  IK IR{t|Z23x1P^c]+͕gb2up0+L-C>so$RZZ\eVX{ʄg 탃޽NM7ˑhM 4hsAAAAD gv4Q`GMuVWWGw!߭,,kz M#=~yņ%שmZv6 BZ2(49KL|y('8R|F !$hB4K'K}EI14_$_{jk1S+,,+ĈQj8=[ef/x۾G<$Xn"|w&sFE#Yb %  ◙ AA1e܂WUsg{/׬Ys=Wiw Y=a M3ڄ}fGR(SS!AoT0UPԨ,lmB;k=ǧ9C j"(C+SUE?NEi $7"շ‘&}cOO`L5+_+3$I-vp4ƒ1Ҍ?KUUL|pEk<3)Io^r$EUAAĴD'AA3K҄~nF M3>ҜPZDI(ND3A&d0μl-z=jB mGvB,<*ZgLT&ޗFrTh5"T7fP6VmȚDf6JRi8oJ_ dhh.>㔟k0֬"xާs۩Sp{J,?))!t gBtذߏ׎wĩf-˗[\0ϋ:4@==Q5N2NgMAADC  n9s(튊 kJbXhnűS:zQWĬJBYĤ>ȧ1%>b/k,[]''<5Ԍ5[},UʯEyNb6Ӵu닰?x~z$:mh7.FثP.SO]39:V%:ݧs$P0iN׋LN _3q:SՅ؁׏_SuU^~y3.?FC:]hvvIIIsI3  N  "BϯT_1x뭷"[$|_?ԟji~8?{,|9II%d0I('|*9y1(Qե9yʗU3KzjIۄ~O/*(m= V#'/w-c?/p*%Q-ð_MAZ%UjJy(=-mҟFBF3 $X1GhĩU_a"77^Oۇ3`JZyaNZĐχݻ/KMLĽeeںuȘARwrFYAe-|ЖeS  N  "lv2;~P~7GNN>ڊ8//VҜPXz$ t]#5H4j$'WvTNiNE RqSIjH*[Ւ9n-1['9u vucqh^]?8LjbSڹ]/#m(0;ӏfNJȦOJ HKʂ3 kCg ^0OWTK"IXi:n:qnۆ4)s ͛d41=!gώz*Μ9sb+#  AAD̹~ F5G5=pnOP$$$oWڌ1H1DÕԞV*i@&Įb+_BPgTl8c)&utBR5tjIOlzCmqCT5SkQ *&IŵӚЮKD0-(<~ͭP=95)Chk =S0c?g+Ö #J _$'].cvFtrvQVd~ڔ> Vau~tx}t>[ SBB cLHtΛ7V59QUy$  b&@  $6ltl(K1_|1qsV8}Sڳ$ BT&J?S^U)(ۯS#s JhҒܼYMS+Ik9WV{O { ukRA[[w?#GpW5 pS$2C ŜB8r>587r4#5@4h|TUd=71Ȍ᝚iΗ[ &7.Y 11=`46m۰WsV0>XU ⃎!9~?H9r~k2dqAAD\C  n50:v@_ܥX HgB;+KѾ惨+ùzZc*$e]#&/uSHo Ǡ5+{_/%dP;%o% V%\, nx ޳(^%&cS!>hlXr: ^cRb25d&樒v]Q$0Z~q$)C Q3&Nf8*_[,?9/=TU]w5{o..c1hOoDߓ٩ƍur8l6+[ @7>Rvܩ<  bfA  0R X:&:;;oG5\|,3ցgOoZ9 Eyd?> $]=1tPD^rrI`Q)aˋC^nOz*QL4)NExr >yoT=:3!Y,T ^⽉=Y^aLƞ}YL%~=ܣ|:^%A}q0ҭ⚢ (cBt2^i:do`j4ӧʱcBߺBܾbD-o2ޫovbuớ6a^z Dw<׌mF!rsggg m Ntv1IBr4*T9_`W-٘t+k^* SODBSD(A=OT*SF4@k_#~IL-~g2V7my^51*^S\To鍝k=&<ALJ⥪*ݿ MpEN;$KKKى#G]Z[['  f$:  x^zR;PG+MMMB;j59s$Y`Slڏ-ܹ-+¬,X-QnJ&.TӜbZ@+*y'Ҷd`awGk^ZA~E$ X"0J$&$È#[&+-,d(^P7=9K=/{Nn !0u#L} a'#INFuz&: t{~0߮fڵ:\AA N  bRC|I>=vo}|n_PSS%z3M]s8UWUȥ9 \"0cZg: $"Dp9LC劧142 ;wE8-șwq`1^jUmr5xm^n*xv$q ͭ}넧pNseE,#S`߰mٔa$hdsG#EcRC‰NtٳORfÃ#kímxN'-(c7cCQW1 j / _tAAČD'AA1)lv @?:kāzPo߾\s G3+@EEr$I5.-k2'3w)p%FKNMZE^JjӜjbdN^| ^^T@XIJM3?G3C3(W5IM"QLBWTevo)bPON-Y40y}Hs%J:3ٙAπ~%:I9&i6dE9\C5i,X$۽~?ڷOH>&Xx0gx>-d'%>[) SB"V85e#/ ],?AAې  ܂W*_?ogz|_9_QJ r&fqD˅8 zgRy2))$IICj $lVTB( )PE,G?C][eXu>2gT$ F%hҞbRU4YZpMz&򋧺~6<<L9 R /$QxzɄ J$ P쌔L,5EGSqA g OWTK9n$|ij,̜fa^_$\4gn^lZ1Jd mۍ'31]AAPZ  bRb`86(1OiZ41fXBQYSXc妇!YkݪsyKN)U1˯Y{Lє])\UwzBYt“DC%1$c"D! 0xk20vfrp^ǣ;48HwnǦs/WUawcpKx1:i$\UZnDsMt3ϠflWFAA;$  &[qo$H_+.ũ~4& : r}$![; ;[ʸ %K0hÿm܈@'_]^r2-+K47:FH;219>)+'?WPlYX[[nܜL( ҝ$_ W>Y*9ŽE5k'k/5YL%'$`NZ:uD^z G?~*~Oh۬vp%ej=80^ Kcit*sdٰZ~9yR.+)f[xN8wsq1^`5xzztdfUb0)hYJ/駟⩧,Ø~Aq  b$   $]x7^Rܐ3X7{6]pF t>MOlӤ'v?Aw iG>Y4'ljSÛJ^M;SklmẓPOrt94^ܚי,&:q9'(}v!?}npiT%Q 2dmX' HJAuoPI{A7LC 4c!I\r&*'9>0gԽ6.7{O+ǎ +,+VLeD[M L;l6|nR\'@Qq)NZN}`}^>jT'}3HHNiJ>iKݚfp Wxד%x{^Hު_V桺:$<%ٸ,auW}=i᪼<\\@m+ݍ©S6Є-  kHtAASmxe_%pul<|fED³m hr:PaIW%/X5(*dRh2UOPjBnNr;OnSKyҔ5H۱۫=؁ܚWHLLtZ"ڣjD'SԊ(D(}6/dgxApo&XXggqz:X(+ɰχƏ>-±D|e|m:dh^3Y/|GsoߎUUU|1vSZ&~AADl"uӤ Ϻm`Jϻs8V'OEN5"Vk#4t3Dg!%:k}v54(m$X{;FFtذ>/=\"5HӉ;W\L'Ԅu ?񏡆Ht_cc&tAADC  rnk{^?ojN0 #! $&+r)L$*^lLiN}:D%_ɟ8`HJ΃KkJP3zɂOs2Qb S,[EG.QtbiNgg+ξvt#7Hy.BXƳGU':#LTAG"4Gnm-~h ulxpIIFKEs3t0^/I.37/[ Tbp{Ս Inoll?k#  AAbwv1sc9/ ODN(UPKҎΣMy!yaXr'[+p83ەgԘ, ~0Z–jj2hp:q)$Vew; Ϲ=9yyBZ$ϩG֨_V>yQp"4&S/ݕ%{< ]:%I [,xpzIKuDKPY)sAIFJɓY_/{Ƙ[$|{://累?x⾠AA AAD\z%xH$+[Ht4 L&' _vMMHͮ5K-q PZeY[6o$I_gDOF8ғ I<{5IX~]}8ϣEի0+hΆ|䈰n`%%ab,K{bSۋӐMIt@_455:88LtCAhjj̥AAqN  "n{7D]l\#&Lo?KΊiF㏅Ac+kHM+B2A JfYwcIN2x|Z]^pRSwO(̀EBf r:Q}}hM \LG%D2-p'x.Wcؐ p/wN4<&cJ)(GKۍB]wXIf&9''Oʈ1VSS.do˲555>&cAAD'AAW܂W/ Nu!')AI%-V$"4l>V&tJkzVbᒝBI ^OE/9ecZe W,hΠ%ي蔙DZ`Ju!BLsD'}+]يҵ[Omŋqܹvh3wkk꧟ _c juኒX颳PԄwkkoz-644AA3 N  +$v89ۄ' JM}ZU){W91g0q ;+ONǜyE)ROu$ դ3yX?F!ŕaeV^]#X0kBe+ ~YS6Dgpe1aeW[kxlsq1Y0+5]]x!1Z+rsqʕȚ_3x)l=y%N''qyAA %  ;JKNܢ "霌':-Dn_NN`9m+ZuRMe*{rjcSJR Ur"EXF6TS,;J+GXf 2N,tlh?f?(]v:zc.:w]WXWƂǏﵵ=v|nRl)1 `ɓY_ovZ-_ʲ6  "$  [*³?;[+ >Q_C'NLU`˻ Ntl%/v6RÙW*mdLL{3PvtM ;22fܙ=s/0^jd-TQʗ ?/:spN'vNMEch3='}Kե̕蜿.iobEZ(:{<̊aj8qB׿$;AJtr%[PW@JbLNM cO655 %  BAAqwpd_;oսqA.>g𰐼Jre {j꒎\ [dNԣZVj.7l`tħUrړ 3'/9MsB@B߻q^z":m} (L+ E: t꧟­8=[eRB `ѣQ_;pVDY^,clm[ը6;M,_ssI\AAqBAAqe\s'&tn)Y EpLR:0 ]ɳRLS(YKeoJӜFqYzߥ\jMĂWs+Q!89ߠdRpQ NJ"RL^\%+U+J[~]|SL/G]qz:vrrӊM*2W~eϧ61#=IrkaԜjj _P܌?}NJ.;7/[6#&a^ma7?ɲf'AAD,B  fدML~FsGJޝIuW=c7$F B ɖ؎o(q{o9=In7qɖ'a%!1 YH =wu͵U^vu7ݼ{ת׮}pqNnis9c/j$՜2$\Bg;-j[SiꌵsƧ3۔zoYk5T_'(sH9֥SiY TlP*:`ߏ✩~J.?Tu^ѣJu^ZnrR Μ ff˗cAQJ ^p`e~ ~SSSnA';{/o$䟤z/ya6*<'Rk?K $,+㕜I9-_d[}/ڬM6[3AjeQ3YXcTs L?-Ʈrf*6sr`Xu1ac_u,?u'/@9@J%pÁ׬A. H}=^> xhlY0ti\558ب{B%𯍍I"PmkcJg憪N󌵱5>>WS)U13k^KwAم@If&ZW{!etR`XviiHwedǵ_5{S׾m^}Am%%xn2._(vu5I;Q$1Ѱ0$""" ev߉_Rx a\gWSWڻ@Sx %$rG|=gUlÑk[v;Ǘch╛UuF-owk]љrCs[T5_Ǻm F  4+K9P'Vk]NInRG'Bn_>w\۲p!6#cKx, _XˇlLǵ[Ww&~hk GDDDDt]tф{~[VZg03Yqt ?\ZϬ4V9mB6{ZTuF9!EB[6jQ%+V2?oBK[CNdnYd&?4 ׬_r2j^}UU_SXQ ǹvt $zT3Z*Ϩ5T9Z_wRAgAzr=0xa8Ý=z[9馠3p~؈W/\P9mtnp ~uޫOx-/- .]US^htI)q jkqj*rp4ͯMMMcI""""&Ǿǁa * x~؈ xd޼ oĉfYy`Cy9-Y4T0х"oj›h\cK}a}g-酈&mxi'| c7daxi̫"@8558R_?"g ƙv8--eCd-k0CuS%OM0%*;!v4kM+6ckca:7E|֨X;' J&j[놔z;Hͦ&v;h x03Iv) tu'O*mC+;L2Һ}>YliIx$3_^ Fu4~Rբ IDATF)̎sGlX=P}k6>>$""""1 :hBہN[)z/F@4|M椁 g<&]fr[ꕍPcәP)%ϢgviSV +ZO"%SJJ(7W̭kl#cqf&_fT+(%#xy ͆fƖ G=huZG \SrR%mR_eIDDDDN""""c~I?I^ ; ?S]xo*XPH <79x~lުؐrwz Kjhljuz5',PImCP^)ji OiJ] "%^ W⿬Zt/˝F.87#?JJ܌7Ity_}! KKw׭׫Ok ˗iiADZL+ʁ~o;4v;%"""" :hR 8cދpN 9bD`$3mz۝)+ N c5U5g &@¬kc14TvB{Mt̳:$1L63vxjݪWᆹJn{tUuvwŦ HCQUf>۫S9!lZdk݇Υ9x~eN_]/"de 9Z1M,RJnkÛUU\# ȗ}%1mŽ,(cIDDDD x!O<ޏ vJH\#xmkSZUfKUln몵sEj"఻F-Ka| ke26RS<[{ܚZanb@jjǮ-{w$>[/¾*el>׫E٥% iZmE@8?~fk`f7/>+V43sTM XSWWk!{ p_oJCVKN'""""tѤ{^ >0#1IN@ <㕋#x~ܬ*'iȩoH9,M՜UhʱvՏgFc;0B ;sxȩVsٚWTF"A\n[gϞMWQ|\C9.a9dc :RO@&j^0^Zf8P^{ƿ^p&Y\Vĉ՞Bl/EvؙJ)9$Y@DDDD40$"""Iimމ7Fd&N*`v˰`$JlL2iȩrUXVuRG^rEYu"l(/Յ 8R_,-ê)SF4*;; gZ[b,kfD0go(}F3Nl -{2JcAȍ õʕgrCNc+We-z l~6aǂYa9ᦈ* '4[ S OT 9M՟skxNc3~NЃNlK`Z{ft=FvZ4 ~}=WW+7VTyFާZ[3g)3fnC3Y SoRdK ޮB|U | a_[@3u`JDDDD40$"""Ik/uTJ5j{W" flvLSmW4jq Vhjz}˧ގ!&y6MahaDV1T>]sZz}iihk4lvI{oӉ)ڱ/zufvZfz5uAVtGxs3\[=u*tݳΜɖ׊33˱h4~B!Ѐz-׸+ po_)CׂXێ]2{%""""tѤ R&/z/Vx+:smgT~e^EĔ/ۑU,jȩc5Vj-kdA垷}X 5NVĎD!dB [2jXV> Sށk}+8ڪ[2u5dearN٢ASj}UUʯɅEE/V  H}=^>^iK D=.6F'z~[W55-dS%Ȉȏoďaןmk :hR|ίIr$TNJa'`vXqMjήԵYk ;JS{X15~"2v0*af5?ŮjR^ s 9iwZZQs3$w*J sZ͊<ޝw1U/>Ϯ]Kxmfn.bss-IMCOxcRyoTJ]X)CKduDDDDDN""""x>IUNhQ͘v!'O)3ٔB־V6A؅ z UT*%dS?$_JzJ;[SiQkyNT}P:$-/?ޫ"+Vξk:Yi9335HsÑ`ŋJU0v<`3Uى}UU8ښ4yR,Y%ݱ ^KDDDD41$"""[6}O=A z? ?Lld+%wv.==Ep283Ixv.ta~6Kf?l\ 7% 0ƹZmg+la~Ocȩ?1lUPNkwMs80;?_;D ֎G R';-O9lqkvԵzzSPӓB|yree=htıF쫪Bs_Xǽ}۞ #sw :薱 ͏ H~Nzv^rkӃSW4T*hY4L ϙv'2cUN CxZI!+Xeg?9ƹƊNsZjN u t6ۥwaa!V ՜06 5W9SdƪACctl;DiVp{/]Ո*3N|abl(/Ɋ?MPihUU,פI;ac_)®uDgFK&DDDDtKy{y@ius;Oet*Ξ>+V0p}e-@JHƱ7p!)z-9זK˖!?-톯OC]cNRGFl iiIx83-[Ŧ aJP$MMx--.Vx p_G/+ Dߎ'F&DDDDDN""""==G͟{RXmOՃgmBNf!T73IbJZ՜w.|6a3#լ>_SO5m?mmb393?~ZU!UZe5!<}#io2f 0G:aٌ AeÖFW`skv **…p;;uu8\[w aS_rã]+}ؼ{򍈈5ɉny۱y@ vj+Jw/Ne9vXjS 2an*-tF;{p~,)W۝4^gBhfBةM7O)a&!jYk=4y5gX ߯GÇ;Agy}-7BFǃ9ctFn.|9*u'[NǃC8R_QAȅ{KS Ŷض{͈3DDDDDv`;e _ĸoe_$Zr,TCNjZcueʹiw+\N9 "63vFAհʻ+ %լ0?!-M|jN>S{z6 V u?FkrʰesGY9dԹ`0! G"x.^L]v;>7w.?v~ǍN'--Hifz KrAxGT۶cDDDDDN""""ؽo'< `w3 Ft8ڹ)}9A!}Xz]8Sv?ݍ~N;VftjפWRvri;7Q>83MTާg{9ֽѯe$3{ Hfv~>GqごN +*r,-MI%0s9 p|?>}u WPXhI)q oVVr"_.`?gwD䩧wXߘhbIDDDDd ;bU^j׎uns՛ОʹĶK)qM#zEY!Ý~;%5M)! \X)Ra'Ks?gI*YjNaPr}ڽn+.Faz:v~REeoACCFZb p*l^̚ q XSWW56S!{ poJB~% 9,0$""""2a/ zƥôbGz'1jY !/>Glv9Q-n[hkN﷖U}Ve/hjk'+R"^ݯ>GuHh] ?>u24[^V/-]!q}~?PM <嚬wdFR#eZ"""""DDDDDv`¿3;6ˮ͞u+bNAѩvԲVHOjL.y%Z5pJCZuVJO% >s̩e2Tjlak])Y3vciI #gz2?`:ٖ߫HSgۍ/]3fBco/ީ+W`Tr)SVx~kDDDDDDDDDDIlk>8R""""[N""""Alîv[ R-kү\SK('dN%ԐOo٪JW>@Ke? J-i>@a6gۍìa>'9Y9ӕL ]yy)ѭǻuu8XS`rMV؁<%X_ð? ^Mc;NlYms1D+lFY<E}i>\v;+Q>-8c4M|i.s~QChojB-nJ0wB8f뎱j`wm7BDDDD4Q0$"""";xnlVP׎3pm0zQz rRgWA/.4׮oenB 'UԼTթD,?ԯL7WmY%,1VacEE&` ^X9y8.؄ C!|v*tkku[ž!SDsToh"aIDDDDt޶kxp,^C= =] +gT&nU;B ..+Zu GoEtSigrHkԀ\jil;]B+˗&/.1Wt:lLҜD"ԦF^XJklľ*4YI|l+Ō@z!|zS""""A'3l IDAT ؆Wo=Om4b^QE6ZM3*!Cxhilk{r4-!4TJahfTvFO+ƪVbr/c:Tu⍏QV}x\͵`\Zg9j~ЀYqGlX=P} xO@@3_."""""A1$""""A`e8b [CYV?=$g: :GՁ I[\SbG??7ž xz( A'M؆W6 {ސv2:c#+}N 6Q$L prJ5{x3ޖPvXkY >Ei|6zNnն9Y{g4]]iVe%h X92}9b{zpǛXgӂ+ `8UR؝Mh :F/aei#uݽyW6{+?S7XZ623^ũ4ׯ+ !;ֆr/ ohneܶICs9WsN3?\Ծ3T7Ю6v<->lz;CC[03YIVCP 9eg;֍|EsŠsrqՠ3Y+[/ 8PS5)[zKQJ7ԗOh}TohcIDDDD4vb띀 <3lz|BIo@nF624S⌞}+d'jy-tI@fW9;uNCI(]9w"rrtiewfOsֵudojJL%X=Pa2>]N""""qiO8Ho]pD_L:O1(U[gm W֠׊W QbEg9V/rWMjj #|pү=!T,k^]4dyDiG\;>zkkq5Yll-b_w 8NBxz#DDDDD?]#Ock0wM⃈M\X2of݋swyJ}aÜ׎V (+]hwi%Q=ѐDhS@?Zw&=bǗ;b/W߼Qf\]$g)TQu!S%+:'ݩE(&YY~5v)[}eV$VNFHǠh9V'+Am M(aZ0XEi0rmÏ l&܆l.9Q54ĜD~Xx4+6i_ "LB qp3񕎹( zi Ňg_BkGe#NloPϚ<8{v3lhe*`IaXk{zpӂWG˵1o4o=TocIDDDD4}?x~'!K`ʍ\Gx9N@9oB*k/5ǯ_/?v2qp{m6am,Ѫ;h'zq}9fm(֞rdЃ1dB2~|eZO;yV]v25pƴELχӑ_q4<]y˽^=;˟o_]ZwalTQ.Cvz#m{iG7CDDDDD׍A'4vI_b=,;kyAaS͊8Yv&(nioN]bnԻ<[Q6{ Prv H7ՍF9ߟIP$EZn82k+;; gZ[ x+Q'p6bѸǠhVwםrH?km/~TXR8{*;p]#up6_a_'7¦2l+F=>[[ar"N]bJ㾒G7zV|.kg#\!EMѼl*:8ֆ7*+QeF@`/NAE@n _}^LN0$""""v`walԎ? `Waa6G+Ԏ{#vGQHڡ=(TvkGL@8 :G{F/ 8P]rCڰ[{PJp/ `'Ѥh؄wC7w4㯹x"/p ꕋKy(FgzFW3x ЛIa^p(gy?7XIDDDD40$""""\l^R=ZiQ3covSj@;KGz}Fsk^O詮L؇A4#JO+WJ28S Nd '۱?""""IA'$2}`KX@󫾉ŋG^mxQ7$t-gJֶ3&xeumnibd땝WU3IYll-"_%z݆lM;R""""" :&ӧoߍ_$M_;︌_G|`:ٜdTgt5Kn oLXJkl[UUh\# ȗz`f`dgNcϯS"""""] :&P(Sۦ݉{<1 GBxG̈1Ok9%<^A CZcBxoWW\&S{JvV'OToFN""""I Rl]mZyO'7+1\O[z !r!M,ӺZ[Wo(d&;:O1/פa"ކ=R"""""; :&iӦ͗Rn&lxoY^ N6IT+y( ȽƳmqk|ͰT"2կȈpbn&#5SWU$녭QBz%{!""""ǠhB|3~>Lϟ} G8V{\_p =[Si7TH6;DSJmm8T[ W&֬@66b/퐓\mjnYO$&_mܷpM]LBⳖ8p\kNx=?˱З{SLT+:h;ANarjjo9ʁ_TcObaYZQj1$""""O`Zx^RdsnzZO]] _z%Y8si KY4qIw=qա|_zĎ<%X_0!|& {NFh|`IDDDD4p8H`8z?tU[Y(MY!! 3L"2̯ 38z ߟF!6bniNHhJDDDDD$DDDDDX}}}ӫM'pVo򽝞v|\.>i8rMfTWym-ƪu-MpP9BӃXWsPz3DDDDD40$""""^@}k;.ƝôYHsf  ׉+hDe^UK-u/-|] 1xIֵZ0P#r, ȗ}%ҷ —ZK7CDDDDDN""""X?q7k]׍/"6e<# _Jȭ7[LSVtN.!S'$[zPJ]MHR@*w{wcYyϙL;pa⒰(,jeO"ыҋ"хR[7T*Q  G7BPB;x3 [83>O|oyf7lI>E's;v8t^>55) IDATOSI~$W4IrpxYU?ԋK+G2ٙYtZ3;U.wJ>8Mv`Ptp?;Zߓd\$=ɖRu۷ox5M\& _$z4SOp&ѱ{tR])9K~\K?vڿc%y^):]k}jֺ-;vt6M[v8Wj:osz{tNu~ROZ֥4CMR?zu,{H#_sn(nMrsjd=蜘:ԥ$̅c &W%5ng?E'0+Ζ8+_դ/ɛT\v3ޥ$̅ČcmKLpH ̪#~>oyQIϓ-ɢn~_t\H쩭'8sQ9i}pC|{ ; )<ߗY +:Lt.$c3}9ԍMGߞ0µ(8y>+.hIM}Wu2):cΥMlI 8)%yuo{ΒYMvozʽS]H&-:]J5P6v`Pt'յHOgL$NnKNt+:C+:D}a('t٥H1$/i$ِ@_sDgGѹ80xOtN'jIP|$ہk)pm_S_479\HGgIdANtևKkWgJNLtl|8LJ5qR/hZҴSjRN2yst+:&|z_TM r,k$_[NumI(y]vBJ=Xt.'b65ѹ0dND {Y.ےܔvj}Rt.$Gx$7ԫΖ(9SN`^8l旒|\unOI}Gv9qdWdMm*tc+z]SN jo\`StΑBcI>vz0YD:kj#9m.&b69Ȍ'k$Jt[9'3/s]I?\VS^7%YlG[֙`3Oα^_Ӿ{ӫN<]N`A$[l5)\deԎRfgZu<)7'uMN0N70Y1ܐrٟ-5:w#ׯ}݈,{+&=YǓ 0]0gvm:$m[NO&$oHr1pҵ#㏞G3GTW^%$[~l1 , N`8tpo'4kZoM9sc'vp.IC}^iigϓTRRwӑE'( et[Zn/nR^4%巒O))I]#nbj|'q$[k)7eE'蕤&%ٖ#Y?X3򦤼q6={S=;$5Г\c_I~e: ac(:1InHrCMs jה$$}Ϝ}wpw'۞)@=8>|R ç{CL k$O[riϮ|i&/OxuDo~:xsSy8\yegn;g{Eho5[Zi%8K:/Nʒ$HW?gMe3.9g\sθ g~nڭYY&?'?;3r+ty/|pIkkMnMw:uvJ 0>F;+WI.NҾop,'Փg|V֬ yA9¬^&+R,Sӓy_dУ?/vߛ{dg x`5GIN|')mrN7e9 g?Tkrkgdsr5\|."/ #yd<y0 ?:pjmҽ^wŷkn;ޑD p2ER^o0+WeXr`U\bٙiRtrо;;f=#;3SNMX1w`?v_lRttڵk/[셵Z_E%y^Mg9˗iKO@,_"3d+ Kwl>~VLv315щMHF'HFGrp|p9'#}#M;ܩ/΍7NCxK/gΝJ)/HREISu58Y9-K.Iϒ,LҴK;V;K$$KnNNM3=dlb4=1:ILu&2>uXƧ<ťdOT@+ǝRWJ~4Ms]w5zRpĺu^Tk}N)ZsZ4uZkm4M_]k;cWp| uNkRKrn)$'9$fu-9# =|I@Rʃ7Mޟ۷/N/K)ھ}} Y/w}iVUIjRʪZRIVRVj]dIjI-5%Kh}IiJ-&4͞RʞRiZݝNg+vnݺ @w):8.\r`Ꝙh/OiZGNL4͡V5^J}G7j&m۶۟8) R0IENDB`gabs-2.3.0/gabs_test.go000066400000000000000000001202071357441163300147430ustar00rootroot00000000000000package gabs import ( "bytes" "encoding/json" "fmt" "reflect" "strings" "testing" ) func TestBasic(t *testing.T) { sample := []byte(`{"test":{"value":10},"test2":20}`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } if result, ok := val.Search([]string{"test", "value"}...).Data().(float64); ok { if result != 10 { t.Errorf("Wrong value of result: %v", result) } } else { t.Errorf("Didn't find test.value") } if _, ok := val.Search("test2", "value").Data().(string); ok { t.Errorf("Somehow found a field that shouldn't exist") } if result, ok := val.Search("test2").Data().(float64); ok { if result != 20 { t.Errorf("Wrong value of result: %v", result) } } else { t.Errorf("Didn't find test2") } if result := val.Bytes(); string(result) != string(sample) { t.Errorf("Wrong []byte conversion: %s != %s", result, sample) } } func TestNilMethods(t *testing.T) { var n *Container if exp, act := "null", n.String(); exp != act { t.Errorf("Unexpected value: %v != %v", act, exp) } if exp, act := "null", string(n.Bytes()); exp != act { t.Errorf("Unexpected value: %v != %v", act, exp) } if n.Search("foo", "bar") != nil { t.Error("non nil result") } if n.Path("foo.bar") != nil { t.Error("non nil result") } if _, err := n.Array("foo"); err == nil { t.Error("expected error") } if err := n.ArrayAppend("foo", "bar"); err == nil { t.Error("expected error") } if err := n.ArrayRemove(1, "foo", "bar"); err == nil { t.Error("expected error") } if n.Exists("foo", "bar") { t.Error("expected false") } if n.Index(1) != nil { t.Error("non nil result") } if n.Children() != nil { t.Error("non nil result") } if len(n.ChildrenMap()) > 0 { t.Error("non nil result") } if err := n.Delete("foo"); err == nil { t.Error("expected error") } } var bigSample = []byte(`{ "a": { "nested1": { "value1": 5 } }, "": { "can we access": "this?" }, "what/a/pain": "ouch1", "what~a~pain": "ouch2", "what~/a/~pain": "ouch3", "what.a.pain": "ouch4", "what~.a.~pain": "ouch5", "b": 10, "c": [ "first", "second", { "nested2": { "value2": 15 } }, [ "fifth", "sixth" ], "fourth" ], "d": { "": { "what about": "this?" } } }`) func TestJSONPointer(t *testing.T) { type testCase struct { path string value string err string } tests := []testCase{ { path: "foo", err: "failed to resolve JSON pointer: path must begin with '/'", }, { path: "/a/doesnotexist", err: "failed to resolve path segment '1': key 'doesnotexist' was not found", }, { path: "/a", value: `{"nested1":{"value1":5}}`, }, { path: "/what~1a~1pain", value: `"ouch1"`, }, { path: "/what~0a~0pain", value: `"ouch2"`, }, { path: "/what~0~1a~1~0pain", value: `"ouch3"`, }, { path: "/what.a.pain", value: `"ouch4"`, }, { path: "/what~0.a.~0pain", value: `"ouch5"`, }, { path: "/", value: `{"can we access":"this?"}`, }, { path: "//can we access", value: `"this?"`, }, { path: "/d/", value: `{"what about":"this?"}`, }, { path: "/d//what about", value: `"this?"`, }, { path: "/c/1", value: `"second"`, }, { path: "/c/2/nested2/value2", value: `15`, }, { path: "/c/notindex/value2", err: `failed to resolve path segment '1': found array but segment value 'notindex' could not be parsed into array index: strconv.Atoi: parsing "notindex": invalid syntax`, }, { path: "/c/10/value2", err: `failed to resolve path segment '1': found array but index '10' exceeded target array size of '5'`, }, } root, err := ParseJSON(bigSample) if err != nil { t.Fatalf("Failed to parse: %v", err) } for _, test := range tests { t.Run(test.path, func(tt *testing.T) { var result *Container result, err = root.JSONPointer(test.path) if len(test.err) > 0 { if err == nil { tt.Errorf("Expected error: %v", test.err) } else if exp, act := test.err, err.Error(); exp != act { tt.Errorf("Wrong error returned: %v != %v", act, exp) } return } else if err != nil { tt.Error(err) return } if exp, act := test.value, result.String(); exp != act { tt.Errorf("Wrong result: %v != %v", act, exp) } }) } } func TestDotPath(t *testing.T) { type testCase struct { path string value string } tests := []testCase{ { path: "foo", value: "null", }, { path: "a.doesnotexist", value: "null", }, { path: "a", value: `{"nested1":{"value1":5}}`, }, { path: "what/a/pain", value: `"ouch1"`, }, { path: "what~0a~0pain", value: `"ouch2"`, }, { path: "what~0/a/~0pain", value: `"ouch3"`, }, { path: "what~1a~1pain", value: `"ouch4"`, }, { path: "what~0~1a~1~0pain", value: `"ouch5"`, }, { path: "", value: `{"can we access":"this?"}`, }, { path: ".can we access", value: `"this?"`, }, { path: "d.", value: `{"what about":"this?"}`, }, { path: "d..what about", value: `"this?"`, }, { path: "c.1", value: `"second"`, }, { path: "c.2.nested2.value2", value: `15`, }, { path: "c.notindex.value2", value: "null", }, { path: "c.10.value2", value: "null", }, } root, err := ParseJSON(bigSample) if err != nil { t.Fatalf("Failed to parse: %v", err) } for _, test := range tests { t.Run(test.path, func(tt *testing.T) { result := root.Path(test.path) if exp, act := test.value, result.String(); exp != act { tt.Errorf("Wrong result: %v != %v", act, exp) } }) } } func TestArrayWildcard(t *testing.T) { sample := []byte(`{"test":[{"value":10},{"value":20}]}`) val, err := ParseJSON(sample) if err != nil { t.Fatalf("Failed to parse: %v", err) } if act, ok := val.Search([]string{"test", "0", "value"}...).Data().(float64); ok { if exp := float64(10); !reflect.DeepEqual(act, exp) { t.Errorf("Wrong result: %v != %v", act, exp) } } else { t.Errorf("Didn't find test.0.value") } if act, ok := val.Search([]string{"test", "1", "value"}...).Data().(float64); ok { if exp := float64(20); !reflect.DeepEqual(act, exp) { t.Errorf("Wrong result: %v != %v", act, exp) } } else { t.Errorf("Didn't find test.1.value") } if act, ok := val.Search([]string{"test", "*", "value"}...).Data().([]interface{}); ok { if exp := []interface{}{float64(10), float64(20)}; !reflect.DeepEqual(act, exp) { t.Errorf("Wrong result: %v != %v", act, exp) } } else { t.Errorf("Didn't find test.*.value") } if act := val.Search([]string{"test", "*", "notmatched"}...); act != nil { t.Errorf("Expected nil result, received: %v", act) } if act, ok := val.Search([]string{"test", "*"}...).Data().([]interface{}); ok { if exp := []interface{}{map[string]interface{}{"value": float64(10)}, map[string]interface{}{"value": float64(20)}}; !reflect.DeepEqual(act, exp) { t.Errorf("Wrong result: %v != %v", act, exp) } } else { t.Errorf("Didn't find test.*.value") } } func TestArrayAppendWithSet(t *testing.T) { gObj := New() if _, err := gObj.Set([]interface{}{}, "foo"); err != nil { t.Fatal(err) } if _, err := gObj.Set(1, "foo", "-"); err != nil { t.Fatal(err) } if _, err := gObj.Set([]interface{}{}, "foo", "-", "baz"); err != nil { t.Fatal(err) } if _, err := gObj.Set(2, "foo", "1", "baz", "-"); err != nil { t.Fatal(err) } if _, err := gObj.Set(3, "foo", "1", "baz", "-"); err != nil { t.Fatal(err) } if _, err := gObj.Set(4, "foo", "-"); err != nil { t.Fatal(err) } exp := `{"foo":[1,{"baz":[2,3]},4]}` if act := gObj.String(); act != exp { t.Errorf("Unexpected value: %v != %v", act, exp) } } func TestExists(t *testing.T) { sample := []byte(`{"test":{"value":10,"nullvalue":null},"test2":20,"testnull":null}`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } paths := []struct { Path []string Exists bool }{ {[]string{"one", "two", "three"}, false}, {[]string{"test"}, true}, {[]string{"test", "value"}, true}, {[]string{"test", "nullvalue"}, true}, {[]string{"test2"}, true}, {[]string{"testnull"}, true}, {[]string{"test2", "value"}, false}, {[]string{"test", "value2"}, false}, {[]string{"test", "VALUE"}, false}, } for _, p := range paths { if exp, actual := p.Exists, val.Exists(p.Path...); exp != actual { t.Errorf("Wrong result from Exists: %v != %v, for path: %v", exp, actual, p.Path) } if exp, actual := p.Exists, val.ExistsP(strings.Join(p.Path, ".")); exp != actual { t.Errorf("Wrong result from ExistsP: %v != %v, for path: %v", exp, actual, p.Path) } } } func TestExistsWithArrays(t *testing.T) { sample := []byte(`{"foo":{"bar":{"baz":[10, 2, 3]}}}`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } if exp, actual := true, val.Exists("foo", "bar", "baz"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } sample = []byte(`{"foo":{"bar":[{"baz":10},{"baz":2},{"baz":3}]}}`) if val, err = ParseJSON(sample); err != nil { t.Errorf("Failed to parse: %v", err) return } if exp, actual := true, val.Exists("foo", "bar", "0", "baz"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } if exp, actual := false, val.Exists("foo", "bar", "1", "baz_NOPE"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } sample = []byte(`{"foo":[{"bar":{"baz":10}},{"bar":{"baz":2}},{"bar":{"baz":3}}]}`) if val, err = ParseJSON(sample); err != nil { t.Errorf("Failed to parse: %v", err) return } if exp, actual := true, val.Exists("foo", "0", "bar", "baz"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } if exp, actual := false, val.Exists("foo", "0", "bar", "baz_NOPE"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } sample = []byte(`[{"foo":{"bar":{"baz":10}}},{"foo":{"bar":{"baz":2}}},{"foo":{"bar":{"baz":3}}}]`) if val, err = ParseJSON(sample); err != nil { t.Errorf("Failed to parse: %v", err) return } if exp, actual := true, val.Exists("0", "foo", "bar", "baz"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } if exp, actual := false, val.Exists("0", "foo", "bar", "baz_NOPE"); exp != actual { t.Errorf("Wrong result from array based Exists: %v != %v", exp, actual) } } func TestBasicWithBuffer(t *testing.T) { sample := bytes.NewReader([]byte(`{"test":{"value":10},"test2":20}`)) _, err := ParseJSONBuffer(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } } func TestBasicWithDecoder(t *testing.T) { sample := []byte(`{"test":{"int":10, "float":6.66}}`) dec := json.NewDecoder(bytes.NewReader(sample)) dec.UseNumber() val, err := ParseJSONDecoder(dec) if err != nil { t.Errorf("Failed to parse: %v", err) return } checkNumber := func(path string, expectedVal json.Number) { data := val.Path(path).Data() asNumber, isNumber := data.(json.Number) if !isNumber { t.Error("Failed to parse using decoder UseNumber policy") } if expectedVal != asNumber { t.Errorf("Expected[%s] but got [%s]", expectedVal, asNumber) } } checkNumber("test.int", "10") checkNumber("test.float", "6.66") } func TestFailureWithDecoder(t *testing.T) { sample := []byte(`{"test":{" "invalidCrap":.66}}`) dec := json.NewDecoder(bytes.NewReader(sample)) _, err := ParseJSONDecoder(dec) if err == nil { t.Fatal("Expected parsing error") } } func TestDeletes(t *testing.T) { jsonParsed, _ := ParseJSON([]byte(`{ "outter":{ "inner":{ "value1":10, "value2":22, "value3":32 }, "alsoInner":{ "value1":20, "value2":42, "value3":92 }, "another":{ "value1":null, "value2":null, "value3":null } } }`)) if err := jsonParsed.Delete("outter", "inner", "value2"); err != nil { t.Error(err) } if err := jsonParsed.Delete("outter", "inner", "value4"); err == nil { t.Error("value4 should not have been found in outter.inner") } if err := jsonParsed.Delete("outter", "another", "value1"); err != nil { t.Error(err) } if err := jsonParsed.Delete("outter", "another", "value4"); err == nil { t.Error("value4 should not have been found in outter.another") } if err := jsonParsed.DeleteP("outter.alsoInner.value1"); err != nil { t.Error(err) } if err := jsonParsed.DeleteP("outter.alsoInner.value4"); err == nil { t.Error("value4 should not have been found in outter.alsoInner") } if err := jsonParsed.DeleteP("outter.another.value2"); err != nil { t.Error(err) } if err := jsonParsed.Delete("outter.another.value4"); err == nil { t.Error("value4 should not have been found in outter.another") } expected := `{"outter":{"alsoInner":{"value2":42,"value3":92},"another":{"value3":null},"inner":{"value1":10,"value3":32}}}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from deletes: %v != %v", actual, expected) } } func TestDeletesWithArrays(t *testing.T) { rawJSON := `{ "outter":[ { "foo":{ "value1":10, "value2":22, "value3":32 }, "bar": [ 20, 42, 92 ] }, { "baz":{ "value1":null, "value2":null, "value3":null } } ] }` jsonParsed, err := ParseJSON([]byte(rawJSON)) if err != nil { t.Fatal(err) } if err = jsonParsed.Delete("outter", "1", "baz", "value1"); err != nil { t.Error(err) } expected := `{"outter":[{"bar":[20,42,92],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value2":null,"value3":null}}]}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) } jsonParsed, err = ParseJSON([]byte(rawJSON)) if err != nil { t.Fatal(err) } if err = jsonParsed.Delete("outter", "1", "baz"); err != nil { t.Error(err) } expected = `{"outter":[{"bar":[20,42,92],"foo":{"value1":10,"value2":22,"value3":32}},{}]}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) } jsonParsed, err = ParseJSON([]byte(rawJSON)) if err != nil { t.Fatal(err) } if err = jsonParsed.Delete("outter", "1"); err != nil { t.Error(err) } expected = `{"outter":[{"bar":[20,42,92],"foo":{"value1":10,"value2":22,"value3":32}}]}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) } jsonParsed, err = ParseJSON([]byte(rawJSON)) if err != nil { t.Fatal(err) } if err = jsonParsed.Delete("outter", "0", "bar", "0"); err != nil { t.Error(err) } expected = `{"outter":[{"bar":[42,92],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value1":null,"value2":null,"value3":null}}]}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) } jsonParsed, err = ParseJSON([]byte(rawJSON)) if err != nil { t.Fatal(err) } if err = jsonParsed.Delete("outter", "0", "bar", "1"); err != nil { t.Error(err) } expected = `{"outter":[{"bar":[20,92],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value1":null,"value2":null,"value3":null}}]}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) } jsonParsed, err = ParseJSON([]byte(rawJSON)) if err != nil { t.Fatal(err) } if err = jsonParsed.Delete("outter", "0", "bar", "2"); err != nil { t.Error(err) } expected = `{"outter":[{"bar":[20,42],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value1":null,"value2":null,"value3":null}}]}` if actual := jsonParsed.String(); actual != expected { t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) } } func TestExamples(t *testing.T) { jsonParsed, err := ParseJSON([]byte(`{ "outter":{ "inner":{ "value1":10, "value2":22 }, "contains.dots.in.key":{ "value1":11 }, "contains~tildes~in~key":{ "value1":12 }, "alsoInner":{ "value1":20, "array1":[ 30, 40 ] } } }`)) var value float64 var ok bool value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64) if value != 10.0 || !ok { t.Errorf("wrong value: %v, %v", value, ok) } value, ok = jsonParsed.Path("outter.contains~1dots~1in~1key.value1").Data().(float64) if value != 11.0 || !ok { t.Errorf("wrong value: %v, %v", value, ok) } value, ok = jsonParsed.Path("outter.contains~0tildes~0in~0key.value1").Data().(float64) if value != 12.0 || !ok { t.Errorf("wrong value: %v, %v", value, ok) } value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64) if value != 10.0 || !ok { t.Errorf("wrong value: %v, %v", value, ok) } gObj, err := jsonParsed.JSONPointer("/outter/alsoInner/array1/1") if err != nil { t.Fatal(err) } value, ok = gObj.Data().(float64) if value != 40.0 || !ok { t.Errorf("wrong value: %v, %v", value, ok) } value, ok = jsonParsed.Path("does.not.exist").Data().(float64) if value != 0.0 || ok { t.Errorf("wrong value: %v, %v", value, ok) } jsonParsed, _ = ParseJSON([]byte(`{"array":[ "first", "second", "third" ]}`)) expected := []string{"first", "second", "third"} children := jsonParsed.S("array").Children() for i, child := range children { if expected[i] != child.Data().(string) { t.Errorf("Child unexpected: %v != %v", expected[i], child.Data().(string)) } } } func TestSetAppendArray(t *testing.T) { content := []byte(`{ "nested": { "source": [ "foo", "bar" ] } }`) gObj, err := ParseJSON(content) if err != nil { t.Fatal(err) } if _, err = gObj.Set("baz", "nested", "source", "-"); err != nil { t.Fatal(err) } exp := `{"nested":{"source":["foo","bar","baz"]}}` if act := gObj.String(); act != exp { t.Errorf("Wrong result: %v != %v", act, exp) } } func TestExamples2(t *testing.T) { var err error jsonObj := New() _, err = jsonObj.Set(10, "outter", "inner", "value") if err != nil { t.Errorf("Error: %v", err) return } _, err = jsonObj.SetP(20, "outter.inner.value2") if err != nil { t.Errorf("Error: %v", err) return } _, err = jsonObj.Set(30, "outter", "inner2", "value3") if err != nil { t.Errorf("Error: %v", err) return } expected := `{"outter":{"inner":{"value":10,"value2":20},"inner2":{"value3":30}}}` if jsonObj.String() != expected { t.Errorf("Non matched output: %v != %v", expected, jsonObj.String()) } jsonObj = Wrap(map[string]interface{}{}) jsonObj.Array("array") jsonObj.ArrayAppend(10, "array") jsonObj.ArrayAppend(20, "array") jsonObj.ArrayAppend(30, "array") expected = `{ "array": [ 10, 20, 30 ] }` result := jsonObj.StringIndent(" ", " ") if result != expected { t.Errorf("Non matched output: %v != %v", expected, result) } } func TestExamples3(t *testing.T) { jsonObj := New() jsonObj.ArrayP("foo.array") jsonObj.ArrayAppend(10, "foo", "array") jsonObj.ArrayAppend(20, "foo", "array") jsonObj.ArrayAppend(30, "foo", "array") result := jsonObj.String() expected := `{"foo":{"array":[10,20,30]}}` if result != expected { t.Errorf("Non matched output: %v != %v", result, expected) } } func TestDotNotation(t *testing.T) { sample := []byte(`{"test":{"inner":{"value":10}},"test2":20}`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } if result, _ := val.Path("test.inner.value").Data().(float64); result != 10 { t.Errorf("Expected 10, received: %v", result) } } func TestModify(t *testing.T) { sample := []byte(`{"test":{"value":10},"test2":20}`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } if _, err := val.S("test").Set(45.0, "value"); err != nil { t.Errorf("Failed to set field") } if result, ok := val.Search([]string{"test", "value"}...).Data().(float64); ok { if result != 45 { t.Errorf("Wrong value of result: %v", result) } } else { t.Errorf("Didn't find test.value") } if out := val.String(); `{"test":{"value":45},"test2":20}` != out { t.Errorf("Incorrectly serialized: %v", out) } if out := val.Search("test").String(); `{"value":45}` != out { t.Errorf("Incorrectly serialized: %v", out) } } func TestChildren(t *testing.T) { json1, _ := ParseJSON([]byte(`{ "objectOne":{ }, "objectTwo":{ }, "objectThree":{ } }`)) objects := json1.Children() for _, object := range objects { object.Set("hello world", "child") } expected := `{"objectOne":{"child":"hello world"},"objectThree":{"child":"hello world"}` + `,"objectTwo":{"child":"hello world"}}` received := json1.String() if expected != received { t.Errorf("json1: expected %v, received %v", expected, received) } json2, _ := ParseJSON([]byte(`{ "values":[ { "objectOne":{ } }, { "objectTwo":{ } }, { "objectThree":{ } } ] }`)) json3, _ := ParseJSON([]byte(`{ "values":[ ] }`)) numChildren1, _ := json2.ArrayCount("values") numChildren2, _ := json3.ArrayCount("values") if _, err := json3.ArrayCount("valuesNOTREAL"); err == nil { t.Errorf("expected numChildren3 to fail") } if numChildren1 != 3 || numChildren2 != 0 { t.Errorf("CountElements, expected 3 and 0, received %v and %v", numChildren1, numChildren2) } objects = json2.S("values").Children() for _, object := range objects { object.Set("hello world", "child") json3.ArrayAppend(object.Data(), "values") } expected = `{"values":[{"child":"hello world","objectOne":{}},{"child":"hello world",` + `"objectTwo":{}},{"child":"hello world","objectThree":{}}]}` received = json2.String() if expected != received { t.Errorf("json2: expected %v, received %v", expected, received) } received = json3.String() if expected != received { t.Errorf("json3: expected %v, received %v", expected, received) } } func TestChildrenMap(t *testing.T) { json1, _ := ParseJSON([]byte(`{ "objectOne":{"num":1}, "objectTwo":{"num":2}, "objectThree":{"num":3} }`)) objectMap := json1.ChildrenMap() if len(objectMap) != 3 { t.Errorf("Wrong num of elements in objectMap: %v != %v", len(objectMap), 3) return } for key, val := range objectMap { if "objectOne" == key { if val := val.S("num").Data().(float64); val != 1 { t.Errorf("%v != %v", val, 1) } } else if "objectTwo" == key { if val := val.S("num").Data().(float64); val != 2 { t.Errorf("%v != %v", val, 2) } } else if "objectThree" == key { if val := val.S("num").Data().(float64); val != 3 { t.Errorf("%v != %v", val, 3) } } else { t.Errorf("Unexpected key: %v", key) } } objectMap["objectOne"].Set(500, "num") if val := json1.Path("objectOne.num").Data().(int); val != 500 { t.Errorf("set objectOne failed: %v != %v", val, 500) } } func TestNestedAnonymousArrays(t *testing.T) { json1, _ := ParseJSON([]byte(`{ "array":[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [{ "test" : 50 }] ] }`)) childTest := json1.S("array").Index(0).Children() if val := childTest[0].Data().(float64); val != 1 { t.Errorf("child test: %v != %v", val, 1) } if val := childTest[1].Data().(float64); val != 2 { t.Errorf("child test: %v != %v", val, 2) } if val := childTest[2].Data().(float64); val != 3 { t.Errorf("child test: %v != %v", val, 3) } if val := json1.Path("array").Index(1).Index(1).Data().(float64); val != 5 { t.Errorf("nested child test: %v != %v", val, 5) } if val := json1.Path("array").Index(3).Index(0).S("test").Data().(float64); val != 50 { t.Errorf("nested child object test: %v != %v", val, 50) } json1.Path("array").Index(3).Index(0).Set(200, "test") if val := json1.Path("array").Index(3).Index(0).S("test").Data().(int); val != 200 { t.Errorf("set nested child object: %v != %v", val, 200) } } func TestArrays(t *testing.T) { json1, _ := ParseJSON([]byte(`{ "languages":{ "english":{ "places":0 }, "french": { "places": [ "france", "belgium" ] } } }`)) json2, _ := ParseJSON([]byte(`{ "places":[ "great_britain", "united_states_of_america", "the_world" ] }`)) if englishPlaces := json2.Search("places").Data(); englishPlaces != nil { json1.Path("languages.english").Set(englishPlaces, "places") } else { t.Errorf("Didn't find places in json2") } if englishPlaces := json1.Search("languages", "english", "places").Data(); englishPlaces != nil { englishArray, ok := englishPlaces.([]interface{}) if !ok { t.Errorf("places in json1 (%v) was not an array", englishPlaces) } if len(englishArray) != 3 { t.Errorf("wrong length of array: %v", len(englishArray)) } } else { t.Errorf("Didn't find places in json1") } for i := 0; i < 3; i++ { if err := json2.ArrayRemove(0, "places"); err != nil { t.Errorf("Error removing element: %v", err) } } json2.ArrayAppend(map[string]interface{}{}, "places") json2.ArrayAppend(map[string]interface{}{}, "places") json2.ArrayAppend(map[string]interface{}{}, "places") // Using float64 for this test even though it's completely inappropriate because // later on the API might do something clever with types, in which case all numbers // will become float64. for i := 0; i < 3; i++ { obj, _ := json2.ArrayElement(i, "places") obj2, _ := obj.Object(fmt.Sprintf("object%v", i)) obj2.Set(float64(i), "index") } children := json2.S("places").Children() for i, obj := range children { if id, ok := obj.S(fmt.Sprintf("object%v", i)).S("index").Data().(float64); ok { if id != float64(i) { t.Errorf("Wrong index somehow, expected %v, received %v", i, id) } } else { t.Errorf("Failed to find element %v from %v", i, obj) } } if err := json2.ArrayRemove(1, "places"); err != nil { t.Errorf("Error removing element: %v", err) } expected := `{"places":[{"object0":{"index":0}},{"object2":{"index":2}}]}` received := json2.String() if expected != received { t.Errorf("Wrong output, expected: %v, received: %v", expected, received) } } func TestArraysTwo(t *testing.T) { json1 := New() test1, err := json1.ArrayOfSize(4, "test1") if err != nil { t.Error(err) } if _, err = test1.ArrayOfSizeI(2, 0); err != nil { t.Error(err) } if _, err = test1.ArrayOfSizeI(2, 1); err != nil { t.Error(err) } if _, err = test1.ArrayOfSizeI(2, 2); err != nil { t.Error(err) } if _, err = test1.ArrayOfSizeI(2, 3); err != nil { t.Error(err) } if _, err = test1.ArrayOfSizeI(2, 4); err != ErrOutOfBounds { t.Errorf("Index should have been out of bounds") } if _, err = json1.S("test1").Index(0).SetIndex(10, 0); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(0).SetIndex(11, 1); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(1).SetIndex(12, 0); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(1).SetIndex(13, 1); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(2).SetIndex(14, 0); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(2).SetIndex(15, 1); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(3).SetIndex(16, 0); err != nil { t.Error(err) } if _, err = json1.S("test1").Index(3).SetIndex(17, 1); err != nil { t.Error(err) } if val := json1.S("test1").Index(0).Index(0).Data().(int); val != 10 { t.Errorf("create array: %v != %v", val, 10) } if val := json1.S("test1").Index(0).Index(1).Data().(int); val != 11 { t.Errorf("create array: %v != %v", val, 11) } if val := json1.S("test1").Index(1).Index(0).Data().(int); val != 12 { t.Errorf("create array: %v != %v", val, 12) } if val := json1.S("test1").Index(1).Index(1).Data().(int); val != 13 { t.Errorf("create array: %v != %v", val, 13) } if val := json1.S("test1").Index(2).Index(0).Data().(int); val != 14 { t.Errorf("create array: %v != %v", val, 14) } if val := json1.S("test1").Index(2).Index(1).Data().(int); val != 15 { t.Errorf("create array: %v != %v", val, 15) } if val := json1.S("test1").Index(3).Index(0).Data().(int); val != 16 { t.Errorf("create array: %v != %v", val, 16) } if val := json1.S("test1").Index(3).Index(1).Data().(int); val != 17 { t.Errorf("create array: %v != %v", val, 17) } } func TestArraysThree(t *testing.T) { json1 := New() test, err := json1.ArrayOfSizeP(1, "test1.test2") if err != nil { t.Fatal(err) } test.SetIndex(10, 0) if val := json1.S("test1", "test2").Index(0).Data().(int); val != 10 { t.Error(err) } } func TestSetJSONPointer(t *testing.T) { type testCase struct { input string pointer string value interface{} output string } tests := []testCase{ { input: `{"foo":{"bar":"baz"}}`, pointer: "/foo/bar", value: "qux", output: `{"foo":{"bar":"qux"}}`, }, { input: `{"foo":["bar","ignored","baz"]}`, pointer: "/foo/2", value: "qux", output: `{"foo":["bar","ignored","qux"]}`, }, { input: `{"foo":["bar","ignored",{"bar":"baz"}]}`, pointer: "/foo/2/bar", value: "qux", output: `{"foo":["bar","ignored",{"bar":"qux"}]}`, }, } for _, test := range tests { gObj, err := ParseJSON([]byte(test.input)) if err != nil { t.Errorf("Failed to parse '%v': %v", test.input, err) continue } if _, err = gObj.SetJSONPointer(test.value, test.pointer); err != nil { t.Error(err) continue } if exp, act := test.output, gObj.String(); exp != act { t.Errorf("Wrong result: %v != %v", act, exp) } } } func TestArrayReplace(t *testing.T) { json1 := New() json1.Set(1, "first") json1.ArrayAppend(2, "first") json1.ArrayAppend(3, "second") expected := `{"first":[1,2],"second":[3]}` received := json1.String() if expected != received { t.Errorf("Wrong output, expected: %v, received: %v", expected, received) } } func TestArraysRoot(t *testing.T) { sample := []byte(`["test1"]`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } val.ArrayAppend("test2") val.ArrayAppend("test3") if obj, err := val.ObjectI(2); err != nil { t.Error(err) } else { obj.Set("bar", "foo") } if expected, actual := `["test1","test2",{"foo":"bar"}]`, val.String(); expected != actual { t.Errorf("expected %v, received: %v", expected, actual) } } func TestLargeSample(t *testing.T) { sample := []byte(`{ "test":{ "innerTest":{ "value":10, "value2":22, "value3":{ "moreValue":45 } } }, "test2":20 }`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } if result, ok := val.Search("test", "innerTest", "value3", "moreValue").Data().(float64); ok { if result != 45 { t.Errorf("Wrong value of result: %v", result) } } else { t.Errorf("Didn't find value") } } func TestShorthand(t *testing.T) { json, _ := ParseJSON([]byte(`{ "outter":{ "inner":{ "value":5, "value2":10, "value3":11 }, "inner2":{ } }, "outter2":{ "inner":0 } }`)) missingValue := json.S("outter").S("doesntexist").S("alsodoesntexist").S("inner").S("value").Data() if missingValue != nil { t.Errorf("missing value was actually found: %v\n", missingValue) } realValue := json.S("outter").S("inner").S("value2").Data().(float64) if realValue != 10 { t.Errorf("real value was incorrect: %v\n", realValue) } _, err := json.S("outter2").Set(json.S("outter").S("inner").Data(), "inner") if err != nil { t.Errorf("error setting outter2: %v\n", err) } compare := `{"outter":{"inner":{"value":5,"value2":10,"value3":11},"inner2":{}}` + `,"outter2":{"inner":{"value":5,"value2":10,"value3":11}}}` out := json.String() if out != compare { t.Errorf("wrong serialized structure: %v\n", out) } compare2 := `{"outter":{"inner":{"value":6,"value2":10,"value3":11},"inner2":{}}` + `,"outter2":{"inner":{"value":6,"value2":10,"value3":11}}}` json.S("outter").S("inner").Set(6, "value") out = json.String() if out != compare2 { t.Errorf("wrong serialized structure: %v\n", out) } } func TestInvalid(t *testing.T) { invalidJSONSamples := []string{ `{dfads"`, ``, // `""`, // `"hello"`, "{}\n{}", } for _, sample := range invalidJSONSamples { if _, err := ParseJSON([]byte(sample)); err == nil { t.Errorf("parsing invalid JSON '%v' did not return error", sample) } } if _, err := ParseJSON(nil); err == nil { t.Errorf("parsing nil did not return error") } validObj, err := ParseJSON([]byte(`{}`)) if err != nil { t.Errorf("failed to parse '{}'") } invalidStr := validObj.S("Doesn't exist").String() if "null" != invalidStr { t.Errorf("expected 'null', received: %v", invalidStr) } } func TestCreation(t *testing.T) { json, _ := ParseJSON([]byte(`{}`)) inner, err := json.ObjectP("test.inner") if err != nil { t.Errorf("Error: %v", err) return } inner.Set(10, "first") inner.Set(20, "second") inner.Array("array") inner.ArrayAppend("first element of the array", "array") inner.ArrayAppend(2, "array") inner.ArrayAppend("three", "array") expected := `{"test":{"inner":{"array":["first element of the array",2,"three"],` + `"first":10,"second":20}}}` actual := json.String() if actual != expected { t.Errorf("received incorrect output from json object: %v\n", actual) } } type outterJSON struct { FirstInner innerJSON SecondInner innerJSON ThirdInner innerJSON } type innerJSON struct { NumberType float64 StringType string } type jsonStructure struct { FirstOutter outterJSON SecondOutter outterJSON } var jsonContent = []byte(`{ "firstOutter":{ "firstInner":{ "numberType":11, "stringType":"hello world, first first" }, "secondInner":{ "numberType":12, "stringType":"hello world, first second" }, "thirdInner":{ "numberType":13, "stringType":"hello world, first third" } }, "secondOutter":{ "firstInner":{ "numberType":21, "stringType":"hello world, second first" }, "secondInner":{ "numberType":22, "stringType":"hello world, second second" }, "thirdInner":{ "numberType":23, "stringType":"hello world, second third" } } }`) /* Simple use case, compares unmarshalling declared structs vs dynamically searching for the equivalent hierarchy. Hopefully we won't see too great a performance drop from the dynamic approach. */ func BenchmarkStatic(b *testing.B) { for i := 0; i < b.N; i++ { var jsonObj jsonStructure json.Unmarshal(jsonContent, &jsonObj) if val := jsonObj.FirstOutter.SecondInner.NumberType; val != 12 { b.Errorf("Wrong value of FirstOutter.SecondInner.NumberType: %v\n", val) } expected := "hello world, first second" if val := jsonObj.FirstOutter.SecondInner.StringType; val != expected { b.Errorf("Wrong value of FirstOutter.SecondInner.StringType: %v\n", val) } if val := jsonObj.SecondOutter.ThirdInner.NumberType; val != 23 { b.Errorf("Wrong value of SecondOutter.ThirdInner.NumberType: %v\n", val) } expected = "hello world, second second" if val := jsonObj.SecondOutter.SecondInner.StringType; val != expected { b.Errorf("Wrong value of SecondOutter.SecondInner.StringType: %v\n", val) } } } func BenchmarkDynamic(b *testing.B) { for i := 0; i < b.N; i++ { jsonObj, err := ParseJSON(jsonContent) if err != nil { b.Errorf("Error parsing json: %v\n", err) } FOSI := jsonObj.S("firstOutter", "secondInner") SOSI := jsonObj.S("secondOutter", "secondInner") SOTI := jsonObj.S("secondOutter", "thirdInner") if val := FOSI.S("numberType").Data().(float64); val != 12 { b.Errorf("Wrong value of FirstOutter.SecondInner.NumberType: %v\n", val) } expected := "hello world, first second" if val := FOSI.S("stringType").Data().(string); val != expected { b.Errorf("Wrong value of FirstOutter.SecondInner.StringType: %v\n", val) } if val := SOTI.S("numberType").Data().(float64); val != 23 { b.Errorf("Wrong value of SecondOutter.ThirdInner.NumberType: %v\n", val) } expected = "hello world, second second" if val := SOSI.S("stringType").Data().(string); val != expected { b.Errorf("Wrong value of SecondOutter.SecondInner.StringType: %v\n", val) } } } func TestBadIndexes(t *testing.T) { jsonObj, err := ParseJSON([]byte(`{"array":[1,2,3]}`)) if err != nil { t.Error(err) } if act := jsonObj.Index(0).Data(); nil != act { t.Errorf("Unexpected value returned: %v != %v", nil, act) } if act := jsonObj.S("array").Index(4).Data(); nil != act { t.Errorf("Unexpected value returned: %v != %v", nil, act) } } func TestNilSet(t *testing.T) { obj := Container{nil} if _, err := obj.Set("bar", "foo"); err != nil { t.Error(err) } if _, err := obj.Set("new", "foo", "bar"); err != ErrPathCollision { t.Errorf("Expected ErrPathCollision: %v, %s", err, obj.Data()) } if _, err := obj.SetIndex("new", 0); err != ErrNotArray { t.Errorf("Expected ErrNotArray: %v, %s", err, obj.Data()) } } func TestLargeSampleWithHtmlEscape(t *testing.T) { sample := []byte(`{ "test": { "innerTest": { "value": 10, "value2": "Title", "value3": { "moreValue": 45 } } }, "test2": 20 }`) sampleWithHTMLEscape := []byte(`{ "test": { "innerTest": { "value": 10, "value2": "\u003ctitle\u003eTitle\u003c/title\u003e", "value3": { "moreValue": 45 } } }, "test2": 20 }`) val, err := ParseJSON(sample) if err != nil { t.Errorf("Failed to parse: %v", err) return } exp := string(sample) res := string(val.EncodeJSON(EncodeOptIndent("", "\t"))) if exp != res { t.Errorf("Wrong conversion without html escaping: %s != %s", res, exp) } exp = string(sampleWithHTMLEscape) res = string(val.EncodeJSON(EncodeOptHTMLEscape(true), EncodeOptIndent("", "\t"))) if exp != res { t.Errorf("Wrong conversion with html escaping: %s != %s", exp, res) } } func TestMergeCases(t *testing.T) { type testCase struct { first string second string expected string } testCases := []testCase{ { first: `{"outter":{"value1":"one"}}`, second: `{"outter":{"inner":{"value3": "threre"}},"outter2":{"value2": "two"}}`, expected: `{"outter":{"inner":{"value3":"threre"},"value1":"one"},"outter2":{"value2":"two"}}`, }, { first: `{"outter":["first"]}`, second: `{"outter":["second"]}`, expected: `{"outter":["first","second"]}`, }, { first: `{"outter":["first",{"inner":"second"}]}`, second: `{"outter":["third"]}`, expected: `{"outter":["first",{"inner":"second"},"third"]}`, }, { first: `{"outter":["first",{"inner":"second"}]}`, second: `{"outter":"third"}`, expected: `{"outter":["first",{"inner":"second"},"third"]}`, }, { first: `{"outter":"first"}`, second: `{"outter":"second"}`, expected: `{"outter":["first","second"]}`, }, { first: `{"outter":{"inner":"first"}}`, second: `{"outter":{"inner":"second"}}`, expected: `{"outter":{"inner":["first","second"]}}`, }, { first: `{"outter":{"inner":"first"}}`, second: `{"outter":"second"}`, expected: `{"outter":[{"inner":"first"},"second"]}`, }, { first: `{"outter":{"inner":"second"}}`, second: `{"outter":{"inner":{"inner2":"first"}}}`, expected: `{"outter":{"inner":["second",{"inner2":"first"}]}}`, }, { first: `{"outter":{"inner":["second"]}}`, second: `{"outter":{"inner":{"inner2":"first"}}}`, expected: `{"outter":{"inner":["second",{"inner2":"first"}]}}`, }, { first: `{"outter":"second"}`, second: `{"outter":{"inner":"first"}}`, expected: `{"outter":["second",{"inner":"first"}]}`, }, { first: `{"outter":"first"}`, second: `{"outter":["second"]}`, expected: `{"outter":["first","second"]}`, }, } for i, test := range testCases { var firstContainer, secondContainer *Container var err error firstContainer, err = ParseJSON([]byte(test.first)) if err != nil { t.Errorf("[%d] Failed to parse '%v': %v", i, test.first, err) } secondContainer, err = ParseJSON([]byte(test.second)) if err != nil { t.Errorf("[%d] Failed to parse '%v': %v", i, test.second, err) } if err = firstContainer.Merge(secondContainer); err != nil { t.Errorf("[%d] Failed to merge: '%v': %v", i, test.first, err) } if exp, act := test.expected, firstContainer.String(); exp != act { t.Errorf("[%d] Wrong result: %v != %v", i, act, exp) } } } func TestMarshalsJSON(t *testing.T) { sample := []byte(`{"test":{"value":10},"test2":20}`) val, err := ParseJSON(sample) if err != nil { t.Fatal(err) } marshaled, err := json.Marshal(val) if err != nil { t.Fatal(err) } if exp, act := string(sample), string(marshaled); exp != act { t.Errorf("Unexpected result: %v != %v", act, exp) } } gabs-2.3.0/go.mod000066400000000000000000000000421357441163300135410ustar00rootroot00000000000000module github.com/Jeffail/gabs/v2 gabs-2.3.0/migration.md000066400000000000000000000045571357441163300147650ustar00rootroot00000000000000Migration Guides ================ ## Migrating to Version 2 ### Path Previously it was not possible to specify a dot path where a key itself contains a dot. In v2 it is now possible with the escape sequence `~1`. For example, given the JSON doc `{"foo":{"bar.baz":10}}`, the path `foo.bar~1baz` would return `10`. This escape sequence means the character `~` is also a special case, therefore it must also be escaped to the sequence `~0`. ### Consume Calls to `Consume(root interface{}) (*Container, error)` should be replaced with `Wrap(root interface{}) *Container`. The error response was removed in order to avoid unnecessary duplicate type checks on `root`. This also allows shorthand chained queries like `gabs.Wrap(foo).S("bar","baz").Data()`. ### Search Across Arrays All query functions (`Search`, `Path`, `Set`, `SetP`, etc) now attempt to resolve a specific index when they encounter an array. This means path queries must specify an integer index at the level of arrays within the content. For example, given the sample document: ``` json { "foo": [ { "bar": { "baz": 45 } } ] } ``` In v1 the query `Search("foo", "bar", "baz")` would propagate the array in the result giving us `[45]`. In v2 we can access the field directly with `Search("foo", "0", "bar", "baz")`. The index is _required_, otherwise the query fails. In query functions that do not set a value it is possible to specify `*` instead of an index in order to obtain all elements of the array, this produces the equivalent result as the behaviour from v1. For example, in v2 the query `Search("foo", "*", "bar", "baz")` would return `[45]`. ### Children and ChildrenMap The `Children` and `ChildrenMap` methods no longer return errors. Instead, in the event of the underlying value being invalid (not an array or object), a `nil` slice and empty map are returned respectively. If explicit type checking is required the recommended approach would be casting on the value, e.g. `foo, ok := obj.Data().([]interface)`. ### Serialising Invalid Types In v1 attempting to serialise with `Bytes`, `String`, etc, with an invalid structure would result in an empty object `{}`. This behaviour was unintuitive and in v2 `null` will be returned instead. If explicit marshalling is required with proper error propagation it is still recommended to use the `json` package directly on the underlying value.