Coder Social home page Coder Social logo

lz-string's Introduction

lz-string

Node.js CI Version npm package Downloads Documentation

LZ-based compression algorithm for JavaScript

Important

The file layout has changed in version 2, this is now a joint commonjs / esmodule project so modern build tools should be happy with it, but if importing a file directly (such as in a direct javascript project) it is important to use the correct one.

Tip

The "old style" minified AMD file is available as dist/index.umd.js via various CDNs or package managers.

Install via npm

$ npm install -g lz-string
$ lz-string input.txt > output.txt

Home page

Home page for this program with examples, documentation and a live demo: http://pieroxy.net/blog/pages/lz-string/index.html

Command line

If installed globally there is a command line tool available, and a test suite that can use it to show things are working properly. If other langauges build a command line tool that supports the same arguments then the test suite can be run against them too.

$ lz-string -h
Usage: cli [options] [input-file]

Use lz-string to compress or decompress a file

Arguments:
  input-file                  file to process, if no file then read from stdin

Options:
  -V, --version               output the version number
  -d, --decompress            if unset then this will compress
  -e, --encoder <type>        character encoding to use (choices: "base64", "encodeduri", "raw", "uint8array", "utf16", default: "raw")
  -v, --verify                verify before returning (default: true)
  -b, --binary <file>         lz-string binary to use (default: "../dist/index.js")
  -l, --legacy                use legacy mode where uint8array decompression must be an even length
  -o, --output <output-file>  output file, otherwise write to stdout
  -q, --quiet                 don't print any error messages
  -h, --help                  display help for command

Other languages

This lib has numerous ports to other languages, for server side processing, mostly. Here they are:

Caution

These are all developed separately, so if you are using two versions to transfer data (such as a client and server version) it is important to check that they are compatible and have identical behaviours on the data!

Note

Version 1.3.8 of this package had a slight change in the encoding which might impact compatibility.

lz-string's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lz-string's Issues

JSON Response Performance

I have a JSON response, about 700,000 characters. I stringify it, then compress. The compression takes about 11s.

However, when I paste that same stringifed text into a textbox, grab the .value, then compress, it only takes about 1s.

Any idea why?

Compressing Javascript

Hey there,

I've run into bug when using the library to compress javascript source code. It's extremely intermittent and doesn't happen with every file; looking at the instances it wasn't even obvious what was causing it.

It seems that the context_dictionary can store a key called 'hasOwnProperty' (which exists in the uncompressed file) which overrides the object's own hasOwnProperty() method, which then errors out on subsequent runs through the loop.

I've worked around it by using the strategy outlined here:
http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/

Basically, calling the hasOwnProperty method on the prototype rather than the object itself.

-m

Not working on IE11

On IE11 we can save a compressed string to localStorage.
But if we refresh the page, the data is not present anymore in the localStorage.

Seems caused by special caracters. Some caracters fail the storage.
This caracter breaks : ؁

This is working :
঄〮⁜ॠ똠猂頞耎ΰ䆸ѢƜ逍耖iꀍ䀡Ǥɐ᷀

This is not working
঄〮⁜ॠ똠猂頞耎ΰ䆸ѢƜ逍耖iꀍ䀡Ǥɐ᷀؁ꐇ၀笁

Compressing a string increases its size

I am taking audio samples, which are stored in a Float32Array. I take the buffer data of that typed array to create a string, which mostly creates chinese characters. (Just as a background information why my strings look so weird.)
I thought I could compress them using compressToUTF16. It works in that way, that I can decompress them correctly afterwards, but the compressed String has about 150% of the original size. This also happens in your demo on the website. Here is an example string to test it:

'㢁촘㢨㤓韇㢮ꍖ력孺㟀�㜻₨㖠അ룃ꀨ룍Ḵ㚡৷떌璀㎢ᗬ뒙튦㜝驝㡸糳㢌ᑺ㤡띋㡿獳㢀驆㡹갃㤚㡰笲㤄운㦱ꉿ㥺鮊㟟哜㟧Ť㚳쎘똏㋉㤀ᰬ㤁렆푓뤔랶㞥ꃪ룳篾룃ऍ뢺뤦䂮뤞ﺼ륺富릡푮릕䫴륉壌뤬猪뤇﹑룞ᱸ뗑떎搰㗒ꃂ롻ᄇ뢼෯룥�률䦆륤䋈랦㢩껜뗢曀㓏㢇㡱㛂Ŝ뙢㲟㜗夸㢮῞럎짽㟦㤰⨆㢢ᒤ뤉뤜幰똢ꌘ㖦솆뒱ݠ㒪涠㑳뛌邾㣃䖳㢴ꭻ㟨蹌럽ौ㟐㧋㟷Ⴘ㥏慑㦊⣺㠢籶뙞聬㠼뎺띅䧤㜨᜝럩潼떘먢㔪룤뉶୼덧㓦닎脐㎔䢀㏖৘떍뛙렲䟊릕ጼ릐徾뤟茱뢼诂뢿ꗬ뛵�럆䗑뢞鶦뢖�㟠㻶㖫㒗㖜꿠딹幚㟙ꃳ㚫䒬뜧⃢뢹丿㡏迄㡱쿿㣵툊㟭嫯뚅䐀둌㢷銂㥤ਦ㤟⺶㟁⡟㞽蕊㡿ꀋ㠻袠㡜歹럣㇑뗘渤㕂㝄㍁낀됷椀㎞㗹圔뤁롶齌뛕ꄬ뢇필㠉䪪렁ﴀ㛐裏뤛쏖룑㠄㞪럷�룞뤖茹뢷猪㡿Ὓ㣻肤㠲϶랝핼㠹垊료谦뢎㤔㟆㗹恀똕쌑룃蓱뢸斔릥혂릂�㟦뇒롄庴룩룃飓럙औ떌㤌뗆鿨딶㱰㠧㳭룳쾎뤝펌뢶㛑攱뢜ᤑ㞈秢㡖羟㙷Ꮪ뚯ႎ㘩쿚㡯꜇㣻셰뢎�뢞㞂뭒뢥褌롥钕㡽⟲㠈羘똃㟌톒㢉烀㝔춣㚦竘㘯笒㟵㨚㢅썤뚼쫷㠍麵㡺炀딌㻀㓶蛰뎿﹜늞鱜㑫嘘듾ᙸ㘙鿷㟳狠똍禼㣇ዧ㤘ᕼ㥂떉㣸ࢆ㢀仂㡿㔈㡽⸞㞙㨴㜒듾㤀渘㥞㟩㦐㤪狾㣄Ӫ똦쒭㢫놘㢃㡱⬗룘틎뤝뫲뢂竴뛯쏐㜙뇅㜛뤆ᠷ뢞萍뤃弑뤄眢뤎㘝뤛鼐뤁Ҽ뢵栏뤢笰름뽶뤉썲럔⟢㠺壊롓埿륄攍륙롹뇌㢃ⲟ렖壟똼渄㠿剮렗⍹㠌呞㟌뮈렞٨뤥졌룾딨뢻꼰뚆颎렯暝뢳䙞똩턀듾䠅㝂숖㠈痉㣼빾뚃⿐㘶읪㡲騖㢄볨㖥㋗롾�㞺탥띝ꡠ뗕ᘬ뤀ፔ㟧२㕶籠땃ﰴ럏찷뢗롫칎㜱擋㣧�㣹㗚뜽Ǩ둔귁렅㧣룃ࢥ뢊呐㡃馵㤼꽛㤴ߐ㡸瞘㢅̥㣁䟰㡻藨㖡똰䭖㟓蚬㚀뒱ꮓ랁츂룅ꮊ뢽斒룓ẉ뢁ᑪ뤃驭뤛歚㡦롢㠋䯭롴揖룇虘㛹熞렯벧롏窴뢪䫟럡욅㟺㕶䁒둗װ땋ꐺ딉렲涺뤁ϐ㡴ञ㡒車㣂䝘㟡藂럨遍㚏拂뗃Һ㑭䷳㏞π㍌儀㍂ㅐ뗀鿇㣜ꯆ㥛㤄룒㠀ᒐ㙦鮋뢱ࣿ뢀썪렫逢㞤的렗㊁㟲ᦴ㖣蛕㖗䎳듌ᑲ던쀘눼鉀㎗멐㏺秜뗰㻢㢇贀㡞ᜀ똏룄倂륿Ȃ뤃娾㞕飼럂펒뤔륧�뤓쏄㟩깯㚹㠁攍럳呂㟷愥㟆ᡊ㢡婐㠋埛랮겦㡠豴략᧴띥룬ﵫ㞾供㣄䡺㣹쾄㢺ꂦ뚝ဖ㘕�㠟㣾﷌㞠蒒뤄뤞薀뚾漽롇鉟료긓련徃㠖┋렵뤄䴜롥锐뢄蜐릐∃릠癬륈嶚뤠䇖뤊赇럎㾸띪扐룞䂪뢀ಮ㚻咦뙧壖㠆殍뚼⌐㓅髦㣴餑㣼轋㢸酁뜊筤㢐枒렕⏰㢅ୢ㥽陦㦡쵧㥩时㣿⥹㤀㢁곾뚝ꏇ㠬ឍ뜋瓩㚣筆럼螵떒⬊㟓궖㠋䍶렖鐱뜑렗렇ꀈ㝄粘㝛团뚫ɠ㠗좿㡱鬋럣朜뙥蛄롖괅뤃%뢐�랤赮똷㿈뗀꣔㗆ݠ뗯疌뤲ʶ륭Ὰ뤋簓㠤첨룻䡤룄銊뢾尒룁⛥렄斻㚧쨶뜋녤㘙呂됺땉⑐㝝◪㢼尘㓒儸㜒䬰㡵侙㠑钎뚻㘤㘑垀㡨騶㢁⃓㢀㡫쉏㣑怇㢥䲼룏썢뤝䣬뛅ẟ㢆뱲㢀㣧Ɱ㤚瀿㥇挈㞑Ⰸ뚂峺룎剧럡₦㡿竛㢁읆㢂ﴂ럑懊똧䙚뒄쿂㒦䲮댕지껗댿됁똜㖁쮀떋�렣릘㶌륛既룺⠖뢎�룀뢼ᄐ떃�롦ﮂ룐₨뢹쉸㜷䳆㡃ࡒ럸珀룫蠦룧X뚒柤㠊⡸㡺묷렒�땥並㔈㊀㐅♩뚂㡲㢫왝㡀媘㖾饾렉␚롿沈㚵ノ뤞᲎뢙퀩㟃偌뤑㹛뤓�㛈顰뛒뤣륝躅뤦뢊㠨꨾㡢둶㡉䘒렗켼룕㫌뢄⚼랪㟕嵓㤠瞔㢱攆㢋㤩풰㤺ҙ㤉皶띮원뙋꒤㢎흿㤞ᾨ㥟敀㤢ⁱ㢞㊈뙙稘뗞汦㢆펐㡗霸럧〟㟫쟈㣆㲇㡱扂㤊퐋㠘ퟥ㚎뢜藶뢽㎰띐㗶㤋뜣⍶㙁秚뗄詘㍹Ṩ㏆税㌩杰돁勴듐槴㘡뇪뢎뢫땓㟾썒렊舺륺�륂涧륋槈뢽ዴ룇暴랶꧀㔲娰㛊袆뤇籚률䤎뢆㿬똚젰㔇ﭸ㐀湒뎉觨뇬ƈ㌤㏕骜딂䴀㏡軬롵걟룋憒렔㠉葤㡃媀뗀䕊렆ᕔ뤕일㠃ᶰ딍⽬㖸ب듩⊀듈Ỵ㞗㢂裳똀樸땖�렬⿢뤬㴝룼㢆緀㤸䷂㣦Ἲ띧襃㖢Ҧ떆ֻ㟖湾㢀�㢂튰㡶宠뒀韠딅ଗ㡾䩮㣜⦄똚�㣯૝㣼๿㢹菘뚮ꗱ㣋⺖㣄뚤峙㠣뜢Ꮸ㚈׉뢩簓룇䧋띪ὰ㟕駸뗻笏뙋挸㟲徰㖦꘠룗چ륤绢뢅襉㠏䎎㠈뢧鳞뤨䄭뤷鄿뢅緒륂⏖릃흅륓モ뤘ꥺ렰∉룇锊뤦㴲뤭㛞뤟뤠䒂뤟㍊뢗�㜕ﳔ떹㉴㕆ᥰ㡰埭㢛♩㠏棾㟙⸀㗞诊㠈属㣅➯㣧셄㥴̆㤇佪㤪与㤩쌇㤲闶㥟㫍㥀ݻ㣆쏄㢀º㠆셺㣙沊㣚兑㠑疣뢢뢥㢃ﺔ딉뒽⾤㐚�ㇸ騄눟�뎨䫠㍋騪㔝ꌰ뛹ᙎ룑न㜸寄㥣䕆㥇俪럎肻룃曀㝄揩㥖䒈㥙巊뜂砒㠄턜㤧惵㤼脗㠎㺩롪Ц랡ɠ㢢ږ㢊㡷㞗萎럐欸뤂㇖륇๨릱됎륂뢉袲뤁웅륇쓫릐쾨륀쥅뢷酲룍뙲릢°링훚릾릴ﮛ륂䧦릥匈릹镶릚뒰륟鲰릘鸢릋埆뤿媰뤝띦뤽˖륮懲뢗ঢ㠋晾럆廿랛豲땗囨딀က됷硸㢀愌㣾ᅭ㠒㍺㤗汰㤽㑨㤈猗㤞蟤㡨⼒㤨謾㦁�㦏㥣㣂␱㡳ㄎ㣦㣖攎㢱躷㢀䡰㣈歪㥚ꄆ㤄㡪昐㢃朽㢁㖤㡃緦㡀⌌㤒훶㤋㬥㥃�㤽㣠㣳⇰㠒䔺㡳薩㢀ἣ㠫꼐랿⒃㢢宷㣪�랈뒂㠍轆㤀双㣊恪㣵䕲㤋㟛탴랧覕랪䢄㘍塓뢙鳐릀㒥뤠饸뜅뢐릓玢맆趾만뉯맄ꏰ먀맼矾맏럒릿閕릞瑬릟n망䭃맹甡맱鸗륿韧륲㜛릫ሀ뤶เ뗄럣鄔륕ꁰ릓浂뢻뗀룬㥨롆䨐㠗⎼㚣菓㣾鲼㥄䮢㣼턛㢊㤮诰㦉䑄㦧蔜㣶긬㢎끘㥁꺃㤽︼㡽싰㣼լ㠎䲽㠄䑂띖䞧㢈㡛⤠략閘뙘⦤떿屨㗪ጙ㢅㦨땼䇜㚃踀룀$㟆壡럀쎎뗭憼뛨罸㚻ᣲ㤀ಲ㠅Ɪ롾ꬤ뛷큚㛴䗁㡑磛㠙墆㢢㢭㣾᪑㤀᷂㤁摖㠃竈㟋⺈㤡㄂㤁ޞ㢙狢㠗頻㡇魿㡸赓럮卆㕤ඛ뙱⏯럑䮒뢌䬏㞕룓⇣㟬틒㢪㜒煠럚뤀쫣㠊퍃㡹架㠖붔렷ꨘ룏앦렭咗㞦℠듡ﵢ딜皴㐻ਠ됎엀돢羈㘟둩뤒�뢮쟄㞔먴룢ꌸ뢻�롎ݻ렚鞤륛숰륂耄룵켗뤉픠뢫㚶뢻ฏ뤨뵳뤠ஒ뤠אּ뤚㡴륆἖뤟ᓩ뢶䵽룓ﯧ뤷谯룼侼뢆ẖ㞰렴鲛뤖༘㟬詤뢁떳ꗖ㚬㟜�㝨䅔㢮뤺㢰�㡓ꨰ뛼ⷰ료漢륾↿뢾鹈뢍闠㗊㟠룞䓀뤟흌뤟ᑔ뤠�뤟鬱뤠ⵜ륂걔륢姢뤽ᄘ뤊犸롑�㝿객㚣⼰㗤ݛ㠖'

