From 4c6f532e05dba116b025966364fc7a2f75e47b1e Mon Sep 17 00:00:00 2001 From: sh0rez Date: Wed, 13 May 2020 12:31:07 +0200 Subject: [PATCH] feat(lib): fn.withArgs --- doc-util/README.md | 138 +++++++++++++++++++++++++++++++++++++- doc-util/docs/d.md | 124 ---------------------------------- doc-util/main.libsonnet | 8 +++ pkg/docsonnet/fast.go | 6 +- pkg/render/render_test.go | 48 +++++++++++++ pkged.go | 2 +- 6 files changed, 199 insertions(+), 127 deletions(-) mode change 120000 => 100644 doc-util/README.md delete mode 100644 doc-util/docs/d.md create mode 100644 pkg/render/render_test.go diff --git a/doc-util/README.md b/doc-util/README.md deleted file mode 120000 index b53090f..0000000 --- a/doc-util/README.md +++ /dev/null @@ -1 +0,0 @@ -docs/d.md \ No newline at end of file diff --git a/doc-util/README.md b/doc-util/README.md new file mode 100644 index 0000000..162415b --- /dev/null +++ b/doc-util/README.md @@ -0,0 +1,137 @@ +--- +permalink: / +--- + +# package d + +```jsonnet +local d = import "github.com/sh0rez/docsonnet/doc-util" +``` + +`doc-util` provides a Jsonnet interface for `docsonnet`, + a Jsonnet API doc generator that uses structured data instead of comments. + + +## Index + +* [`fn arg(name, type, default)`](#fn-arg) +* [`fn fn(help, args)`](#fn-fn) +* [`fn obj(help, fields)`](#fn-obj) +* [`fn pkg(name, url, help)`](#fn-pkg) +* [`obj argument`](#obj-argument) + * [`fn new(name, type, default)`](#fn-argumentnew) +* [`obj func`](#obj-func) + * [`fn new(help, args)`](#fn-funcnew) + * [`fn withArgs(args)`](#fn-funcwithargs) + * [`fn withHelp(help)`](#fn-funcwithhelp) +* [`obj object`](#obj-object) + * [`fn new(help, fields)`](#fn-objectnew) + * [`fn withFields(fields)`](#fn-objectwithfields) +* [`obj package`](#obj-package) + * [`fn new(name, url, help)`](#fn-packagenew) + +## Fields + +### fn arg + +```ts +arg(name, type, default) +``` + +`arg` is a shorthand for `argument.new` + +### fn fn + +```ts +fn(help, args) +``` + +`fn` is a shorthand for `func.new` + +### fn obj + +```ts +obj(help, fields) +``` + +`obj` is a shorthand for `object.new` + +### fn pkg + +```ts +pkg(name, url, help) +``` + +`new` is a shorthand for `package.new` + +## obj argument + +Utilities for creating function arguments + +### fn argument.new + +```ts +new(name, type, default) +``` + +new creates a new function argument, taking the name, the type and optionally a default value + +## obj func + +Utilities for documenting Jsonnet methods (functions of objects) + +### fn func.new + +```ts +new(help, args) +``` + +new creates a new function, optionally with description and arguments + +### fn func.withArgs + +```ts +withArgs(args) +``` + +The `withArgs` modifier overrides the arguments of that function + +### fn func.withHelp + +```ts +withHelp(help) +``` + +The `withHelp` modifier overrides the help text of that function + +## obj object + +Utilities for documenting Jsonnet objects (`{ }`). + +### fn object.new + +```ts +new(help, fields) +``` + +new creates a new object, optionally with description and fields + +### fn object.withFields + +```ts +withFields(fields) +``` + +The `withFields` modifier overrides the fields property of an already created object + +## obj package + + + +### fn package.new + +```ts +new(name, url, help) +``` + +new creates a new package with given `name`, `import` URL and `help` text \ No newline at end of file diff --git a/doc-util/docs/d.md b/doc-util/docs/d.md deleted file mode 100644 index 02d47c1..0000000 --- a/doc-util/docs/d.md +++ /dev/null @@ -1,124 +0,0 @@ -# package d - -```jsonnet -local d = import "github.com/sh0rez/docsonnet/doc-util" -``` - -`doc-util` provides a Jsonnet interface for `docsonnet`, - a Jsonnet API doc generator that uses structured data instead of comments. - - -## Index - -* [`fn arg(name, type, default)`](#fn-arg) -* [`fn fn(help, args)`](#fn-fn) -* [`fn obj(help, fields)`](#fn-obj) -* [`fn pkg(name, url, help)`](#fn-pkg) -* [`obj argument`](#obj-argument) - * [`fn new(name, type, default)`](#fn-argumentnew) -* [`obj func`](#obj-func) - * [`fn new(help, args)`](#fn-funcnew) - * [`fn withHelp(help)`](#fn-funcwithhelp) -* [`obj object`](#obj-object) - * [`fn new(help, fields)`](#fn-objectnew) - * [`fn withFields(fields)`](#fn-objectwithfields) -* [`obj package`](#obj-package) - * [`fn new(name, url, help)`](#fn-packagenew) - -## Fields - -### fn arg - -```ts -arg(name, type, default) -``` - -`arg` is a shorthand for `argument.new` - -### fn fn - -```ts -fn(help, args) -``` - -`fn` is a shorthand for `func.new` - -### fn obj - -```ts -obj(help, fields) -``` - -`obj` is a shorthand for `object.new` - -### fn pkg - -```ts -pkg(name, url, help) -``` - -`new` is a shorthand for `package.new` - -## obj argument - -Utilities for creating function arguments - -### fn argument.new - -```ts -new(name, type, default) -``` - -new creates a new function argument, taking the name, the type and optionally a default value - -## obj func - -Utilities for documenting Jsonnet methods (functions of objects) - -### fn func.new - -```ts -new(help, args) -``` - -new creates a new function, optionally with description and arguments - -### fn func.withHelp - -```ts -withHelp(help) -``` - -The `withHelp` modifier overrides the help text of that function - -## obj object - -Utilities for documenting Jsonnet objects (`{ }`). - -### fn object.new - -```ts -new(help, fields) -``` - -new creates a new object, optionally with description and fields - -### fn object.withFields - -```ts -withFields(fields) -``` - -The `withFields` modifier overrides the fields property of an already created object - -## obj package - - - -### fn package.new - -```ts -new(name, url, help) -``` - -new creates a new package with given `name`, `import` URL and `help` text \ No newline at end of file diff --git a/doc-util/main.libsonnet b/doc-util/main.libsonnet index 1db425a..60cc669 100644 --- a/doc-util/main.libsonnet +++ b/doc-util/main.libsonnet @@ -51,6 +51,11 @@ withHelp(help):: { 'function'+: { help: help, } }, + + '#withArgs': d.fn('The `withArgs` modifier overrides the arguments of that function', [d.arg('args', d.T.array)]), + withArgs(args):: { 'function'+: { + args: args, + } }, }, '#fn': self.func['#new'] + d.func.withHelp('`fn` is a shorthand for `func.new`'), @@ -83,6 +88,9 @@ array: 'array', any: 'any', + 'null': "null", + nil: self["null"], + func: 'function', 'function': self.func, }, diff --git a/pkg/docsonnet/fast.go b/pkg/docsonnet/fast.go index d51d42a..ada9f44 100644 --- a/pkg/docsonnet/fast.go +++ b/pkg/docsonnet/fast.go @@ -98,9 +98,13 @@ func loadField(name string, field map[string]interface{}, parent map[string]inte } func loadFn(name string, msi map[string]interface{}) Field { + h, ok := msi["help"].(string) + if !ok { + h = "" + } fn := Function{ Name: name, - Help: msi["help"].(string), + Help: h, } if args, ok := msi["args"]; ok { fn.Args = loadArgs(args.([]interface{})) diff --git a/pkg/render/render_test.go b/pkg/render/render_test.go new file mode 100644 index 0000000..58d9544 --- /dev/null +++ b/pkg/render/render_test.go @@ -0,0 +1,48 @@ +package render + +import ( + "testing" + + "github.com/sh0rez/docsonnet/pkg/docsonnet" + "github.com/stretchr/testify/assert" +) + +func TestSortFields(t *testing.T) { + api := docsonnet.Fields{ + "new": dfn(), + "newNamed": dfn(), + + "aaa": dfn(), + "bbb": dobj(), + "ccc": dfn(), + + "metadata": dobj(), + } + + sorted := []string{ + "new", + "newNamed", + + "aaa", + "ccc", + + "bbb", + "metadata", + } + + res := sortFields(api) + + assert.Equal(t, sorted, res) +} + +func dobj() docsonnet.Field { + return docsonnet.Field{ + Object: &docsonnet.Object{}, + } +} + +func dfn() docsonnet.Field { + return docsonnet.Field{ + Function: &docsonnet.Function{}, + } +} diff --git a/pkged.go b/pkged.go index f9d3729..afbfd77 100644 --- a/pkged.go +++ b/pkged.go @@ -9,4 +9,4 @@ import ( "github.com/markbates/pkger/pkging/mem" ) -var _ = pkger.Apply(mem.UnmarshalEmbed([]byte(`1f8b08000000000000ffec3b5b93a2bad67fe514af9f7b04bc4c63d57968992da2ddceb4ce88706ad7544862a00d844340a577cd7fff2ae122dad7b3cf7efa3e1f5a929545b2ee2b59a4ff54c278cbb832fa53216116e4fe27c8a22e0fd4143f7511839cc531cec4f0973055464a376011ee667e0a00dc750f2cddb5b03a8a1d252ccdbe812c50466f4ed8511620c2ca488940182b1de50b83ca48513aca779012b1e0c54a8475fd303e9b60c9d873bc6714dd830c06cae85fca27e58f8eb2ca00c5ca284b735c75961870162b23858bde3f104e708c700c8bd13f5af413f61ba4e1b10b69a874148b4d428ab9985590ff8930a5a3243b829168fe518b4122e0183214c6a4fb2896e928db4850f5ead4ad8108a43b1f649877c5d4e99b83e2572c12e1e81cef52f002f14c3eefe1a6421c6271ca8860ed0b4e245f7ebe0d05db7e9161ae7414c8a224c59c77b71464b80d204f6122fb7106c218a75d1af2ac02e0a36ca54592b1a6d105e58c650786492009a8faa83d88383875303cef227d30d08c67806e1867388d01ed62740029e2976894864916c213248840abd7bc9e8218e559485f18e2b99f517c1a88d0e0d411efb57ab0dfeab419e001d0ce7afa6078d61f687aab7fb164465b723a0e54e3bcd74d76e151e934f6d96a76018fb576df071c0ffb6790300669d18640be6f7703dc9ebc36fea69f483bc569ca5241e59602f21ff906617ebedd02caba014ef1e51823148b771f5fb2f267c35dc03f824270fc1ed6c9b06ac63e869e80945f3af85be8292329b870f400f020842c4dc4e2871424af0d13f65b94d32c9434fe1711a721270209ff206a067698c5dd1751039c80bf699aee36a419be54c0cb21f3436115473e467f2d00bf82c733c42ee84b18c7223c8ac0497186df1eedc208bd8fd10d6391d7e8df9c142e977e3b73bc87c8697ea1069e6cb55e37a9c20261c98e7c0ae36e0122fa692f629e30e7ead18529ec0958631e2225014ada2098e4edee36ca384bb33628c659960288db30c66b2739f91e93c26ce580f35752bca5186634cccec03c8c09c55b1a92e06c555e700828ede2238638debf3494c7324e9f8c1ff34ce4e28e22f36fc8ba21abb250098ec4eeab7c74fd909c9a19afdb55068ac208578fae8c0909904291807fe72cc32849c33803be4c2ca56d08ad055996b49af2a7965e03ac29ae6022d7272993795ef4f3548c4827605c0aa06ce565284c4a2ec443b833aefa957c658be063d234babc8833202495e671563256b5ba506ed0ea1ec2beb4b7badf4816642c9269ffd94825d267705e08f22b53e2590a99d421cfd2302672a88861f5384d5f6956e9284226dd0c4749b5673aeb976941404b6ef238840cb55add3cdb6ac3f3fe8dec72b015787b1c23967609a320269f584abac76ebdab0a000c80ae7e0c2b61b4d07aeae01d6cf910def751bc7aa7f116729eee71bd437b032fd8a1eddb18cf37676f20bfc3b130601473f11761ce01796dba331721b974c177f192941d8b7710f56e9000b87b032b443178659817bc0a892f8d4a2be418e629eefa210ad3fc556995069b82986f591abd8554dba898f02378b198ef8f8ef21df3ac396fc539a525a8396195a07b860491a33f950f9d3defc5a1b33a057ee85c6bb17b86de41eb12f629624862af71ca4379acd43e697de5d7af5f1d655bf2f0e6417b249abf8970de95274b1afaa713b838a98b27c21908a99c2a3e1da25bb81d85874f5819e99f8d7e478944d018f57555367fca6832527455577f5307bf69fa77edf368301cf5079f54ad7f630cd57eef7f547da48ad810f29f48c8670b28c73272c95200de2ba3e1b0a77eee2876cc9451efe6b371a3ab371d6541c378a78c34a912ac8c7a3dede6a6a3fc089132d25455ed28d6a9b9f9f933014855466a47592231a9da51562dd2c7745772d2578da1e832b8e3cae8a6a3dc666124485961a88cb4c18da11b86aa0e3aca820b48ef46d70d7da8fdea28f76f63d62cffea28e68731373f7fe671ce315246ff523b6a47fd436a589c533f542969b47c593239d5455a18cf6b23a7eac7a9c2513a4255e0a8f4755ee168572e4aec0b2f2acff62727fb3fe077d5464210b8231fad49bdea84bf3a0a021950460a3e30625bc7bdab4fb86d3d906fabf193e71ca3bb5b36b7cddbd82ee0d0b61e726865f1fc9611dbbc257e34c9bcef6aec99909be12db1cdb106a38331d35182ac4073c3c1a3afab7ba893038cd6c33b7db9777b8bbd1f0d28328d9dafdf6768b34cfcd3fbcc738e876f9be3cdb65ac336c7c4b38cc7bbcd5a05ce91d8d371e1f7bcc4b3d64fb6f53b59c5f57ce3c48f9714465ee0ea3f881719856d8d77bebe78f2f541ee6d96e4ae99f396b8abf11cea46ee476bd5fe7df2b05a09dc7be2e9ebdcdbcc026419853d5d3277f340d0664161317e42d399e6f6961a8cd63bdb5a06c8fa9d0067f084ac75e099e3bd178e1f7d5dcb3c67a0c2a23f976b4d6b5e76dc244cd01fb87a1678fa8fe1d770fcb9e4fb36b6f501450514b0dd5de4e57313e5de0612b737a3ee6629d60f6c4be08c0faeb3485d07517b2ae44c88a753dd73fac4b50681efacc99d3926c0d10e7e6fa6bae678fd637720ae33d8d956295fd71cabde265067c58138fa43ee6e66b158cf75342a609e39f871172f5418d1dc2b76c21e6a1ca1dff8ce1cefeee8430e7bcb02388378be92ef0430826ca607d49fde3ec759abc95d6337ebde5cd2fa83dbd375e19b0702ac3587e6ae2d13629738c3eac91bb889127f33dec3f821fe1a4a9b3b8d5972fd61f5acedca906b4fd55207267c14763c7b62437bbaa0bee5e6d09a3c027d127b6b5ee90221dbe482af083983c7bb58dab42de4373791e4c135c789d4cb74c184ac8135c93d731cf93d9bb8273de7252e2cf99f8e53efa57557c24661439fafcffeed390b75f6c4040d7b37626ca6491f489045853dc875846f2047a37ebc4cfc08b6fd61ef4609757bcb27db0c082ec6866beef259b19332a9e7fffac888f47df396cc8a85a021fe800d56733f70db320ec8a27b3f9a707fba2348a72a30c73b6fb37884112dc7c271e0470fc48b28f5ade5d385dd499b7cc7ee22e0acb937bd6fecaaa6ffb95d49bd1bb30272dbf212cf39eee093f679bb2a6dcbb68c1044eb47f4e5c4771d77be86e5f31427c6f5bac366aee250f9b54ab6abc3bcf1df9eb40faba6b325c31fc0fa41dc697b7c416c4bdb7b1615f317b665e8de66560047ca5815f8b5acca7837a6305e0e6ccb886c6b92dbd684c3681d78d35da31bafd1f78390917ff2c79a6e28e5b996b624f192cd6a57fb470f6c966c59ad396f78bd9099c987f694cfeb185ad3f8356ce4d4f89bf4b5f0b691d1ac58081b167229f34a54cd39f9a0bfe9b37fbfe96f0d8dc28f1069dbb9b071a8afb91736bc4b9f6c729bee697e54ebcc084148e2b5c84b16558123f28cb787a1cc259aefac736409fb6ce50f4ba3c80af6def49ecc2d31d742058e91c342e684c60fe7ab2a06489cfb17e3bfd09117f5d94cf6c78f42cfa8ca758266bb35ff9d39dec3e932f1f541e05bc741930facf593db9b25e558bf8afd93c2ebadabdc04b93dc97677d1a4f00a1297b1b7b10f91e7442c49a4af5538c20f5b3614c07816e01545f355e50f322e0465fe5e416183018cd0d3b77586e6df45bce364a69f681771ed03fe27e7f81a96cf1abe5dc9785ed9217c9431e7f732efbce2777695f7325f5f26c26e6138dea3685dc088eebccd3d415640ab9c4190b5eea3329f0b1b53db32ff58ec6ac9a5b57e159b1219738bdbd88bd7b9db933a8ae71f9047c5f7591ef3a27ecba7249d7e1dc737abdb54da9358a718481f5f495d93d8b5bcfc6d7f92367a9ebb2c2f6ff952e9a722d7148d8f0b3d953e6296b62cfc6f6ea235b228079b6522645deecd1685e74c54b13768cbf7c24eab754fb0bf98af24ad752e3ad17820c89aa49286e99279abb1dc03dd99650c46537af05665eef27b63499feb1c395e897997d48b269a3f7d20289a70e4fcb888bb72cff2f65ea9b71c40eb1d1cbd59a7c1739dc1e0f5fd949c93b7e833fc78cd7d737716bbcbbd3b2b9fb5ad9ab725cfdf59c5fb09ee59ebc8ddacb9c89b4ddb6c726113ebcb582662066cf638c257aa3dcab96d56e32fdbe7a4f0f45b0236f762affe042ca3405650ed23e4debea5c7f3982fd66bd9e9055ebdc7bad9db9307e2ea468eac49e2c7f7b20d7bcbc08f9775cc9776d0da53a97833a650d8dfe4a115bfc775cc1cda26aadb7113a3ac81e63b330a1f1999e9553b6cce3c891f3f34b2f2e375e646eba2d62d7006aae720f96ec54f8e1c2df43676c5c72d712363ef5bebc097f3cff6be7e88ebf7e5d897e6ddd0d70dee3993fc44db693f34d3eb1cdad056c6f9ef626c52c0683298d5fb1f6b9257f01caf60438bd4e5133b8ff5f53b266af9e1c93e4afd0b1c11df12c324fffca72c95a738ce4e47ddd379569e83dfac3a5106d0478b4d17b875b16968a81fac35dd8cb4dea75ebfdf1f6843e3e6afd49a3ef7d5fedf576b9294ff67a5264dd6baca5293f1f9b37a73a319afd59a8c5e835af3fc5ab1e965d4ffb6da74bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd9773bd97f3fffd5ece5fbb1f705102395d0bf02de3d1750ec4b768687f51cbd2eb74f1e83a7d264bbbdfd9b02a15715f5f04be390ed086305f3feeeecc71ea6d7689fd456dca88c07109ec2d7777f142753733156af2930503fa7a70575e33486429d6e937efd8e65885f19a9e3ef1ae39d47f9ce68ae8d3f7fa73a065848066a9b7a168be1aabc05ae7a7cfc6b744d0654f8f37b6f99043416f18ecdd28f1c5fa9bd5ee54ee748e4fde6abcf7ad8753894f7e9a31b8671d98bb59f7ef2a58fd29b2fe7cf6e01cb9a423dc71dbf2027fbaa0f3ea33986f1981670607a0c3e4eb635d82abaf5d4c346c06624d6e5b19c5ab1df9f6d07c1248bcf03678c6ef749c7adadbfcbe2eb7aadcf77b104075c2fd922e6e9bf07156dca6350db3d025d0ca62a768c1d72af9f65d25339dc67e6414de0aca4f283e395db1b02d837ba74f3954c8c1b668644f17aa670e9e903529d074b1019b259bcbb50e6456dcc7afeb8d1347976b0fed16dff654bdd45be2d59f44e2d25625fde5bb2fd9979039c1c5b8b483ef8ccccc01f7f5c9ee42bec6a52c055d5f0f951e4bbe2201f3cd40f801af60f527c0faf3efd8b70e92a6f9ea40704f4dee6e1343cc6346c7c40debb5e5bb144f978ebbb1d94cd702e0f4e3f96a377faf3c5a96d3f0b59e76ada75deb69d77adab59e76ada75deb69d77adab59e76ada75deb69d77adab59e76ada75deb691ffa3fb7ff050000ffff010000ffffdce249f251460000`))) +var _ = pkger.Apply(mem.UnmarshalEmbed([]byte(`1f8b08000000000000ffec7b5b93aab8f6f85739c5ebdfd9025ebab5eaffd0b0b78876dbbbb5b708a7a67641c040131286808a53fbbbff2ae1aa7d73ce999733e5434bb2b248d67d258bf49f4280b7840ae33f0518a47ee67c0124ea525f4cbc63d72580128cbd940d7f0d12612c747d1279ddd4496c1b84dd3d49c2165647d0a39824e9773bf585f187137684851d79c25888ec000b1de12b01c258103ac2b39d40b6e0d94a90749d009f4cb024e435de2b8a1eec14f8c2f8dfc217e1f78eb04a6de409e3ad8da857f6969e4d092e16d7c824401e65e88cae2f90081d210ea1e7b2e6ef157f1cc1c380b80186dd17f67a47d8466cb916cf90fc065070e802149c0e4476123a76ead12e9b3af97090fdb245222f3ac53b9728433c61fc33dcc4c32e5f1c11c858fbeac59c2f27db068c6d274f3d2a740440a238f128ed6e919d7a6d003c0631efe3d40eb097745140d312e01d782bc9e394d48dae5dcc58744010fb9c80b2efb6075d6a371d0f9c765d79309046af00dd00a75e826dd4f5dcbd9db8f41c0da1204e03d040fcc86ef5ead7131bbb591aa0378668e6a4c86b062277d074d87bad1ee8b73a6d06a86f4b273d79303ce90f24b9d53f5b32452d391d06e2e8b4d78dc3e020746afb6c35bb36c552bbefd8d41bf64f2001b693bc0d0174d7eefa5e7bf2caf8eb7eccedd44b1292302ab7c8867fc9372071b2edd646a4eb7b89773e4620f2d8bb2f6f59f9abe1ae4d2f41811efe0c6b4b92c84ed3735f7d8dd858602581cbd0fffa02b19dd0bf844e2fa7264e084cecb380e3dbd40f004962c6db3eb1e3f78621f92dca501a7011fc1791af2627b2637a216a6a871ec1dd37517d2fb6ffa669badb00a5deb97edf0edd1785772f723cf73f4b04efe0d1d42567f4c5847a2c4cb3008ebcd4fb78b40b22f7738c6e80696a23f43727a7f3a53fce609f2152949da981c65ba9d78dcbf004491cc22f01eee67684beec58ec65e65c3eba20013d06abcd83a5461bc13608c459bbbb8d524a92b40dc25e9a2636f0da30422b27697c8f7061b672d1e92b89b7451e4851909e80698021f2b62880fec9aa34a7c046a8eb1d3ce0e1dd5b4319e6f9a2317e8fa66c4fd011f83e2020dd8094d9b000476c7b573cba4e009b664aab769909a320f2ca4797c784d8e642e1803f32927a6e9c0438b51d9ee00adb605af3d3346e35f94f25bd1a58515cc2d89e234e08df6fb07e96b011ee0484720114adac889b71c1057b3077f6ca7e295fde82de21ae1b5d9ae3d466924a329c168c95ad2ee01bc5aae77a0eb7b7aa5f4bd64e49c4b71faf464a91be82d39c915f9a124d1340b80e699a0418f2a11c83f2d14c5f6a56e8084c26ddd48be272ef76d22f72088316dc643800c46db5ba59ba9586a7fd5bdea5f696e1ed3cec92a40b09b231fc4212d83d74abdd9d6f03df96c5cbb0628272a9270e3ec1e60fe67d97e2553b9e8f90b364e7553bc50ff0fcd0dd7e8cf17a93f801f2271c33037631657f9147a90ddf9beec44560c65df053bc382187fc1344b9ebc736083fc00a5c6cbf334c735a86c4b746b915520f6489d775023748b277a555186c6263cab6481f215536ca26bc040fb3f97eef08cf1e4deb731fce102a40f549af003d10971139fe53b8e870fbc04eb5e334c9bcce6507678d3c10f713b42e245f22e272ecb597d0801f5ba52f525ff8f5eb5747d8163c7c78921fb3e66f2c9c77f90917054e73c40ff096b0a7eba57680f854b839a5b7703b020d8e9e30ee8972af23442c688cfbb2c89b3f7934190bb2288bbf8983dfa4deb3248de5feb8277e19de8a37b7c31bf9f6ff89f25864b121a03f5d269ff244cee2ddf84fe1abb713c6c3614f1c76041d1361dcbbbd19ddcae26d4758a00087c258e22a6124f4a4dbdb8ef0237085b1248a6247d09ae6e6e7cfd87645612c7684a5cb26153bc2aa45ba82c28293be381ab22e012115c6b71de12e0d2246caca03c2581adc8e7a43e9f646ec080bca2037625f92462351fad5111e3e46ad99fed511d4cb51373f7f6638a39e2b8cff2d76c48ef83b57323b325f548da9157d5e96696a2f2d8cd7f597a6c2d254510a5fb8b48852609f3952516668fcec1fe07ae55e821118c24beb5eeffae1af8ee0daa92d8c056f4fa0ae1d76a63ca1baf604bfaf94a3651ca2fb3b32d7d53bace760a86b4f19d0523cbf235057efa0134d52eb59c4960aa81adc415d552410ed4733d98d5dcd97cc60f0e2c8e20ec8700fa2f5f05e5eeeccde62e74403e4aaa3d0911f5277b38c9de67d621987fdf7cde1765baea1ab0ab4b4d1cbfd662ddac601ea5325777a566c69eba3ae7d832b5ccda7c40e5e221059be29ff805634ca754d091d797174e441666d96f0be9ef30e9a2b650ee451e6446b51ff36795aad18ee03b4e475666d66beab8d727dba24e6e609ba9b0502b97274a733c9ec2d2510ad435d5bfaaef60ddac6e0e86a6bdf52959d15282f8e2ca596311041de9ff3b5a6152f21552161f4fba69cfa96fc63f818283705df77589707c8cd018385f79195cd5537b336009abd1932374bb6beaf6b0c47d99bc622310d17e9532667082d19c996d187a636f01d630def5505da86b4777a33d15495f58f700f4d6310ea5a215f5355446be38bb37c0f0df929333733ccd6330d093198a50e7edce3850822945979c8eca1c261fac5f7aa12dea3a70cf496b96d0cf07cc5dff14104c84cf69133bd7b8db316e3fbda6ed6bd39a7f507d5a7ebdc51f7d0d6d614a8615b26502f7086e593d670d58d9d8db203f8093f06dce69a318daf3f2c9f955d8df8da53b1d0810a5e981dcf8e64a84f17c8d1cc0c6893175b9e606b4d4b5db8aeae52c657e41a83977bcc6d5a67f29bab2ee7c1549598eb65ba204cd6b636c92c55899c9e0ecd46cf59810b0afea74a62bdb5ee8ad928a8e973e4d91f96b1106747c268d89911213389fb40ec6a88d9035f87f9866b48c8c1cbd88940db1f76661423b3b73ceaaa0fbd5c19996a98cdf290cba49afff18540eefbea1d9ce50b4603bec006cbb99fa8ae8df6ae86764e34a1ce3484ae8c445b55426bb37801112ac602c577a22768450839daf2786677dc263fb1bbc836d6d49a3ed47655d1ffdaaeb8de47b31c505db362cb3884e028dd6c57856de9da28b0a3f58bfbb5e1bb8a3b8f41f16ce28452ad3bace7caf7a55f8b70bbdacf6bffed71fbd02a3a5b32fc616b3fa0396d8f2fa0ae493b4b436cfe5cd746b2b599e5b6c1652c32fc4a5645bc5310c0cb81ae8d225d9b64ba36a1205afbd634ac7563d5fa7e6232721a7face8065c9e6b6e4b1c2fdeacc2ca3f7af6664996e59af39ad73399a974a84fe9bc8aa1158d8f412da7dadfb8af0577b58c66f982d930934b9157a272cec985fe26cffef8d0df6a1a991fb9b06de7ccc681bca65650f3ce7db2ce6db2253951a5b351600710af595ed290681b2ccf583b10f05c2239c63a7335669fadfca149c8d5fc9d357d80738dcdb5106d6394819ce784da0fe7ab3206709c8737e33fd39115f5c98cf79517a667b7cc758c66bd35ffbdaaecc074193bf2c077b4c3a0ce07dafa68f6667131d62f63ff24b77aeb323701aa4fd2f03e9ae4560e71117b6bfb60798ec59298fb5a89c3fcb065433ec033df5b2177be2afd81c705bfc8df2bc06cd007917bfcbe4eddf9338b7714cee4867616d72ef03f3ec763503c2bf876c5e3796987e085c79c6f45de79c7eff432efa58ebc8c99dd8240d9b9d13a07110aadcd0374351f953903badabaef16f99cd998d896f965b1ab2597d6fa656c8a79cccdefb085d799d9e33ac2f30be4f136df4cafefc61b85c9ed73bedbb67111df5c1f35dfd12407d164d08ec52dda48613b652c515deedf455e00c969bee7f8c3f2f9512c899c00d43994cb51ba2c8e58d100ea1ae2fe04647f07f09214fec17d1cf2b9f2d3186245fd6113b7b8ef9ec58f467e9fe5eb325ef35cddd23f64322af3f8b15ab7813dfdd7b1a2ade37b55114d23e53414faaff6624b62ad14d1db28cc26586c3d8d2fda37165b2273b3a6aeaac8a67190acd5692ce16782d5c7b1c49da2fd6738cd3a4dcc7170d8ce596c6f428af5f6b09873dfd0f755ca5ce340e7cf277ec5f7b88f41f16cc519cef363503c5bf0d0322cdf350e22f3afaaddf823f3f1726fa98217a62fa6ff32d7301f29f2c5598e2bc7dfb44f36c7c779aea547b6cf93ef70116b588c042d3b3dc3cb0bdfb9cf6fe15a65e79481681a88e528d63ebada2473a70fc5b9a9b08326bf4d9703a0ad8fecfdf557d2ec01a665bc3b1238eb55b1afdac3de4107af53335ae78fc11daedab36affa4a18cedc14a5964ae2105d646af74cbcf7296ccdeadf819488e314320a8f65c4ae0c8236a19938ccd6f46a35deb1cc9c79a77673b473e20d3e837b455fb5f465bb5d7ae692be2199f7733cbcd4d882b9d9bc6a0801b83c12c6ff67f0e5e534705435d9d71bbd3833a2fc64ec527a241813773eb385eee05cee262759e39c9116731a88e89db27f2fff9d785c4c369531a68ceffbc6ef061a10e11dbbdb43e77865bd5e78623f1d3f29cfc2cdd8c07b763a9f7a5d7eff707d270f41f95e76efa62ffef2bcf71caff42754e1e8d245e1ee475b4dee8e646bcbd95466f55e7e4d1481cf56ad48ae7b7aa73efa3feb7d5b9ff9dbb524525edb4ca4759ef5fae177bd8f530c8c7ff7af76ec7f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad5f54ad53ff25ec75929a6b9cee168a317d3d843474381fe552c4aa0d3c58b69f48923cffe983f9361592aa38ebcf01d55f1dd0d248e7c08ef5525b13661ac7f15eb4f7db66142d05b86f778219a9b990824fea989d8f27a705f5c0f8979d9d0e8d7efe8aa2202bc464d797f4d81fca3992b42c7e7ea33ae360a6c9426d606b9f39522dada3a6b3ef7df4146973e3ddceaea530618bd81bf33a3d861eb6f5661f349d2381cad95b273b4a7b25cae1c5d5e261f514bdb1373b3eedf97b0aaec577df67c320e94d3118454d72cdf992ed0bcfce4e06823df52fdbd2d83f8f1a52add55d7652692a7fa6c4daa6b29f25621fcfe549790632bb8f35ff13b55124bfa98dff7e5567eeafce6fb409c50a7a08bea2a7899e5774945c32c3021d0526ce42df85a84df9f4538931176a2516ead8a4f000e6caec6e8da885a4d091c3139e81a8af4e942b4545e2acedde962636f9664ced7dac359fe80dfd71b8586ccd71eea2dbef5a978aeb7d8aa4a9cb8b0554e7ff1ee5bf6c5640ebd5c29ece099c0993aa08e3c09cfe43b3a9725a3eb714fea32b6a50e220673549ff9012d61d5a7dbeab3bde2687b4ed37cb5875e4f8cefefe2119b478d0eb119546bf37791375d1ae646273359f26da38fe7ab70fe5999b628eb79ffb4badef57f20af05bb6bc1ee5ab0bb16ecae05bb6bc1ee5ab0bb16ecae05bb6bc1ee5ab0bb16ecae05bbffd182ddafff030000ffff010000ffff99b91d64ce480000`)))