Password protection

Am just wondering about the feasibility of adding some form of passphrase protection. Am well aware that it's a relatively weak form of protection but increasing the level of obfuscation even by a tiny bit could be helpful in slowing/ deterring would-be crackers.

License missing?

Great project! Did you decide on any license for the code? Could you please provide a license file for it? Thank you.

MAJOR PROBLEM IN AT LEAST CHROME 39+

In Windows 7 (at least), LZString.compressToBase64 results in a leading space character.

console.log (LZString.compressToBase64("{}"))

This will yield " N4XyAA=="!!!

Error Handling

Hi!
Would it be possible to throw an Exception on error instead of a custom string ? I would like to be sure that an error is in deed one and not a compressed "Error" string.
Thanks !

decompressFromEncodeURIComponent fails

Using the 1.4.4 version of your javascript file, after I compress a string with LZString.compressToEncodedURIComponent(), and then call LZString.decompressFromEncodedURIComponent() on it, it fails 3 quarters of the way through not being able to find an entry in the dictionary and returns null:
} else {
if (c === dictSize) {
entry = w + w.charAt(0);
} else {
return null;
}
}

Any idea why? I'm testing with Internet Explorer 11. The decompress call is below...

    var test = LZString.decompressFromEncodedURIComponent("N4IgpgliBc4G4EYC0CQBoQCcCOVY4QGpUMBnSGEcxAEhJAGMHKBVAZXSzyzFIAd6pbvTgAXSgHsAiiwASAdwBqAJgCuADgAW62QDMAwgHFCAMQBsAEQA2AEwBymALJg2 + 0QGY7ZgEYBWOLJsLABKDACeAFIAHgDmALycfNzKAAxoaWnKAOzKAD6 + aAhoACwIymXpuWAAdgCG3lZgNrkIRRmVuZ1duZw2fJQAggC2YJgQDLUA9HZg8gD6AJoSmADWucq + ZikAGgjFxSnrxUjKxbmimKpguQDqENXueQjqAJwvuY9nuYcAKpqqAAIIrVqgDlOoAQh3NBfAhoAcAQBRNg - MEpBBmD4pQ41JDsc6Xa4XK75GIALWUMUcVgssikCH0 + mKUhWtXUcCsAAU2CZMCwJBFisETEIADIREx2CwxBhkgDSLGKcVJFKpNLpDKZLLZHM5P0mpCkb3cFj4tQQUTM2AA8j8bmSFikYvJlbldLUrOROGB + rAWp1DocNlsWu51Fl1m6srV3GYXspamANt53Lpii9SuozLpsylfC8UgcGC9dN8y3kg2WA5w4L7QGAhpQbLU4BAbAABYm8WpDPiNAB0DAkjYwfE0MGqqisVjIojAlFhANFYQk1VIAJutTCnAY3Dsy1EmgBA2qNgkcFGnFI4lgjgGiWYsHR6mKvl6jdgXdIPb7YEHw84aoPxACwWzbAE2FEfsAU5CBRkwecMHkUhKE0URRD4aBJkmeRcP7L8fwHIdGwAXwwGxHwAbSKZQAF0MAuGAuxIoA");

Allow changing chars for index 62 and index 63

As you can see here (http://en.wikipedia.org/wiki/Base64#Implementations_and_history), there are various reasons for using different chars for index 62 and 63, the main one being using "web safe" chars in regards to using base64 encoded strings in the url (as + and / have specific meanings in the url).

I currently run a regex on encoded/decoded strings before using them as a workaround.

I suggest providing a method that allows changing those chars.

If you'll approve, I'll fork and push this functionality.

Thanks.

Invalid code points effected by replacement character

When trying to use LZString to post to a Go HTTP server I ran into invalid code points being replaced with the unicode replacement character (65533) by Chrome.

While this is addressed with alternative compression methods, e.g. Base64, the drawbacks appeared avoidable (and I had already written the decompressor in go which I didn't want to write again).

I found this could be avoided using the Uint16Array type. A drawback in this approach is it's default to the endianess architecture of browser - in my case LittleEndian. This could be circumvented with DataView.

See implementation with tests here. Minified version not updated.

Comments on implementation or a more ideal way of handling this problem?

URI-component-aware syntax

I'd like to store (short) compressed data in URL, after hash.

Could you add [de]compressToURIComponent? Base64 should be encoded, thus lengthening the data even more...

decompressFromUint8Array test failing on Safari 8.0.3

Thanks for the nice library! But while packaging up a version for Angular (https://github.com/carlansley/angular-lz-string) I noticed a test was failing in Safari. The issue is the apply is failing. Patched the my angular version to use forEach instead.

Failing 1 spec
37 specs | 1 failing
LZString uint8array compresses and decompresses all printable UTF-16 characters.
RangeError: Maximum call stack size exceeded. in file:///Users/carl/Development/lz-string/libs/lz-string.js (line 263)
decompressFromUint8Array@file:///Users/carl/Development/lz-string/libs/lz-string.js:263:61
file:///Users/carl/Development/lz-string/tests/lz-string-spec.js:69:38
execute@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:1064:22
next_@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2096:38
start@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2049:13
execute@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2376:19
next_@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2096:38
start@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2049:13
execute@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2521:19
next_@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2096:38
onComplete@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2092:23
finish@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2478:15
file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2522:16
next_@file:///Users/carl/Development/lz-string/tests/lib/jasmine-1.3.1/jasmine.js:2106:24

High memory usage (11x).

This code:

var x = '601141b710b1f1b8a916f51dfbb3006fcff6b9d22a278ae1b39577fc769f19117bb4a1b87028c1d97b1d1cadcb0833a0000000000000000000007e2058e704be20dcbffaa883cca0ee64f9e4646985321a110f1e3e9ab96bef86768e958fa793f569d03fe8e18cfe74ea9e2772942aa734c3e761efe7b919240c95c1d26b563b591be9074af935899a61f0158b674de70eea061ce0e7cbf00fc799180ec22d5d6fb769cda33332fe646ba9002b2f9832555f9a5e904e4bd2f43cfoofoofoofoofoofoofoo15153e156fc9534bafd6b5d66fc6275d 601141b710b1f1b8a916f51dfbb3006fcff6b9d22a278ae1b39577fc769f19117bb4a1b87028c1d97b1d1cadcb0833a0000000000000000000007e2058e704be20dcbffaa883cca0ee64f9e4646985321a110f1e3e9ab96bef86768e958fa793f569d03fe8e18cfe74ea9e2772942aa734c3e761efe7b919240c95c1d26b563b591be9074af935899a61f0158b674de70eea061ce0e7cbf00fc799180ec22d5d6fb769cda33332fe646ba9002b2f9832555f9a5e904e4bd2f43cfoofoofoofoofoofoofoo15153e156fc9534bafd6b5d66fc6275d';

var m = [];
for (var i = 0; i < 50000; i++) {
    var y = i + x;
    y = LZString.compress(y); // Without lz-string: 17M. With lz-string: 900M.
    m.push(y);
}

Takes about 900M in v8 and about 700M (inaccurate) in Firefox.
That memory is released later by the gc, but the peak memory usage is very high, and if you increase the number of rounds even more, gc will kick in at the middle and will slow things down.

The x string in the code above isn't special, that happens generally.

You could test the memory usage with console.log(process.memoryUsage()); at the end of the loop in iojs/node.

Broken in Firefox

I've got a fairly contrived use-case whereby I am using the URI-safe base64 methods to encode URI-encoded URIs (my SPA uses the browser history API to produce completely RESTful URIs and every significant action modifies the URI and then decodes it to infer application state: the result is that any application state is bookmarkable and shareable).

In this scenario, the core decompression method always returns null using Firefox (everything behaves as expected in Chrome, the only other environment I've tested with any rigour).

After a bit of stepping through, it appears the core decompression loop always manages to reconstruct part of the string, but always eventually encounters a failure switch case and returns null. I suspect certain characters or substrings are to blame and I will try to isolate these for a reduced test case.

Exception with odd-length array input to decompressFromUint8Array

When an array of an odd length is provided to decompressFromUint8Array, an exception is thrown. This is because, internally, a new Array of length / 2 is created, which can result in an invalid (decimal) length.

Specifically, this happens when I store the results of compressToUint8Array to a Blob, persist it to an Object URI and try to decompress it later. Checking the array length and adding the necessary padding to the end would allow arrays of all sizes to be used.

Works on Node but not Google Apps Script

Hi,

I have used this without any problem to compress and decompress a string running in Node.js, but when I send the same compressed string to a Google Apps Script API that I made, and try to decompress it, the same code doesn't work. In that case, decompress returns null. I have verified that the compressed string is the same length at both ends of its trip. Maybe there's something else that happened to it on the trip or Google Apps Script treats characters differently. I'm not sure what else to check.

Do you have any ideas why this might be happening?

Thanks,
Dan

1.0.2 and 1.33 NOT completely binary-compatible in 'compress' method

when the encoding string is long enough, there will be ONE Character size difference.

for the following file:
“hello1hello2hello3hello4hello5hello6hello7hello8hello9helloAhelloBhelloChelloDhelloEhelloF”

compress("hello1hello2hello3hello4hello5hello6hello7hello8hello9helloAhelloBhelloChelloDhelloEhelloF").length()

1.3.3 ver result length is 23.
1.0.2 ver result length is 22.

1.3.3 ver add a ZERO character at the end of the string.

This issue makes the "Porting to other languages" on blog cannot be achieved.
AS:
"
Port the compress and/or decompress methods from the version 1.0.1. All versions are binary-compatible and further versions just incorporate ugly optimizations for JavaScript, so you shouldn't bother.
Port the (de)compressToBase64 and/or (de)compressToBaseUTF16 from the latest version depending on your needs.
"

Ability to passthrough uncompressed data when attempting to decompress.

Is there a way to detect if a string is compressed? We are looking to use LZString in an single page EPOS app.

Ideally, if I supplied uncompressed data, it should pass through the decompress methods clean.

If not, signing the data with a prefix ('LZ#' maybe) and detecting that, would be OK, but I don't know how to determine if that could be part of the data and therefore a false positive identification.

Please change the license

The license you are using is not OSI approved. I can't use your software in our software until there is an appropriate legal disclaimer. You'll also begin caring a great deal about the license you use if someone decides to sue you using legal terminology such as warranties, merchantability, claims, damages, and/or liabilities where you will have to defend yourself in a court of law and you will most likely lose any such lawsuits.

If an organization interested in your software cares about their legal rear (most do), they can't use your software because using it could drag them into a lawsuit in the event of a catastrophic failure of your software product. As-is, organizations that do proper legal due diligence will simply avoid your software.

The MIT license is the most liberal OSI approved license that contains the appropriate legalese that protects everyone using your software - including yourself! Given the current legal landscape, the best option is to cover your butt with a license that has been run through a gauntlet of lawyers. The MIT license is harmless and much more widely used than the current license of this project. You should adopt it or any of the other OSI approved licenses. You can even dual-license the software if you want - that is, give people a choice of which license they wish to license your software under.

Make compressToEncodedURIComponent URI safe.

Hey there,

unfortunately we recently discovered a bug where we would compress data, append it to a URI and then be unable to decompress it after parsing the url because the compressed string contained + characters which were interpreted as whitespace.

When you check the base64url standard, the _keyStrUriSafe map, should actually be

"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="

without the + character. In fact + is a reserved character in URIs (RFC3986).

I wanted to write a pull request but was uncertain about the consequences for backwards compatibility.
Is there anything I can do to help?

Cheers

Demo Page Hex Incorrect

Hey there,

It looks like the hex output on the demo page is incorrect. It seems to be swapping pairs of bytes.

For the string "ab", the hex output is 82 21 00 32, which is 130 33 0 50 in decimal.

Whereas LZString144.compressToUint8Array("ab") gives [33, 130, 50, 0].

Use ES6 Map/Set when possible (2x speedup in compress).

Using ES6 Map for context_dictionary and ES6 Set (or Map) for context_dictionaryToCreate instead of a plain Object speeds up the compression twice in v8.

See #46 (comment) for test results.

Map and Set are supported in current Chrome and Firefox versions.
IE11 has partial support for them, but that should be enough: see «Map → basic functionality» here: https://kangax.github.io/compat-table/es6/

I haven't tested the speed in Firefox and IE11, but it shouldn't be worse than using an Object.

Supporting old browsers would require some kind of feature detection.

Browser support?

What's the browser support for this lib? I couldn't find any info on the web site. Or at least what js features does it use? so I can look it up, and also possibly for polyfills. Thanks.

PHP version?

Is there any ready code to decompress the compressed string in PHP?

any way to get usable string from LZString.compress()?

(question, not problem)

I'd like to use LZString to pre-compress some long strings for inclusion in JS code, then decompress them during code execution as needed. When I run compress() on text in a JS console, it produces what looks like chinese or japanese characters --- IOW something I can't copy/paste into code. Is there a way to get what I need with LZString?

BTW, the code does great compressing my long strings. About an 80% reduction in size.

save compressed data to MongoDB, can't be decompressed again

Hi, my app need to compress some svg code and save them into MongoDB using Mongoose, save as String or Buffer neither of them will work! the decompress all return null; the output of comressed string and mongodb string are same string,same length.
i checked lz-string code and here is some info:

// from line 468
if (dictionary[c]) {
        entry = dictionary[c];
      } else {
        if (c === dictSize) {
          entry = w + w.charAt(0);
        } else {
          console.log(c)
          console.log( dictionary )
          console.log( result )
          console.log( entry )
          console.log("__can not decompress? but why?__")
          return null;
        }
      }

// and herer is the log message: 
31
[ 0,
  1,
  2,
  '<',
  's',
  '<s',
  'v',
  'sv',
  'g',
  'vg',
  ' ',
  'g ',
  'x',
  ' x',
  'm',
  'xm',
  'l',
  'ml',
  'n',
  'ln',
  'ns',
  '=',
  's=',
  '"',
  '="',
  'h',
  '"h',
  'ô',
  'hô' ]
[ '<', 's', 'v', 'g', ' ', 'x', 'm', 'l', 'n', 's', '=', '"', 'h', 'ô' ]
ô
__can not decompress? but why?__

is mongodb changed some of binary data? or is it a lz-string's bug?
if anyone knows how to solve this problem, great thanks.

or, is save gziped data to mongodb is a better choice?

Safari breaks UTF-16 compressed strings when sending data through XMLHttpRequest

I noticed a strange behavior when using LZString.compressToUTF16 (in JavaScript), sending the data (a compressed string) via XMLHttpRequest to a web server written in Java, and then using decompressFromUTF16 (I tried both proposed porting of LZString in Java).

When I do all of this with Chrome or Firefox, everything works. Instead, when I use Safari, it sends the UTF16 string generated by LZString, but, when I decode the string on the Java web service, I got a null string.
After a bit of debugging, I noticed that:

  1. The decompress algorithm fails at a specified character
  2. The sent UTF16 string (in Safari), and the received UTF16 string, on Java web service, differs for some characters.

Are there issues in UTF16 encoding in Javascript for Safari, or it's a LZString limitation?

Getting Unexpected EOF when decompressing from UTF-16

I used compressToUTF16 on JSON.stringify'd data then stored it in an SQLlite db using cordova.sqllite. However, when I retrieve the data from the db it causes SyntaxError: Unexpected EOF

Storing a normal string or a compressToBase64'd string does not cause an error so I don't think it's a problem with the DB.

Performance Tweaking

I'm looking through the code, trying to find any way to speed it up even more. I think I found one way (at least for browsers that need it, like IE). I see a lot of loops that use the string's length. Some browsers will recalculate that on each read, so it would speed things (slightly) up to set a local variable to be the input's length and then just reference that, instead of the property itself. Also, in compressToBase64, the while loop is testing against length * 2, which will have to be recalculated each time, so a local variable would help there too.

Also, great lib. Using it to store a rather large bit of state information in local storage with no (as far as I've seen) issues.

Creates a larger byte string?

dfhjksfhjas fgjdhsaf lSDFHLF ljljkahjdfhaljkhfdlknvjasnfhlHLEUYRIUORWYIUjdkjhafkjdhfhjdafclkj:
93 characters, 93 bytes
TO
ӡ䰥䃌඀析䄬〡Чᡸメፀᬡ䨠䐣ࠡဦ⩡ਖ䃄僠⽴てྴČǚს眯䕠ᑠᕠ፠ॠቢ⁜䙠㪴䋬⠰ⱡ㖇䘫䀸淢ࠠ:
44 characters, 128 bytes

Firefox silently corrupts LZString encoded data on browser restart.

Firefox localStorage works great with this compression scheme, until you restart the browser. When the browser restarts, it seems that the reloading mechanism silently changes bytes resulting in data corruption. If you're experiencing maddening corruption issues when Firefox restarts with your localStorage data, this is why. If you always clear your localStorage you'll never see this bug.

An example from my debugging output, the "RESTORED" data (data put in localStorage after a browser restart) is 442 bytes, here shown in hexadecimal, the base36 number that follows after the ! is the sum of the row and the one at the bottom of the column is the sum of the column, I used these just to help me identify which data was changed without going blind. The original data prior to compression is just JSON.

The "CHK" data is the original data saved before the browser restarted. There are two errors in the restored data in the second to last row (yg in RESTORED, uw in CHK) in columns 6 and 8. d9 cf df ad has become dd cf 5b ad.

"RESTORED" 442 
 82 37 7a 20 5c 20 c0 08 0c 2c ! jz
 36 c0 72 02 ac 20 03 d0 90 35 ! r2
 44 26 42 03 54 00 40 51 00 07 ! bf
 6a 46 09 c0 b2 8d 4a 5b 19 03 ! op
 3b 60 97 26 bc 4b 9a 01 27 67 ! p4
 8e d6 2c 34 05 f8 e0 33 da 10 ! xq
 5c 39 62 95 04 61 00 40 48 99 ! lu
 5b b8 e7 73 0b 93 e5 09 48 a7 ! yw
 9a 9c 34 0b 13 d9 cf df ad 1c ! yg
 35 64 a7 a1 9a eb 72 7e 02 20 ! vs
 00 f9 
----------------------------------
 0d 07 0a 0z 07 00 01 0y 01 0u 

" app.js:63:172
"CHK" 442 
 82 37 7a 20 5c 20 c0 08 0c 2c ! jz
 36 c0 72 02 ac 20 03 d0 90 35 ! r2
 44 26 42 03 54 00 40 51 00 07 ! bf
 6a 46 09 c0 b2 8d 4a 5b 19 03 ! op
 3b 60 97 26 bc 4b 9a 01 27 67 ! p4
 8e d6 2c 34 05 f8 e0 33 da 10 ! xq
 5c 39 62 95 04 61 00 40 48 99 ! lu
 5b b8 e7 73 0b 93 e5 09 48 a7 ! yw
 9a 9c 34 0b 13 dd cf 5b ad 1c ! uw
 35 64 a7 a1 9a eb 72 7e 02 20 ! vs
 00 f9 
----------------------------------
 0d 07 0a 0z 07 04 01 0a 01 0u 
"

From https://en.wikipedia.org/wiki/UTF-16#U.2B10000_to_U.2B10FFFF it appears that
0xD800..0xDBFF and 0xDC00..0xDFFF are the values to avoid (at least on Firefox) but if you're
going to share compressed data between browsers, these values should be considered toxic.

Base64 issue, lz-string.js -> lz-string-php

Have run into an issue when using the compressToBase64 from lz-string.js and then trying to use the decompressFromBase64 from lz-string-php https://github.com/nullpunkt/lz-string-php I have tried 1.4.4 and 1.5 Beta - both seem to handle Base64 the same. The PHP is what is current at the link above.

Most of the time it is fine, but on some strings it fails. Think I have tracked it down to how the Base64 is done. Appears for the PHP support has been added for extended characters which is causing the issues.

In testing, found a simple sample. "ABC".

Compress it with JS and I get: IIIQwkA=
Compress it with PHP and I get: IIIQwkAA

The result from PHP will decompress in PHP or JS just fine. The result from JS will decompress in JS but causes the PHP code to throw an error.

My original sample was a much larger string, but the ending of each was similar. JS ended with A== and the PHP ended with AA=.

Any thoughts on adding extended character support to lz-string.js?

Localstorage tab freeze

HI there, I am testing different methods in Lz-string to compress strings and store results to localstorage.

it shows a good result of compression:

Orginal Size of sample is: 148920
Size of compressed sample is: 13349
Size of utf16 sample is: 14240
Size of compressToBase64 sample is: 35600

BUT I noticed local storage tab of Dev tools in chrome would be freezed once I store compressed string produced by methods: compressed/compressToUTF16. There is no issue with compressToBase64.

Any clue?

Dummy code:

var strings = "........."
var compressed = LZString.compress(strings);
disk.set("key1", compressed);

Thanks

compressing in webworker and passing a transferable arraybuffer to save in LocalStorage

I have a use case that I believe is quite common in JS frameworks.

We are using LocalStorage for caching app assets (js, css, html templates, etc.). To offload the main thread we use web workers, that xhr the app assets from the server. Ideally we would compress and save to LocalStorage in a web worker, but they do not have access to LS.

So, we need to compress in web worker, pass the result via postMessage to the main and then save arraybuffer to LocalStorage. To avoid expensive copy, postMessage allows to pass the result as a 'transferable' arraybuffer.

So it seems converting a compressed arraybuffer to utf16 needs to be done in a separate pass on the main thread.

Cannot decompressFromBase64

Hi,

we have a webpage that compresses a generated JSONObject with the compressToBase64 method.

This is send over to the server where we use lz-string4java to decompress.

Sometes a compressToBase64 string fails to decompress.

So we tried to decompressFromBase64 using this javascript library to see if this in an error in the Java port or an error in this library ?

We can't decompress the example below with this library.

Could you have look and see what the problem is ?

For your information:
The compressed string is send over in a Ajax Post request (where the string below and some other info is encodeURIComponent before added to the request body).

The string that fail to decompressFromBase64 👍

"66BaC_Q3_vXoKBfKCB7CKdL-TpV_ANXzAftPGaRnspUWaWfvsLY3xQKpH1h1nSX6p0EGb95smC3YdSYqGHPYUmvGtn_XqtJxPLndWBDpBz4YRAM_mSDDRPElCeGSHAKBQ5wIx-I5dy_Hdv29puDknZX6utRpb0s6OeNi-9wDs4L-AXd7pOiOSLJbgZMC1eVKLoCha4lVtoLi8J29LgHaPzpg9bj8HvkbpTnm7ti_cpyZA1jNGS5LDO-NApu3K2WAhxtkriP2KFUQhai2TtvUoMMVRQQH-DjmxSknEQvRzHQX2SseHu1Sd6l66ZjsLvHQFbvMpCLgizRWKSK7GGPISzFb1P01IU1UC4WszTtWN0eRfc6NpvmM8ZkylvLR6l-GU3m6K9jIVTa_njpYAEzCJtO3Ib-QxQlueNNV5eIICHstys2U006Me1vBq5V5Z-KeL6IUprUMSTvv2RNQWYxNIos-fpfeM4QjHxSeLe8UpteZSAdMizcAbC52potvhubZZVfyqEAWQILPELx4b5adTNIhWv4xTRASumS5R_UDUjv5RgrC8UkZXlqkbprrDSun6e4EU47mrZuXIjmEHEPh4ZYEfCuBVPvyGcOUKxinwfRVA1EsgNeDOA1WaP9D-ZVd9ErD0QSawU7x-7I3uTDTxZ5DwO5pFu1igFarYd4MOQUeFjs7Yc-nA2YU-2ivH08IrkIr77Zc7z8bdaBV2-8AFpZqavTuhwe9zefLvK-NuEBTLNyJo4bO1T6KtD2VZrWtaze0T5U-Lznp0NNJcr—r2kT2fi-FZBo3PUe3foFcpuDYRG6nrKmN-Hmlpn_0B5CUwap_M2MaCcHVA8FptnexaIXgiisehbjy65SqlySARH55lbWtzpxk4KczErguNkQb7m4tTqM3T1eG1ot5m8KvK0S8_jit399CCVuJz68mfXGAWrdZjqiQPfmBT2_YlTlrZpxwl_tlI9ypJYAz6Z-bKUUcyOvl2gPTTv6jef5F7Q5ca-6vT5PRUAkqrHzHznycny2MIr0scnvT8MymcdCzDPVkKiU7uPpGcJtG9K7J52KvbuKTLProcQ5FSHeMmbG2r8Mx81qn-fKMxbEX44DFsRIwgr_uyLImJjVLCDu2gc-2OxbBoXS4MidGwN6-qxFoxB204VQoSds6JSHrUuiXRaWN7-yIm4nC1jheKUYLb2sLaEHToEeCmkbJk-pgnYjLYwln6FhSmHRe-jCZ9-uhwP2oSfY9wXLSqRTAUCc_pIf1PMudcFWf4c6fOdsbZXbzDBoX2KH9DISGAFClpn7lSL3bJIIZYh-p9r2ZZ3amxedgPYZ0vCvNdFfng48uO4Kpby_7o_i_zkT3AuIBQS30jdWWjHrnViTI_Ig3lzjIBOYeoVBPp-blPH-uyqUBBg4Tq092cfPYMGagLh3NJHAQnZqvnF5Kzn2AICZJyPejV6iJtakclYz9M0YkL4pInyW_DXPKoPflby1NQ-pElW2DcCgCIxNmSQo9GRPxahyo03ZBg_lh-fGwpyrE9x1kondKP0CQ5p8ruUobuP1zvXU9ovWyjzhUP-CJLRaKgT1FSIvsaGhyZHNgRrkl7Fcv6ZBgI34YzGaCDuzhgNaG2a0DpRhItupAmsFJSIDqAtwJao3zAvYen8fJLQZkPEmB-x7xhXKjQMnra5GUKnYy_vW8i1JeqTebDxTvDWdT8AWZvws3PYdNg7Ga3t99NnjMfe6XynoP0bJhRhk6vmquHzmBri2nez5gqvkO2qCSJOpVtF4C9zk6cRux7GhPBgoNEEBNcC5VNOU-0iw6pbNwSU1TPIUkR7s0abILFxTseLD_KmafGTOUQy9z_uNTJXnE77Wj0cR0AwwPsOq0JaX3lGaXTGnefgppXQOQ2pz-eFCCHOFDykoRh89m6TtBTYGlkOYXdvHZ6BUSH_nnCe8rmhPc8-2ifDpx_cvTRLXo6IwHXDlRs3tNe1zR0syqFG2e62fhMZ4VT4QRFs7tU_tb97hcU7FMkoUE7QPko9QZyE5Wzp0TDK86ZH4fQxpAQ3n41HLf2XrRryKakwiiJ6TL14NCmrhosxaY0JQyVvQsrBl8cJ2TPGlHC2Q-vzKJiyPMNhAdzKwA5LYebYCI2yu8AndV1u-YLIK7xLPbbozjWEpKx0QwVQJSjzOcMWxUtwwtITHtbD18D-oaXxZgax9KK-zlF-2gJ77cnEMXM-yrE2qHVkWqt_FpDocj6uorTowxArTn3R6yxXjWfWRVxFeWkOyKBIyZjH2yW5GwZw1kAa3JtF2-DDvd8fTuieAYMV2DYu1TBnsIve_T6kB8nDF_3j5XwtFnKjXpnPev03VohefDFG8-6ClSdaUpZcYGocSAXP_fENAJZmLgDmxYMQjSvgrxWezZOZx3UNWFW4oKao0DBu5T28umVUpyJEikDr96gWPkK-mDgk7A4iVtP254g0UMqTKPanDnvWyta3wZ9VNcFewnWL1OMebVLrENBGac5BxPZ0mSUyToY_XzoAzKgfbkIebW1A-upKGtQZubBi5pQIsiuXYNDoP3r8FvVa6ZXsX_1-bFw8h6KJtwIN5WH-wzpttL4cLf7pxktBUOTnoSwQTTEozKoxH5w_gR5dMvcn8xBQFt-TkexR4Ul2825cVLfl-kkZHMQmhy7OT2eCK3gaD6e-TeqCXcevPy3tZWeJvjvgu_3M80RdrcpJm9LZ69_FTv1GlIja301RYG_ja9KsFnJBRjh6f1BMVPD_sZ_F89ZQkzg7pSBpix4nYL1q2zcjKuLrcw7ySyD6gby5iVbdD8iA5-lvFj3P1mBBQTrbb0ffCqilFt690Amc12pmn02CCTrDQi-DFAncOyEgE4hWueA_2edCB7F1ryTE7mJrJJDHHZQa0-ahWIuZYZYuvm0Tu6iiL5dnxtLo83aQmox6-WJxQPKzL5XuxBOGpX0SoLc5ZA3xUDYM8ym9NztkSsaWHo3S2rYYnWByA_QJOWMa1527HzIaHRRkomaw6K_OHf_fSXzwVhhwl8RVj7-XTFdbTtqj23lP3nrzJ9kt5LrTCKAVcSj2kyxQeuR-H4BmVBUpf11O2kQaN_8QfsZDOLd3IL0sIxR6yStrycB7Ym7x_bKIAfKM7S9_uXHKCCom04a37gFEovEqZ8loUFhYvSHuiQogF69RTBM6Hakh1gssoy0gJXidUROr20yF_SBXqbkWms2OjRKt8Ofz2Fa8rZpgcwTJbz9Zv1QSFDhq1Otpq83Oh0iGsilbcSDL143WFZdoszPc0jih0t4rkC_Z08AMZw-9RyuldDpZ78zmrwBOROQlxOagLvjlNfKZ692Lxd7kKhm5YDni-L9sz8V4rzhY1W_-EXHG7waJpyd3E8BO2MqU9IT6wNCKOj4Qly_njY8RTydzq-OqB1AFkEz3eJGpYnN_66iPlGI9P3tKieiHUYLEaY6N1Z1zdpreJaFbEJs8hyosgd4O_Mrc-Tf9njPAACA-jmjuuCGc2T7ER3a0VZeO4uWszzpxFKXW9SsNDimMrpa9LWrYElbVwKK1y8NLykIk442Yo8AuvZ2u9u3vNkldgIFVnCYKLYEuXlQQ04YAbMUu50t0SZ16tAL9gt6_nDrWmbGvQXNYz2HLU-RfeyjeE6gI2CJROV-Qdh8DPNwft91XfVt_RLOurG3L31lpaqPXuUcEsIWzMCcq_CpaauoLDV2X1z4kqAx9ftxkkwfPG30KykcZuShBGyhij7Wd9orrYd19yHCmGfvIs0tYUdkItE0c2yseTXfDFTkBOV3HiY7QhyHHpYaURQUGoddStMrNmJnX-Dg1kqkYJkVHNIHiq9xRyC7sbUHZE0RJ853gzqYEtpl9GXw3F-dOZ5UTczuQrLCchKq-bVdv-vXgGTHl0uD2PRC9CEmyt0VqQfox7vdYJgLpZT-j7pr_SUsxMkY8Wq-BQDcTPpSKtTXJ8npY5cRdLm7kkDQYuNOT23Pex2WOnRH9c8NPf7gczBiPDauAHUSV3ij_HAwtM2GRigJ9ivIEF-ZW6NYcp-sCmlF7PaUJhroUp-lyzNlEoLguBSedzobY0ZX28bUbRrqWzingoZgDYQRKTyBE6hxE6gI3My6B7Q20WgOorL-vY9gyQH0nhh3LwAljrxOxfjskm-xhU1tyHApsZt7MsXRI6ofP1j9ZOpMLFHv_u0pt66ZPNelksXWqVdmeW4VOdmBSbanJuPvuuTuI-FViXUc7ZtyUpGjGE5i7Tv0uMvsSC83zwknIfLcPdGKVKIdgrsPR8hWAJq-9drJZDNDwMF744w6skij2CVPqLLgvmWwjPdPt1cIp1ifGRZu0Sj0k6CZsRDY1yUuBOU3wXouKMOGgsNYHPBvUhEBHEp-u8JJfYP5beEjEj85QPmv0H6supniwxmI2JmC6Ks1M3mKgYm-IVisUgpRk4d_ns9yVNd44PiHHh23L0h8cCXy0nd5F6H4aNlEYGkFcuuUieWKtJNPsMaLeVZ6HfNcfkp1DxHkltGVDizC4q0Q6fP1VN-toDmEiMLyBSD2xbytfsqpqvInV9wSmDTnF8HhAwaxVUbT7OXhu0HecwqziRh277Ktb9VrNxK_RpgcFCi3yZXkJ8TuETE3kRhtR1LrZGTCY7BrHxUIGUdGgxZVbzk_w3wv_tpqQkjY2hv4YakMEk4k8kN66Tn5niwRf0iG-SwpJRfiR7vbFFfsoWD72BdnopksH99bUft2AyL5xQ6OH-sn5rfszU3RAUPt_1fDopxm7oJulOTqfRlbfg4kMVpbb2BPi4VKgRdqF3yqJeidCssipztCT_F1bf_AKkJ8SnVG1mWfSfyUhqekb9mdT1-hHi5kiCcB73dgzP5BHawGNSlR4sI7QeSTFUV922I4tbNMxhkooI0vSBQKorxU4U8ShYXZ1CcnjSX2TXPn8snPhFD9eXDIsXPgEdErdR_Sn5PHLweuBLKKlaegxb_YNEumc3OpGRZxsAjsvlZOz2mnvOh0wVXyjT7H2kasPi5mHLJmTfwY3IlOIssaSUA-aCuXhKvNZbFdboFbPvP4n-OaZfrJ9WG2ddcSDsaueIxjCvl4eUAp2HmjYpraw_FdL0xlnHiXA5VEOQ1TiJj37-kzgUKgU9rthPjtK34PLGorLXYiXp6RxFgNCtNWEDn3p4d8IeAGu5afMWbB5mJVECQahCNsZQZ0wutaJnjlRWyxCl6bVZNFEudS_nxqCTnmqUR31d2dtzOwbjqYRp4rovfLm6lEO6J7Fw_DaQs-EFhqT_sZyHA0xJz-9GXHSWkaAtnWsgP40zI6cjhq0d4soRAvVsBQk3smH5BwXCicQC4rKckV7e-bN0v3ZEOKrbu8BmNrqWAoIiIXe805KnpkBcgEjT3w56dQvhrj4Vf965-agDnIBATzVIisHkySEkB7H5DmtTWh1X_ShCab6WWvGnvCtdQPO36179jE0K4yTDT7Qc4GEQtK70JJtJXYOYMYRTK3TyVPiLe7_3vo7Yw3snxT9Ow1nbcARBGaDuU1bfB0OGRLgB5XhrzEQGidVNh2kxOjtltawZFyW83glL1hGl5SPhF9yWfNalOD5DKOWfgbJlWLkG9eShbjdz5xF7wdKOn-oT6QZOP9MGnitgrbV-XyRY_u-mdnCoSsgjpFE2BrFaTu8WUYh0dZeI_660LQZfu8h3e1E9FPE5Adfmc3V94Zj_hVZ9HYwEfTfhcV2sisLRlxyRPc8wwRUGYqFM3jp4IaFbvMLcOXXwWCNqxBwhcaife3T-AAgKs70jNc6FgzFND5U0_v4QdDmYMdHau5BCOgxfeXUFrdH-O_vG2DCItSs1BRZX7brUbC_IeBFSHNCpJpMmidWCguckFtKxbIWQCxPtyC-ajpX0yx18SuenswUCNOJWlzK494maJDMDSLXmnXA750Lb7xMFEQKjEXtQ2lye7uerR7pHQIMsr90D1NxktIlCbYHzv9Ri5CZ4MrB0yAHgWM-hsTLGO2Zk6M4siQXqTCESHBbNbMYJhZncGOigJ7F9uWORMMtG3qTiBDS8WT6nkcGPQgvuAzcgvk_uR_83IVrWhbsK0VkMZ5hT8f-DbWA6xOE4XU-Q4fNw9rl19cjAOYYtUl6Ikgczhty4hHotdfMYRf20L7GBdQDNkSVdXQXYnagCl3p3GiZTDJ6WgYr40kNEboq5jC1AeSeSCn54OUAWE4JhBq4wtkvMy8b128t-aB7HSW1nKA8GdWXE71ofFUDaaStFBM7V9DLy6P_oPLnf1N9Q-5yBxiI2Gu-mA1dlLvkiauFHjgs70w35d5x0U9mT426NLpm_l4JWag_5Eu1inTrBw6fK0ILZ4UeKfeFhxcuPvKrlNXzprO52dgDJSIOfZv30Q-B9FYCYvwzCZ-Ytn8CyMzADjbXRSLqy1425YMiu-MX2wdW8WiXElfBt-sMCg3qD95kcCblemvuiY5VYFmG-4-jcDF8ak5I-AECJ7Ht67LjwchtAfpvf4F-Dc9qYZYnEwxWJxPg6Qy6-5POBNi2bMA-xVFCp-KmVmRbhJELwd7XylvAkAx6Ov4vt8Ql1HVIIbPxUKk5c5pTwaoDkwbAjHldDj8bJE3NRW9SVLoK_gzpdyRgKEd3GFZ787sJK3wSHAUXMom7XM1qdJHIiad5inzQLRYvhE2n_4eDqB7i0sH9Gk0vMNEyL8N7hXwFWFXL30Y5WWpI-JPL_Q6sSBqEc0dclkOl0ZeCYH1Z6_DqcNJRybn2oEi08Wjyfojl0HZkkbU76XzbvNvVCcjzZiQZEH7kY3_d48hOgDdxNvMgk_FExSd-7qeQJzj-f3UmjpIZppXZKQ0lXQXLEnGI1KX4aNghrOmTLL51KlEycKm4x3a0gH1ILy12_KPzTlRFSWhL4vX5MDiGXFFUMERNnC1qshrBTvQXiLKDNTQ42Vw-NwBnzFDVAZJsYw3f1U0LV7G8N_srCXyr1qIgbOICbjP_gnKevX4zQBE5wx4hiMWSHqmYXnnbNpe5vtPEGOj7wD4qF_H3U7jH94RLHweOEwCqAXTsBG9uJDNrI3Jw3H1DUmLfNTm0IEfpJW05V2WXMJepO1hsyfR6oWr67Rm1uRZGnIXKbr95UjJdjcPVg5hWNRtFKw-IWkAX7H68AEbjcmXNk0UfdlTdszEvhdKII2LtcCZYTiEMZeplvcRtMNZYK-FsN2Y9WZB2KVZkWbPaPX14F6DVM5zaLba3iqUTj2qgMv1fQdeRYYTINS3Qd7w0yOM6ob4c_RcYkdvw-QVIoe31YYgWodC5gSZlJ7HSXPCvg9MbLNX5mXsCS01jd-yIW-R26BFGkiW1gATTqWLdLtiOdUg0AjMuaTYaP0Gi2K6C5H2RFJcwk5YhB3UXEBveeTrZC2TMSC8hGElAyTZDFfRtK9EszeU29wPT4hQYBhKSQOwUpKOg7ieIkEWnd_exbUS2DxoR0fbaxAtziyw6urBwU6NanncYr6y8hDsWru_QyI1MtqNE2zKdnOix1AKBXubeWwaCjo0gD2grAhjLlTDLO82jPtTuus4oii1GHPzQL66V74lNZHH_flMVkqsmpm-x2KuR9C4DYYyOyH-RQ1-yNmIKB3VlYqMQb9vzLE1oOfCW7q_qJ_m_kr9LSzhMHRduPWn8VOm_ntsC9IbyjFQvLPwv8KtKFlrsfSeb50qpWcB_1OsRdZEO_pv60i3fG3wFawEeK6Vlq5oTEIsso-dsgNDeLJldpclsbiADSMbkVaQOjq75OjAT0ZVo0Kh-keC_hiqJKTlZojvtpRd5tj1Ag2sviXmmmcZQTe5fmEszO2v7L_8afjmxasmz_aOh66-L5rvJ55oo9kTvxddka9W4bJuzDlcMu1PsJ1Wm6C8q9d78mCAI0aMqE1GN4MjgVmhNWyMtJIcwcC6fGqxioCjJMGsIb-9wtuXZYJzVhpoQnzlzF0scqSmS7nFYjjT10fY_-ALJyKfeKPkarwG48N-yhO_I4ChCamRa6ZOMOSoTsoIIjqLBJOrFULCld1UA5l22Hfs0dZLYp9quyo9Y3J5_OyRfHfrBJtvEwnOAFYaAxhG3qvIFR8SIuYH0Gd65m4L87AR1JNscvPd22ySW5f_5S_lsVbFC33AFejPchzmkHrGz1CzeVuI1jacg5DMAviBVSD17CFxnNFAB15GWNeujQ7lD2DmrTPiYUq1Iwyl81Q4h-voPNfUqsVinroIXm7UvWvx2VGI42V1tyILHKl6u-bU49hWut3_qwnT90uCxG7CNozYwSR14h4GmiModkCqLA5rti8sdkKglv5JeBdZH31GAouekNLuYNc_bFN6irYMSr693YHvpsYJiWGzNnhFohlydzke6B5wTdKsKYasdvyQT_OZtoW-lc1qAnouTPlbAuTAWdVynloKz8AYVojEC1O9mv8_4pdyZ0771eOydrdX_aiBI84I—0azGjX4lCfmW2_mZclxGmQyco8PNIb1JbSjXgeVzG54Yum_cNiqmb9hX5UWt-NN1UBaD2PJlGSKXvzeT_LnVs2NkdbgxgSdAWPi6Mi6Hc3qF_9oyhTcRynQYW-hgBOX7xpFcPIlInvxnc4rWg5V2vCx1lLlNn_QPIXhG6FY5xIkpluhT5irUi1gKKN1rITe_LNavXygxRfB2kMFvUFIEgqz8WZM-K7lvG7VBkHdnLa38hVHrg9IDm93_It0BwrvEt6AzeJNlRysKNwgBy208hjelfcezyxZDzB7jkPyHk91ypZmytONq9Y0gKMzO9089FEH2U48bavvMvl1hQ_jjZ5n0KoycyoMXF-f1gPb3P_7_VDN3TtE0ZFd2i8aWECyLq0Y2ciYR1ekJeEZlSywM6vrfi3MdWoEF6fns-cu4kw2fWtxETbU2MGO3HuGAZQem4-wwhyuz9wHf_TAR_ld8PcNvOc1831IuyVydUltAeTfHI0Ypg6W_UJt3htacngRLenol42xIrlWZNanz08pbaSy4hMV0T4E6lT4eqPI0UtUbFOPixjW9B72xX-9jbSgDd99nY2HNWH-8sTcwKj52Ekrc6oe4IGJqE_6O_1bguTVw3eZJkTIshZpmNVqL8jTMANmUBMqpWQrsGRevt1NfG8jLPyrMjq1oriLKq0-ryxw58ePS-vZ7D8hLDaOg9y1uRu2okylkvYMI8B0HYtuGPAOym9J0FDX2a1ZSowbT-AniNUyZEgIDoVXX_B92bNhvAjO5lsMvVf-17SzbqcmsP51NGDE2ba73ly0jbCOFNcrZz_O6JVmPmLf_IjZdNswWPBbSQzhwbAZPYPtQihkXwwVsDru3uvtVpp4Q4larp6WCILpIbnf11fvjQ8kbp91795hTeB3t8ZezSqmaR1HcjkNqoQWub6wjm1_Sd24fZbQCRBWeAGuBQRAHVlhCkyR5UnMZtMffCmx6aXZO-LsiXTDvftaqD688My9kHDYAX-n8FAIQQkSk0QQtzmD4OYVF4FcK3c8jSaZ-CEPBxj6WmflQn9G8fdj_kvOjHTBh_6Bh2uLX2Z1lbRnUNwMasT2ow72usxc8FKhtndUWBD66QzpLsCEkIit5DhohQO-8KZMaGnzmCcOm4CoEtUiJeVmg-0U4uAdAwOMtt5vEScKV90jSvrnUVCMUf0OW65deYF8z1tLXgqFFwCNbnjITyaeo9ISq_XADdlGi_gpN4DXPAw-kzC2nuoDaXDQbjPbQ2Wfd47pdniixH2GIWGuEmT1CIe58AwkcoVTmEYJZThl9vJbnUKDNUFROPz0ecYLQdXsz-Q63eNjUOQ0ECL0lDhCbiUqFYPEIIAqJpzNrL50RMayL2-660nbzox9ePwOVKDbrTBFC4oMtUfcVhT3h04n1Gntw2kQjpZv3xpnLZlVXIxt0xLvDJxgXBpdiET6e9nSDd5DhZNiI9nFZjsKktLFeQXDOTifJtDvaiMjYrlDrq6wDNcYx60R75lAoruZAF4szyC5VY85tH6JhvuwB0GW80iaTH4Sq-6RXvEn_mgvzJoGIq_lk43ZuhxWNThk-t8mptOuopFFTccQGdsjGeFMrhl5zOG5TSdkuCFMQokFye6nhB-jnH4cV1XIcTVrjcepoEYdFVe0CHniXwCN5Cj-pfhWR7qP93HxhTYD479zkEUt58KW2UGtgEaXOnqrydqbiWoFrSGeN7ju4IwnSUXPARTS99_6WuiDcbZnso9pRKhc1dmdLApM5A9O4lqTPbyiMtyCpKnpgbebLDq6aEGhR_okkqa5ll3XNN6l6rERspuLnwiVfaQDidCVR3vBixg_BEqwks3rKFlvKwSb58zroEUmftgeUWqx2cHB0utqaVU8CGhe0-6jvPKYrNA1rhtL-53gKms1ccCB7txRWIFUFy2CaBFUYjUtM50TP3Kmc1-GGzJb1YmiSnqN45BqlS8LNnj18PTmEWFwarbbqrpYB3G_0iCLjegGdVFOmXpanZG6HVYCZsTp_Ik66ves2uMzuIfwFY3C0fblGG3zgcsZ8suB7wy0bnfL2szej4x5TEJN0jLT7Tq3ig6JDiujhbw7FbRc-8i-nv07Wfvmi9YYl5a3mWKZWVj8SJBPl4zTWEBDlIqJr77JeFSMNVxpx6GQRDKSOukvvrYX0aUEScEEoagRE9BDkCHPCAipPfivlTAaN0zSxv8x4XeDnaYgO58F0qVTxt8pZdWfoVkC7cOpeQYu2eSHc1pxM941vK0E-iRFrG6mtvN-MQOX4hNtO2RdT2kZWOnzEUwQzDu35-uDMqQqlCpqOEmZtiVwzDBhfOkMUdAA0OTvMTRH7wjvXNWq6QhwZUroEvvny5pTkiwCpGWqHeGa3fplV4rMLbcseiL1lfUynUJ_IXGhh0_Nn7tfU9vjqyGj9SpgOj_7L3F7Mcep_FPQLRzdmSnJNq6xEJ9XGosTwqoRQBNgyafruAuy-WpexMqmyD5WZJfk1IukCMLguB3oPFz5j5I9OebnyAcrqf4TD9QkGI11N_qIJD_Dm5hmmmKFfslVUGxR9MbPTfPJifBz9_YczXlN8DXZTu-Ym0tgrA5aAjylwaKcgzFhFpTZFvzfelGjdgMDA2l7N0Iw5d5I1ud5HT48nSmhniOuerJcbTdibny7HmFx-4JderfUKf7na9YYy4VwgQU1U_TqM28uJM8PsvWc79_HYUW-vFMwFNSWqcgGgSbVCAb3rcIQU09yqHynmjHXyD5ArOHnAQU1fw92Y1nfzpZkxxRXBgvnvR2QhZazktEDKTYwdoJlnSalRlc4anjM1tYru-GWneCEZj2niUDTSvmy2fao73V30vSFDUTZM6ngNyKSMKP-7L60si6pFJvv8nh0LpRI7HkCd99HWqKQz9XdtiSSJqvsNlOHV21IrtngMrqKqiybGRw3oQCt0o3qPKL8RVDaPXTG0mF8dCYKI2AwSY2SDQemb-DZPQuhAF-TdrckcmlRMx7WzHBiILJJYkCzW-P63FGe2A4O3ON_cogMbmwAwDvmR0W98BhMlfRRK8N-tOKT6fHr7DsMYkbdIDdpuNgIhJe4LOiG1qMF-3bfuhKc2ylT4RFTUFg4Ur3IkxhoJjKARDoqYF6BwSu3WrIFzmmwKbGwjG67gXGe2B7dWyOnuizWyU0p3jwz3ue1vhLLdqV1UslyBRes41DXPvWfDnTSOi2cJITTITxJkJH23exdxduP3UtRp5v_GD0bgnxj2UuDTiYWPO0quH4sYseR2JVkyAXCyBJg_T32YkMMi_wyEGz46rgm6Fkj3FlNS6H-sTXS32c5gIjC488IGvbZbTWLTAb2GuLolvXSqu3o2lQvAY0AWw-l8J5DYo_BJDBI2ZR3t6Ap0RcbLogKxlW-KS9oZAaX5s8fyggo6PxC-1mCpwUT5b97oiqSDyD_ouYUMYU6EEl7zNtm_4JWlqIvRjj8KeGAbWXtBsBFnvDwy7CHT7QW9JU7HWjXsUpVyRuK3Gp6LCrmsP7yjJpEwjZ6Fyp-CFKIlpM5pl-7mtHaqgsWb0Ee1xFwdqV5es9hqmcGLhJgXxdogBzk8Omn16q25vyMWSnoIhE0l1LfGo0FxiE2_Gwd3Mqi6N5e4fqvyLL8Rv5VxnjzePjoiFUylFAzUkUzTfbM9FvnBD5Sokh5y07G01maZMfKWXLHy2AOfOxJiU98CdMLSgqLnvTCKeB7Ql-a5dykEzieGXKx8-kDoDdZ_BowcH0UWtzP9RY1DDZOWjPe67A9mw02nOHWzxSeJVhOpoM4wsF2BdTVtp_w7kuvGKcoX5H72sb2tDGwntEli-WeLLO4jAkbEcoUuTFcUS1LmdaYSWx4gz45hWfvHTpYMNRDBq_7ez5dFZrUWV3bPArSvD23b-mOHfBhJFnvrQsXuEreQCvi5IaLJVVZogyuLDluGR1-v3vQzsV869mF1tcfi4QAgzRLtFzJnybylOL3b-WHkVGI43B3akF4efP4xK8aMvtXYCcDwV9nIkbcLqPBqk_kVyvx6b2gp8a5XsJRas8XqZgu2aSnEQ32mmt5KA6XgQ-P46UoPhr-wrSMa70j2wfKLNMS3x8HHUM5QnRa9X7jZC5PUQm_FT_BLCu_GKs0w2Wxvw2V9MCUETDlEPU7TUHCpPzEm9uQljrrojkamlclRt-Eo5e2g7rsJCxT9egVU0xZniAvfGTsfq4vlVi-cob6-vcmsOrn036-nv2C-NGNdb42L_d7DGq8exVdFYWe7hIXJKBILxTmzb014zqkrNVKB3uXVhR1sswYITuiJ79TCRUU23tfVydpljWPkzJWvukBevpi1Dv1LE4_JZRMjiSeAF3-na5NMqQqo2cF4RVZKELTREjBU8-ffBJwMFLg3miMKtqcOOdw6uQqMbUPaMa6-uSqPND6Z2cDSKMVBbkeN_Lhvty4b5UY9qCvMgX4IAntfu1Fp4GcC6CNdK6LpWoXxOqJb7bucpyTWK5Velguf_skgOeWJJo1T8Eq14gHsUtgAuR0mQnuMADjp0mHdQEJTiuXzZkLZdrkN4tAgbfIYOynMZuam0FCCrr5Oj7uAZxdWustdtT_e8_VXDsK7S3HbCmytYfobJU9dYQZJZjGBJZnSlesnM6FAMr1VuXWySGkDuSoHM5g4L7aKZIMTTCpazBYNUBCyEwiQHnTYtRpOWPWxGmURxjsWvzT5ie6j1gsUKndA_teAHoJ_izT1ZpHx3rzBMPfQktC97yqc2aH7Jdmud4kzdrOK1y-gb4i5zVSC_NPiMlsfTuTLQSIstiNbYvBe9FiPGU0OVSWM9HASOUdSEcUOkZsDdVlZfaCybqpgJtKNgmdnqXiTLIRmFw5FhWtJ6R1qtcZJNjKafHPAXutGEFYkLdvwV5vpHzr8SJnt6fK3aeflWqPOKETQUCwzfob4zIvn1CMSKP5kGkN9Mqq1F1IqkDE1GiWtMUD6zxFKPvenX7YqFq1WO-a4hc3bZnPHDmsROjAOnCuJeVhCDOGgL97ddOgY76LOjo4q7iQJRDoJioN20ADWzKuWGf6RwsObH4E9yQ9Gk2FcJUltawmkWHKZT1qJK4X3Gi3YPMNEqlh_BcbpYVp8Zrb4cB1M-Z5DojqY3edsRz8VqyHjYdXTiENGWrizF89kTx5QhoSLi9ZA-MG7Kxnq0PRATHNDHCrDmCqxmHhCOPqxpmAtyA8yrCKH5IIT669VG-91vfO9budF-RJlF6es9lhxlEvSBj269JNfIkCw5v_8rw_ktYpVcQHTcIpZ-5fFvG9SC6k2k9BNcRoD1IGhI1NpOAsr_0uSCN-GSo5Zo5U0xt0Jk0_zmOXSID75JA016BQf4PjSsSmSm391PZiJJ-VHoZoPiPMEkaxrnouV4iK7D2pvNSjDb6G8mClaAGkusSPYBtDOBVsizGW4yircs3LrolLWuXxjW5oF8i_B1-E2ejWYvagTs6_0qqR-s-kFhOY11pFinxz6h4KPfSqioLpfktMWEA_amGjFwlHci5g5vo7AhT8zp3SML8z7OcaX3Wq52mjZtZlbomaQY72xYOU-Mfy9mBQbs8rJjVo6B8Oq5Db9vyKUQowQgMpJzhqkNDgraXNkhuMI2xKUNZqalnsjv02ZduZF4m-FY38BuhkHRG1I6XT6duNkSS8D4IuGcYi207q5_8KlzGfANGQUD_TewkdCXSg1FcZr81Ajjnkb-9O9gH7QJxgU5f9ACpKPZSgV9TgXR62JyI-hfcG6IDVCewoDdkQVjaJgCEtDEjLySGCOuGcvv-sJQo8TCGrLe9l1SrHll-LWrED9j0Dirwres1yu81SB2hUkghPMHrKY4HtIzNTThJI_jBxwtyW29rNCXGgJQxlpIOddlSmmvoVODPztflh4imrm-09BLRVx9O4ftUf4ANjsk9JLT3iqrUHw-PIsbGOjlI4jEQkXXS8QIBB50aG6OJYS1LfvWAe5NS4P697IFcycEhjolOhQk90j_ASzigVNFBKFm0_HP9-SDVh5OhJAr9ROONrgpPafRtVVt1SMpSYT5BuwCgs1Eft1jODkLop-Fm7zcWigqbDzZCaStce5vhSpmekJO5XjtzZHU_SOvBnRhEwuICOJSzEiGR4OJlaCo_ixiWs5S-GF6-kKr-FkxqaOVUooE7yeEEQM0GPb88Mxif3vvppDQ-IQjNWNf7cXTMSwVYJZGJABtLdxv8Hv64zcxanpMiYTH3Gl_kJnQf467Acf-u42AyB6r42-1CyNXGagaZlD1blsranAdtii1zNBI8liZQJ6FeaOuQefhZ69JjRDvW-YnLntMsV45KgXOAHY7nV1L37eFVaMavFgdXQ6Ueh7mlla0XOo3FfrKagXXs0RjQg_GeZ6RgBBIJZJXc-ogUrFLhDUpI-4A7NsI6qmq8jtLoGJUaf8kcx19Ya7HeV9I3gZQ2zKWrHNyyylPxx7_mxC456eY8x3XJVgf4pAwI8rbFLK0KhkJ4yN5X8PYRHQDhP6X8C7hrtWR9IUPV-l9b3l5xdq6YtFdBK40ALTFbYgGisGacANGKKOmk-S0mdG56p9baLk-1EP2AnCnj-2puzzKTyRtSabRAKp3zhY0YNMSB-n7V4hazyD9D6ukNfA6q_UFtK1L5JDZkOIvurMivR8PvGz5qFkyPGhexB8EKqsLzdoKfeTeBRQrKApooh2iIIqicNYlyCViJc8ZqLDCBt90dVqFlDfYgH31QDCx1VaRXvZLLxnGlxeoKc0gFTh-PYQsaQIcQzufXnFQ0oQASERB4YLS2T1do3cayiYPf_Ddmhn5ol_P531u_SgHA0fnfwjMDQqSrh8Le5JlU20-DrO0BmbKIZh8QU6fuIZJ-Zie_oL91yZ9xC2HQw8rO2LFrkGiCHK2gauld4NIOEEycYZA_PX6kPk7qhRs6eKqlzGjcrpsRFp7Brrcg00qkgnA53a5CxygqvHrEUH2PEWxcv_WQm4qcIjWWaoXtObAJPHPt5h9cDyM2S2Egy2NRBQiN2kGaKsk5o-oBunK0dIK19Z8eZkGlZE-GzKjKYy40yTB8-ZkrJ7dSBexLW22TnsBALa2pYAyrjrXNsO_t3vX0gvBdSSDaryvWdtT_KVCgHrsgZ604D-JoPGtu6-gAzAwT_Ic-2ngfJ_ZeXXP3uywbN59xtyrwXemUAjSh6jLSQiyqEwcr2CQDu8Nxty1NqVGzyYIiTQjqBFcfd8qXWhj-YfmFlBllfxL9KrJHrgpAwXVyDORKj4HuTMMPvrpL9hucYtY_Nck_ySuUVPuIPE-1SlFnffdXgfhaj0rS9dmWg4_yIJeXZ6KbMLTYFZyDajNdmkHHtv3Lw75lrAQV2E2GLRIhRlwcoTpkBCPLNUSincLZafgdNhti9BqTY-GmujOAY-aNGXMn3Ua5Yu-qmD7exJmZUeC0ZYNYfcp1XR1cpLx8fU3lWT-lh8nwg22NNu6M2qubDkd6txH6NCXkPNr6h_8v5blOlWR3y0APTSEQDMKbHvdHEeXQ2IEUVD0aAZIwIwzpjuhYs8zfH5b7XoGrh1v72ZvDut7fHW41Azwc9EljMn-SwN3zvzaXZ17R7xRzbf0-H98fQrgUWIQoUHN3pyA9I2xMP5toiKbPHuoewNjGB8KxJr2pcN3K4d7CFwfsdG06q-V1jWAte-2pVXUQNKhRYAVsoDjZ0MeUZABY1xPiBTx16ujTGX3NmMm4cMq1hPfytLe2Li-EimMoUHA7DNU40Sz26NZKc_nBgOHDBNlx3gklAVtfrR14GwDLz_WT0RweoV2m0n5XKiEaItIbTOdA9KXS-0_3okcYtEUKvI318Mz1wr-1XEhHTPwHi1miWOdjvdc_UUbFEt1RnCiqg1JRRJnAkB4XZtbkC0pLvD2R6Z-7m31jPFdP9oGFRvCC4-saqbLGPEkwYvSeUXj5kTamdJhEplKBfOWrjILn0hdXgngy-SbP7Gj0QIqGClKI2FqOuiuFAhVHpxBkzj0Cj4Z_xfrfjHxvh_O6aHWN-32TC74dbv0ZiLkCgtQwcvMoi1xlr1mrl77Kv8mhLfrsJbbraG4eG0Shh5vyOTET2SSizXKu7doNAy8ZBKvzToH5IfR8WECfYTtxTS4tzBggJJCtyyjNjFMsTIEF6iqGIi23eJAZTPi_TcX7yng95_bkBkIw6r9Y9rxGHN7vaO-dN6m7prqkRmtUtDU7QhJ8clu0K7vhGX0K-azvY_MR7Q6HA1OgPVQeG1CnwvuJ2lA1PIHbDf-0lvEB1FeASb_YfFpUujjolSMEtBh28ELoSlfsNdfAyOgI7fRlzuiA-xP92hEVOV1BClYQBLz7kydX8JK5wbIVFMOxWdWxtNEE-xLelgPhHPjtKMi-U4PQpjokP7m634Q0y0BLG9QXdOY7ddApOOcZBSKoqhAK2DaGk8gqwcAoH0jg7W3fwZMwUMspLPSVUYz5Guxcw3maJhsKGtog8PAHVn8xL0zK4ydQUEYs_nWbLgTmOqK4WUSvx5perof1z2NEsXdVatPTrvLG0l-8KY4GZZUvFPF7OXMJAoey6s0h6nIb5V7IOOYlbMsy4zxokCCiNPQXcKVzMjW8_lATmiO161FmTw7Kq8hD28X8KuBM0nTJj71OqWX9A-ofYTMZG90NWtT9E3ocHZ1AAsCyKBHc3gYffgObinT1adxb1fPe5Bx6sOgANSXKQCFs_3EfNHmK-w-BHjCjS9Dssh13XMfURPg256480r_kZnHn5fop3CIOqnPACw9CWPwLVd2GWDyj9E4iajIaYp0-2eSy1sx-09Y_hBIF4QG5lxRSJVEeNhGO_YeyEWONT6l4872p83wjabnPDEkFJry2SRduY0EnfUJEhKd6-eZ3FmqnxT4dkL6hLwcp4vnEZjiiVscAKxYFIfyAiZ13XNJo2cpQE2XKa3s-ezvbWF79k2DG-oYsRPx9NgpfWcHhqvuuuTWZ-K9ZnHJsaXaJlJj35YEdNlXpPyZ-KqynaXjqZTJYtxC95GYymfRXU4pMdmP6l1MivwrvdnflgWR84oK24TXqKwEZMo0kCRz3xgateseBOxkSc51L5Sd2Iz3wFW4PUT_d39CFXVfE3h9qr8RAbDBzJ2xBSDJEg9d3B5XHa6ikZcM9uTFCzfFJdl137DVL_pSqA8xaoVqtJ493cVsLRWtqT9h1wJZ_3WQJ2QrHWkdXQqL_33QdJN-mzLs-njsiWOQhf7BpzNWRJpivYCAWT13jrF176qKZ_TdBU-kw8Rc1ghto_OazoGNHRo6u1kfA3dlsscSQmdGRBqStverQjb7B7y7LcnyCUw2Ck0uKOzPPqPs_v_19EZMuK3nVheSNGJxlVcsNqVk1rx8GRZhniWZ7-zrwQqHOIh5HQTcjHHcg74-MuHWH3IIyzRed7bd9eWEKloOPHJVbo2jxo7-vLgpCox8BvISmOxeJ93HqO3BezBDkgEU9FZVJXsPkRn7AD6s86UR2Qz42xMQtSK2gCZhRfuU9OQ7CPn4QE3s81sTShE29n-KoDtU18eIxTriATM5q3JbFZvVK0vNhbHPQU07WPo0VXENNI45aQAObrpnWVRQVAvAFB4PN3nl4_PTgOyJy_mCNZKmuVC9ArCjQPIsCC0lpqRUj6RDe4xh2-l94YSDOrSHMyBWsjNEeXsPHhtgK4QpearKsIDdQukni4bI-uAUb7QjYKdgM_17oVM_98isGGKfObeR5VPGHfNTD7bR9U0zpbVKlfHg7IiMkVaikj8vLD-1-Nx6g4erLee2H8yPBTfoXDOC5zlPmpT-6UvaRWxGUs9kuBHxgLdvmF5j2R4vZap099jca7_XQBYzPWYOd8rdDMapGBQYMoYKy6O3b-5KeNeXEYpNWabNXaX-5wQe4cR7zBSqewAUNHQ-aXY4OamZuneWkcvlqWQzHA5LyE9Ug5zb0nNP3OKeaHi6O1ynhaOEhgzgVyRWSr3euov3iNcswgSziLvRLr6EMtt4vMMwOIQ8YG4VjMQRqBujQkKNOKcKhVJ9a96yDDG1p_7Xpzi9BiWSQUjI-cWTCKtVmue9ZqANS-Iwj7kSpqaoG9n_eiabFe_WICabk3Xg-QBE_yvwfpbBaEaANhDavnnRfpEr72fX8Nf6JuYEOdCuLfqgQQGPmn23AlCjOPD_j1cyD8KUoCAdXK_BBQ23Kx9rHOr2z0e2lQpKgrgE-0WY1t672bAUpQZDtf8fEnc68OkXesXYJzg625MOKlCM6l4tyhSbc9RSFwrnhzF6sxuMSVyQWYnM4eGJD5vODSTOCJH1yAfmIWzO5k3MghupBYCn0914knqLUSvFdzVe_bU34g7qqTU_DzcxFe4AbkavmcksdraNJiSJkrLsDk-Kn6rlg4bQgl7i-pzJhSDjsXS1x3YjAdoYswDx1D7Zp_O_5y0gOr8s9MSvwFtV1wx_hWc1aeAQKoG9vJUV4YDY5UsdL_CFBECWCXNdcnXGb3g92djKfxe_2J_RcxhldOODK82DEawoHf79LTs3-D2Lc3c967sxMpFRNovijVrEA_bmGFuM0tlHEFzwip-NT-XxCR_Fa7a3ilWzESLtNQeivaMFysyg0AHsrMbf_ORqpNGFrHIz9DRSzHY9QeJj_6zVCiTaZ7asEjcjFDFmzrKJ5og38r1cQKYuIumXq3uSoI4S8rnHA5-cToeWayNTOM1OLH3l_ipDM_diLwjIppH0jD7XZJu27nvM5YXH4gJYKtXCwHHxYm_U6gp1bZkp7kwmQnM803Vp2GiK7VWCPztbvMgKCc1qOg_qXJzPe4V31-bnopw5KbhDgTxRLy1IzMNczl2fXIQO9xyAU3zqOYMnmrxQ8if2XbjjpNTT9czWNIBVxS3r2Y39MkGJwX-BJIcMbYJAI64z7uaIAh1r7HM11HyyEnCLeu7pQYU5u6sdD8Ae4DvDogkH5WOiwn9or5Ku8v-wPjOxPtKnVa4qXarUDfQhH8iwAlwQQVEFp4TS-S4RTrewTOCQOJFUy3N_e2rcTja_gArOq4bdwcNB0WAKDjDVaUFauOz9N8tPsF3sL_Uk-lXFYhuHJ6qU6UGhY5vzUWEQ7lqp93-ZlA9R5wLA5on0vnV81FPvA5hk-2OjP_NYnmub0cI-2Y4lj_6JXHSs3zoJCjv076hrbjxnT3ZQNDWuT_ONZvmivCgzTBF-4LqXTxK4OVp088QABFL4sQKjOovUABQcZVWL8eU8iZiyEbctRYT_YBKc6S_NyPR8g0_5omDy_4lj5ZlR28MqVJEkCQZ33IdhBxntGPoqJWy7TfiQcI3_sALbexK7NySnt5qvPDBsWVnKyYGJfOueO24JYmRxXpXVvBpN7WEyLSQk_OQeS1lTbzinR7702vZnF_e3x_rIOjsfQOjs7Tl6gKg8Q7xGyrqrDS0EuhXAyARbc1hwVrkz2B5WZv724APp6b-TatIBnU5Mby5VcgdwZcjdc4SiEsuwwHmrMDSaojuAc3qyIuVZL1qhGV6U9RWVe_dk-6PkJ5BPi4edsL4PaiXvaRul9D6fBlDJMM0RoFVcRLihDc4Obv1ULXCTnu04HThJAR55bqNr6OpaQtqPVV5pA9A1m_grUnLtqDWyKNgMMEM_nrGOISND8qW3Mu_0AB1ZYDqQS6Xr1qrXiaLWalBQo1o1IVTSF5oU8-eSFZZx0THF4V0BwL1fESbh8sFiT0FBCfV2wkY8qVCIPiTiujBITCKjNLQV1jIKvWAzdRDlnY9DbisRsHdQ7-qTAina3Fc_LEN-LtdWDwbtdFk7ITUQe0bTv1wpwDgV0BY1mjZqpTfI6ycazfyfnCkChAM6cXpF-rWxW8tewkd_MyjlO6nWtoVpJNvi0GCZOxvnBH4F4vxxTy1TpI6wRPytJC8hPvFu0PNF2whvEDuZUcUK7cLCBD1FntBAxNqThCrCk9NqINw_FvZdelTlkx7aoiwtLSaT68l9gRTahR0iB6wpdbnakB7sM6tBAQll5S1q15BjDSK_u4PZus-k2Aom-Sy9d3KdYXFjeixvQYLNGEf9pDtfBFHoi8UqNNkbPyjCF8dPXRAmsWB73zpRXl_MQopG7JI7ec5sQRDA19eqNqEjTAgFWiA8m8qxI_DfdWc_EAnZCtC4kIMsD4ynw1tC9MaFdA2kKS8SMli5mK8a3TPKfYMmPFxinxfhG6QmlX_eBp-xYNrxmY0TMJBSnt8cSIjuVoNW7fNmgO_JQy4uSHNMzceRTaiTJxvBvWjy_37HypW0kFHwnWsl0ufi9XKJwAxeds-HShFe8wCOFA1WmUwQZoM-IGFXgHSkbxICaqObwfXIN_mDNbZTI_f1X69IBz0PV6cuZO-oYpK9QlrydFqTeTdN5s_oZsFydc1D5CzDCrIozrFuhpKi8vuOHI2hS-OcyESN_k6HkUvFlCb7N0Xa7l2CEarkU87gnobA_4-ZIVFhc_pTK6DA3zR6ysdoTYamJvLSHL6LOkXthbj3y-MS1G-s0W8_aHeJBU8ROnVB7IhFXA6xMB8HQ0fMH-67LLfZxrqAXFC6lbswKIdPcCOauaF_nPy4J3-4UuOrGdM4f665DmQouGlM8XmU-C9zhUHLz71Jckza_BDhxAvOylnOmeAj8IzwNMGTzt95WDuUSxznzNaVEuV-8II2gEV5zdBni_G0TdwlX3IUrs0viD0BwMUdDKsG4V08uU3U2ao2w-awqd9IvFaKuXNPtYXmlZX1f6uHRVrP8AID3_pBQ-BR801sgXHU5LZG54LpMJ1fvtpn7TGijhnYRwMcQnzk9Qm2qh9Gn0Pc5PA7omyZSyL2nbFfdjXd0Lqx_FGfDxv8HlZs7ts62i47-f8YlrS3lC61vj8EywPnGIKds3J86TD_BwO8SUeph1zKkGnUvhGafCiLhzoHJsT05ak1SG5cwL_AFd5CTsf6bRwLx1tkBbHMDkdlB5LOCmGNWirt47Qq2gOH-TuAPIGKC6ooDzBd_WKgKN0VKSUhoKmll8moYonucZqEGwpnU-kjo50L_QOXfS24-QS8qQ-8UDZvcB-2AmzH7EUIJuT_Y1ymPNLEoxsfWGJgPP85jX4yC6Qcpe7pd0_CQpl9bLzV6LFggTPcrGPAZAAt_OV609HmBrlC_k9iX8vHt3L-81Kux1GZXVWI-dGijpMrRz-dh5DycOkyGrCJxysBbXVwh1ii_EmBlwbx6VhcC5hOzCPprY5MoAmFBdSVOliIK7eSKJGLwJTiS0tpTeLi9NUyAkDJCvFwmm7SmoAqh-WZ1PikfCRxLaP1vcZ3MO5FOFxh7vjJYBwmy0YvS2V6JcsWVrP0drkTTPFb4x11Ta124z5KKRhbDA348OVzgWnwLlNlDQJ86KGrXmmEko-rlIgNiSIS4bagAWtuhFO-Reb7xHxADivy21Zkl07yzhDkSZID-aSE4fiUmR4kA1_eEfgDeAzLoPDvK3UK1vEBqHsYEPXW0NCsItSr_wG9kZk6qUUj6mDT4EMl6BhxioWQj4zj2QgDL9L-e-4U1r-0oT5HdBsJtmpk-86mJdlXB08-B4a6s9_rqr1-8W5kPzcB3t1iY734JPwSJ_qFdshf7VhlNd0u0GVZZV1vHwHQDaS1piXABURZdmBrJdjG11ms9Awyii0w1WFqsyby7gBnElk1kq1PxKojwiSO3e5qjuEVFcrptCziuqwZ2rPq4bwB4OoRMR-HNxSqKbgueVnsc1ACBCyUDN1p7rJE169g4VaWlyPvcO5xrVNpRFwaRK3go7wCL6kXWh0WkdIFznNCY_zSKKOMSuc8GhPdLcO7PCdPZ54xciPmafAvASRLIYnmt7NIV77lsZ9HU5s4yQaYwnlCwJzwN3Vh8mfjhF0Bql-Kbot1Rkd6vdW2CuECWioTh2H3fDjevALElTEu3zMULG2YE6Npq3vEzRRSeTIW0OBeyRuIBWZ-5KNif3fk8iLADth_pXUasCUughIJxx3E0zMsJl61gzqMHPAuCQaO9TOrH4zvgfuxGX1npxaZIgONF3wLH-SOaDFM4xq1GOFGEJxidBy_eX93jWZ_pPHnVtijcto48gCcjVom5nFwZDgnylq0WnBK-BCYlG_Du90hrNlKB5yO48G8Xu1b6QyS7MkIyL_V2HQKaqyVtwSU9dvI-o9zVCTjZT7BzEZTrjaVrxuoxLaJMlBEh_tHjo08IV680tgQaOzoX5Dbbuev_9LGjTAhLalmw-TaN7eusWqQSVs3wbUjk7BzcGImCIqrIGWbeyLnJfoz7BWvMrOL1ptjgrUkHVRce9E4wiq2xRXcW6GXIdy5YCmTcBPhmfeTEimlXJ3YTOumhGh5bki0H7mmExsTfuUPprAHrhJUORCHfYcOxL-rwBqak_Kbj1jbuJI5BS1KPRduwsJ5Y5g9SbO_cOQTKh2iqA53eypWZB6DSuY3W1zykXACu_mWtv5p9cGyUwhRpHNf5t6mfZmqhNX1qbTzrb8Y2M5eW9QFE5fFSubbiB4iOZDVAS4t1-OsP-aXSm04TGfXKJksa9uYoK9KobnJeQMmPkHSPeU2_ul3ulU6wskunw0ERwjs90OFsvcQFtpQ9ixO9a6BCj8xrf_sxT7VlbQkIDICE0lB6WfSvrKwF1kunKXYElq8u3Jxeysrfxl_8oPzH579g3EIIdJZnx9od0eOdSmKjkU7HVJOOqds1HOQYZbvJ_EKSdNekqq7WS3tSPiMNyIuztouzNcdR09fGEkI76qZluxcC7RghstmBwA7a3GAaCupNFJ9u1Eg7x506-NDcGm7C3xuUQ2Z_nYeXbB95W7gdWnjs_-1XFnEABFildkXcCcW8fJePC8w59_bEMBnxTCj6iI8RiDd3aBfXuhlNDGBKSc0yCm4QegW7qkSi5zS21mbI-lNZx-7g6HqP2jJuu2HUmKKzy8samULePulkhUs3Bq-r4r2YN_CyYHEv3LTVghM95F-QheC4gj6—UPOVGggNnRsNdlnuxTtk91a258FBJA37i7DZqPgxewbLupPn2qxiVF_NPIcwzwGNp7X08aoSkcYypPGMnLqixmoWDPaV-ykzpNj_a4wD4KUfK_k9FqKtfmg0G6KRfdhrSKJbpu6enUItZvm5QqlNSJkJPLDiNP4WMS2ut1QFC3vCoeEQOqMxIkyxeamCOGf29S1YdVktdmvQDFD-L_Dh71XsnbOv9kreiNG0HD2i1AL-ezQ25h30WhQpCE_vcv92DSgtB7d2DGq4p7OcF5ikKe3vF9G5EVAfql5GPX0yOt_vXp4x7q_td6VBA13kvr9sz4MoyrrKXj01lonOySxwJPTeT2upsOQ1_Iiwva1zP3KTEaJU7Efn5RrlrIeqO5zKra1b9JAMnpNMAkdX8SknFLN5yfXYvAV2-kwt-cng4qY9RluT9z_ltIPWCDEUIMck8k3EKYfp-YRglJwAl2oIXG_mLBgIkJdjeLLLgAsnctEfPvDRhpIuv_MUGR0f4dcULxyNma5UObUpwyxQC1_D44gORHWEiYUeagL208ecXtpNpLBVZ9v7aLLOrTav-4SiDJZDD6bk_mVQ2t_Xsp-RCe2ylzzxD3jJ6NPb-apRt5MNLw4e7OrFWa9uvrpZ7z4JAWVVXvYy8idFK3COa25GWh4ZhW8lt88sL5G9nshwm_wyxaSIyuoSebeejjaTGsDRbhf6Cql92rh4wIWBP5y1kRO5YzwHRkA6R-VsaVkoVikIrZ6LsSGPWPevxVtNtbKSbpK2QiDeqAMSdKaHj-S_Z2cQTwl1K-AviHZt8-zeU0AQcaepi-u4dnpFtSxjCrCTJPwIMTazXwj64GyKzuEo2VgtRF3_mcZ9z1DCYUTKFY1d2xOMszy1fNqylz7MvAMFhl9mvfuZdUn5iXr4GVrNGfSjONGfLzA5DaYij7tqpZgqKYvEboFts6XP0ZMqpF-btNlOcGB_1cOYbmFzphDpb4u9u_LylCsTj0d_Gl4El3TE8eZX5HTnM5udIQLI-AfHGJGQfyMtnl3EzFP5fwbznKqsXEnXpVW7g7huX7p64xFrie8-vm50CKh8l5xLiYGrxFa-v4JlK5kKygWiqCfAWQUiktk7XHiWHqaeX30VGvnZdOt2WMHkD86sN7otgOqDqBgQ06JRHFqsKRjZBdE906AW_xjHDPb1pfT4BcWSr7_dOnFbJHFSeyd9wjKnsycHABXRTRPleabCaakA1RyhiApj5BqQUhVI-NpHs8DnorsQDgjlWSdrLVLXXAqxRNUuu6BgfGRqjl23_6PbKXoWPHktF05ScCqB5bc6baaNCsN4Agxz6xiqwTE9UUEwgW_mFpAY9L3f9dVax-xFgQO9URrofZUIswlYd-oJVCThiNh9URWGScm54Z6DBJnoeHC8rWrNYYqp_G1KNiEpKC_hNDjwmsgJnspwGv7Uq4UyTrhXEDTowL52IY6mJ-S5s76DhlgGWXKtgjqcRlVzM-sGng-idOFZAhIwRe0MLEpMdbX6qI_L183g2-yhcTFRQ73T8OWxzD3MDI6uOBqEAnj3HE2Z70CLpTT3QylMwD7g_Ds0D_iIerA_AAuUYp6s2ahLR-6SD6FToDiCFeRaDBoroF3VbWJxyMDHGjTtVbbADPdxtp4eN4GZskp0prJEhBD735i2Dp-1ZfiXQqE8J8g03IwE9OznOvg-5Xq2_N39JuhY7vwNOyWwn4WLiQ0RQyDFac4UF-RnoZNYqAFWuVQBPbfquZbwyP85tEdMbW-fx5I0qFjF6pEEoxZ6XUBn2xhmUjLTc27IXVsUf1p7Uo7oqQEIKHZLLiFTp97rq1x2lmNYBWx81ZCE2I1difoZiE4xGdVSQZqv2bvgh8H_10Q-HKkv7CSE1Z7Oy_jv6_m1I8sJD9_xcMfoYzLIim60rXlcfeviy2vPFuz7Sty2A4L2YektKhJLJzcnCR9dDZzQl6FlhVeiDp4_jiEH7XrCc_bcM1_67h3-v8DImjZvij8fOGGSabK2GkIOVzPaBKse3pjPV9VajRzqhOTM2Oxn7gX-LBjMg_Je683GGVhZ2scyMOhb4wy0t5MGSfwdQDOInG6vwtKZRmvljI61zY-JVl8fdDgSqhFxJOqij81wOufucjCuFpRb5_Lptt8fo9LKH5dB_9ybUt0YPVmgWPdfyOdobmi51_IX6GckfwUHsbea8uevIpCqorfKRnZCL1V_EOfghDkQimrj1mofFBodw-sTcMIu9M5GWD_PGvvT9AvlGR7puVGn51mzkqOOAP7JZnbxA4rWH742Nwjd1n24ecdQi5REEM4S18hqUZy6Ccdxx4ibtAdlXC2FNUhk4jT2hSxu1pdsly45J-oERfRNLLaxp_9HUed5y7HCd5tx1sGyEO2cMIPUfWjnAw0-8ZdIW30j374ZiPcQJW9lSA2ygIO95SZkbuGEr78wrEnmJpgq8gVWTJ2oojMVGSbMO9NheWbXEPmV9AW1J1mvrtdvlwR-PAPKypL9c4eCFb8vIQ6vJkuo3NH2PJeP90LAdf57hzn0FgTDMuTadSfOymLZ0jiJDv7xpqeE0cCE_F22SfySPc4vabbH55TnvwgHGLKmhxIfhMfSZsibY3oMiNx2S3MmqEho_E7tblDlcwWHirEgUfNeNfScTwNYQVjRG_vlgXeB2OSTC7eCiXvdTIhUdG47HNRnTGqP8yqExJjYUboJore_cwDEcTNl7xJnH4eCPipViw8IikzLNPEK0Yj_UJTOlxwQM4ug_BYkDEOlWYSJQq1UMJaGMe7ScFNsuYs5T1DhoKVfPrSrYPqDSNqRTA0WmsipyT6dmSwRHtlzWQNcWRwYj4gsqcA2lqWcVllaCed0OtYYjlllVTL9QozHQKK50FvGC4Sc-Pp2dwTaeKscCZZMXwR5_KyKFVlNT6S5xyd9c2LiMKSqi6ku1psQhLpG_mIpZkl1TN0sG1FEin7ZDViFglU86M7vl7PsO0XNh2e7cKGsolupfFNeJgkZTpy_CNsnLeiNgJWYk1vpO4cKxi2QEPd7choYL7HxQZ7FFHHguTxHkSO-93KZLI4rEAPLN4hQW5IPZNfKqSpY2iKL1X8wkY6ZbKtLSCADZ1ttcjVUG7YjxQDQGHSHjKtFbCjvti4J2YupjiHwFQqYswgU5vNMlfutja4mc2PFZrmQwJ3QRQMCPU4CubAPbW1PTqx_OcJShogW1k_-XCgmhYdsXifqo9TuhdxV8i3Q4dCC0s73X1YmeUSHmwkgdZmIRwaDcQGGpOJBu8gh5jovBYP2Ez9RIjTrMBExf_WiV4EAffXTkOLxzaZ_3e3JWiBobx9Z8tFl7m6HKkb5xs0neDEZiNSXpcgcE_qVcasaeWx9hk8fGDrI4TkEdIOyTnvLq2B2H6asfdQLFpnTI3kohnmvlfZzj9h6AHrbDp1ckalrACBm_mXwsBQO1T2_bHvtf1s3CRpWEe_5u18zkISJ4lz6h0GvJYHnudV52lVJ8QgUJnb2hq2ZlRgO_NZMyh_Wu8bxLlpGRFH7rMj7Hy0cDhWjXwhSixGXOD-OPrLhv4NRyhFu9UKOlRZTKxNGB0tOM-mUm3PQ8N84cy8Vkf1Kzku_rVSfMgz_4AgULj5jtwPOjCy_ehRHw-c9Y-XGDgzaYG1OGxyzr8ZzaGY7e2ikn4kS96fr4o4RVeCdW0AKpQUkcIywQu3ECJCdAxo2lt91npmKfmo05_Nc_vGfT_VnIook_dJeLMmfVvd4cwrGrVEmyA3KUc_1vhpXgqAOHDc6w74_wsHtp6wWgrr1z4rUlZfQc3NeXZZlJ0X-1VWOB0MU-4pOIOv3tkVrRRy718MGTx67XMdC9ih8OKFjyRMewzj431l-H7bfAuKvQqnrGt_B2DgeFDolCBCvkSj6TkHF0S0XCzXtqwON-8O_ufJP5C0jXpcRi6f7dYw5UvSvGlmHA0hhxTGeK6EBQKLObb-G-BIwLdTpHp8-algCpWiL5QgF31-y6hccGQzSbuaFIeU9XdqDWllleoCtbtsZGjl2tEaAKbnGEowCSl6xUy7lTm0R2p15ixRmKREoB5Jw5l56VbiWRI7UezhodIeciBiDoeFoGyeF4GuVy5hviHk3Xvd9LST4C3hbF6YxQuYYsv1CSJbKN4=";

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.