aboutsummaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
authorJoshua Peek <josh@joshpeek.com>2012-06-22 10:09:24 -0500
committerJoshua Peek <josh@joshpeek.com>2012-06-22 10:09:24 -0500
commit5521dd08a062f39036b045c819201b6235fb32f8 (patch)
treee3085d9a7a58f084eabefc19987f3acb029d03ac /samples
parentb571c47a1c61394d371dcd4b8d2165b4e16f4e84 (diff)
Move test fixtures to samples/
Diffstat (limited to 'samples')
-rw-r--r--samples/apex/ArrayUtils.cls458
-rw-r--r--samples/apex/BooleanUtils.cls185
-rw-r--r--samples/apex/EmailUtils.cls98
-rw-r--r--samples/apex/GeoUtils.cls55
-rw-r--r--samples/apex/LanguageUtils.cls740
-rw-r--r--samples/apex/TwilioAPI.cls114
-rw-r--r--samples/applescript/center.applescript50
-rw-r--r--samples/applescript/center.scpt50
-rw-r--r--samples/arduino/hello.ino7
-rw-r--r--samples/autohotkey/hello.ahk1
-rwxr-xr-xsamples/binary/dog.obin0 -> 8648 bytes
-rw-r--r--samples/binary/foo bar.jarbin0 -> 28 bytes
-rw-r--r--samples/binary/foo.binbin0 -> 28 bytes
-rw-r--r--samples/binary/foo.pdfbin0 -> 38179 bytes
-rw-r--r--samples/binary/foo.pngbin0 -> 5570 bytes
-rw-r--r--samples/binary/git.debbin0 -> 2048 bytes
-rwxr-xr-xsamples/binary/git.exebin0 -> 8648 bytes
-rw-r--r--samples/binary/github.po21
-rw-r--r--samples/binary/hello.pbcbin0 -> 912 bytes
-rw-r--r--samples/binary/linguist.gembin0 -> 3072 bytes
-rw-r--r--samples/binary/octocat.ai4424
-rw-r--r--samples/binary/octocat.pngbin0 -> 5570 bytes
-rw-r--r--samples/binary/octocat.psdbin0 -> 136522 bytes
-rw-r--r--samples/binary/zipbin0 -> 28 bytes
-rw-r--r--samples/c/blob.c25
-rw-r--r--samples/c/blob.h25
-rw-r--r--samples/c/cache.c99
-rw-r--r--samples/c/commit.c1228
-rw-r--r--samples/c/commit.h226
-rw-r--r--samples/c/cpu.c725
-rw-r--r--samples/c/diff.c784
-rw-r--r--samples/c/git.c609
-rw-r--r--samples/c/hash.c74
-rw-r--r--samples/c/hello.c7
-rw-r--r--samples/c/hello.h6
-rw-r--r--samples/c/http_parser.c2059
-rw-r--r--samples/c/http_parser.h318
-rw-r--r--samples/c/markdown.c2551
-rw-r--r--samples/c/process.c462
-rw-r--r--samples/c/rdiscount.c129
-rw-r--r--samples/c/redis.c2538
-rw-r--r--samples/c/yajl.c164
-rw-r--r--samples/coffee/browser.coffee55
-rw-r--r--samples/coffee/classes.coffee21
-rw-r--r--samples/coffee/coffee-script.coffee130
-rw-r--r--samples/coffee/empty.coffee0
-rw-r--r--samples/coffee/hello.coffee1
-rw-r--r--samples/coffee/intro.coffee28
-rw-r--r--samples/coffee/lexer.coffee709
-rw-r--r--samples/coffee/rack_application.coffee256
-rw-r--r--samples/coffee/xipd.coffee110
-rw-r--r--samples/coq/interval_discr.v419
-rw-r--r--samples/cpp/bar.h10
-rw-r--r--samples/cpp/bar.hpp10
-rw-r--r--samples/cpp/cuda.cu39
-rw-r--r--samples/cpp/env.cpp81
-rw-r--r--samples/cpp/env.h52
-rw-r--r--samples/cpp/hello.cpp8
-rw-r--r--samples/cpp/key.cpp382
-rw-r--r--samples/cpp/key.h162
-rw-r--r--samples/cpp/main.cpp74
-rw-r--r--samples/cpp/scanner.cc1088
-rw-r--r--samples/cpp/scanner.h576
-rw-r--r--samples/cpp/utils.h71
-rw-r--r--samples/cpp/v8.cc288
-rw-r--r--samples/cpp/v8.h152
-rw-r--r--samples/dart/point.dart15
-rw-r--r--samples/delphi/program.dpr14
-rw-r--r--samples/diff/dude-thing-okay--001.patch4
-rw-r--r--samples/emacs/dude.el1
-rw-r--r--samples/gas/hello.s63
-rw-r--r--samples/gosu/Hello.gst7
-rw-r--r--samples/gosu/Hello.gsx9
-rw-r--r--samples/gosu/Person.gs106
-rw-r--r--samples/gosu/hello.gsp1
-rw-r--r--samples/gosu/hello.vark3
-rw-r--r--samples/groovy/build.gradle17
-rwxr-xr-xsamples/groovy/script.groovy2
-rw-r--r--samples/gsp/bar.gsp10
-rw-r--r--samples/gsp/hello-pagedirective.gsp16
-rw-r--r--samples/gsp/hello-resources.gsp9
-rw-r--r--samples/gsp/hello-var.gsp9
-rw-r--r--samples/haml/hello.haml3
-rw-r--r--samples/ini/.gitconfig3
-rw-r--r--samples/ioke/hello.ik3
-rw-r--r--samples/java/HtmlDomParserContext.java243
-rw-r--r--samples/java/Hudson.java322
-rw-r--r--samples/java/NokogiriService.java598
-rw-r--r--samples/java/clojure-type.java872
-rw-r--r--samples/java/clojure-util.java197
-rw-r--r--samples/javascript/bootstrap-modal.js218
-rw-r--r--samples/javascript/classes-old.js46
-rw-r--r--samples/javascript/classes.js69
-rw-r--r--samples/javascript/dude.js1
-rw-r--r--samples/javascript/empty.js3
-rw-r--r--samples/javascript/hello.js3
-rw-r--r--samples/javascript/http.js1838
-rw-r--r--samples/javascript/intro-old.js37
-rw-r--r--samples/javascript/intro.js44
-rw-r--r--samples/javascript/jquery-1.4.2.min.js154
-rw-r--r--samples/javascript/jquery-1.6.1.js8936
-rw-r--r--samples/javascript/jquery-1.6.1.min.js18
-rw-r--r--samples/javascript/jquery-1.7.2.js9404
-rw-r--r--samples/javascript/json2_backbone.js1152
-rw-r--r--samples/javascript/modernizr.js1295
-rw-r--r--samples/javascript/parser.js2963
-rwxr-xr-xsamples/javascript/script.js2
-rw-r--r--samples/javascript/steelseries-min.js24
-rw-r--r--samples/javascript/uglify.js1320
-rw-r--r--samples/julia/stockcorr.jl37
-rw-r--r--samples/kotlin/Foo.kt47
-rw-r--r--samples/logtalk/foo.lgt9
-rw-r--r--samples/markdown/tender.md2
-rw-r--r--samples/matlab/average.m9
-rw-r--r--samples/matlab/make_filter.m38
-rw-r--r--samples/matlab/matlab_class.m29
-rw-r--r--samples/matlab/matlab_function.m9
-rw-r--r--samples/matlab/matlab_script.m12
-rw-r--r--samples/matlab/matlab_script2.m13
-rw-r--r--samples/nemerle/hello.n9
-rw-r--r--samples/nimrod/foo.nim3
-rwxr-xr-xsamples/nu/script.nu2
-rw-r--r--samples/objective-c/ASIHTTPRequest.h1004
-rw-r--r--samples/objective-c/ASIHTTPRequest.m5125
-rw-r--r--samples/objective-c/Foo.h8
-rw-r--r--samples/objective-c/Foo.m6
-rw-r--r--samples/objective-c/FooAppDelegate.h10
-rw-r--r--samples/objective-c/FooAppDelegate.m12
-rw-r--r--samples/objective-c/JSONKit.h251
-rw-r--r--samples/objective-c/JSONKit.m3059
-rw-r--r--samples/objective-c/MainMenuViewController.h21
-rw-r--r--samples/objective-c/MainMenuViewController.m157
-rw-r--r--samples/objective-c/PlaygroundViewController.h22
-rw-r--r--samples/objective-c/PlaygroundViewController.m200
-rw-r--r--samples/objective-c/SBJsonParser.h101
-rw-r--r--samples/objective-c/SBJsonParser.m100
-rw-r--r--samples/objective-c/StyleViewController.h26
-rw-r--r--samples/objective-c/StyleViewController.m185
-rw-r--r--samples/objective-c/TUITableView.h217
-rw-r--r--samples/objective-c/TUITableView.m1248
-rw-r--r--samples/objective-c/empty.m0
-rw-r--r--samples/objective-c/hello.m7
-rw-r--r--samples/ocaml/Foo.ml82
-rw-r--r--samples/opa/hello_syntax1.opa9
-rw-r--r--samples/opa/hello_syntax2.opa11
-rw-r--r--samples/opencl/fft.cl13
-rw-r--r--samples/openedge-abl/Email.cls574
-rw-r--r--samples/openedge-abl/SendEmailAlgorithm.cls23
-rw-r--r--samples/openedge-abl/SocketReader.p118
-rw-r--r--samples/openedge-abl/Util.cls57
-rw-r--r--samples/openedge-abl/openedge.p1
-rw-r--r--samples/pasm/hello.pasm5
-rw-r--r--samples/perl/Ack.pm1684
-rw-r--r--samples/perl/Request.pm695
-rw-r--r--samples/perl/Response.pm309
-rwxr-xr-xsamples/perl/ack2844
-rw-r--r--samples/perl/fib.pl42
-rw-r--r--samples/perl/oo1.pl27
-rw-r--r--samples/perl/oo2.pl27
-rw-r--r--samples/perl/oo3.pl29
-rw-r--r--samples/perl/perl-test.t10
-rwxr-xr-xsamples/perl/script.pl2
-rw-r--r--samples/perl/test-perl.pl2
-rw-r--r--samples/perl/test-perl2.pl3
-rw-r--r--samples/php/Application.php1066
-rw-r--r--samples/php/Client.php492
-rw-r--r--samples/php/Controller.php1230
-rw-r--r--samples/php/Form.php586
-rw-r--r--samples/php/Model.php3640
-rw-r--r--samples/php/drupal.module140
-rw-r--r--samples/pir/hello.pir5
-rw-r--r--samples/powershell/hello.ps12
-rw-r--r--samples/powershell/hello.psm15
-rw-r--r--samples/prolog/test-prolog.pl12
-rw-r--r--samples/python/django-models-base.py938
-rw-r--r--samples/python/flask-view.py150
-rwxr-xr-xsamples/python/script.py2
-rw-r--r--samples/python/tornado-httpserver.py486
-rw-r--r--samples/r/hello-r.R4
-rw-r--r--samples/racket/scribble.scrbl33
-rwxr-xr-xsamples/racket/script.rkt7
-rw-r--r--samples/rebol/hello-rebol.r5
-rw-r--r--samples/ruby/Capfile3
-rw-r--r--samples/ruby/Rakefile3
-rw-r--r--samples/ruby/foo.rb2
-rw-r--r--samples/ruby/formula.rb703
-rw-r--r--samples/ruby/grit.rb2
-rw-r--r--samples/ruby/inflector.rb323
-rw-r--r--samples/ruby/jekyll.rb138
-rwxr-xr-xsamples/ruby/macruby-script2
-rw-r--r--samples/ruby/resque.rb385
-rwxr-xr-xsamples/ruby/script.rake4
-rwxr-xr-xsamples/ruby/script.rb2
-rw-r--r--samples/ruby/script2.rb2
-rw-r--r--samples/ruby/sinatra.rb1775
-rw-r--r--samples/ruby/wrong_shebang.rb2
-rw-r--r--samples/rust/hello.rs3
-rw-r--r--samples/sass/screen.sass11
-rw-r--r--samples/scala/build.sbt169
-rwxr-xr-xsamples/scala/script.scala9
-rw-r--r--samples/scheme/asteroids.sps435
-rw-r--r--samples/scilab/scilab_function.sci14
-rw-r--r--samples/scilab/scilab_script.sce2
-rw-r--r--samples/scilab/scilab_test.tst3
-rw-r--r--samples/scss/screen.scss14
-rw-r--r--samples/shell/.bash_profile1
-rw-r--r--samples/shell/.bashrc1
-rw-r--r--samples/shell/.profile1
-rw-r--r--samples/shell/.zlogin1
-rw-r--r--samples/shell/.zshrc1
-rw-r--r--samples/shell/PKGBUILD43
-rwxr-xr-xsamples/shell/rbenv-sh-shell32
-rwxr-xr-xsamples/shell/rvm57
-rwxr-xr-xsamples/shell/script.bash2
-rwxr-xr-xsamples/shell/script.sh2
-rwxr-xr-xsamples/shell/script.zsh2
-rw-r--r--samples/standard-ml/Foo.sig27
-rw-r--r--samples/standard-ml/Foo.sml75
-rw-r--r--samples/supercollider/BCR2000.sc114
-rw-r--r--samples/tea/foo.tea1
-rw-r--r--samples/tex/reedthesis.cls245
-rw-r--r--samples/text/README2
-rw-r--r--samples/text/blank0
-rw-r--r--samples/text/defu.nkt1
-rw-r--r--samples/text/dump.sql1
-rw-r--r--samples/text/file.json1
-rw-r--r--samples/text/file.txt0
-rw-r--r--samples/text/foo.txt1
-rw-r--r--samples/text/md2
-rwxr-xr-xsamples/text/script.foo2
-rw-r--r--samples/text/txt1
-rw-r--r--samples/turing/turing.t19
-rw-r--r--samples/verilog/sha-256-functions.v86
-rw-r--r--samples/vhdl/foo.vhd14
-rw-r--r--samples/viml/.gvimrc2
-rw-r--r--samples/viml/.vimrc8
-rw-r--r--samples/visual-basic/cApplication.cls240
-rw-r--r--samples/xml/net_docfile.xml1121
-rw-r--r--samples/xquery/xproc.xqm70
-rw-r--r--samples/xslt/test.xslt25
-rw-r--r--samples/yaml/.gemrc5
241 files changed, 86568 insertions, 0 deletions
diff --git a/samples/apex/ArrayUtils.cls b/samples/apex/ArrayUtils.cls
new file mode 100644
index 0000000..b08cd51
--- /dev/null
+++ b/samples/apex/ArrayUtils.cls
@@ -0,0 +1,458 @@
+/* ============================================================
+ * This code is part of the "apex-lang" open source project avaiable at:
+ *
+ * http://code.google.com/p/apex-lang/
+ *
+ * This code is licensed under the Apache License, Version 2.0. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * ============================================================
+ */
+global class ArrayUtils {
+
+ global static String[] EMPTY_STRING_ARRAY = new String[]{};
+ global static Integer MAX_NUMBER_OF_ELEMENTS_IN_LIST {get{return 1000;}}
+
+ global static List<String> objectToString(List<Object> objects){
+ List<String> strings = null;
+ if(objects != null){
+ strings = new List<String>();
+ if(objects.size() > 0){
+ for(Object obj : objects){
+ if(obj instanceof String){
+ strings.add((String)obj);
+ }
+ }
+ }
+ }
+ return strings;
+ }
+
+ global static Object[] reverse(Object[] anArray) {
+ if (anArray == null) {
+ return null;
+ }
+ Integer i = 0;
+ Integer j = anArray.size() - 1;
+ Object tmp;
+ while (j > i) {
+ tmp = anArray[j];
+ anArray[j] = anArray[i];
+ anArray[i] = tmp;
+ j--;
+ i++;
+ }
+ return anArray;
+ }
+
+ global static SObject[] reverse(SObject[] anArray) {
+ if (anArray == null) {
+ return null;
+ }
+ Integer i = 0;
+ Integer j = anArray.size() - 1;
+ SObject tmp;
+ while (j > i) {
+ tmp = anArray[j];
+ anArray[j] = anArray[i];
+ anArray[i] = tmp;
+ j--;
+ i++;
+ }
+ return anArray;
+ }
+
+ global static List<String> lowerCase(List<String> strs){
+ List<String> returnValue = null;
+ if(strs != null){
+ returnValue = new List<String>();
+ if(strs.size() > 0){
+ for(String str : strs){
+ returnValue.add(str == null ? null : str.toLowerCase());
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ global static List<String> upperCase(List<String> strs){
+ List<String> returnValue = null;
+ if(strs != null){
+ returnValue = new List<String>();
+ if(strs.size() > 0){
+ for(String str : strs){
+ returnValue.add(str == null ? null : str.toUpperCase());
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ global static List<String> trim(List<String> strs){
+ List<String> returnValue = null;
+ if(strs != null){
+ returnValue = new List<String>();
+ if(strs.size() > 0){
+ for(String str : strs){
+ returnValue.add(str == null ? null : str.trim());
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ global static Object[] mergex(Object[] array1, Object[] array2){
+ if(array1 == null){ return array2; }
+ if(array2 == null){ return array1; }
+ Object[] merged = new Object[array1.size() + array2.size()];
+ for(Integer i = 0; i < array1.size(); i++){
+ merged[i] = array1[i];
+ }
+ for(Integer i = 0; i < array2.size(); i++){
+ merged[i+array1.size()] = array2[i];
+ }
+ return merged;
+ }
+
+ global static SObject[] mergex(SObject[] array1, SObject[] array2){
+ if(array1 == null){ return array2; }
+ if(array2 == null){ return array1; }
+ if(array1.size() <= 0){ return array2; }
+ List<SObject> merged = new List<SObject>();
+ for(SObject sObj : array1){ merged.add(sObj); }
+ for(SObject sObj : array2){ merged.add(sObj); }
+ return merged;
+ }
+
+ global static Boolean isEmpty(Object[] objectArray){
+ if(objectArray == null){
+ return true;
+ }
+ return objectArray.size() == 0;
+ }
+
+ global static Boolean isEmpty(SObject[] objectArray){
+ if(objectArray == null){
+ return true;
+ }
+ return objectArray.size() == 0;
+ }
+
+ global static Boolean isNotEmpty(Object[] objectArray){
+ return !isEmpty(objectArray);
+ }
+
+ global static Boolean isNotEmpty(SObject[] objectArray){
+ return !isEmpty(objectArray);
+ }
+
+ global static Object[] pluck(SObject[] objectArray, String fieldName){
+ if(isEmpty(objectArray) || fieldName == null || fieldName.trim() == null || fieldName.trim().length() == 0){
+ return new Object[]{};
+ }
+ Object[] plucked = new Object[objectArray.size()];
+ for(Integer i = 0; i < objectArray.size(); i++){
+ plucked[i] = objectArray[i].get(fieldName);
+ }
+ return plucked;
+ }
+
+
+ global static String toString(Object[] objectArray){
+ if(objectArray == null){
+ return 'null';
+ }
+ String returnValue = '{';
+ for(Integer i = 0; i < objectArray.size(); i++){
+ if(i!=0){ returnValue += ','; }
+ returnValue += '\'' + objectArray[i] + '\'';
+ }
+ returnValue += '}';
+ return returnValue;
+ }
+
+ global static String toString(SObject[] objectArray){
+ if(objectArray == null){
+ return 'null';
+ }
+ String returnValue = '{';
+ for(Integer i = 0; i < objectArray.size(); i++){
+ if(i!=0){ returnValue += ','; }
+ returnValue += '\'' + objectArray[i] + '\'';
+ }
+ returnValue += '}';
+ return returnValue;
+ }
+
+ global static void assertArraysAreEqual(Object[] expected, Object[] actual){
+ //check to see if one param is null but the other is not
+ System.assert((expected == null && actual == null)|| (expected != null && actual != null),
+ 'Assertion failed, the following two arrays are not equal. Expected: '
+ + ArrayUtils.toString(expected) + ', Actual: ' + ArrayUtils.toString(actual));
+ if(expected != null && actual != null){
+ System.assert(expected.size() == actual.size(), 'Assertion failed, the following two arrays are not equal. Expected: '
+ + ArrayUtils.toString(expected) + ', Actual: ' + ArrayUtils.toString(actual));
+ for(Integer i = 0; i < expected.size(); i++){
+ System.assert(expected[i] == actual[i], 'Assertion failed, the following two arrays are not equal. Expected: '
+ + ArrayUtils.toString(expected) + ', Actual: ' + ArrayUtils.toString(actual));
+ }
+ }
+ }
+
+ global static void assertArraysAreEqual(SObject[] expected, SObject[] actual){
+ //check to see if one param is null but the other is not
+ System.assert((expected == null && actual == null)|| (expected != null && actual != null),
+ 'Assertion failed, the following two arrays are not equal. Expected: '
+ + ArrayUtils.toString(expected) + ', Actual: ' + ArrayUtils.toString(actual));
+ if(expected != null && actual != null){
+ System.assert(expected.size() == actual.size(), 'Assertion failed, the following two arrays are not equal. Expected: '
+ + ArrayUtils.toString(expected) + ', Actual: ' + ArrayUtils.toString(actual));
+ for(Integer i = 0; i < expected.size(); i++){
+ System.assert(expected[i] == actual[i], 'Assertion failed, the following two arrays are not equal. Expected: '
+ + ArrayUtils.toString(expected) + ', Actual: ' + ArrayUtils.toString(actual));
+ }
+ }
+ }
+
+ global static List<Object> merg(List<Object> list1, List<Object> list2) {
+ List<Object> returnList = new List<Object>();
+ if(list1 != null && list2 != null && (list1.size()+list2.size()) > MAX_NUMBER_OF_ELEMENTS_IN_LIST){
+ throw new IllegalArgumentException('Lists cannot be merged because new list would be greater than maximum number of elements in a list: ' + MAX_NUMBER_OF_ELEMENTS_IN_LIST);
+ }
+ if(isNotEmpty(list1)){
+ for(Object elmt : list1){
+ returnList.add(elmt);
+ }
+ }
+ if(isNotEmpty(list2)){
+ for(Object elmt : list2){
+ returnList.add(elmt);
+ }
+ }
+ return returnList;
+ }
+
+
+ global static List<SObject> merg(List<SObject> list1, List<SObject> list2) {
+ if(list1 != null && list2 != null && (list1.size()+list2.size()) > MAX_NUMBER_OF_ELEMENTS_IN_LIST){
+ throw new IllegalArgumentException('Lists cannot be merged because new list would be greater than maximum number of elements in a list: ' + MAX_NUMBER_OF_ELEMENTS_IN_LIST);
+ }
+ if(isEmpty(list1) && isEmpty(list2)){
+ return null;
+ }
+ List<SObject> returnList = new List<SObject>();
+ if(list1 != null){
+ for(SObject elmt : list1){
+ returnList.add(elmt);
+ }
+ }
+ if(list2 != null){
+ for(SObject elmt : list2){
+ returnList.add(elmt);
+ }
+ }
+ return returnList;
+ }
+
+ global static List<Object> subset(List<Object> aList, Integer count) {
+ return subset(aList,0,count);
+ }
+
+ global static List<Object> subset(List<Object> list1, Integer startIndex, Integer count) {
+ List<Object> returnList = new List<Object>();
+ if(list1 != null && list1.size() > 0 && startIndex >= 0 && startIndex <= list1.size()-1 && count > 0){
+ for(Integer i = startIndex; i < list1.size() && i - startIndex < count; i++){
+ returnList.add(list1.get(i));
+ }
+ }
+ return returnList;
+ }
+
+
+ global static List<SObject> subset(List<SObject> aList, Integer count) {
+ return subset(aList,0,count);
+ }
+
+ global static List<SObject> subset(List<SObject> list1, Integer startIndex, Integer count) {
+ List<SObject> returnList = null;
+ if(list1 != null && list1.size() > 0 && startIndex <= list1.size()-1 && count > 0){
+ returnList = new List<SObject>();
+ for(Integer i = startIndex; i < list1.size() && i - startIndex < count; i++){
+ returnList.add(list1.get(i));
+ }
+ }
+ return returnList;
+ }
+
+ //===============================================
+ //LIST/ARRAY SORTING
+ //===============================================
+
+ //FOR FORCE.COM PRIMITIVES (Double,Integer,ID,etc.):
+ global static List<Object> qsort(List<Object> theList) {
+ return qsort(theList,new PrimitiveComparator());
+ }
+
+ global static List<Object> qsort(List<Object> theList, Boolean sortAsc) {
+ return qsort(theList,new PrimitiveComparator(),sortAsc);
+ }
+
+ global static List<Object> qsort(List<Object> theList, ObjectComparator comparator) {
+ return qsort(theList,comparator,true);
+ }
+
+ global static List<Object> qsort(List<Object> theList, ObjectComparator comparator, Boolean sortAsc) {
+ return qsort(theList, 0, (theList == null ? 0 : theList.size()-1),comparator,sortAsc);
+ }
+
+
+
+ //FOR SALESFORCE OBJECTS (sObjects):
+ global static List<SObject> qsort(List<SObject> theList, ISObjectComparator comparator) {
+ return qsort(theList,comparator,true);
+ }
+
+ global static List<SObject> qsort(List<SObject> theList, ISObjectComparator comparator,Boolean sortAsc ) {
+ return qsort(theList, 0, (theList == null ? 0 : theList.size()-1),comparator,sortAsc);
+ }
+
+ private static List<Object> qsort(List<Object> theList,
+ Integer lo0,
+ Integer hi0,
+ ObjectComparator comparator,
+ Boolean sortAsc){
+ Integer lo = lo0;
+ Integer hi = hi0;
+
+ if (lo >= hi) {
+ return theList;
+ } else if( lo == hi - 1 ) {
+
+ if (( comparator.compare(theList[lo],theList[hi])>0 && sortAsc) ||
+ (comparator.compare(theList[lo],theList[hi])<0 && !sortAsc)
+ ) {
+ Object prs = theList[lo];
+ theList[lo] = theList[hi];
+ theList[hi] = prs;
+ }
+ return theList;
+ }
+
+ Object pivot = theList[(lo + hi) / 2];
+ theList[(lo + hi) / 2] = theList[hi];
+ theList[hi] = pivot;
+
+ while( lo < hi ) {
+ while ((comparator.compare(theList[lo], pivot)<=0 && lo < hi && sortAsc) ||
+ (comparator.compare(theList[lo], pivot)>=0 && lo < hi && !sortAsc)
+ ) { lo++; }
+ while (( comparator.compare(pivot,theList[hi])<=0 && lo < hi && sortAsc) ||
+ ( comparator.compare(pivot,theList[hi])>=0 && lo < hi && !sortAsc)
+ ) { hi--; }
+
+ if( lo < hi ){
+ Object prs = theList[lo];
+ theList[lo] = theList[hi];
+ theList[hi] = prs;
+ }
+ }
+
+ theList[hi0] = theList[hi];
+ theList[hi] = pivot;
+
+ qsort(theList, lo0, lo-1,comparator,sortAsc);
+ qsort(theList, hi+1, hi0,comparator,sortAsc);
+ return theList;
+ }
+
+
+ private static List<SObject> qsort(List<SObject> theList,
+ Integer lo0,
+ Integer hi0,
+ ISObjectComparator comparator,
+ Boolean sortAsc){
+ Integer lo = lo0;
+ Integer hi = hi0;
+
+ if (lo >= hi) {
+ return theList;
+ } else if( lo == hi - 1 ) {
+
+ if (( comparator.compare(theList[lo],theList[hi])>0 && sortAsc) ||
+ (comparator.compare(theList[lo],theList[hi])<0 && !sortAsc)
+ ) {
+ SObject prs = theList[lo];
+ theList[lo] = theList[hi];
+ theList[hi] = prs;
+ }
+ return theList;
+ }
+
+ SObject pivot = theList[(lo + hi) / 2];
+ theList[(lo + hi) / 2] = theList[hi];
+ theList[hi] = pivot;
+
+ while( lo < hi ) {
+ while ((comparator.compare(theList[lo], pivot)<=0 && lo < hi && sortAsc) ||
+ (comparator.compare(theList[lo], pivot)>=0 && lo < hi && !sortAsc)
+ ) { lo++; }
+ while (( comparator.compare(pivot,theList[hi])<=0 && lo < hi && sortAsc) ||
+ ( comparator.compare(pivot,theList[hi])>=0 && lo < hi && !sortAsc)
+ ) { hi--; }
+
+ if( lo < hi ){
+ SObject prs = theList[lo];
+ theList[lo] = theList[hi];
+ theList[hi] = prs;
+ }
+ }
+
+ theList[hi0] = theList[hi];
+ theList[hi] = pivot;
+
+ qsort(theList, lo0, lo-1,comparator,sortAsc);
+ qsort(theList, hi+1, hi0,comparator,sortAsc);
+ return theList;
+ }
+/*
+ global static List<Object> unique(List<Object> theList) {
+ List<Object> uniques = new List<Object>();
+ Set<Object> keys = new Set<Object>();
+ if(theList != null && theList.size() > 0){
+ for(Object obj : theList){
+ if(keys.contains(obj)){
+ continue;
+ } else {
+ keys.add(obj);
+ uniques.add(obj);
+ }
+ }
+ }
+ return uniques;
+ }
+
+ global static List<SObject> unique(List<SObject> theList) {
+ if(theList == null){
+ return null;
+ }
+ List<SObject> uniques = createEmptySObjectList(theList.get(0));
+ Set<String> keys = new Set<String>();
+ if(theList != null && theList.size() > 0){
+ String key = null;
+ for(SObject obj : theList){
+ key = obj == null ? null : ''+obj;
+ if(keys.contains(key)){
+ continue;
+ } else {
+ keys.add(key);
+ uniques.add(obj);
+ }
+ }
+ }
+ return uniques;
+ }
+*/
+
+
+} \ No newline at end of file
diff --git a/samples/apex/BooleanUtils.cls b/samples/apex/BooleanUtils.cls
new file mode 100644
index 0000000..80a57f9
--- /dev/null
+++ b/samples/apex/BooleanUtils.cls
@@ -0,0 +1,185 @@
+/* ============================================================
+ * This code is part of the "apex-lang" open source project avaiable at:
+ *
+ * http://code.google.com/p/apex-lang/
+ *
+ * This code is licensed under the Apache License, Version 2.0. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * ============================================================
+ */
+global class BooleanUtils {
+
+ global static Boolean isFalse(Boolean bool)
+ {
+ if(bool==null)
+ return false;
+ else
+ return !bool;
+ }
+
+ global static Boolean isNotFalse(Boolean bool)
+ {
+ if(bool==null)
+ return true;
+ else
+ return bool;
+ }
+
+ global static Boolean isNotTrue(Boolean bool)
+ {
+ if(bool==null)
+ return true;
+ else
+ return !bool;
+ }
+
+ global static Boolean isTrue(Boolean bool)
+ {
+ if(bool==null)
+ return false;
+ else
+ return bool;
+ }
+
+ global static Boolean negate(Boolean bool)
+ {
+ if(bool==null)
+ return null;
+ else
+ return !bool;
+ }
+
+ global static Boolean toBooleanDefaultIfNull(Boolean bool, Boolean defaultVal)
+ {
+ if(bool==null)
+ return defaultVal;
+ else
+ return bool;
+ }
+
+ global static Boolean toBoolean(Integer value)
+ {
+ if(value==null)
+ return false;
+ else
+ {
+ if(value==0)
+ return false;
+ else
+ return true;
+ }
+ }
+
+ global static Boolean strToBoolean(String value)
+ {
+ if(value==null)
+ return false;
+ else
+ {
+ if(StringUtils.equalsIgnoreCase(value,'true'))
+ return true;
+ else
+ return false;
+ }
+ }
+
+ /************************************/
+ //Converts an int to a boolean specifying
+ //the conversion values.
+ // Parameters:
+ // value - the Integer to convert, may be null
+ // trueValue - the value to match for true, may be null
+ // falseValue - the value to match for false, may be null
+ //Returns:
+ // true or false
+ //Throws:
+ // java.lang.IllegalArgumentException - if no match
+ /************************************/
+ global static Boolean toBoolean(Integer value,
+ Integer trueValue,
+ Integer falseValue)
+ {
+ if(value==trueValue)
+ return true;
+ else if(value==falseValue)
+ return false;
+ else
+ throw new IllegalArgumentException();
+ }
+
+
+ global static Integer toInteger(Boolean bool)
+ {
+ if(bool==null)
+ throw new IllegalArgumentException();
+ else
+ {
+ if(bool)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+
+ global static String toStringYesNo(Boolean bool)
+ {
+ if(bool==null)
+ return null;
+ else
+ {
+ if(bool)
+ return 'yes';
+ else
+ return 'no';
+ }
+ }
+
+ global static String toStringYN(Boolean bool)
+ {
+ if(bool==null)
+ return null;
+ else
+ {
+ if(bool)
+ return 'Y';
+ else
+ return 'N';
+ }
+ }
+
+
+ global static String toString(Boolean bool,
+ String trueString,
+ String falseString)
+ {
+ if(bool==null)
+ return null;
+ else
+ {
+ if(bool)
+ return trueString;
+ else
+ return falseString;
+ }
+ }
+
+ global static Boolean xor(Boolean[] boolArray)
+ {
+ if(boolArray==null || boolArray.size()==0)
+ throw new IllegalArgumentException();
+ else
+ {
+ Boolean firstItem=boolArray[0];
+ for(Boolean bool:boolArray)
+ {
+ if(bool!=firstItem)
+ return false;
+ }
+ return true;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/samples/apex/EmailUtils.cls b/samples/apex/EmailUtils.cls
new file mode 100644
index 0000000..320529b
--- /dev/null
+++ b/samples/apex/EmailUtils.cls
@@ -0,0 +1,98 @@
+/* ============================================================
+ * Contributor: Caleb Sidel
+ *
+ * This code is part of the "apex-lang" open source project avaiable at:
+ *
+ * http://code.google.com/p/apex-lang/
+ *
+ * This code is licensed under the Apache License, Version 2.0. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * ============================================================
+ */
+global class EmailUtils {
+
+ global static void sendEmailWithStandardAttachments(List<String> recipients,String emailSubject,String body,Boolean useHTML,List<Id> attachmentIDs) {
+ List<Attachment> stdAttachments = [SELECT id, name, body FROM Attachment WHERE Id IN:attachmentIDs];
+ sendEmailWithStandardAttachments(recipients, emailSubject, body, useHTML, stdAttachments);
+ }
+
+ global static void sendEmailWithStandardAttachments(List<String> recipients,String emailSubject,String body,Boolean useHTML,List<Attachment> stdAttachments) {
+ List<Messaging.EmailFileAttachment> fileAttachments = new List<Messaging.EmailFileAttachment>();
+
+ for(Attachment attachment : stdAttachments) {
+ Messaging.EmailFileAttachment fileAttachment = new Messaging.EmailFileAttachment();
+ fileAttachment.setFileName(attachment.Name);
+ fileAttachment.setBody(attachment.Body);
+ fileAttachments.add(fileAttachment);
+ }
+ sendEmail(recipients, emailSubject, body, useHTML, fileAttachments);
+ }
+
+ global static void sendTextEmail(List<String> recipients,String emailSubject,String textBody) {
+ sendEmail(recipients, emailSubject, textBody, false, null);
+ }
+
+ global static void sendHTMLEmail(List<String> recipients,String emailSubject,String htmlBody) {
+ sendEmail(recipients, emailSubject, htmlBody, true, null);
+ }
+
+ global static void sendEmail(List<String> recipients,String emailSubject,String body,Boolean useHTML,List<Messaging.EmailFileAttachment> fileAttachments) {
+ if(recipients == null) return;
+ if(recipients.size() == 0) return;
+ // Create a new single email message object
+ // that will send out a single email to the addresses in the To, CC & BCC list.
+ Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
+ //the email is not saved as an activity.
+ mail.setSaveAsActivity(false);
+ // Assign the addresses for the To lists to the mail object.
+ mail.setToAddresses(recipients);
+ // Specify the subject line for your email address.
+ mail.setSubject(emailSubject);
+ // Set to True if you want to BCC yourself on the email.
+ mail.setBccSender(false);
+ // The email address of the user executing the Apex Code will be used.
+ mail.setUseSignature(false);
+ if (useHTML) {
+ // Specify the html content of the email.
+ mail.setHtmlBody(body);
+ } else {
+ // Specify the text content of the email.
+ mail.setPlainTextBody(body);
+ }
+ // Specify FileAttachments
+ if(fileAttachments != null && fileAttachments.size() > 0) {
+ mail.setFileAttachments(fileAttachments);
+ }
+ // Send the email you have created.
+ Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
+ }
+
+ /**
+ * null => false
+ * '' => false
+ * ' ' => false
+ * 'x' => false
+ * 'x@' => false
+ * 'x@x' => false
+ * 'x@x.x' => true
+ */
+ global static Boolean isValidEmailAddress(String str){
+ if(str != null && str.trim() != null && str.trim().length() > 0){
+ String[] split = str.split('@');
+ if(split != null && split.size() == 2){
+ split = split[1].split('\\.');
+ if(split != null && split.size() >= 2){
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ global static Boolean isNotValidEmailAddress(String str){
+ return !isValidEmailAddress(str);
+ }
+
+} \ No newline at end of file
diff --git a/samples/apex/GeoUtils.cls b/samples/apex/GeoUtils.cls
new file mode 100644
index 0000000..26faf45
--- /dev/null
+++ b/samples/apex/GeoUtils.cls
@@ -0,0 +1,55 @@
+public class GeoUtils {
+ // generate a KML string given a page reference, call getContent()
+ // then cleanup the output.
+ public static string generateFromContent(PageReference pr) {
+ string ret = '';
+ try {
+ ret = (string) pr.getContent().toString();
+
+ ret = ret.replaceAll('"','\'' ); // get content produces quote chars \"
+ ret = ret.replaceAll( '&','&amp;');// we need to escape these in the node value
+ } catch (exception e ) {
+ system.debug( 'ERROR '+e);
+ }
+
+ ret = ret.replaceAll('\n',' '); // must use ALL since many new line may get
+ ret = ret.replaceAll('\r',' '); // get these also!
+ // system.debug( ret); // dump the KML
+ return ret ;
+ }
+
+ public static Map<String, String> geo_response = new Map<String, String>{'200'=>'G_GEO_SUCCESS',
+ '400'=>'G_GEO_BAD_REQUEST',
+ '500'=>'G_GEO_SERVER_ERROR',
+ '601'=>'G_GEO_MISSING_ADDRESS',
+ '602'=>'G_GEO_UNKNOWN_ADDRESS',
+ '603'=>'G_GEO_UNAVAILABLE_ADDRESS',
+ '604'=>'G_GEO_UNKNOWN_DIRECTIONS',
+ '610'=>'G_GEO_BAD_KEY',
+ '620'=>'G_GEO_TOO_MANY_QUERIES'
+ };
+
+ public static string accountAddressString ( account acct ) {
+ // form an address string given an account object
+ string adr = acct.billingstreet + ',' + acct.billingcity + ',' + acct.billingstate;
+ if ( acct.billingpostalcode != null ) adr += ',' + acct.billingpostalcode;
+ if ( acct.billingcountry != null ) adr += ',' + acct.billingcountry;
+ adr = adr.replaceAll('\"', '' );
+ adr = adr.replaceAll('\'', '' );
+ adr = adr.replaceAll( '\n', ' ' );
+ adr = adr.replaceAll( '\r', ' ' );
+ system.debug( adr );
+ return adr;
+ }
+
+ public static testmethod void t1() {
+ PageReference pageRef = Page.kmlPreviewTemplate;
+ Test.setCurrentPage(pageRef);
+ system.assert ( GeoUtils.generateFromContent( pageRef ) != null );
+ Account a = new Account( name='foo', billingstreet='main', billingcity='springfield',billingstate='il',
+ billingpostalcode='9',billingcountry='us');
+ insert a;
+ system.assertEquals( 'main,springfield,il,9,us',accountAddressString( a) );
+
+ }
+} \ No newline at end of file
diff --git a/samples/apex/LanguageUtils.cls b/samples/apex/LanguageUtils.cls
new file mode 100644
index 0000000..faefba6
--- /dev/null
+++ b/samples/apex/LanguageUtils.cls
@@ -0,0 +1,740 @@
+/* ============================================================
+ * This code is part of the "apex-lang" open source project avaiable at:
+ *
+ * http://code.google.com/p/apex-lang/
+ *
+ * This code is licensed under the Apache License, Version 2.0. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * ============================================================
+ */
+global class LanguageUtils {
+
+ global static final String HTTP_LANGUAGE_CODE_PARAMETER_KEY = 'l';
+ global static final String DEFAULT_LANGUAGE_CODE = 'en_us';
+
+ global static Set<String> SUPPORTED_LANGUAGE_CODES = new Set<String>{
+ 'zh-cn' //Chinese (Simplified)
+ ,'zh-tw' //Chinese (Traditional)
+ ,'nl-nl' //Dutch
+ ,'en-us' //English
+ ,'fi' //Finnish
+ ,'fr' //French
+ ,'de' //German
+ ,'it' //Italian
+ ,'ja' //Japanese
+ ,'ko' //Korean
+ ,'pl' //Polish
+ ,'pt-br' //Portuguese (Brazilian)
+ ,'ru' //Russian
+ ,'es' //Spanish
+ ,'sv' //Swedish
+ ,'th' //Thai
+ ,'cs' //Czech
+ ,'da' //Danish
+ ,'hu' //Hungarian
+ ,'in' //Indonesian
+ ,'tr' //Turkish
+ };
+
+ private static Map<String,String> DEFAULTS = new Map<String,String>{
+ 'en'=>'en-us'
+ ,'zh'=>'zh-cn'
+ ,'nl'=>'nl-nl'
+ ,'pt'=>'pt-br'
+ };
+
+
+ global static String getLangCodeByHttpParam(){
+ String returnValue = null;
+ final Set<String> LANGUAGE_CODE_SET = getSuppLangCodeSet();
+ if(ApexPages.currentPage() != null && ApexPages.currentPage().getParameters() != null){
+ String LANGUAGE_HTTP_PARAMETER =
+ StringUtils.lowerCase(
+ StringUtils.replaceChars(
+ ApexPages.currentPage().getParameters().get(HTTP_LANGUAGE_CODE_PARAMETER_KEY)
+ , '_' //underscore
+ , '-' //dash
+ )
+ );
+ if(DEFAULTS.containsKey(LANGUAGE_HTTP_PARAMETER)){
+ LANGUAGE_HTTP_PARAMETER = DEFAULTS.get(LANGUAGE_HTTP_PARAMETER);
+ }
+ if(StringUtils.isNotBlank(LANGUAGE_HTTP_PARAMETER)
+ && SUPPORTED_LANGUAGE_CODES.contains(LANGUAGE_HTTP_PARAMETER)){
+ returnValue = LANGUAGE_HTTP_PARAMETER;
+ }
+ }
+ return returnValue;
+ }
+
+ global static String getLangCodeByBrowser(){
+ final String LANGUAGES_FROM_BROWSER_AS_STRING = ApexPages.currentPage().getHeaders().get('Accept-Language');
+ final List<String> LANGUAGES_FROM_BROWSER_AS_LIST = splitAndFilterAcceptLanguageHeader(LANGUAGES_FROM_BROWSER_AS_STRING);
+ if(LANGUAGES_FROM_BROWSER_AS_LIST != null && LANGUAGES_FROM_BROWSER_AS_LIST.size() > 0){
+ for(String languageFromBrowser : LANGUAGES_FROM_BROWSER_AS_LIST){
+ if(DEFAULTS.containsKey(languageFromBrowser)){
+ languageFromBrowser = DEFAULTS.get(languageFromBrowser);
+ }
+ if(SUPPORTED_LANGUAGE_CODES.contains(languageFromBrowser)){
+ return languageFromBrowser;
+ }
+ }
+ }
+ return null;
+ }
+
+ global static String getLangCodeByUser(){
+ return UserInfo.getLanguage();
+ }
+
+ global static String getLangCodeByHttpParamOrIfNullThenBrowser(){
+ return StringUtils.defaultString(getLangCodeByHttpParam(),getLangCodeByBrowser());
+ }
+
+ global static String getLangCodeByHttpParamOrIfNullThenUser(){
+ return StringUtils.defaultString(getLangCodeByHttpParam(),getLangCodeByUser());
+ }
+
+ global static String getLangCodeByBrowserOrIfNullThenHttpParam(){
+ return StringUtils.defaultString(getLangCodeByBrowser(),getLangCodeByHttpParam());
+ }
+
+ global static String getLangCodeByBrowserOrIfNullThenUser(){
+ return StringUtils.defaultString(getLangCodeByBrowser(),getLangCodeByUser());
+ }
+
+ private static List<String> splitAndFilterAcceptLanguageHeader(String header){
+ List<String> returnList = new List<String>();
+ String[] tokens = StringUtils.split(header,',');
+ if(tokens != null){
+ for(String token : tokens){
+ if(token != null ){
+ if(token.contains(';')){
+ token = token.substring(0,token.indexOf(';',0));
+ }
+ returnList.add(token);
+ if(StringUtils.length(token) > 2){
+ returnList.add(StringUtils.substring(token,0,2));
+ }
+ }
+ }
+ }
+ return returnList;
+ }
+
+ private static Set<String> getSuppLangCodeSet(){
+ Set<String> langCodes = new Set<String>();
+ for(String langCode : SUPPORTED_LANGUAGE_CODES){
+ if(langCode != null){
+ langCodes.add(StringUtils.lowerCase(langCode));
+ }
+ }
+ return langCodes;
+ }
+
+
+ global static String getLanguageName(String displayLanguageCode, String languageCode){
+ return translatedLanguageNames.get(filterLanguageCode(displayLanguageCode)).get(filterLanguageCode(languageCode));
+ }
+
+ global static Map<String,String> getAllLanguages(){
+ return getAllLanguages(DEFAULT_LANGUAGE_CODE);
+ }
+
+ global static Map<String,String> getAllLanguages(String displayLanguageCode){
+ return translatedLanguageNames.get(filterLanguageCode(displayLanguageCode));
+ }
+
+ private static String filterLanguageCode(String displayLanguageCode){
+ displayLanguageCode = StringUtils.lowerCase(displayLanguageCode);
+ if(DEFAULTS.containsKey(displayLanguageCode)){
+ displayLanguageCode = StringUtils.replaceChars(DEFAULTS.get(displayLanguageCode),'-','_');
+ }
+ if(!translatedLanguageNames.containsKey(displayLanguageCode)){
+ displayLanguageCode = DEFAULT_LANGUAGE_CODE;
+ }
+ return displayLanguageCode;
+ }
+
+ private static final Map<String,Map<String,String>> translatedLanguageNames = new Map<String,Map<String,String>>{
+ 'cs'=> new Map<String,String>{
+'cs'=>'Čeština'
+,'da'=>'Dánština'
+,'de'=>'NÄ›mÄina'
+,'en_us'=>'AngliÄtina (Spojené státy)'
+,'es'=>'Španělština'
+,'es_mx'=>'Mexická španělština'
+,'fi'=>'Finština'
+,'fr'=>'Francouzština'
+,'hu'=>'MaÄarÅ¡tina'
+,'in'=>'Indonéština'
+,'it'=>'Italština'
+,'ja'=>'Japonština'
+,'ko'=>'Korejština'
+,'nl_nl'=>'Nizozemština'
+,'pl'=>'Polština'
+,'pt_br'=>'Portugalština (Brazílie)'
+,'ro'=>'Rumunština'
+,'ru'=>'Ruština'
+,'sv'=>'Švédština'
+,'th'=>'Thajská'
+,'tr'=>'TureÄtina'
+,'zh_cn'=>'Čínština (zjednodušená)'
+,'zh_tw'=>'ČínÅ¡tina (tradiÄní)'
+}
+,'da'=> new Map<String,String>{
+'cs'=>'Tjekkisk'
+,'da'=>'Dansk'
+,'de'=>'Tysk'
+,'en_us'=>'Engelsk (USA)'
+,'es'=>'Spansk'
+,'es_mx'=>'Mexicansk spansk'
+,'fi'=>'Finsk'
+,'fr'=>'Fransk'
+,'hu'=>'Ungarsk'
+,'in'=>'Indonesisk'
+,'it'=>'Italiensk'
+,'ja'=>'Japansk'
+,'ko'=>'Koreansk'
+,'nl_nl'=>'Hollandsk'
+,'pl'=>'Polsk'
+,'pt_br'=>'Portugisisk (Brasilien)'
+,'ro'=>'Rumænsk'
+,'ru'=>'Russisk'
+,'sv'=>'Svensk'
+,'th'=>'Thai'
+,'tr'=>'Tyrkisk'
+,'zh_cn'=>'Kinesisk (forenklet)'
+,'zh_tw'=>'Kinesisk (traditionelt)'
+}
+,'de'=> new Map<String,String>{
+'cs'=>'Tschechisch'
+,'da'=>'Dänisch'
+,'de'=>'Deutsch'
+,'en_us'=>'Englisch (Vereinigte Staaten)'
+,'es'=>'Spanisch'
+,'es_mx'=>'Mexican Spanish'
+,'fi'=>'Finnisch'
+,'fr'=>'Französisch'
+,'hu'=>'Ungarisch'
+,'in'=>'Indonesisch'
+,'it'=>'Italienisch'
+,'ja'=>'Japanisch'
+,'ko'=>'Koreanisch'
+,'nl_nl'=>'Niederländisch'
+,'pl'=>'Polnisch'
+,'pt_br'=>'Portugiesisch (Brasilien)'
+,'ro'=>'Rumänisch'
+,'ru'=>'Russisch'
+,'sv'=>'Schwedisch'
+,'th'=>'Thai'
+,'tr'=>'Türkisch'
+,'zh_cn'=>'Chinesisch (Taiwan)'
+,'zh_tw'=>'Chinesisch (traditionell)'
+}
+,'en_us'=> new Map<String,String>{
+'cs'=>'Czech'
+,'da'=>'Danish'
+,'de'=>'German'
+,'en_us'=>'English (United States)'
+,'es'=>'Spanish'
+,'es_mx'=>'Mexican Spanish'
+,'fi'=>'Finnish'
+,'fr'=>'French'
+,'hu'=>'Hungarian'
+,'in'=>'Indonesian'
+,'it'=>'Italian'
+,'ja'=>'Japanese'
+,'ko'=>'Korean'
+,'nl_nl'=>'Dutch'
+,'pl'=>'Polish'
+,'pt_br'=>'Portuguese (Brazilian)'
+,'ro'=>'Romanian'
+,'ru'=>'Russian'
+,'sv'=>'Swedish'
+,'th'=>'Thai'
+,'tr'=>'Turkish'
+,'zh_cn'=>'Chinese (Simplified)'
+,'zh_tw'=>'Chinese (Traditional)'
+}
+,'es'=> new Map<String,String>{
+'cs'=>'Checa'
+,'da'=>'Danés'
+,'de'=>'Alemán'
+,'en_us'=>'Inglés (Estados Unidos)'
+,'es'=>'Español'
+,'es_mx'=>'El español de México'
+,'fi'=>'Finlandés'
+,'fr'=>'Francés'
+,'hu'=>'Húngaro'
+,'in'=>'Indonesia'
+,'it'=>'Italiano'
+,'ja'=>'Japonés'
+,'ko'=>'Corea'
+,'nl_nl'=>'Neerlandés'
+,'pl'=>'Polaco'
+,'pt_br'=>'Portugués (brasileño)'
+,'ro'=>'Rumano'
+,'ru'=>'Rusia'
+,'sv'=>'Sueco'
+,'th'=>'Tailandia'
+,'tr'=>'Turquía'
+,'zh_cn'=>'Chino (simplificado)'
+,'zh_tw'=>'Chino (tradicional)'
+}
+,'es_mx'=> new Map<String,String>{
+'cs'=>'Checa'
+,'da'=>'Danés'
+,'de'=>'Alemán'
+,'en_us'=>'Inglés (Estados Unidos)'
+,'es'=>'Español'
+,'es_mx'=>'El español de México'
+,'fi'=>'Finlandés'
+,'fr'=>'Francés'
+,'hu'=>'Húngaro'
+,'in'=>'Indonesia'
+,'it'=>'Italiano'
+,'ja'=>'Japonés'
+,'ko'=>'Corea'
+,'nl_nl'=>'Neerlandés'
+,'pl'=>'Polaco'
+,'pt_br'=>'Portugués (brasileño)'
+,'ro'=>'Rumano'
+,'ru'=>'Rusia'
+,'sv'=>'Sueco'
+,'th'=>'Tailandia'
+,'tr'=>'Turquía'
+,'zh_cn'=>'Chino (simplificado)'
+,'zh_tw'=>'Chino (tradicional)'
+}
+,'fi'=> new Map<String,String>{
+'cs'=>'Tšekki'
+,'da'=>'Tanska'
+,'de'=>'Saksa'
+,'en_us'=>'Englanti (Yhdysvallat)'
+,'es'=>'Espanja'
+,'es_mx'=>'Meksikon espanja'
+,'fi'=>'Suomen'
+,'fr'=>'Ranska'
+,'hu'=>'Unkari'
+,'in'=>'Indonesia'
+,'it'=>'Italia'
+,'ja'=>'Japani'
+,'ko'=>'Korea'
+,'nl_nl'=>'Hollanti'
+,'pl'=>'Puola'
+,'pt_br'=>'Portugali (Brasilia)'
+,'ro'=>'Romania'
+,'ru'=>'Venäjä'
+,'sv'=>'Ruotsi'
+,'th'=>'Thaimaalaisen'
+,'tr'=>'Turkki'
+,'zh_cn'=>'Kiina (yksinkertaistettu)'
+,'zh_tw'=>'Kiina (perinteinen)'
+}
+,'fr'=> new Map<String,String>{
+'cs'=>'Tchèque'
+,'da'=>'Danois'
+,'de'=>'Allemand'
+,'en_us'=>'Anglais (Etats Unis)'
+,'es'=>'Espagnol'
+,'es_mx'=>'Espagnol mexicain'
+,'fi'=>'Finnois'
+,'fr'=>'Français'
+,'hu'=>'Hongrois'
+,'in'=>'Indonésien'
+,'it'=>'Italien'
+,'ja'=>'Japonais'
+,'ko'=>'Coréen'
+,'nl_nl'=>'Néerlandais'
+,'pl'=>'Polonais'
+,'pt_br'=>'Portugais (brésilien)'
+,'ro'=>'Roumain'
+,'ru'=>'Russe'
+,'sv'=>'Suédois'
+,'th'=>'Thai'
+,'tr'=>'Turc'
+,'zh_cn'=>'Chinois (simplifié)'
+,'zh_tw'=>'Chinois (Traditionnel)'
+}
+,'hu'=> new Map<String,String>{
+'cs'=>'Cseh'
+,'da'=>'Dán'
+,'de'=>'Német'
+,'en_us'=>'Angol (Egyesült Ãllamok)'
+,'es'=>'Spanyol'
+,'es_mx'=>'Mexikói spanyol'
+,'fi'=>'Finn'
+,'fr'=>'Francia'
+,'hu'=>'Magyar'
+,'in'=>'Indonéz'
+,'it'=>'Olasz'
+,'ja'=>'Japán'
+,'ko'=>'Koreai'
+,'nl_nl'=>'Holland'
+,'pl'=>'Lengyel'
+,'pt_br'=>'Portugál (brazíliai)'
+,'ro'=>'Román'
+,'ru'=>'Orosz'
+,'sv'=>'Svéd'
+,'th'=>'Thaiföldi'
+,'tr'=>'Török'
+,'zh_cn'=>'Kínai (egyszerűsített)'
+,'zh_tw'=>'Kínai (hagyományos)'
+}
+,'in'=> new Map<String,String>{
+'cs'=>'Ceko'
+,'da'=>'Denmark'
+,'de'=>'Jerman'
+,'en_us'=>'Inggris (Amerika Serikat)'
+,'es'=>'Spanyol'
+,'es_mx'=>'Meksiko Spanyol'
+,'fi'=>'Finlandia'
+,'fr'=>'Prancis'
+,'hu'=>'Hungaria'
+,'in'=>'Indonesia'
+,'it'=>'Italia'
+,'ja'=>'Jepang'
+,'ko'=>'Korea'
+,'nl_nl'=>'Belanda'
+,'pl'=>'Polish'
+,'pt_br'=>'Portugis (Brasil)'
+,'ro'=>'Romanian'
+,'ru'=>'Russian'
+,'sv'=>'Swedia'
+,'th'=>'Thai'
+,'tr'=>'Turkish'
+,'zh_cn'=>'Cina (Sederhana)'
+,'zh_tw'=>'Cina (Tradisional)'
+}
+,'it'=> new Map<String,String>{
+'cs'=>'Ceco'
+,'da'=>'Danese'
+,'de'=>'Tedesco'
+,'en_us'=>'Inglese (Stati Uniti)'
+,'es'=>'Spagnolo'
+,'es_mx'=>'Spagnolo messicano'
+,'fi'=>'Finlandese'
+,'fr'=>'Francese'
+,'hu'=>'Ungherese'
+,'in'=>'Indonesiano'
+,'it'=>'Italiano'
+,'ja'=>'Giapponese'
+,'ko'=>'Coreano'
+,'nl_nl'=>'Olandese'
+,'pl'=>'Polacco'
+,'pt_br'=>'Portoghese (brasiliano)'
+,'ro'=>'Rumeno'
+,'ru'=>'Russo'
+,'sv'=>'Svedese'
+,'th'=>'Thai'
+,'tr'=>'Turco'
+,'zh_cn'=>'Cinese (semplificato)'
+,'zh_tw'=>'Cinese (tradizionale)'
+}
+,'ja'=> new Map<String,String>{
+'cs'=>'ãƒã‚§ã‚³èªž'
+,'da'=>'デンマーク語'
+,'de'=>'ドイツ語'
+,'en_us'=>'英語(アメリカåˆè¡†å›½ï¼‰'
+,'es'=>'スペイン語'
+,'es_mx'=>'メキシコã®ã‚¹ãƒšã‚¤ãƒ³èªž'
+,'fi'=>'フィンランド語'
+,'fr'=>'フランス語'
+,'hu'=>'ãƒãƒ³ã‚¬ãƒªãƒ¼èªž'
+,'in'=>'インドãƒã‚·ã‚¢èªž'
+,'it'=>'イタリア語'
+,'ja'=>'日本語'
+,'ko'=>'韓国語'
+,'nl_nl'=>'オランダ語'
+,'pl'=>'ãƒãƒ¼ãƒ©ãƒ³ãƒ‰èªž'
+,'pt_br'=>'ãƒãƒ«ãƒˆã‚¬ãƒ«èªžï¼ˆãƒ–ラジル)'
+,'ro'=>'ルーマニア語'
+,'ru'=>'ロシア語'
+,'sv'=>'スウェーデン語'
+,'th'=>'タイ'
+,'tr'=>'トルコ語'
+,'zh_cn'=>'中国語(簡体字)'
+,'zh_tw'=>'中国語(ç¹ä½“字)'
+}
+,'ko'=> new Map<String,String>{
+'cs'=>'체코어'
+,'da'=>'ë´ë§ˆí¬ì–´'
+,'de'=>'ë…ì¼ì–´'
+,'en_us'=>'ì˜ì–´ (미국)'
+,'es'=>'스페ì¸ì–´'
+,'es_mx'=>'멕시코 스페ì¸'
+,'fi'=>'핀란드어'
+,'fr'=>'프랑스어'
+,'hu'=>'í—가리어'
+,'in'=>'ì¸ë„네시 ì•„ì–´'
+,'it'=>'ì´íƒˆë¦¬ì•„ì–´'
+,'ja'=>'ì¼ë³¸ì–´'
+,'ko'=>'한국어'
+,'nl_nl'=>'네ëœëž€ë“œ'
+,'pl'=>'í´ëž€ë“œì–´'
+,'pt_br'=>'í¬ë¥´íˆ¬ê°ˆì–´ (브ë¼ì§ˆ)'
+,'ro'=>'루마니아어'
+,'ru'=>'러시아어'
+,'sv'=>'스웨ë´ì–´'
+,'th'=>'타ì´ì–´'
+,'tr'=>'터키어'
+,'zh_cn'=>'중국어 (간체)'
+,'zh_tw'=>'중국어 (번체)'
+}
+,'nl_nl'=> new Map<String,String>{
+'cs'=>'Tsjechisch'
+,'da'=>'Deens'
+,'de'=>'Duits'
+,'en_us'=>'Engels (Verenigde Staten)'
+,'es'=>'Spaans'
+,'es_mx'=>'Mexicaans Spaans'
+,'fi'=>'Fins'
+,'fr'=>'Frans'
+,'hu'=>'Hongaars'
+,'in'=>'Indonesisch'
+,'it'=>'Italiaans'
+,'ja'=>'Japans'
+,'ko'=>'Koreaans'
+,'nl_nl'=>'Nederlandse'
+,'pl'=>'Pools'
+,'pt_br'=>'Portugees (Braziliaans)'
+,'ro'=>'Roemeens'
+,'ru'=>'Russisch'
+,'sv'=>'Zweeds'
+,'th'=>'Thais'
+,'tr'=>'Turks'
+,'zh_cn'=>'Chinese (Simplified)'
+,'zh_tw'=>'Chinees (traditioneel)'
+}
+,'pl'=> new Map<String,String>{
+'cs'=>'Czeski'
+,'da'=>'Duński'
+,'de'=>'Niemiecki'
+,'en_us'=>'Angielski (Stany Zjednoczone)'
+,'es'=>'Hiszpański'
+,'es_mx'=>'Mexican hiszpański'
+,'fi'=>'Fiński'
+,'fr'=>'Francuski'
+,'hu'=>'Węgierski'
+,'in'=>'Indonezyjski'
+,'it'=>'Włoski'
+,'ja'=>'Japoński'
+,'ko'=>'Koreański'
+,'nl_nl'=>'Niderlandzki'
+,'pl'=>'Polska'
+,'pt_br'=>'Portugalski (Brazylia)'
+,'ro'=>'Rumuński'
+,'ru'=>'Rosyjski'
+,'sv'=>'Szwedzki'
+,'th'=>'Taj'
+,'tr'=>'Turecki'
+,'zh_cn'=>'Chiński (uproszczony)'
+,'zh_tw'=>'Chiński (tradycyjny)'
+}
+,'pt_br'=> new Map<String,String>{
+'cs'=>'Tcheco'
+,'da'=>'Dinamarquês'
+,'de'=>'Alemão'
+,'en_us'=>'Inglês (Estados Unidos)'
+,'es'=>'Espanhol'
+,'es_mx'=>'Espanhol mexicano'
+,'fi'=>'Finlandês'
+,'fr'=>'Francês'
+,'hu'=>'Húngaro'
+,'in'=>'Indonésio'
+,'it'=>'Italiano'
+,'ja'=>'Japonês'
+,'ko'=>'Coreano'
+,'nl_nl'=>'Holandês'
+,'pl'=>'Polonês'
+,'pt_br'=>'Português (Brasil)'
+,'ro'=>'Romeno'
+,'ru'=>'Russo'
+,'sv'=>'Sueco'
+,'th'=>'Tailandês'
+,'tr'=>'Turco'
+,'zh_cn'=>'Chinês (simplificado)'
+,'zh_tw'=>'Chinês (Tradicional)'
+}
+,'ro'=> new Map<String,String>{
+'cs'=>'Cehă'
+,'da'=>'Daneză'
+,'de'=>'Germană'
+,'en_us'=>'În limba engleză (Statele Unite)'
+,'es'=>'Spaniolă'
+,'es_mx'=>'Mexicane Spanish'
+,'fi'=>'Finlandeză'
+,'fr'=>'Franţuzesc'
+,'hu'=>'Maghiară'
+,'in'=>'Indoneziană'
+,'it'=>'Italiană'
+,'ja'=>'Japoneză'
+,'ko'=>'Coreeană'
+,'nl_nl'=>'Olandeză'
+,'pl'=>'Poloneză'
+,'pt_br'=>'Portuguese (Brazilian)'
+,'ro'=>'Român'
+,'ru'=>'Rus'
+,'sv'=>'Suedez'
+,'th'=>'Thai'
+,'tr'=>'Turcă'
+,'zh_cn'=>'Chineză (simplificată)'
+,'zh_tw'=>'Chineză (Tradiţională)'
+}
+,'ru'=> new Map<String,String>{
+'cs'=>'ЧешÑкий'
+,'da'=>'ДатÑкий'
+,'de'=>'Ðемецкий'
+,'en_us'=>'ÐнглийÑкий (СШÐ)'
+,'es'=>'ИÑпанÑкий'
+,'es_mx'=>'МекÑиканÑкие ИÑпанÑкий'
+,'fi'=>'ФинÑкий'
+,'fr'=>'ФранцузÑкий'
+,'hu'=>'ВенгерÑкий'
+,'in'=>'ИндонезийÑкий'
+,'it'=>'ИтальÑнÑкий'
+,'ja'=>'ЯпонÑкий'
+,'ko'=>'КорейÑкий'
+,'nl_nl'=>'ГолландÑкий'
+,'pl'=>'ПольÑкий'
+,'pt_br'=>'ПортугальÑкий (бразильÑкий)'
+,'ro'=>'РумынÑкий'
+,'ru'=>'РуÑÑкий'
+,'sv'=>'ШведÑкий'
+,'th'=>'ТайÑкий'
+,'tr'=>'Турецкий'
+,'zh_cn'=>'КитайÑкий (упрощенный)'
+,'zh_tw'=>'КитайÑкий (традиционный)'
+}
+,'sv'=> new Map<String,String>{
+'cs'=>'Tjeckiska'
+,'da'=>'Danska'
+,'de'=>'Tyska'
+,'en_us'=>'Engelska (USA)'
+,'es'=>'Spanska'
+,'es_mx'=>'Mexikansk spanska'
+,'fi'=>'Finska'
+,'fr'=>'Franska'
+,'hu'=>'Ungerska'
+,'in'=>'Indonesiska'
+,'it'=>'Italienska'
+,'ja'=>'Japanska'
+,'ko'=>'Koreanska'
+,'nl_nl'=>'Nederländska'
+,'pl'=>'Polska'
+,'pt_br'=>'Portugisiska (Brasilien)'
+,'ro'=>'Rumänska'
+,'ru'=>'Ryska'
+,'sv'=>'Svenska'
+,'th'=>'Thai'
+,'tr'=>'Turkiska'
+,'zh_cn'=>'Kinesiska (förenklad)'
+,'zh_tw'=>'Kinesiska (traditionell)'
+}
+,'th'=> new Map<String,String>{
+'cs'=>'สาธารณรัภเช็ à¸'
+,'da'=>'เดนมาร์à¸'
+,'de'=>'เยอรมัน'
+,'en_us'=>'ภาษา อังà¸à¸¤à¸© States (United)'
+,'es'=>'สเปน'
+,'es_mx'=>'สเปน เม็ภซิ à¸à¸±à¸™'
+,'fi'=>'ฟินà¹à¸¥à¸™à¸”์'
+,'fr'=>'à¸à¸£à¸±à¹ˆà¸‡à¹€à¸¨à¸ª'
+,'hu'=>'ฮังà¸à¸²à¸£à¸µ'
+,'in'=>'อินโดนีเซีย'
+,'it'=>'อิตาเลียน'
+,'ja'=>'à¸à¸µà¹ˆà¸›à¸¸à¹ˆà¸™'
+,'ko'=>'เà¸à¸²à¸«à¸¥à¸µ'
+,'nl_nl'=>'ดัตช์'
+,'pl'=>'เงา'
+,'pt_br'=>'โปรตุเà¸à¸ª (บราซิล)'
+,'ro'=>'โรมาเนีย'
+,'ru'=>'ภาษา รัสเซีย'
+,'sv'=>'สวีเดน'
+,'th'=>'ไทย'
+,'tr'=>'ภาษา ตุรà¸à¸µ'
+,'zh_cn'=>'จีน (ประยุà¸à¸•์)'
+,'zh_tw'=>'ภาษา จีน (ดั้งเดิม)'
+}
+,'tr'=> new Map<String,String>{
+'cs'=>'Çekçe'
+,'da'=>'Danca'
+,'de'=>'Almanca'
+,'en_us'=>'İngilizce (ABD)'
+,'es'=>'İspanyolca'
+,'es_mx'=>'Mexican İspanyolca'
+,'fi'=>'Fince'
+,'fr'=>'Fransızca'
+,'hu'=>'Macarca'
+,'in'=>'Endonezya Dili'
+,'it'=>'İtalyanca'
+,'ja'=>'Japonca'
+,'ko'=>'Korece'
+,'nl_nl'=>'Hollanda Dili'
+,'pl'=>'Lehçe'
+,'pt_br'=>'Portekizce (Brezilya)'
+,'ro'=>'Romence'
+,'ru'=>'Rusça'
+,'sv'=>'İsveççe'
+,'th'=>'Tay'
+,'tr'=>'Türkçe'
+,'zh_cn'=>'Çince (Basitleştirilmiş)'
+,'zh_tw'=>'Çince (Geleneksel)'
+}
+,'zh_cn'=> new Map<String,String>{
+'cs'=>'æ·å…‹æ–‡'
+,'da'=>'丹麦文'
+,'de'=>'德语'
+,'en_us'=>'英语(美国)'
+,'es'=>'西ç­ç‰™è¯­'
+,'es_mx'=>'墨西哥西ç­ç‰™è¯­'
+,'fi'=>'芬兰文'
+,'fr'=>'法语'
+,'hu'=>'匈牙利文'
+,'in'=>'å°åº¦å°¼è¥¿äºšæ–‡'
+,'it'=>'æ„大利语'
+,'ja'=>'日语'
+,'ko'=>'韩文'
+,'nl_nl'=>'è·å…°æ–‡'
+,'pl'=>'波兰文'
+,'pt_br'=>'è‘¡è„牙语(巴西)'
+,'ro'=>'罗马尼亚文'
+,'ru'=>'ä¿„æ–‡'
+,'sv'=>'瑞典文'
+,'th'=>'泰国'
+,'tr'=>'土耳其文'
+,'zh_cn'=>'中文(简体)'
+,'zh_tw'=>'中文(ç¹ä½“)'
+}
+,'zh_tw'=> new Map<String,String>{
+'cs'=>'æ·å…‹æ–‡'
+,'da'=>'丹麥文'
+,'de'=>'德語'
+,'en_us'=>'英語(美國)'
+,'es'=>'西ç­ç‰™èªž'
+,'es_mx'=>'墨西哥西ç­ç‰™èªž'
+,'fi'=>'芬蘭文'
+,'fr'=>'法語'
+,'hu'=>'匈牙利文'
+,'in'=>'å°åº¦å°¼è¥¿äºžæ–‡'
+,'it'=>'æ„大利語'
+,'ja'=>'日語'
+,'ko'=>'韓文'
+,'nl_nl'=>'è·è˜­æ–‡'
+,'pl'=>'波蘭文'
+,'pt_br'=>'è‘¡è„牙語(巴西)'
+,'ro'=>'羅馬尼亞文'
+,'ru'=>'ä¿„æ–‡'
+,'sv'=>'瑞典文'
+,'th'=>'泰國'
+,'tr'=>'土耳其文'
+,'zh_cn'=>'中文(簡體)'
+,'zh_tw'=>'中文(ç¹é«”)'
+}
+
+ };
+
+} \ No newline at end of file
diff --git a/samples/apex/TwilioAPI.cls b/samples/apex/TwilioAPI.cls
new file mode 100644
index 0000000..4c49d28
--- /dev/null
+++ b/samples/apex/TwilioAPI.cls
@@ -0,0 +1,114 @@
+/*
+Copyright (c) 2012 Twilio, Inc.
+
+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.
+*/
+/**
+ * Entry point for accessing Twilio resources that are pre-configured
+ * with credentials from the Twilio Config custom setting (TwilioConfig__c).
+ *
+ * To set up your Twilio credentials:
+ * 1. Get a Twilio account at http://www.twilio.com/try-twilio
+ * 2. Find your Twilio Account Sid and Auth Token at https://www.twilio.com/user/account
+ * 3. Log into Salesforce and go to: Setup | Develop | Custom Settings | Manage Twilio Config
+ * 4. Create a new Twilo Config instance
+ * 5. Copy and paste your Account Sid and Auth Token and click Save
+ *
+ * NOTE: The Application Sid field is for use with the Twilio Client softphone
+ * SDK for Javascript. It is not required for the rest of the Twilio API.
+ *
+ * Now you can get easy access to Twilio from your Apex code by calling:
+ *
+ * TwilioRestClient restClient = TwilioAPI.getDefaultClient();
+ * restClient.getAccount().getCalls();
+ * // etc.
+ */
+global class TwilioAPI {
+
+ private class MissingTwilioConfigCustomSettingsException extends Exception {}
+
+ private static TwilioRestClient client;
+
+ private TwilioAPI() {}
+
+ /**
+ * Get a TwilioRestClient pre-populated with your TwilioConfig credentials
+ */
+ public static TwilioRestClient getDefaultClient() {
+ if (client==null) {
+ TwilioConfig__c twilioCfg = getTwilioConfig();
+ TwilioAPI.client = new TwilioRestClient(twilioCfg.AccountSid__c, twilioCfg.AuthToken__c);
+ }
+ return TwilioAPI.client;
+ }
+
+ /**
+ * Get your primary account using your TwilioConfig credentials
+ */
+ public static TwilioAccount getDefaultAccount() {
+ return getDefaultClient().getAccount();
+ }
+
+ /**
+ * Get a new Twilio Client capability token generator pre-populated
+ * with your TwilioConfig credentials
+ */
+ public static TwilioCapability createCapability() {
+ TwilioConfig__c twilioCfg = getTwilioConfig();
+ return new TwilioCapability(twilioCfg.AccountSid__c, twilioCfg.AuthToken__c);
+ }
+
+ /**
+ * Get a new TwilioRestClient authorized with the credentials provided
+ */
+ public static TwilioRestClient createClient(String accountSid, String authToken) {
+ return new TwilioRestClient(accountSid, authToken);
+ }
+
+ /**
+ * Load the org default TwilioConfig record
+ */
+ public static TwilioConfig__c getTwilioConfig() {
+ TwilioConfig__c twilioCfg;
+ if (Test.isRunningTest()) {
+ twilioCfg = new TwilioConfig__c();
+ twilioCfg.AccountSid__c = 'ACba8bc05eacf94afdae398e642c9cc32d'; // dummy sid
+ twilioCfg.AuthToken__c = '12345678901234567890123456789012'; // dummy token
+ } else {
+ twilioCfg = TwilioConfig__c.getOrgDefaults();
+ if (twilioCfg==null)
+ throw new MissingTwilioConfigCustomSettingsException('Please enter your Twilio account credentials under Twilio Config custom settings (go to Setup | Develop | Custom Settings | Manage Twilio Config)');
+ }
+ return twilioCfg;
+ }
+
+
+ @isTest
+ static void test_TwilioAPI() {
+ System.assertEquals('ACba8bc05eacf94afdae398e642c9cc32d', TwilioAPI.getTwilioConfig().AccountSid__c);
+ System.assertEquals('12345678901234567890123456789012', TwilioAPI.getTwilioConfig().AuthToken__c);
+ System.assertEquals('ACba8bc05eacf94afdae398e642c9cc32d', TwilioAPI.getDefaultClient().getAccountSid());
+ System.assertEquals('ACba8bc05eacf94afdae398e642c9cc32d', TwilioAPI.getDefaultClient().getAccount().getSid());
+ System.assertEquals('ACba8bc05eacf94afdae398e642c9cc32d', TwilioAPI.getDefaultAccount().getSid());
+ }
+
+} \ No newline at end of file
diff --git a/samples/applescript/center.applescript b/samples/applescript/center.applescript
new file mode 100644
index 0000000..3049271
--- /dev/null
+++ b/samples/applescript/center.applescript
@@ -0,0 +1,50 @@
+set windowWidth to 800
+set windowHeight to 600
+delay 0.1
+
+set AppleScript's text item delimiters to "x"
+
+set res to text returned of (display dialog "Enter the width x height:" default answer ((windowWidth & windowHeight) as text))
+
+if res is "" then
+ display dialog "You need to enter a correct response"
+ return
+end if
+set {windowWidth, windowHeight} to text items of res
+
+set AppleScript's text item delimiters to ""
+
+tell application "Safari"
+ set screen_width to (do JavaScript "screen.availWidth" in document 1)
+ set screen_height to (do JavaScript "screen.availHeight" in document 1)
+end tell
+
+tell application "System Events"
+ set myFrontMost to name of first item of (processes whose frontmost is true)
+end tell
+
+tell application "Finder"
+ set {desktopTop, desktopLeft, desktopRight, desktopBottom} to bounds of desktop
+end tell
+
+try
+ tell application "System Events"
+ tell process myFrontMost
+ set {{w, h}} to size of drawer of window 1
+ end tell
+ end tell
+on error
+ set {w, h} to {0, 0}
+end try
+
+tell application "System Events"
+ tell process myFrontMost
+ try
+ set {{w, h}} to size of drawer of window 1
+ on error
+ set {w, h} to {0, 0}
+ end try
+ set position of window 1 to {((screen_width - windowWidth) / 2), ((screen_height - windowHeight) / 2.0) - desktopTop}
+ set size of window 1 to {windowWidth -w, windowHeight}
+ end tell
+end tell \ No newline at end of file
diff --git a/samples/applescript/center.scpt b/samples/applescript/center.scpt
new file mode 100644
index 0000000..3049271
--- /dev/null
+++ b/samples/applescript/center.scpt
@@ -0,0 +1,50 @@
+set windowWidth to 800
+set windowHeight to 600
+delay 0.1
+
+set AppleScript's text item delimiters to "x"
+
+set res to text returned of (display dialog "Enter the width x height:" default answer ((windowWidth & windowHeight) as text))
+
+if res is "" then
+ display dialog "You need to enter a correct response"
+ return
+end if
+set {windowWidth, windowHeight} to text items of res
+
+set AppleScript's text item delimiters to ""
+
+tell application "Safari"
+ set screen_width to (do JavaScript "screen.availWidth" in document 1)
+ set screen_height to (do JavaScript "screen.availHeight" in document 1)
+end tell
+
+tell application "System Events"
+ set myFrontMost to name of first item of (processes whose frontmost is true)
+end tell
+
+tell application "Finder"
+ set {desktopTop, desktopLeft, desktopRight, desktopBottom} to bounds of desktop
+end tell
+
+try
+ tell application "System Events"
+ tell process myFrontMost
+ set {{w, h}} to size of drawer of window 1
+ end tell
+ end tell
+on error
+ set {w, h} to {0, 0}
+end try
+
+tell application "System Events"
+ tell process myFrontMost
+ try
+ set {{w, h}} to size of drawer of window 1
+ on error
+ set {w, h} to {0, 0}
+ end try
+ set position of window 1 to {((screen_width - windowWidth) / 2), ((screen_height - windowHeight) / 2.0) - desktopTop}
+ set size of window 1 to {windowWidth -w, windowHeight}
+ end tell
+end tell \ No newline at end of file
diff --git a/samples/arduino/hello.ino b/samples/arduino/hello.ino
new file mode 100644
index 0000000..469cecd
--- /dev/null
+++ b/samples/arduino/hello.ino
@@ -0,0 +1,7 @@
+void setup() {
+ Serial.begin(9600);
+}
+
+void loop() {
+ Serial.print("Hello");
+}
diff --git a/samples/autohotkey/hello.ahk b/samples/autohotkey/hello.ahk
new file mode 100644
index 0000000..ff963eb
--- /dev/null
+++ b/samples/autohotkey/hello.ahk
@@ -0,0 +1 @@
+MsgBox, Hello`, World!
diff --git a/samples/binary/dog.o b/samples/binary/dog.o
new file mode 100755
index 0000000..4703976
--- /dev/null
+++ b/samples/binary/dog.o
Binary files differ
diff --git a/samples/binary/foo bar.jar b/samples/binary/foo bar.jar
new file mode 100644
index 0000000..d3b48df
--- /dev/null
+++ b/samples/binary/foo bar.jar
Binary files differ
diff --git a/samples/binary/foo.bin b/samples/binary/foo.bin
new file mode 100644
index 0000000..d3b48df
--- /dev/null
+++ b/samples/binary/foo.bin
Binary files differ
diff --git a/samples/binary/foo.pdf b/samples/binary/foo.pdf
new file mode 100644
index 0000000..68c8d29
--- /dev/null
+++ b/samples/binary/foo.pdf
Binary files differ
diff --git a/samples/binary/foo.png b/samples/binary/foo.png
new file mode 100644
index 0000000..a9ec5a2
--- /dev/null
+++ b/samples/binary/foo.png
Binary files differ
diff --git a/samples/binary/git.deb b/samples/binary/git.deb
new file mode 100644
index 0000000..709e828
--- /dev/null
+++ b/samples/binary/git.deb
Binary files differ
diff --git a/samples/binary/git.exe b/samples/binary/git.exe
new file mode 100755
index 0000000..4703976
--- /dev/null
+++ b/samples/binary/git.exe
Binary files differ
diff --git a/samples/binary/github.po b/samples/binary/github.po
new file mode 100644
index 0000000..772ecc0
--- /dev/null
+++ b/samples/binary/github.po
@@ -0,0 +1,21 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: x\n"
+"POT-Creation-Date: 2009-02-15 09:22+0100\n"
+"PO-Revision-Date: 2009-02-15 09:22+0100\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: tools/files/simple_translation.rb:1
+msgid "a translation"
+msgstr ""
diff --git a/samples/binary/hello.pbc b/samples/binary/hello.pbc
new file mode 100644
index 0000000..b30393e
--- /dev/null
+++ b/samples/binary/hello.pbc
Binary files differ
diff --git a/samples/binary/linguist.gem b/samples/binary/linguist.gem
new file mode 100644
index 0000000..04cc759
--- /dev/null
+++ b/samples/binary/linguist.gem
Binary files differ
diff --git a/samples/binary/octocat.ai b/samples/binary/octocat.ai
new file mode 100644
index 0000000..b0ad88a
--- /dev/null
+++ b/samples/binary/octocat.ai
@@ -0,0 +1,4424 @@
+%PDF-1.5 %âãÏÓ
+1 0 obj <</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R]/Order 6 0 R/RBGroups[]>>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <</Length 70487/Subtype/XML/Type/Metadata>>stream
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.2-c063 53.352624, 2008/07/30-18:05:41 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <dc:format>application/pdf</dc:format>
+ <dc:title>
+ <rdf:Alt>
+ <rdf:li xml:lang="x-default">octocat</rdf:li>
+ </rdf:Alt>
+ </dc:title>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmp="http://ns.adobe.com/xap/1.0/"
+ xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/">
+ <xmp:MetadataDate>2009-01-26T14:06:16-08:00</xmp:MetadataDate>
+ <xmp:ModifyDate>2009-01-26T14:06:16-08:00</xmp:ModifyDate>
+ <xmp:CreateDate>2009-01-26T14:06:16-08:00</xmp:CreateDate>
+ <xmp:CreatorTool>Adobe Illustrator CS4</xmp:CreatorTool>
+ <xmp:Thumbnails>
+ <rdf:Alt>
+ <rdf:li rdf:parseType="Resource">
+ <xmpGImg:width>256</xmpGImg:width>
+ <xmpGImg:height>256</xmpGImg:height>
+ <xmpGImg:format>JPEG</xmpGImg:format>
+ <xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq8&#xA;p/5yd82S+Xfyi1IQM0dzrEkelwyKaUE/Jpf+CgikX6cVfBsU00Mgkhdo5F+y6EqR8iMKHovlD/nI&#xA;X82PK8kYt9bl1GzTiDY6kTdxFV6IGc+qi02+B1wJfRP5cf8AOW/k7X3hsPNEP+HdSeii5ZvUsXbp&#xA;X1CA0NT/ADjiO74q93gnhuIY54JFlglUPFKhDIyMKqysNiCNwRiq/FXYq7FXYq7FXYq7FVk88NvD&#xA;JPPIsUESl5ZXIVFRRVmZjsABuScVeEfmP/zlx5N0B5rDytD/AIi1FKobkN6dijCor6lC01D/ACDi&#xA;ez4q+dvN/wDzkL+bHmiSQXGty6dZvyAsdNJtIgrdULIfVdabfG7Yq86lmmmkMkztJI32nclifmTh&#xA;Q+8v+cYvNkvmL8otNE7NJc6PJJpc0jGtRBxaL/gYJY1+jAl6tirsVdirsVdirsVdirsVdirsVdir&#xA;sVdirsVdir5d/wCc3dSAh8paYr7s17cyx+HEQpGx/wCCfFXythQ7FXYq9E/K788vO/5e3CR2Fx9d&#xA;0QtyuNGuSWhINamI7tC29apsT9oNgS9G8w/85pecroumg6JZaZEwor3LSXcqn+YEegn3ocVYBq//&#xA;ADkd+c+qLwl8yTW8fZbSKC2IrT9uJEft3bFWM335l/mLfqEvfNGrXEYIYRyX1wyhgKVCl6VocKpT&#xA;Jr+uyOZJNRundjVmaaQkn3JbFUTaecPNtmUNnrd/bGM8ozDdTJxatarxYUOKp7YfnX+bVjIskPm3&#xA;VHZTUCe5kuF79VmMgPXvirNdE/5y5/N7T5K309lq6EUK3VssZ+YNqbffArF/zN/PHz1+YMpi1O5F&#xA;po6kGLR7SqW4Ip8UlSWlbatXJp+yBirz3Ch2KuxV9U/84R6mDF5s0tn3VrO5hj335CZJD4fspgS+&#xA;osVdirsVdirsVdirsVdirsVdirsVdirsVdirsVfJv/ObdvMuteVbkrSGS2u40eo3aOSNmFOuwkXF&#xA;XzNhQ7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+mP+cJIJm1vzVcBawx21rG7+DPJIVH0hGwJf&#xA;WeKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kvnz/nM7y4975F0nXY6ltHvTFKoGwhvE4lyfaSK&#xA;NfpxV8b4UOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KvrT/AJwofRE0TzHGl6h1ye5iabTzs62k&#xA;KUjmFftAyTOpp9nav2hgS+l8VdirsVdirsVdirsVdirsVdirsVdirsVdirsVfDH/ADkf+c0/nnzI&#xA;+j6VcH/CmkyFbZVNFuZ1qr3LeI6rHXou/wC0cVeN4UOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2&#xA;Kp15P82635S8xWev6LMYb6zfkvXg6HZ45APtI42IxS/RDyB520rzt5TsPMemHjDeJ+9tywZ4Zl2k&#xA;helN0b23FD0OBWQ4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8g/5yh/MB/Kn5bTWVnJw1TzCzWFuR&#xA;9pYCtbmQbfyfB4guCOmKvhPCh2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV9Ef84defZNO&#xA;82Xnk65kP1LWo2uLJCdlu7deTUqdvUhVq/6q4EvsPFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXxd/&#xA;zmN5kkv/AMyLTRVdvq2i2KAxE/CJ7o+q7AduUfpD6MVeC4UOxV2KuxV2KuxV2KuxV2KuxV2KuxV2&#xA;KuxV2KuxV2Kp35J1+Xy75w0XXIzQ6dewXDDoCiSAup6bMtQcUv0wwK7FXYq7FXYq7FXYq7FXYq7F&#xA;XYq7FXYq/P3/AJyNvJbv86vM8kgAKTxQqBWnGG2ijXr4hKn3xV5thQ7FUXpel6jquoQadp1u91e3&#xA;LiOCCMVZmP8AnuT0wEgCyyjEk0H0F5N/5xdsFt47nzbfSS3LAMbCyIREJ34vKQxf34gfM5gZNZ/N&#xA;djj0A/iL0Wz/ACU/K20UCPy/bvtSszSzH/ko7+GY51Ez1ckaXGOiJ/5VJ+Wn/UuWP/IoYPHn3svy&#xA;+PuCDl/I/wDKqX7Xl+Ef6sk6df8AVkGS/Mz72P5XH3Jfcf8AOO/5UysDHpctuAKFY7q4IPufUd8I&#xA;1U+9idHj7kEf+cZ/y1JJ/wBOHsLgf80ZL85PyY/kcfmqw/8AONv5YRij291Ma1q9wwPy+ALg/NzS&#xA;NFjTSL8h/wAp4jVdAQ71+O4un/4lKcj+Zyd7L8pj7kbF+T35YxLxXy7aEVr8alz97EnB+Yn3shps&#xA;fcuf8ofyzdSp8uWQBFDROJ+gggjB48+9fy2PuY5r/wDzjj+W+pQt9Rt5tIuCDxltpXdeXYtHMZBT&#xA;2XjlkdXMc92ueigeWzwD8x/yk8y+RpxJdAXmkytwt9ThBCFuoSRTUxvQdK0PYnM/FnE/e67Np5Y+&#xA;fJg+XOO7FXYq/TXyhdS3flPRLuY1luLC1lkJJNWeFWO536nAlNsVdirsVdirsVdirsVdirsVdirs&#xA;Vdir8+v+ciLea3/OjzTHMvF2uY5AKg/DLBHIh28VYHCrznFDYBJoNyegxV9c/kj+VVv5R0SPU9Qh&#xA;DeY9QjDzsw+K3jYAiBa9D/P77dBmq1GbiNDk7nS6fgFn6i9QVWZgqgliaADqScxgHLJpP7TytyjD&#xA;XUpRz/utKbfMnM6Gj29Rddk11H0hQ1Ly7LbRmaBzLGu7qRRgPH3yGXSmIsbtmHWCRo7FJsxHNdir&#xA;sVdirsVXxRSTSLFGpZ3NFUYYxJNBEpACyyC38qJ6YNxMeZ6hAKD6T1zOjotty66evN7BAaroU1kv&#xA;qo3qwd2pQr88pzacw35hvwaoT2OxSDVtK0/VtNuNN1GBbmyukMc8LioZT+og7g9jvmPGRBsOTKIk&#xA;KL4v/M/yFdeSfNU+luTJZSD19OuDSrwMSByp+0pBVvlXoRm4w5eONujz4vDlTEctaXYq/S/yKCPJ&#xA;Hl4EUI0yzBB/5h0wJTzFXYq7FXYq7FXYq7FXYq7FXYq7FXYq+FP+crrCW1/OjU53Uqt9bWdxGSKc&#xA;lWBYKjx+KEjFXj+FDOvyT8vQ67+ZOkW1wvO2tna8nUgMCLdS6hgdqNIFBynUT4YFyNLDiyB9n5p3&#xA;eJz5YtFlu3nYVEAHH/Wbv9wzL0kLlfc4OuyVGu94z+YH5t+YNV1meHRr6aw0m3cx25tnMTy8TT1G&#xA;daN8VKgVpTDlzknY7O00fZ0IQBmLke9kH5Qfmjq1xq8fl/XblruK7BWyupjykWUAng7ndg/QV3rT&#xA;J4Mxui43aWgiI8cBVc2c6vaC11CWJRRK8kHs29Po6Zi54cMiGGnyccAUHlTc7FXYq7FU80AW1pZ3&#xA;eq3TBIbdGLSH9lEXnI33ZnaSIAMi6/WSMiIDq8H81/mx5t1rVJJ7S/uNNsVYi1tbWRoqIOnNkKl2&#xA;Pev0ZXPPInnTutP2dixxogSPm9E/J38yL7Xnm8u67J9YuhEXtLl6c5UXZ43p9pgDUHqRWuX4MvF6&#xA;ZOs7S0Qx1khsE+vLc291LAd/TYqD4jscwJx4ZEN2OfFEF43/AM5M+XI9Q8iR6wqA3GjXCMZO4guC&#xA;InX6XMZ+jMnRzqVd7i66FwvufKmbN1C5FZ2CKKsxAA9zir9QdPs1srC2s1YuttEkIc7EiNQtfwwJ&#xA;RGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV8n/8AObHl501by35jQEpPBLp0xpsphf1oqn/K9Z/uxV8y&#xA;YUPXP+cYpYk/MeZXQO0unTpGxp8DCSJuQ/2KkfTmLrPo+Lm6H6/g+rc1bt02tWkTytrLwf70LBMY&#xA;+v2hCeP45nab6JOBqf72F8tvvfLGYr1Ca+UmmTzVozQV9YX1t6dNzy9VabZKHMNOorw5X/NP3PpT&#xA;zTT9IpT/AH0tf+CbLdZ9fwef0P0fFJ8xHNdirsVdiqt5qZ1/KzWTESGMLhqeBYBvo49cz8f9yXCj&#xA;/jUbfNGYj0zL/wAo2lX8xNG9KvIySA03+Ewvy/4WuW4PrDhdo14Er/G73LX6fpe4p/k/8QGR1P8A&#xA;eF1Wk/ux+OrzL89iB+U+v1/kg/6iYsdN/eBOr/uy+M827o2Rfl3ov6b8++XtINeF7qNrDKQK0jaV&#xA;fUNPZKnFL9KcCuxV2KuxV2KuxV2KsB/x3+YV6VfR/wAvrv6q/wBmbV7+009/esKG8lX6VxVun573&#xA;DdfLGnRsNhS/vXX6a2anFWm8ofmzK/KX8wkiG1VttFtUH/JaW4OKqv8AyrvzPcIP0j+YOuySoTwa&#xA;0TTLNeJpsypZtyNe5OKqLflJOzFj5581VJqaahEBv7C3piqF1HyHo+np6d/+Yev2ThQxefV4om41&#xA;pyo8YFCR4Yq82/N/yZ5I1LyLqcUX5l3Gr6nZx/XdO07UtYs54nmhBNBEFRi7Rl0Sh6nFXyNhQzr8&#xA;kNZj0n8z9Dml/uriVrRt6b3KNEn/AA7LlGojcC5GllWQPs/NQ7xOvLFyi3Ets9OM6/CD0JXt9IOZ&#xA;mjnRI73B10LiJDo8D/MH8vtV8s6zOsdvJLpMrlrK6VSycG3EbEVo69N+vXIZcRifJ3Wj1kcsBv6u&#xA;oZJ+TP5e6nd65b+YNQt3t9NsT6tt6qlTNMPsFAf2UPxcvEU8aWafESbPJxe09ZGMDCJuR+x6XrF0&#xA;tzqEsimqA8EPsu1fpyjPPimS42mx8MAEFlLe7FXYq7FU60aK11HTL7RrscoLqN1de5SReD0+WZ2k&#xA;kCDEuv1YMZRyDo+c/NfknXvLeqSWV5bu0fI/VrpFJjlSuzKR3p1HUZROBiaL0On1UMsbBel/kl5A&#xA;v7K6fzRq8LWqxxslhDKCrnmKPMwNCBxqFr1qT4Zk6fHXqLqe1NWJDw4b9/6mW3tx9Yu5Z+zsSPl2&#xA;/DMLJLikSyxQ4YgPIv8AnJbVRZ/ls1oHAbUryCAp3KoTOTTwBiGX6SNz9zja6VY673ybm0dO9R/5&#xA;x58v6DqfnaS81vzF/hm10y2eaC/S6gs5zcSERqkUk9QPgZySFPT3wJD6ftfL3k2d09D82dXuOR+F&#xA;E1yzblTqPhjr92Kp0n5XxXkST2vnrzM0LV4yQ6lG6NvTZjE4+7FVSP8AKq8iBEXnvzSpalS15ayn&#xA;avT1bV6de2KrpvI35goOFh+YuoJEoCxrd6fplywUCnxOsEDM3ucVaXQfzmsl523mzStWfp6OoaS8&#xA;C061ElrdKa7U+x3OKrf0t+d2npyu/L+ia2DsF07UJ7OQe5W6gkT/AJKf2qpx5X8165ql7JYax5Xv&#xA;9BuoojMZpntrizcclXhHcW8j1f4q8Sg2GKpXcfnR5Jaea10RrzzNewNxlttDtJr7jvQkzootwAdq&#xA;mXFVIebPzX1QldI8lw6XBIOUF7r2oRoQvb1LWyW6kDEfs8x88Vb/AMP/AJzaiqvfebdN0Rhs1vpO&#xA;mfWBTry9a+lk+Lt/dUxVs/lVd3LiXU/O/mW7mO8ghvY7GIk9eMdnDBxHtXFWm/Iv8s5iHvdOuNQm&#xA;p8c17f39yzHqWb1J2FSd+mKpjZflF+VlmgSDylpGxJDSWUEr7/5cis344qmEXkHyLEFEXlzS4wu6&#xA;hbK3AG9dqJir47/5yZ/J+byb5ofX9Lt6eWNalLx8F+C2um+KSA02VW3eP2qP2cVeLxySRyLJGxSR&#xA;CGRhsQQagjCr7Y/K7z7aedPKltqKuo1GICHU7cdUnUbmn8sn2l+7qDmmzYuCVdHe4MonG+rMEdkd&#xA;XQlWU1UjqCMrBptIsUWRWfmmL0wt3G3MdXShB+gkUzOhrBXqDrcmhN+kqGp+ZDPEYbVWjRtnkanI&#xA;jwAHTIZtVxCotmDR8JuW6R5hue7FWAfmF5e80ajqtrNpoeW1WMIqI4T05ORJY1I6inxZIEOXp8kQ&#xA;DbN9PiuIrC2iuX9W5jiRZ5P5nCgM2/ici4siL2RGKFSCeWCZZom4yIag5KMjE2GM4iQosit/NVsY&#xA;x9YiZZB14UKn7yMz46wVuHWz0Er2OyA1bX5LxDDCpjgP2q/ab506DKM2pMthycjT6QQNncpRmK5j&#xA;5V/5yT86xa15th0S0fnaaErxzMOhupCPVH+wCqvz5Zs9JjqN97qNbl4pUOjyOKKSWVIokMksjBUR&#xA;QSzMxoAAOpJzLcJ95fkp+S+j+Vfy/tLDXtNtb3Wb1vrupC5ijn9OV1AWFS6n+6QAH/K5EdcCWaTf&#xA;lx+Xk9TN5X0iWooedhbNt4bx4qlN5+SH5S3UhlbyvYwOab2iG06Cgp9XMVP8ziql/wAqW8p26cNG&#xA;vda0Kv2/0dq9+itTpVJJZU23p8Pc4q0fy7852SBdE/MLVol6smqQWWpAn2ZooZAP9nirfp/njpzg&#xA;LN5f8w2sQqxkS70u6lpvxHE3sKsegNKeOKrB+ZnmTTyF8yeRNYsuW4n0z0dXhAHUsbZhMtP+MWKp&#xA;/wCVfzB8m+anni0HU47u4td7m1ZZIbiME0q8EyxyqK7VK4qn8cccaBI1CIuyqoAAHsBiq7FXYq7F&#xA;XYq7FXYql/mDy/o/mHR7rRtZtUvNNvEMdxbyDYjqCCN1ZTurDcHcYq+Hfzo/5x+8yfl/dzahZpJq&#xA;flR2rBqKDk8AY0WO6Cj4SOgf7Lex+EKsG8ked9d8na3Hquky0b7NzbPUxTR13SQD8D1GQyYxMUWz&#xA;FlMDYfV/kD84/J/nGKOKCcWGrts+l3LASFv+Km2WUf6u/iBmsy6eUPc7fDqYz8izrKHIdirsVUby&#xA;KaaznihkMM0kbpFMOqMykK30HfFMTRYpY+cb3TLcWXmCwuvr8A4i4hj9SOcDoysCBU9/8xhpyJYR&#xA;I3Eikw8uT6/f311qd/G9nYSKsdjp8n2wAamRx1BOJYZBEAAblkOBpdirsVaZlVSzEBQKknYADFXi&#xA;f5t/n/pum2s+i+U7hbzVZVaObUojWG2rsfTYbSSeBHwjxJ2zMwaUneXJwNRqwNo83zRHHcXVwsca&#xA;vPczuFRFBd3dzQAAVLMSc2Tq31z/AM46/wDOOUmgS2/nDzlbga0tJNK0p6MLWo2mmG49bf4V/Y6/&#xA;b+yFfRuKuxV2KuxV2KuxV2KrfTj9QycR6hHEvQVoDWlfDFV2KuxV2KuxV2KsV/MH8zfKHkDTIr/z&#xA;HdPCtyxjtIIo2llmdQCwRVFBQHcsQPfFUk0H8+vIGraZHqsjXulaXNIIoNQ1G1khtXYtw/3oX1IV&#xA;HL4auwFcWPELq93oUE8FxBHcW8izQTKskUsbBkdGFVZWFQQQagjFk3LFFNE8MyLJFIpSSNwGVlYU&#xA;KsDsQRirwn8x/wDnEnyZ5geW/wDLEv8Ah3UnBY26L6llI5qf7uoaKvT4DxH8uKvnfzf/AM47fmz5&#xA;Xkd5tFk1K0Qnje6XW6QhdyxRB6yD3dBiqWaD+cH5l+WyLWDVZnhiops75ROFC9FHqgug9lYZVPBC&#xA;XMN0NTOPIs+0v/nK3XYz/uV0K1uR42sslvT3/eC4yg6IdC5Me0D1DI7L/nKvyw5H17Rb2Ad/QeKb&#xA;t/lGHvlZ0R6FtGvj1BTFf+cofy7Kgm11NSexghqPumOR/Jz8mX5+Hmr/APQzP5a+F/8A9I6/814P&#xA;yc/JP56Hm03/ADk1+WwUkLfsR+yIFqfvkAx/Jz8l/PQ81Fv+cofy7Cki11NiOwghqfvmGH8nPyR+&#xA;fh5pde/85V+WEJ+o6Lezjt67xQ9v8kzd8kNEepYnXx6Asa1b/nKzX5ajSdDtbQUI5XMslyfmOAgA&#xA;/HLI6IdS1S18ugYDqvnn80fPtx+jmubzUvWoo0uwjYI2+wMMC/Hv/NXMiGGMeQcXJnnPmWaeSP8A&#xA;nFD8zPMDxTavHH5d056FpLs87jjyoeNsh5cvaQpljU+m/wAsfyG8h/l+qXNjbG/1wAh9YuwGmFRQ&#xA;iFfsRDr9kcqbFjir0bFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqwv8ANj8v/K/njyz+iddVkb1V&#xA;/R99FT1redzxDqTsVp9tTsR70InCBkdmvLlEACepA+b49/NHyV+ZX5cLb+W9cvbi/wDJckoNmYJH&#xA;SznUP6piINfTkrVuBrQ/EK9cgngF31ekflj+Z+qaM0mt+UfLN1D+WFlGy69Zy3QmaKRP3ktzaJNI&#xA;WDRxsC6r8LjqA3xBYRnwmpHcvqbTtQstSsLbULGZbiyvIkntp03V45FDIw9iDi3IjFXYqlOt+UfK&#xA;uvCmt6PZanQcQbu3imIB/lLqxH0Yq8U/OT8q/wDnHjyd5Wn1vVNDa2uHYx2FtY3M8UtxOVNI0Vne&#xA;JV/aZuHwgd+hVfHNw8Uk8jwx+jEzExxci3Fa7DkdzTxwoU8VdirsVdirM/yrH5bzeZYbHz7Bcfoq&#xA;7ZY0vreYxCBzsGmWh5Rmu5BBXrvgS+1dG/5x+/JnSpEntPK9pMyj4WumlvFNe/G5eVD92Ks503St&#xA;L0u2FpplnBY2oJYQW0aQxgnqeKBR2xVFYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUh822&#xA;WoXcFslpGZArlnC0qDT4T+vMzRzjEnidZ2niyTjEQF7u84eTtH84eV7ry/rsIlt7uOhYAcopgPhm&#xA;iPZ0bcfcdq5iHns7KN1vzfBNzoR8l+fLzyj5wub2HRra4aPVI9PcqZ4+BMEqo9FZXqjbivE+OBjM&#xA;Gtub3/8AIv8ANzRdAmby3LDqNt5BuZwvlbX9TjKxRzSCstrJOB6XBpeRjNdujeysJdDXE+lcWbsV&#xA;dir4Q/5yd893Pmf80L+xWQ/ozy8zadaQ9hLGaXL0qRyaUFa/yquKvI8KHYq7FXYq7FXYq+4f+cUf&#xA;Pk/mX8uf0VeymXUPLkgs+RqWNq68rYsSf2QGjHsowJe1Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq&#xA;7FXYq7FXYq7FXYq+Yf8AnMv8v3lttM88WMBJt/8AQNYdR0jY1tpGoOzFkLE91GKvANH86Xt7Z6Z5&#xA;V8y6rdJ5Kt5g89tapEZVUEt8LFeTfEejE06gYWowqyB6nrP5b/mf+YUPmqfy1+WmoHXfLNpAJra1&#xA;8xUrHGoRGCSL6UwVXaiKNv8AJwKcnDG5M50b/nIX81tXtddez8raUZPLpki1FmvWCiWINy4DcMv7&#xA;sn7VP8rFMssRXmwjzN+e35l+YvIE/mC2826ToLcvRby/pwMeoMPVCVEkrSTBqfHWOg49+oxXxPVV&#xA;fF88yyyzSvNM7SSyMXkkclmZmNSzE7kk4WaeeSPJupeb9fTSLAhG9Ka5uJ2BKxQW8Zkkc09hRfFi&#xA;BilIMUPTfyG8haD5z1/XbTWopJrbT9Fub63WKQxkXEckSx147kUdtsCQ8ywoT7yr5O1XzMmtfowK&#xA;82i6bLq00J+08EEsUcoT/KVZufuFoN6YpSHFD6N/5wp1CaPzj5h04MfQudOS4dexe3nVEP0C4bAl&#xA;9fYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqhNX0jTdZ0u60rVLdLvT72Nobm3k&#xA;+y6MKEbUI9iNx2xV8N/nP/zj15l8hXs+oadFLqnlNiXiv40LPbKT/d3SrXjxrQSfZb2PwhV5Kjuj&#xA;ckYqw6EGhwocruoIViA2zAGlR74qtxV2Kvff+cTbWFrvzvdkfvotEeJD/kysWb8YlwFIeBYUPoL/&#xA;AJwt/wDJjaz/ANseT/qKgwJfPuFD3r/nDSKOX8zdXilQSRSaFcK6MAVZTd2oIIPUHAl4dqMCW+oX&#xA;Vun2IZZI1+SsQP1YVe8/84W/+TG1n/tjyf8AUVBgV9k4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXEAggioOxBxV5z5o/wCcevyi8xySz3egRWt5KN7qwZ7Rga1LcIisTMe5ZDir&#xA;EG/5w3/KksSLvV1BNQouIKD23gOKpnpH/OJ35N2D87ixu9T2oBeXUgAPjS39Cv07Yq+FsKH0H/zi&#xA;V/03n/bH/wCqmApD58wofQX/ADhb/wCTG1n/ALY8n/UVBgS+fcKHvv8Azhf/AOTR1T/thz/9Rdrg&#xA;S8P1v/js3/8AzEy/8TOFXuv/ADhb/wCTG1n/ALY8n/UVBgV9k4q7FXYq7FXYq7FXYq7FXYq7FXYq&#xA;7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX5aYUPoP/nEr/pvP+2P/ANVMBSHz5hQ+gv8AnC3/AMmN&#xA;rP8A2x5P+oqDAl8+4UPff+cL/wDyaOqf9sOf/qLtcCXh+t/8dm//AOYmX/iZwq91/wCcLf8AyY2s&#xA;/wDbHk/6ioMCvsnFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVK4u7&#xA;S2UtcTRwqBUtIwUUHepIyQiTyDCWSMeZAflzgZPd/wDnFrU7ex/xt6wZvX0sRpwAO59TrUjLMeIz&#xA;uujTm1McVX1LwjK257p/ziNqf6M89avdGIyodLaIgMFoWuImHj/IcuwYfENW42q1QwxBq93hmUuS&#xA;9v8A+cRNVg038ydSnnVnRtGnjolCam6tj3I/ly3FhOQ0GjUamOGPEe945rJrrF8R3uJT/wAOcqbw&#xA;be4f84a3dtbfmJq7XEqxK2kSKrOQoJ+swdzhjAy5C2M8kYC5Gn2XFNDKvKKRZF8VII/DAYkc0xmJ&#xA;cja/AydirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVQ97qNlZR87mVYx2B3Y/IDc5OG&#xA;OUzsGrLnhjFyNMcvvO+5Wyg/56S/80j+uZ2PQfzi6nN2v/MHzSO61/V7mokuXCn9lPgH/C0zMhp4&#xA;R5B12TW5Z85foeUfnX+Y1j5e8uXWmQXCya9qUbQxQA8nijkHF5n/AJfhJ416n5HKtVmEY0OZb9Bp&#xA;pZJiR+kPlLNQ9I95/JbSpdC/LfzR5pvFWOO8t5PqhkFC0drHJuOmzyPxAHUr8sz9NHhxykXTa+fH&#xA;mhAdD97wbMB3L3L/AJxf06ZtR13UukMcMNuPdpGZzT/VEe/zzP0EdyXT9rz9MR5vHNesJ9O1vULC&#xA;cUmtLmWGQe6OVP6swZCjTtoSEogjq9A/5x11FLT8xkgag+v2c9stfEcZ9v8AkTmTopVk97g9qQvD&#xA;fcf2fpY1+Z/lq58ved9UspYykEsz3Nm1KK0EzF0psPs14mncHKs+PhmQ5GkzDJjB/FrPy483t5S8&#xA;3WerkF7VSYb2NerQSCj08SuzD3GOHJwSBXVYPFxmL7E0fWbHUrKHUtKuluLWdQ0NxC2xB+W4I7g7&#xA;jvm7BEhY3Dysoyxyo7EJ7a+ZtZtqATmVR+zL8f4n4vxymelxy6OTj7QzQ63709sfO1u9FvYTEf8A&#xA;fifEv0jqPxzDyaEj6TbssPa0TtMUyK3ure5iEtvIssZ/aU1zClAxNF2sMkZi4mwqZFm7FXYq7FXY&#xA;q7FXYq7FXYq7FXYq7FXYq7FXYqkXmLzGNP8A9GtwGu2FSTuEB6VHc5mabTce55Ot12v8L0x+r7mE&#xA;z3E1xK0szmSRurManNtGIAoPOzmZGybKnhYpd5jh1GfQdQh025FnqElvItrdNUiOQqQrbAnb2GRn&#xA;EkUObZilESBkLD5hH5J+fru6d7l7dXdqvcTTs/Ik7sSqu5+7NaOz8pPL7XoT2nhA2+5nfkz/AJx1&#xA;0dJ47nzBqYvyh5HT7YNHGaH9uRqSMviAq/PLodn1vNw8/axIqArzLX5/+e7Cw0xfImkII3pEb8Rq&#xA;EjigUB4oEA7t8LGmwG3faGsygDgCezNMSfFl8P1vDtG0XVda1KHTdLtnur2c0jijFT7sT0VR3J2G&#xA;a+MTI0HczyRgLkaD6+/LfyTB5O8rW+kqwlumJnvpl6PO4Aalf2VChR7DN3gxcEaeV1eo8WfF06PK&#xA;fz9/K+8e9k83aNA00Uqj9LW8S1ZGQU9cKOqlR8fgd+5ph6zAb4h8XZ9m6wV4cvh+p4voer3ejaxZ&#xA;araEC5sZkniB6Eoa8Wp2PQ5gRkQbDt8kBKJieRfU0mneSfzc8o22oTxMlOSpOpC3FrMKeonIggjp&#xA;1FCN82/DDPG3m+PJpchH4LyXXf8AnHrWLWZ/0Tq9rfwinESiSCTf/VEqbf62Y8uzcnR2OPtaB+oE&#xA;Mh/Jn8vvPPl3zalxeXEdtpTJILq1SXmLglCEooFAVajcjTbbvlmn0mWErOwaddrMWTHQ3l9z3nM5&#xA;0rsVRFlf3dlMJbaQo3cDoR4Ed8hPHGYotuLNLGbiaZ5oetw6nATQJcxj97H/AMbL7Zp9RgOM+T0u&#xA;j1YzR/pDmmeY7mOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5ffSyTXs8sn23kYtX59M6DGAIgB43&#xA;NIymSedqGTa3Yq0yhlKsKg7EHwxVJ9S0tYkM0FeI+2nWg8Rl8Ml7FUsVipBU0I6EZarEvM35YeWf&#xA;MmuS65qSzm6nEaziOThG5jQIpIoSDxUdCMwsmgxzlZtzsPaGTHDhFUyjyjpXl7y1CbbTNOitIpP7&#xA;yaMEyt4c5GLO4HucsGljH6RTj5c88n1G2YAhgCDUHcEZBpULy9itY+T7sfsoOpyUY2rzHzJ+WPkr&#xA;XrqS6m09bO5kNWlsyYanxKCsZJ7njkZ6DFLpTm4u0MsNrseab+WvL1h5Z0YaNpwkW1ErTSeo3J2k&#xA;agJY7DooFAMtwaeOMVFpz55ZZcUk1iieWRY0FWY0GXk00Mgs9OgtqMPilpu5/gMxpTJVFZBXYq7F&#xA;U18rzSR63b8P92Eo48VIP6qVzH1cQcZc3s+RGYV1ehZpHqXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F&#xA;XYqwrzToUsFw99Apa3lPKUD9hj1J9jm10moBHCebz3aOjMZGcfpPPyY7mc6p2KuxVp1DqVPRgQfk&#xA;cVYnmYqNsYRNbXS9woZfmtchI0QqCyap3ok5eB4mNfTPw/I5RlG6pbqE5mu5Gr8KnivyGWwFBW9N&#xA;hEt5GpFVX4j9H9uMzQVQm3mkP+Uf14RyVHaGoN0xPVUNPpIyGXkqeZjq7FXYq7FWYeU9ClgP1+5U&#xA;rIRSCM9QD1Y/Ptms1moB9Id92bozH1y59GT5r3cOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVxAII&#xA;IqD1GKse1TyfaXBMtm31eU7lKVjP0fs/Rmbi1so7S3dVqOy4S3h6T9jGb7QdUsiTLASg/wB2J8S/&#xA;eOn05sMeohLkXT5tHlx8xsl+XOM7FWJZmKmeif8AHx/qj+OVZeipZlqpnoR/fyjxX+OVZeSpaTUk&#xA;+OWqmGh/71v/AMYz+sZXl5KgZv75/wDWP68sHJUfoX+9En+p/EZVl5KneUK2ASaDcnoMVTSw8s6t&#xA;d0Ii9GM/7sl+Hb2H2j92Y+TVQj1tzcPZ+WfSh5sp0nyvYWJEsn+kXA3DsPhB/wAlc12bVynsNg7n&#xA;TdnQx7n1STnMV2DsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVQd1o+mXVTPbIzHqwHFv&#xA;+CWhy2GaceRaMmlxz+qISufyVpj7xSSRHwqGH4iv45kR10xzouFPsnGeRIYtc/lRfKf9Gv4pB/xY&#xA;jR/8R9TM2PasesS4suyJdJD8fNbZfl9r9oZg3oyBlAUo5oev8wXJS7Qxyrm0y7KyjuSg/l/5tHSx&#xA;r8pYf4vl/wCfw9/2Fr/k7P8AzftH60RpHlHzJDcSGWxdRxpUlaVqPfBk1eIj6mH5HN/NQKeSPNTs&#xA;FGnvU+LIo+8sBkzrcX85Rocx/hTPR/I3meG5ZprQRqUIBMsR3JH8rHKcmuxEbH7CzHZubu+0OT8s&#xA;/Mc0jMzW8IJJ+NyT1/yFbE9pYh3lsj2VlPcE40f8tLm2kaS6vU3FOMSFu9erFf1Zj5e0weQb4djn&#xA;+KXyZBb+TdIjoZPUnPcM1B/wtP15iS1szy2cuHZWIc7Ka2unWNr/ALzwJGf5lA5ff1zGnllLmXNx&#xA;4IQ+kAIjINrsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir&#xA;sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs&#xA;VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV&#xA;dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd&#xA;irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVf/Z</xmpGImg:image>
+ </rdf:li>
+ </rdf:Alt>
+ </xmp:Thumbnails>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
+ xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
+ xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#">
+ <xmpMM:InstanceID>uuid:2fc2161f-af22-0941-b3ad-42b04ffce0be</xmpMM:InstanceID>
+ <xmpMM:DocumentID>xmp.did:0980117407206811994C9C3651001104</xmpMM:DocumentID>
+ <xmpMM:OriginalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</xmpMM:OriginalDocumentID>
+ <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
+ <xmpMM:DerivedFrom rdf:parseType="Resource">
+ <stRef:instanceID>uuid:25309aba-6ddf-a941-9bd2-dd7653570218</stRef:instanceID>
+ <stRef:documentID>xmp.did:F77F117407206811818C85DF6A1A75C3</stRef:documentID>
+ <stRef:originalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</stRef:originalDocumentID>
+ <stRef:renditionClass>proof:pdf</stRef:renditionClass>
+ </xmpMM:DerivedFrom>
+ <xmpMM:History>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/pdf to &lt;unknown&gt;</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:D27F11740720681191099C3B601C4548</stEvt:instanceID>
+ <stEvt:when>2008-04-17T14:19:15+05:30</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/pdf to &lt;unknown&gt;</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/pdf to &lt;unknown&gt;</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F97F1174072068118D4ED246B3ADB1C6</stEvt:instanceID>
+ <stEvt:when>2008-05-15T16:23:06-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FA7F1174072068118D4ED246B3ADB1C6</stEvt:instanceID>
+ <stEvt:when>2008-05-15T17:10:45-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:EF7F117407206811A46CA4519D24356B</stEvt:instanceID>
+ <stEvt:when>2008-05-15T22:53:33-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F07F117407206811A46CA4519D24356B</stEvt:instanceID>
+ <stEvt:when>2008-05-15T23:07:07-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F77F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T10:35:43-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/pdf to &lt;unknown&gt;</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F97F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T10:40:59-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/vnd.adobe.illustrator to &lt;unknown&gt;</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FA7F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T11:26:55-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FB7F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T11:29:01-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FC7F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T11:29:20-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FD7F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T11:30:54-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FE7F117407206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T11:31:22-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:B233668C16206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T12:23:46-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:B333668C16206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T13:27:54-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:B433668C16206811BDDDFD38D0CF24DD</stEvt:instanceID>
+ <stEvt:when>2008-05-16T13:46:13-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F77F11740720681197C1BF14D1759E83</stEvt:instanceID>
+ <stEvt:when>2008-05-16T15:47:57-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F87F11740720681197C1BF14D1759E83</stEvt:instanceID>
+ <stEvt:when>2008-05-16T15:51:06-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F97F11740720681197C1BF14D1759E83</stEvt:instanceID>
+ <stEvt:when>2008-05-16T15:52:22-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FA7F117407206811B628E3BF27C8C41B</stEvt:instanceID>
+ <stEvt:when>2008-05-22T13:28:01-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:FF7F117407206811B628E3BF27C8C41B</stEvt:instanceID>
+ <stEvt:when>2008-05-22T16:23:53-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:07C3BD25102DDD1181B594070CEB88D9</stEvt:instanceID>
+ <stEvt:when>2008-05-28T16:45:26-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>converted</stEvt:action>
+ <stEvt:params>from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator</stEvt:params>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F87F1174072068119098B097FDA39BEF</stEvt:instanceID>
+ <stEvt:when>2008-06-02T13:25:25-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F77F117407206811BB1DBF8F242B6F84</stEvt:instanceID>
+ <stEvt:when>2008-06-09T14:58:36-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F97F117407206811ACAFB8DA80854E76</stEvt:instanceID>
+ <stEvt:when>2008-06-11T14:31:27-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:0180117407206811834383CD3A8D2303</stEvt:instanceID>
+ <stEvt:when>2008-06-11T22:37:35-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:F77F117407206811818C85DF6A1A75C3</stEvt:instanceID>
+ <stEvt:when>2008-06-27T14:40:42-07:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>
+ <rdf:Bag>
+ <rdf:li>/</rdf:li>
+ </rdf:Bag>
+ </stEvt:changed>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <stEvt:action>saved</stEvt:action>
+ <stEvt:instanceID>xmp.iid:0980117407206811994C9C3651001104</stEvt:instanceID>
+ <stEvt:when>2009-01-26T14:06:13-08:00</stEvt:when>
+ <stEvt:softwareAgent>Adobe Illustrator CS4</stEvt:softwareAgent>
+ <stEvt:changed>/</stEvt:changed>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpMM:History>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
+ <illustrator:Type>Document</illustrator:Type>
+ <illustrator:StartupProfile>Print</illustrator:StartupProfile>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+ xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+ xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/">
+ <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
+ <xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency>
+ <xmpTPg:NPages>1</xmpTPg:NPages>
+ <xmpTPg:MaxPageSize rdf:parseType="Resource">
+ <stDim:w>3.000000</stDim:w>
+ <stDim:h>3.000000</stDim:h>
+ <stDim:unit>Inches</stDim:unit>
+ </xmpTPg:MaxPageSize>
+ <xmpTPg:PlateNames>
+ <rdf:Seq>
+ <rdf:li>Cyan</rdf:li>
+ <rdf:li>Magenta</rdf:li>
+ <rdf:li>Yellow</rdf:li>
+ <rdf:li>Black</rdf:li>
+ </rdf:Seq>
+ </xmpTPg:PlateNames>
+ <xmpTPg:SwatchGroups>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Default Swatch Group</xmpG:groupName>
+ <xmpG:groupType>0</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>White</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>Black</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>100.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Red</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Yellow</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Green</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Cyan</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Blue</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>CMYK Magenta</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=15 M=100 Y=90 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>14.999998</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>90.000000</xmpG:yellow>
+ <xmpG:black>10.000002</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=90 Y=85 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>90.000000</xmpG:magenta>
+ <xmpG:yellow>85.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=80 Y=95 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>80.000000</xmpG:magenta>
+ <xmpG:yellow>95.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=50 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=35 Y=85 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>35.000004</xmpG:magenta>
+ <xmpG:yellow>85.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=5 M=0 Y=90 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>5.000001</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>90.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=20 M=0 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>19.999998</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=0 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=75 M=0 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>75.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=85 M=10 Y=100 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>85.000000</xmpG:cyan>
+ <xmpG:magenta>10.000002</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>10.000002</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=90 M=30 Y=95 K=30</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>90.000000</xmpG:cyan>
+ <xmpG:magenta>30.000002</xmpG:magenta>
+ <xmpG:yellow>95.000000</xmpG:yellow>
+ <xmpG:black>30.000002</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=75 M=0 Y=75 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>75.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>75.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=80 M=10 Y=45 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>80.000000</xmpG:cyan>
+ <xmpG:magenta>10.000002</xmpG:magenta>
+ <xmpG:yellow>45.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=70 M=15 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>70.000000</xmpG:cyan>
+ <xmpG:magenta>14.999998</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=85 M=50 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>85.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=100 M=95 Y=5 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>95.000000</xmpG:magenta>
+ <xmpG:yellow>5.000001</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=100 M=100 Y=25 K=25</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>25.000000</xmpG:yellow>
+ <xmpG:black>25.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=75 M=100 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>75.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=100 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=35 M=100 Y=35 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>35.000004</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>35.000004</xmpG:yellow>
+ <xmpG:black>10.000002</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=10 M=100 Y=50 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>10.000002</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>50.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=95 Y=20 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>95.000000</xmpG:magenta>
+ <xmpG:yellow>19.999998</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=25 M=25 Y=40 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>25.000000</xmpG:cyan>
+ <xmpG:magenta>25.000000</xmpG:magenta>
+ <xmpG:yellow>39.999996</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=40 M=45 Y=50 K=5</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>39.999996</xmpG:cyan>
+ <xmpG:magenta>45.000000</xmpG:magenta>
+ <xmpG:yellow>50.000000</xmpG:yellow>
+ <xmpG:black>5.000001</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=50 Y=60 K=25</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>60.000004</xmpG:yellow>
+ <xmpG:black>25.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=55 M=60 Y=65 K=40</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>55.000000</xmpG:cyan>
+ <xmpG:magenta>60.000004</xmpG:magenta>
+ <xmpG:yellow>65.000000</xmpG:yellow>
+ <xmpG:black>39.999996</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=25 M=40 Y=65 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>25.000000</xmpG:cyan>
+ <xmpG:magenta>39.999996</xmpG:magenta>
+ <xmpG:yellow>65.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=30 M=50 Y=75 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>30.000002</xmpG:cyan>
+ <xmpG:magenta>50.000000</xmpG:magenta>
+ <xmpG:yellow>75.000000</xmpG:yellow>
+ <xmpG:black>10.000002</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=35 M=60 Y=80 K=25</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>35.000004</xmpG:cyan>
+ <xmpG:magenta>60.000004</xmpG:magenta>
+ <xmpG:yellow>80.000000</xmpG:yellow>
+ <xmpG:black>25.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=40 M=65 Y=90 K=35</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>39.999996</xmpG:cyan>
+ <xmpG:magenta>65.000000</xmpG:magenta>
+ <xmpG:yellow>90.000000</xmpG:yellow>
+ <xmpG:black>35.000004</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=40 M=70 Y=100 K=50</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>39.999996</xmpG:cyan>
+ <xmpG:magenta>70.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>50.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=50 M=70 Y=80 K=70</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>50.000000</xmpG:cyan>
+ <xmpG:magenta>70.000000</xmpG:magenta>
+ <xmpG:yellow>80.000000</xmpG:yellow>
+ <xmpG:black>70.000000</xmpG:black>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Grays</xmpG:groupName>
+ <xmpG:groupType>1</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=100</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>100.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=90</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>89.999405</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=80</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>79.998795</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=70</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>69.999702</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=60</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>59.999104</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=50</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>50.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=40</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>39.999401</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=30</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>29.998802</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=20</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>19.999701</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=10</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>9.999103</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=0 Y=0 K=5</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>0.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>4.998803</xmpG:black>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>Brights</xmpG:groupName>
+ <xmpG:groupType>1</xmpG:groupType>
+ <xmpG:Colorants>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=100 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>100.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=75 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>75.000000</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=0 M=10 Y=95 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>0.000000</xmpG:cyan>
+ <xmpG:magenta>10.000002</xmpG:magenta>
+ <xmpG:yellow>95.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=85 M=10 Y=100 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>85.000000</xmpG:cyan>
+ <xmpG:magenta>10.000002</xmpG:magenta>
+ <xmpG:yellow>100.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=100 M=90 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>100.000000</xmpG:cyan>
+ <xmpG:magenta>90.000000</xmpG:magenta>
+ <xmpG:yellow>0.000000</xmpG:yellow>
+ <xmpG:black>0.000000</xmpG:black>
+ </rdf:li>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:swatchName>C=60 M=90 Y=0 K=0</xmpG:swatchName>
+ <xmpG:mode>CMYK</xmpG:mode>
+ <xmpG:type>PROCESS</xmpG:type>
+ <xmpG:cyan>60.000004</xmpG:cyan>
+ <xmpG:magenta>90.000000</xmpG:magenta>
+ <xmpG:yellow>0.003099</xmpG:yellow>
+ <xmpG:black>0.003099</xmpG:black>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpG:Colorants>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpTPg:SwatchGroups>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+ <pdf:Producer>Adobe PDF library 9.00</pdf:Producer>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?> endstream endobj 3 0 obj <</Count 1/Kids[7 0 R]/Type/Pages>> endobj 7 0 obj <</ArtBox[17.8257 13.333 197.175 194.999]/BleedBox[0.0 0.0 216.0 216.0]/Contents 8 0 R/LastModified(D:20090126140615-08'00')/MediaBox[0.0 0.0 216.0 216.0]/Parent 3 0 R/PieceInfo<</Illustrator 9 0 R>>/Resources<</ColorSpace<</CS0 10 0 R>>/ExtGState<</GS0 11 0 R>>/Properties<</MC0 5 0 R>>>>/Thumb 12 0 R/TrimBox[0.0 0.0 216.0 216.0]/Type/Page>> endobj 8 0 obj <</Filter/FlateDecode/Length 2389>>stream
+H‰ÜWËŽ7Ý×WèJõÖ6=Á¬ŒÁ`ù€Ff²¸à1`ÀŸsH©ºêºínÛÙ$â¾déÁÇá!õæ_îÍÛ‡à~úǃÛÞoÁE©úÿÎþÿëö‹û}{óðŸà?¸àS‰.àoˆ ݇G|ü'>þïÃöÞ ?á_)Ý—]É~Œ*îñÝÆüw¾%œ}IÝíÒ|•æö‚“Ó8äÇmoÝ Ž8VH¾¶±XbàÚ%ا¹S7Žãè)bõ¼{*‚í
+zÒ·ã¿÷›Dñ-fWН°ÿчÚ\©¾µâ(Šà{÷y$'Ò}íՕ曜s§¸m†C¸?á6 Ý÷œ\.>Kt·;ÅM5³Ï5Ü*W}ÞRpÛºbà|Ú®òmÑ÷Þ—FJæ^s#ùتyŸàÎU¾m}øŠ«âs×NŠ®"eফ|Û®ÌÕ¡˜èTwÚQb·Õ'g_JÜÕ²ë@CÔ‘ÂÆµgñ¶ÄT¼ Kn.&Ÿ+ãÑ‘àšR‘î7BA£ƒ‘ÆhLOòMó?`µªÂ¢Œí-á”Øý
+`’K @øq»Sp=~å¶B”Ù÷éߥ¯ »ùìÌy·dxWF&¸¦q"Ù !³ÂØ·’É ¤±?É7u
+coZ¥ME.DïàÑHúP°æÐMÌøº³?LO˜8­‹‘ª™ =NßQð?ª'vsŠFvÄR6ÉùÜÑXY<1 þ
+f¨b7“˜Õ°/îðvшqm¢¦Y¶×±€w*ǽ “[ÓɲVt
+\ÆsÅd¢åË™gl…j˜pÑ$¶j{GE5äBãg¨?m‡fæãö¤Y)Cþ“kIÅ\ØÎYÇ‚Jo'0°KOÈ¡ ó
+b‹ß§Ép{ÒÄÙ?:¦Íœ  >´P­›`.éZkØHbAw ¥[±7S©;¥h³M**‡œJI&U”•ùÕ¸kQÚ5/6"´`ç ¢Ð$²&:¥7­=\»(ÛEÑ«€M w²!É¢]C%Ë€¼rp
+ʃ$&m ¾ÇEºOãÌRÀÞ<4¶c`9Í0Bø Ó0À÷Ç0€>Ë€€êQsqïúºš,|™=nSÆ 0,’$EN•æø_ä­Gó}z¬
+‹mÍ8P¾%Ö ÙåÛf Ævu¯gÛy§sÒ›šãè\§ȃÎáfÛ@G*Ójô$ÌÍê Ô:öó˜Q,êÈóD1ˆÄÃ,74ÊvˆDr[».(‡:Ü„ø=¼ f‘LŒ>@‘Ž3FЭ¶Ð¢y·êÄ
+ߨ£àÙW¿ˆqùŒ¿ðW`\^q¼ 0~7ãHÅßñgiå…ˆ÷ø
+8;X^8_$u#N$q6_4fRRDa$_G.,KG,$G>8CTS:-U\@;\M]!9uNm.,>4?:?G?A<IjcM@
+;g4KjjeY4Jm2LqQda?_h_MV=H8V&W2),Q$.,>'FpP_cIi[6$W[!b@]VmuHRO.qI[>
+OPH9=b\7i6K=`Wd>0,2!,;/XAojq<C-<TtX-Wh$r;^3@jo&Q'e%uQ/4>N1M8;4^M]
+5OQ(3>$n3;go8kZs0).s9BsgQ~> endstream endobj 13 0 obj [/Indexed/DeviceRGB 255 14 0 R] endobj 14 0 obj <</Filter[/ASCII85Decode/FlateDecode]/Length 428>>stream
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
+VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
+PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 5 0 obj <</Intent 15 0 R/Name(Layer 1)/Type/OCG/Usage 16 0 R>> endobj 15 0 obj [/View/Design] endobj 16 0 obj <</CreatorInfo<</Creator(Adobe Illustrator 14.0)/Subtype/Artwork>>>> endobj 11 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op false>> endobj 10 0 obj [/ICCBased 17 0 R] endobj 17 0 obj <</Filter/FlateDecode/Length 389757/N 4>>stream
+H‰””uTÔKÇtKˆ”ˆKJI,‚t(Ý‹»4ìKÇÒÝ%Ò¹´H—4J#‚Ò’‚(H
+w¹÷þqÏyïyßó~Ï3çùÌ™gÎ<3óÌ
+
+
+SÊhª¦›«·HhÀ´†t^ôé ·'0±ß…€Â™kYæX¯ØY9îYq†q•p÷ólÝ'äãäW°Œ£…z„EÎÅèÄE$´%D>,–ê‘^|t*K)Ç%/§`¢è¦¯\¦Ò«úùñ‰•:¯†¼&D ®¦“¥[«7¨¿dplDa5–|ªmbõÌÓ4Ê,óy•y‡Å{Ëeè¾¶5• «­œ½Žƒ©£µ“3Üìã’âš…,t+w¯ñhòl÷êõðõóõA  ö½ î é m k
+¯‹xYU“›ï“€H„&%«¥È¤
+¥q¼ O'M¿zù3ãKæTÖ@v[NUnn^\¾o]¡a‘b±Tr£ä¢t»lºümE]e~Uü+¿jû×åZÑ:¶zª a·q®i ¹¡¥°5¡Íÿ};¤C¹S¤‹¥›¢û²ç[ï\_ÿÛ†w…ý‰þƒCa…¾Qº1¬±÷ŸÆ;>”L$Lz}4ÿ¤:%8M7ÌlÌŽÎÕΧ/ø}¶XT^â]¦X>\™Ym[Ëùâ³n¸!´ycsk«ûkƶóŽÊ·;ßöv{¿§ýpßÓßúIþsû ÷0ëÈõXõ„éäàôݯÔ3ós®ó‹’ß&—$—õWÆWW˜*ØÂ)ÁãÅ!è$Ü$Æ%¡!e$cºÁHNOAKIMEq“„—úâÖÍí ÝýÃâíÆ•;KLów§@˜‡YúX;ØšÙë8^Ý+çDspçñäðfßÏàKåOˆÅTC˜P°p J%†’D=ðè+å+íûÈO%$*©«˜¤”®œ¡’©šý8÷I¡Z½\£Z³^«U»K§_wLï“þ´Á¬á¼Ñ"dÙxíé¦Éö³]Ó}³£ç§æ–
+—üW´×÷‡3ƒgƒÿŽŸL¬C#u!ó¼áMîMáMéEåvAmsË”F‡V‘NŒž‹Aø¶£ÌGL’w…A¼Ì¬,ô¬äl¸l¿Ø¿s¬Ý›ááêænä©àͽŸÌÎï+€„‚!Bê²"¢¢<bÌâ4$’€äñƒÝ‡kRóÒ“†eúdÛåšäk*ÑJùÊY*/TS'<‰S‹V҈ԌЊЎԉÐЋÐ0ˆ4Œ2ІÄÇ?M4I~–fšŽù²Ís-ò,ó¡…°"+´u‰M©m™]¹}¹C…c…S¼Qæ\êRìZˆÌsËvé‘ê™èãîäëëçîGÙ˜i«„H‡Š„ñ„3GÐF’FaGDïÆ¬ÆÎÄÆ÷&´$V''g¥$¥F¦¼ðLG¼´Ë°Ì4Í‚dëçèäjåiækhêéCÐf%V¥NežåAq•YU¯ÚªG^/ÕÔՃĵ›šÃ[ŠZ{ÚVÚ±:˜;å»`Ýá=å½c}ûïnõK ˜ ¡‡‡FvÇ(ÞƒÇõ> 'R&k?¾ÿ´3?Ã4+:§6o¾€ü¾˜¾T²\·Ò±Ú¿6úåÃúÄÆøæÈVß×ÖíÊÌoá»ðïF?LöžîüT;:ä>¢::>ž:©;ýeqÆvx^saõ›òwÝ¥úåÊ•óŸï'_
+­*‰êÕãƒ'[j êão5[´ª´óuR1uà­ïh`f¨m$1æxJgBdrölÇtÁläy»y¥E¦e$Ôfe¥g-g#`ËdGbwj¿î0áØáTOC9;¸¸Ê ¹Ü¨ÜÎÝ¿xŒz6zåx‡ù8ùøIû³£ÈP‡ó=A¥Á±!.¡†aÂAxÛ‘
+£\®€l‹GâN‚ÐØ»ËÚæÐN´ÄÈa5€tÁ…N€ºzlÕß´ÍS<º°H6Ѽ¥*ôŠîÝ<’a|˜Çkz|C±V޲|šÇ|ƒ—‹·|IkžŠ‚|±Ru‰ˆ}26މ'~2‹ÖÞÛF‘-†ËÆB…„°*ƒ„o™²‹üƒ‚ƒŠ­‚òjŒ‰‚ˆQ^ˆ£‚@5zˆI‚`Š˜ƒ†Ù³
+~æˆ2—êgK‡>•-N}†o’Ì2Ù†‘Á N‡%ˆÿÕ§±°ÇÀ›‹â¬
+‡0§ež†6¢çM …_Ÿ¯1… ? …—ˆ1Ó£ŒËÆÇ¾³‹ÀI©‰^º’ç‡ô´I|B†Ì¯dö…ܪwL‡…§e1$„¢¡: ®…‡æÒàŒrÒW½ûаÊñ¨]‰ Ãá’1‡£½S{†z·|di…‹²ÇL „½®g0·„\ ð U„އ©Ìû›ò{[¸“™G{!£ö–¸{ ŽÞ”`{&y’E{xbie{ùJrŽÚ|“/cŽ5}~
+~ÀË:šâ„ý·f˜#ƒç£•™‚üú“M‚Kx+‘CØa|ušI~÷.yWã ÏŽ›ƒÉәߎ­¶—%ŒÂ¡å”˜ŠøŒá’[‰‰w!^ˆT`ƒŽž‡^H˜*† -§ŒŒ† 5G…èȨ˜ç˜Ž´ã–=•Π§“½“4‹»‘rív†Žÿ_ÒRG±Œf‹û,ދ̋ͯŒ…ßÇ|˜,¢†³Æ•„žúŸ{“ ›¬Š„Ò˜ªtìŽÙ•ô^1“ Fî‹Å‘Ä,;‹'‘¾<‹!…‘Æ_—›¬¥²²”õ¨Yžl’~¤J‰„J tŽS#]¸Œš'FA‹4—÷+®Š”—;ÛŠL…PÅf—&¶ì±É”€±Þ‰’­ˆªÕ¨šs@å¤]Œ0¡E±Š´žÓ+:Š›ÈÖ‡SS ð§ ¨ŒêjVPp,<XQ)‚XVl€(Js•&ÂJ!ÒûK{é½@B€ÐEŠ. œe°íÙWqQ䆓³â½ý~3ßÌ7ßÌçF£HI^ý\¨¯§¬ÁïÓ¡ƒåœ+j&YÅöÉbƒdMLÅÑÅ{#©©úè÷)Fø£ÛAŠ;q²ïT0Œ±_à ^¿£9IݤšxÖº¶Æ¢YFh“9Õ…ÆhX;Jõ²Æl¬ äÀín+jÅÄ¡®(Ým,Ö¼Jƒî(Va÷V/ÀÈÄaØ‘”@ÈQ~.æ8_]±Èš7]ñÕÕÌÃãõÚgÕ|ÓFÕ) -—/ªºMH—|­Š¼øP´œ8™%èoFxàCÿ­<÷ªR×l® AÒ^«™ ÷QÊlÚÁzÙrJAUžUÿá™ÿd*ÿùüîNî$iÜÉ #Ÿv b7FÚ*4Þ‚÷ú¯
+ Ëû§pv€iâxÖ–¼÷ŒÁIªão¥‡ìžËÝJÛ±á#ç
+í¾ËW—àh…Ô‰Šlý"ùkx wVjGá“Dzˆ–ç*¸ÇßœxŸWÁŠ:Æ©d¿ÙPÀîæ$¹üΠjÀ4W·Mž¨[ÎÝ,]¢Ê«$ˆ–ÈWæî$I×&®åNˆ¬‘}ì7¨ sØþ»‹1@À9‡TÉŽ.íEÙÓzN¾Dfº*uwr þ„úØ ˆ *ª#Øùòë­,¥hØ…Ãð"ä8=”³NÍ ¡%ƒ=(‰p¤â…QuÃ9søLÓ“(î&Ãâˆwì¥Ú¾õ?±‚ER AHi.RÚ~ Ô„Ë6õ ØÑâ¿:Ò*tД3*»–¿Û±ôx#ç‰m?r~3×û2ÿ+Êwù‰¾šp¸ÞÇ6‹yXaœTOÃ#Œã
+U‹Â¼<·_)ÂIø(»Œ}¹ÓW2SÎX U€àwü õ¦ M¨–f›_T‰)•úà“Ÿ?k–YYF…ôK8ãÁu;J$óÀþ*1T…
+µ´.—÷Jl6mçHj†uÁ,béU6£+s©ì ýhܸdœ-Ê¥ª}…wi
+{;SÂm¶`vâîØ¿`~È?g¥a.œ
+3žÃŒì{§­Çæ†L¨^–»¡¦ÔÅåW€ƒ¹¾ÍY¥eÆ4õ‚]¸Lû<pqÞ´…Ž—§´Îá‹÷åkêñÕ!ùÊGø”]µ l
+ì%€üÎÓ‚æB®sŠW’îÞÐÁÄ[ÇØ~á}F.+}W—ÅöötÕ=¥Ž
+é‹õKÏe&ÓÑ,Û^çÜ-¹œv«a¡(0¾Þê… ·t¯tzþSûœúwÂ0D¿6jäã¿tlÔÞ.\ÑlVÕ¤©í—äã)žÌ-gØ [Ñ­š'’Ïxí|Ú|D­D”ßµ£ö÷øáv••wi¦Ù×8”ư=×Ý‹¿bY¨õ
+¿¤ïTC7jŽKIžùšXš7
+æÿÏù_L‹
+u´®Râ_,^V¹àS&åaR´~PfÄÜLL_Dw¸ž*Ž‹`\Í-¦9]qîõ®¯¡ ¦Ý –Çô¹T¸Ií6)ÿ>u6 DÝ`eÍ¢/»–¢xq¶Y%¤9ÖÛÊœ¼;«Ã¥Oêd\˾ÊP…&eRËzïç;¡]¥¹‹é.íRµ<oªÊΡù]öP{?¯:¦ r̨\˜µÝÊ»b õÒ¤â3|ª×íâm ¨s¢ØŸîÛÖW°9¢¨ñµæoZt]Rn“ÏÃ…\½c…Wö#+…n•I&õgϽØÝy¸œAjsÑN‹06”HiD­±¸È'@ÿ¥ðõJ+¦ša5V~€cRˆIµõÌ«ïvwtUÓc[3+?F|l(ÑÑiÆÚU—žøô¥^+O—?Rí°Þs©šÿÿ—
+ØÁ1¯B›¹vÑŸ¡®nÛ˜F/±ã BsßG³åÄÄÃMœYø«ëç9>£Ü–3……È—qIôƒ Ú£±¿5ƒµ¨ÕV‡Ü_À1È£¿Î²œåiÃñ¬JiXû0ÛWV–ÁîH[8g„«
+n3¥ `€ ¤Ðþ38A.|«f|ј0Iõ½6Öbå v«%Ó&ý ;Y®ý㿜ÕÒ„#
+¨ýQ<ŸÁÑÇ
+›˜;/¹¥Ô±ìJB"¬‘ü7þØtÍšù©,mºóXdÚ¾^ë;ì´žŽñ&sà`«YÑ"à놠ÑYŸüŠ™¯{>ÆŠÕ.õßÀz^48¯‰e!¹ôR‘6}ØõvcûöìÆMiŸozî²Úoñ0¢'“=˜~µ¯Âi,3Ã:?-‹©Ý?oS,Ùõô9áÎüwß¼„µ¹þ#îÎRçOaƒìÙü£˜˜—ô;Æ ?àþäïŒåpïÍBäÒ­À
+±ÖžI̼üO´ ÝŸ÷e#}Ô¯èN$í\êŠl?¼],÷ý Å·y,ÜÑ>&РôŠî䩸q]Âyhâ0AqñâK)ÄBÍF¶“ÒcH:ñì—-h-ñÇŸcȵêf)¯K9T£ã127]çÑä÷q·EjLˆâí•<>hÁ‹;ù|ãU
+Ÿþ¼ø×à”dÜp‚G
+Æ«½`¹&!…Ïä8aðÉlû³›`83>Ì.‘òq„ŸÉ‚ænç…ŽÚÀ¤Aß9
+;èœ ï ¡‹­ê`¨¦H†ÚBÕyåg¼ Þ KBý ¯*kïã°—2fF„=#Ÿ OMã· éeT?
+m­òTmê_¡å“ãOBÛŠV
+wnKÎÜ‘QVñBá"ÿÃõý×Ô½pü  *Ö+ت€ED¯ È*  ¡¶ˆ©(¨Ê²o B‚'{p’œì½wH؆ʴ*JѪmA-n¬¨WjGE­íùÞ¯çy~ú~?rÙI^WOåÔ@÷«Ÿ´¼l)ÎÝÆ¶±WïògiX¾›¬"æC¯#ÌkýànA‰Šæ¸ §Ë»¤rî¥ó)bÈ!¢UýÚ²I’ƒb=³>Ç0ãžß´©ãŽy}ĸB)Hä[èöžÝðFs´ ²òV¡+Ÿ±ò̯+Y(IŠ•™(º³‘Àx&Úá9JAIìÏã'tXmyœGÄ=œX[8éTK³)¦2œí©“<ÇTSRüv¥x£ã€lÈ“GO¤|Òg‚/÷{Ø>ãºÑ4Öù/’gËRçáŠF–ȶíà&æAÓ5”š2Ü uЯ*B<óiòë¾
+ar†Žæ>Ý>Ƶrn®[É­äF
+p†-œàú1ÏÝÛz(þ=Ôf)
+‚vŠë¦Ä›9øØ¡Ö2ƒ w uûþç…¼çïתÑõ#è…­Á{P6„+DÉËq3HIiŽû·Ù%„ûBóC‚’b¨!ÔÖÔkc5‡&U ):X®$ÜŽ–ºˆ[®Èb‰2¤*@ÀPkcÓ˜‚dÓoT©B_L1Uwi"É)ÉÌ=2#ù‹˜pI9, RêO>‡T@>è;¬bn¿Ðø¢€DÈþPuCý«ë›ŠfkÚéã^´^ü°\ÀŒG~•  »o—LÃRc¬HqÜ®=-8^5OÅ„µy*9‹û:-\òg8:T<á?*ÞýŽ´C;¢¶[‚µ·y¿XªÍïµ+±•ÙÊI©;÷lRLß­“$Dõ‹vYþTQ6üDyƒ€VmÖÛfy%/sIsƒ†mXP1ªLռȭ’¥«òvow)„QBµb™†ÐÀ_
+Ùÿ®ß[Ak]Ùíûí„åJÛó‚͆áVBÆMí"{íNrÜòãæùQÕ¿òih
+ET9¢¤Ù²FÞW¬Àc»ƒã¹ám$úµ="G
+}KÜe‹š6±~Ö”G_å³ÒìMËDÔ¿µs`± gî]£v¥p-9§\Nôžd{:´:Žgv¶HÖ_3fC;}ÐÆ“ 
+œ¯è‡-£"{â ‡TZG ôˆRØ,†ÍɇjࣶZw8j ICFöêÕ:½b [ Æ\+å?ˆ
+èþ™oA n'a
+
+ÚôO¦j0ÀÕä`ê%µ|¶^é/si×_¦XÇ£ýLqiÔw]–Iç©Î­÷D"J¤î‘!y½!ñÔ¶®nˆ0²Rá zå WC¯³câcúh™üÈÅ«KÒ_R [úÓ*)! OSo‘©u©µ¶OÕ·ÓÜŒWÖãíze¯WÞCΓña½²¼ìÈÏ’Lê·Í÷/ÏQB´o³_“£kÇ/cg£ªÊ&MV?ÇÿÖ«5zõÝ !gÇôÌë"?wœ–PÍš—IÏQ"´Î§ÈP­AdtE)q4þ±jŒðúGÁsƒÆÕnñ»{"” ‘óí}e®ÔUMÑrkÊùzvi89C}´ø®XóLB1W¾".ê•^M ”ÔûÄût?WK¢LÛwÕDP]t£U‘æŠ1r»J«ð·ÓÕ¸KÎÿV¹—ÈÿGM;phÌÕ·m æ‡v>èÀ-wgØõ©ë¡FNý ŒÊcIÜPþ#q‡òW• ŽÐËI –˜;ÇNáÙ¶þÅAÐ)ŃÞH~¡7iÛ tòhlƒ~ýÁ~dz²YË CûþåÎxŸ2>*c&Ÿm‰b›‰­ä{9f¢1X*²L˜ ß#>àíú
+ V@÷…¤gè’¼]7n249=MÅÌÞìK% ¬;™’,ÙÓéœøFÎ\j˜ ð1ÅÑkl·Zå‚i¢×ÓØŠ’áíôœ²Î¡.|Q¼…9˜ŸÐ°¦$_.!“Ë;¤Ì¿¼l€ÎôEË,€ž²ûÉ¥Di­ùõ‡´¦êŽ}D‰É3^Êa`Y5Åg{J=mãìɳØéËyž3¸¢‰C¤M'¼jMê£-iŒÐ¦ÑmêÜ »n5? ÌSJÙE+U~ ô;·ˆí™Åq.t¶Xd÷¤Þ~œ˜Í~p*QÐeS¯¤%·.‘ŽÐ‹œ"ưºöBs¡ª…ZÏ6-çîøÄ•6[\d;¤^z4’`;64è—¸ùú¤æͱw¶àô;ùŒÝ|ì+&®A©f¬LUàŽ3¬å¦åŸXòT»ßm°åô–¾)lãFí'l óVæÉºÃgcGÙObßbÉœ9ª;v \ºCƇL,Ž >B?ÝKGC¬·e"°z -@¼E´É£³Hûù»</äsˆ=/Ýá‘Y¡Œå{øûPü®w\Lí1A “|U{‘_®
+ö\®€wçJ¹çàÊ»³ÜuY/|Ï•d<w›àΤc¤~ÏŽéÀ¢êñx¯Â¡ò#d£ g3 ‹ÅÓð~è÷ÂËgŽÂ‡ó‰ xT2íæÈµÉ’6s3Äi75À‡Æ»íZÐE\1Æ\éɺ¶W†A' ײ¿Š M }?;Ê‚È8|RÊ0SÁ#⣓
+£5„æù Elœ["Þ,0Ê eØòµÙ[Ozò‰Ì0~lúUO¤›+ùÓÚë&x£ì®kPù²c|uÉÙ$kþ.™?Ý{Qp""kÉí÷óÃÊrš6ÿisVûa‹ÄÏ=~‚@Wí_®
+.<Ë7ª
+­Å2Ë#h?c~¥m'r°ÕEÂ_ªxs…ë6‡ò¾øÍa»‰¥ÝÙü±¶Gµ+øš¶K 1¢þ4Œªä‰L^kUpø^^ù•_–ñ
+ Kû*ÃË/@hUvŽ„«…‰¯$÷jœ!@ጠÃvyבmŠû´,W²|-Í¢ –^¥ Õ~€Dô„²_Áô†­’"Æüĉâºàø#cØç¦˜Ï*íX/‡¸Ïœ“e>Ü|õXHÍù;û:˜Å)Ý÷‹d9gÆ–â4àaBŒÈŸÿQ4
+Û¯B UÊ#à>ÉSV§$Lì-íÏ5gˆV ϯ*¢BüüÂ#‚ß}¶ npþÒtd½­¼ U˜$Dõb&$^¡\^Ò&Z"/˺+Ü-}¼Ò%è‚Z眅:}ðû9•‡ÆA‚ºYu§À æírTÜlP0Ô"šî·â~! ÎÍš*@5Kâ?óß«Zæ-â¿PÍó÷˜³µ=jô³>—܈¬[üO?)¼a5û
+®™›÷ñ?靯WóÓÐUs»ù‘°yÌ5^(ge›${C­‰âÏm>² "¤éÝG¸Õ£+Ú$Øè¸¿©—Ï«&¼æ XwÎÝÍÓêÆãÊ8?´Ígž‚,'Å=Ò"‚·—–/xÕúÏÂNþƒ¦îM)üïê'Ôòöº–ÌåÍEFÆÔq§rfÔ CįQ9ZY‘$r!ñ¤Òê6m<YZС.©<š©V^«z{UÑS=ìP
+køX%3Öxúõfj¨îŠn%^‡ý¨·«ið´µ»Ÿ«´s—Íô±3ä0ý—àqŠhÆ$¬Ý`kܯÐD&
+è2ø»Ì#œsL4õjñšÃ&ÞD•Ÿîi¾b&,ÜA‘ýMS’ÖÝUµD£à9åù yà
+vÁþÀ98ÚßÜášÍj‹c7MI·~¬(‘sL#ù}Ò3†¬´(Mˆ^
+´ãÙO²ají×X†_84ÍV(N“à-{D)´éF¡0«$½!àoJþÃdv/_øõ(ÿÇ€ñÚUØH8ÞB
+Šlz&,f^ª_! ì?ãlâÛóŸÛ2x2¦X’yÎèåÅ„Ü3Dó㌼)Ø\
+™á´øEˆV¿D<ŠO0ä‰Vbj&¦¾mLBÎ2Z܈öb9êðU¿È;nµ©â6L0 ƒ+–êoˇCrÑE.~µ>TüSØ“Ô7X¬³‰®?MMå!ö½†Ô¼u–Oèôt›ñ’÷P Cbt;´·ðièñȕްÌãaøº@gWÙÛ#Û@ë¼ô4cœ9.Dõ¦æo‘ z2>M÷5¶þi—~Òu0¬ ¶qºsæwî®´QÃ9çǸLt–ý삟‘Mz)>©ûßkÉ£ûËI÷;º”îioï"’²›U
+ü\Y&kñâëVÉî»x—ë…£3ª¢9µô1ÙµqQ±=ébÃeMq\—Áª¨¯Žêî©`/nņ|ƒËþ2†ðÍŒJkz¦DºmÍ«ŒIR4ü\~5NlðÔÖ…›ôÇKÉZ]TÉC¯3ål•Ì…©…Dî3jSS©)’tÍêWw$IXß[›wºV Ÿ
+WT™«úUòŠw^PÙeU±‹®ô‚hÝWEµ^Ø“~WÓcÊhŽÿ¾s ±s£õI›g‘ù¨ƒ¶`Šÿw…¶gäs (€5£ªè
+xÆ}ÃЂM0Sõ,r„™ùV+ K¿×O&ÿƈ´`¨;­E{irf0
+-°•cœ8òC¹êÌj¢L=L«Î1þTDJ—º»7ÅÆë>©Òê²Î)BHå*£cúHY}~ñxI,{驯7‚W…îûjWÖÞ§…ʇçãh¿g»_óÀ·YovÜMKiÖN>ü”˜ QÕÖRÒÒòǧ}È¥Aû–Qžj^¢©G ¿s› yJG"ñ?txt,LýÕ>«Öp°±_‘˜>ÞPo$¦Õ^<…%}¨ÊØK¦D”ÔS4¥
+·*S’<Ü–Òyñd;’šÔéIJ»Ó~J®Mn>ÉÌȸcI6ðu¯¢†É–Ú˜€å䩊i77Ê_’5WŽ2' 9­øtÆ^}/ü˜8%wdáÉ
+¤A h­ämŽ4™5
+à lÙƒÄ=ô3¹þ»_2~”–­OgP³¥s
+˜C¢‚þÕÔcº¾ÆdÚú²[ÚaÚÛ’{<Ð¥ùƒjÔA {! ß²Û“¬Èñ;O'Èá9+wEHŸEÞÀµž¡&†J¯ÒVé?fáiÓº j0¢5瀶bhW”ZxÝo=”¥Æº 0–ˆÂz‰äöhK5å’m¢ä•oçÑv (Y€Ouô±óà¿t¹;‡ÁËe=¾ðRÉ*ÍïyMVõn²,¡$v:•ÄQçÚ³EãÔ.›yŒVlò;æsvn·Ö,ôõWi.[¡÷ÂÉ¢@•¦­„¤34SüD_!“ÔMú›ßF‰>J柣ND ü»@$ÊYÀ~³-³’µïCÎMu (+lBpБÌÈ^úâ#$œ~÷2è /@Ì£ë6 3nðhÛ
+ž;÷ª¡û‘Ûª.âþ—«¬æÞæŠ3F¸¿q3ã™Ü\ÙŽvÞZnþZ"/ËvNF”ÜüÖNŽJ2V{œ#ΚV¹sØâÏÈe_ì‘®àTˆöa“8C¢°!ΗÌ÷>FîÎL¡ó\ÞþM{û—ü5®ìeH~7;FÎã Ù ²AäúÁ³»¤—B?³ÿÈV¬ùÂYš=Û©‹»Q i9ÌJ.sÓ¿c%–è•FVb‘dÕ¥iL–º`Ûa)kD麛=W®Á³Èœ¸œ‚ãÍ \nÀèçÃeÕ>ŠN¸X7Ƒ†2Iª¾Yf-Òto7±¶«/®~ÁòUýè„as[š†`W›®*v3_”`~©û‡æ:kêÌâ
+*Š€‚
+è´¢e)DDIØÙss³Ü,÷f_n6Â"¸€Š:hm±h+Ô]Aqñ•ÑQq¡âSaž¤Àç9ï{~8ç|³~bÌh6âG«îíZÄ ×N\Èh´ü+„Ë(E3§0ï~Ýùô–ÞÀ†kô–T‘MGºÎ²¼èüÎ1ü:zk›a'LáøG‚±¨‘2‡>ˆºýÅ,
+ýœ¸ˆ=@€ÆIhÆs¨ˆ²á)H…ÞU§OeX^mÈÁ7R7Ê~Ë,ñûê,é ñ\jí´±»¿J„ÔŒfÌͬíÝð¥ð8!*¥]˜JR:æ‚öWâRÖ]ÓMˆÉš” ‹PöœZ«ð;¨¸JÃNÞ.8ɦ®ÏÙÆÀ,Á[¶r*ýΑ¶]MˆòŒM"waX)Lbjd‘`ç>:ÿ?ü¾íù|:?uƒ>^ÒÏGÁ©û$˜ÖfaŸâ.Š
+Ê¥_àƒS‹ø%²EDð–´8½ ¤J¶Æ=£Ä•K¢Àâ©{ƒ·À´Ü6r zþGÄãGêò«‰  ‰Þ óÆU‘ií<‘‚—ÄK’gçå"÷¦¶^ €qŒ
+ü£ôùI6±°¬±vP¦¶‘éWÜy^,š†u¯c/
+ƒŒÂÌ÷pt¬ +ü°;Br\Ü•ì'> ¬Þ-övø÷C¹NeÊ”Ûô²ÈL-ÊŒ•qÔçK×H½”¯r Ä7ÉIÐ d<òޤù¤BðgNelB^ÖƒRγF2èÒAÓqCR€&ätŠƒô³7ß„ü´{" D9š·Þÿu)—òCð­’w²¡æ1úˆt³}?"þ¥¾'Ç[Ý7oÉÌ©«~ž1…{Ÿ>Rìu*ô ‚íÊ–dÌCãôlÉÈÁuøtqf‹2[ÍlÒ~{S4Ã>±J$Œ«÷÷.nQ¸nlåPÝ#ü
+ÔDæQ§žd¶¡‚‘V„Óídè24¥Œ±ÄKªGÌMËv«ü˜U35«˜KJ~Ù4
+§;•^§ jÆã›‘Qõ`ØexHý³;±JË\*Ë`lú¬Ë´KÄh¦×k
+&”ƒ–ñtùˆéF|(²Ý8VÇ¡‹Ü·ÁR×€§:ϳû´oÛG*UjSKk¨ÌnR¥“ï±gl¸ ÷ÛÞ…-‘6¹ûÉ&¥±NÅ—´ÍÇÇÖÄ7Oâ4”ÑrGm´òO[du_T’×vYÖ{‚ ÌIyØ\ëaR Ky¶¼&•Pï7ݪJ)éløƒ¼"—Wÿ5Å{K¤å åÓÚS_õ¥àå¦j
+W>èÇ/wÕͺÍhú4EèõkÙ5Ë–’<1òU‘¼Î[¦táþÝD°Å>îQ!˜.kR¿Ïæ¶§ä7˜uÆ
+lûªˆ/ûüi^¸á3;i÷°š‚éÚÝ0ƒæsÜèÁôÄ€­˜ZÛnÃSÆ
+¥»ÀqW7ÏN³“p:ó([ˆ½Ý5Ì6‡8·½„Vùi•AFÞœì~h¹9£PÚldüjì2îdO
+²+íÄñŸ¿6Âü1Ÿ--üß1Eú×üwvø¦ þ=JCH«šW¼3ž’4Ü&ŒxÝñ½8,¸Ëà&ºëãÐÔ#RcŠ3Dåvzý6‘…RS„¨yuè´_N/nm÷ÙƒØÐvTÖÃàòÖ¥Y˼?êåRF×°KzÁnª¡9ó¯ã•ÉþQ®æ4ùgœC×^Ù5l`ºP\ܲº€âýG£&ÿ¦¡Þ«`÷² õ±9ÏP¨ÒñÔÒžÙ²XÜÊÎrÕ6Ô«Œ
+V4,ùÖ{²a Ø„ÑÖý\tcY`]lÎǿԾ¿a®§°r‡é´¯Ø=bèÖ!ÁÊ&ÖÉûYÃb Ó^[\ºaYt­$w
+¥êø[ÝÄR¨)¡§§iÛìº[ô{$™ì7f"¥˜o ŒÓò“¢X‡‚p
+»Àz¶BzðÎÒŽ'hºO|ÅŒ4ǧ|-•jáÂ
+:ž•É}Ì´a«
+ªUé6§DUÎeõ"udPO:ª¶Ç]£x+¿ì ¶GºÿB½®˜]´€q:óÊ¥—'ý:¶-ML3\ÑF$ç7…Kà3ê
+Ü3NÝÃ|›ó/Ð'ëèO¬-R_—1Vh&׺ N’Pz8de™ºî 勊ZTH;X‚†ÐåŠQÊ6Æ}™ñ˜+°¢ 'h´Ï_î
+[•¤YmÎ Ê~uå8Áíp`6*I ß•Î`S8‚Ì»8s™nŽû9O3nX¬OE /7f•^ê…lËbNÖ[®PBµF¦O¬‚.Ü9¬€ó·ïZç¾_.Í5¡>¥F ›õ S¹µÌ‰µR×'}ΪѬ`_dÿXš|ê{´dâÐHXÔ¾3šQlZe7Pã©R¡ªq¸Ø´³O5¬ºOkZþÏrx5u`a
+DPQÊ®²²°dß“——¼ìûJÈŠ ƒ£Rk=H+
+*­#u)£èèh§Õ) …Á)B§6ü¿sï9ß¹çžøïHªZGòµzçG’†ÍTø"9ªÕ3hŠD×ͺ ³Ïs¡îr|ÆÅbã4þÅy ¤$”õTK ºá•"ù$×IÚÖ~Î$v(ˆBìÖ#].²ïqiÁ?­C°¹N ù–ú¯ñ~úÞ±Ë|ìíÜ·àLcOÀnÎö¯T~ø³ÜýäþvxjºÀ̦5<.ïÇf\K<ÏÞ2pô:CpËõá•Sây,66‘ê>|zŸC
+Œ“¨ë
+T
+®†Šï!Œàzõœ@#(˜ãóT ¿œ¿6ªŒ ^ó¢ð!§R S”¼#Ð>E/áSÁq9¥z_ /G%Óˆ0Câ9Æ[Û¼‚@”(Ù©”ÝP•é À, }ð¾XŸT…Ok‘‡p¼µQÈ«U¥G6 ª€ ¯x2¿e,¸Ú>
+ÖgS'µ"b'zL=Nª)c¨‘s×*æÈ÷“bä¿Rˆ¡ëä÷)W<ü#–S ²µç™›)Kê÷Ìý £ª
+&¡ô”âLã¾\¹Œ9WËtÌW!Y1󲫬å«ä7i*ø%”ÕþêúwJ_°½ä¦Ü îÌé–¥ñnÁWJñ„!pÉåþ³ä-0éT`¿:K6BÆ+»¥SõÅzÉl‰¯L,~“³J#ZL´HBEeÛ߈E÷q1
+Ú¸¬ÂTD}é­bB;º*OÑTCÒnÕlŒ$OYŸ¸Qœ®0m·‰z7ÝoŠ„9NÅ» |hý–DV¤[V¢’eÆÖ©b7É£Y¯ÅZÖ¥H¦Çlþ¤Ûä~Iè)Ü»¬J•5§ø¤ÍòoO–ÕÝ‘ù´©%(ûŽ,¾hÞZ°GøÊÒ¼mR˜d´¹ë!¤ð/NEWutV57z;ÂjôÇÿj¬s·ý»^’^lDçǾ0-a®û­_a®ÏLíØŠówÚûö44ç°b^³ppi‡úÌúô&nÓXì ã¾æûþuÆ»-«Ý‚ ž-cÎY4Œ½_Œ´¦¶³šg ?ÚjºGìIãfHõ±ú ¤%ÿÃJÒ‚[ö%Ï©¨C6Oz
+Óú°#ý‘Qî`”Kõá¶ò|ͨ%cï˜j¨Í/&\™Û: [­øFƒtû^Z
+ªžw" ~><¡Ã óŠ8Éièú½µ}×XåT8°Ç³QÙ†VY<…Ä<ÏJ=áÀEÀÛ7G8v6Á¼Z:¢%¿·¶hBòò94^™Ô¥m!1¼ˆOD>íp%¬HüÙÞGè‡ÙðÄ/Û`rqÑ;¨±N…mï~MýÓñs§\©‡/žˆ¢Zh:•(¶í¦¥…MXžÐ°^FÛ.꜋‹.–Y½¬þÏs}5™`a
+õ“|ï°i-‚¿0ÐWs…÷
+Q_Gå½pRjŠyÓ0˜òÑ׿’t¶©¤øj²ÑTÌŽóԊD1·ëÚ…”Ú›NÊ:ka? ¦øç7•eê“æ£†k–Ø_µ%]a;ëזו¯F«=Ë9™Ô-b= &¿óMm0-­vDÅ'^Çj´Û+µ¡/5(erÀ^õ+E·çL F1ä$¨1KŠæ•×WE|fÔ×O£õFMK¬º¡¦ömèí:ƒ:1`»Ú¥fÂóƒŠXýЩMÕ*i€9ÈÊ
+l?+Lw?-NxÕÃ͈Ôwɳ\C0–óÙ瑃f ÎsÒŠë·M;iÄ‘`°Ê$èöµµO»“0zúÁ*RÙ¹œB9é@Â"´k¬5ÏÚíÛçvÀü~•.lð³B?îu
+^DMk„œ,2ý.#£÷ɲÝ\õ³Ò!{®^âƒI4ßÔˆåµ.~°Ã§ælDc‹BU\bò«"c äj¤Ûvý‚ÊÃöJGÉ|ÏH`_ÕÈ2ÜrÔÜÉë„§ëæH»³Ñ¥ä ¸§tøŽHH®†BaGŸ :‚ú‹ýBÖfšó°{–'ñª9
+[ˆj¥øaЧøñe
+&hè´°z6FdÄy?¢>g¦Û‘x&él$¦ŠÙÜ^œåÎ:¬¶Áæ³^Šèøn¶‰úŽx-'-µ¸’û]èO 5×⽜äØÛÎ@Sä UÞÚy]°Tu à_,zŠáWPT„|BJ,É•}`8Çß´y?pŸ7gË¢uóŠÏ\JO(_ØâŽvOUue4•öÙ+Q±¸Œbi²?A.jÞÿ´Cxy“˜´R¸ÊÖJìé§¥þPÕÝt㸲rðƒTôúfd½d$ÖºâFR>Pâ¶aºL·ñ'î´ˆv÷¥¥äê2M—Òë*‰¨×µ¾TÕ]`WÖÝ*ßcDÜ*åûhøA¹e#ï"ûɆKO9öJ“ùíKL‹Ò2š¹JÍñçø( KgK3jÔ‰f¬Z¶÷nLåå5‹ýo•‡M(½_ø¸×>éFO‡‘—ÚÓ¹Gi}<´ÃÄ@w¶¬#Ndº­áÙhoo4öYÜ Ì¾¨FÙ¸2¬…—ºÁYÔAƒšz$ÃW±„Öœ5ÃÃCoplâiÆÂ\ ³ˆÞ3Ä2‚l„;…òaá¶<;šSç?
+Úoe¹*óë
+°pù‡²}¸÷²äœ%„á
+Kö"G²*æ yE™%ø¸S\ê¡#§‹áVï9¿ ¬Û¦2¦4=µâ¬ÙZH…W}dÃñUU¢ù$¤UúÇ>*;ÿ$;Éd'=–NYÝÌ ,|ܶ3Û4§¾”ù±q¦T˜=©ka%â´õ™h±s¤€ä¬ººÖU¯X7Fl«”[õå oµš1apuxf¼9µäQ¶Gk4³;e
+˸€7è‡ß5ôîxB:¤y¿î¡¬Zd¥Í«,¼`2²—?_Ía[0Å~9iY Fs®3öÿ˜g¹× ¸Ã‹9u¤¾ ò<,yxí87¼þ© ´1‡‘‹¯¨JaÆ¥¢,O€@/g½½ÐOÙã”›Ñ94 ð|.]»1÷6ýè'Å^Ô
+.´¥2…}”´"!<ý4t¢H~(®-rñç2¢æ5DºH@ùÜlÚÀ"K”»““îæ¿£,“¼/S}ÊëÓÎÒÂÊ"+ ïÅ~w ¥™«£—F}V dRz™,¸¨:Èw&?­ÚC~Fq¨ôJ}JÝ¢ð‹˜¢ÊJý’i¤´ÕrjzEÒgUÿ#±Úp]ZFšÖã%‘Õ+Èþ [íPj
+D¥¸àRNXAÐ\0JSФ£H‡é30ý½7åÍ›7½3 C•Wc+–ÅU#Ær# aQOL4E³Ñ™?às~¿{ïïÞs¿I§èªÖyô?›y>ßÒ’LŽÔÖ¸Kd-Þ£J1Êv
+%sdŸRÛ”eï[þ$z,¨åZù2ÁH5ŸÿˆÊ[&Ht LÁÑ ‹UàOà ì·¯Ë<Äu–ºÉŽrEðá¾€{i¼,8+ןwM2ßÀO–œ\ŸÏ7I8Ïùi’l»2Ýewkv"9kÔr6±ªŒU\¬RšÍ*¤q¹oäGCxxy¨‡„;€:j™œY°E)a»Ô²Î°!Xidÿj¸” q(ú ¸8ÎSí“Ð=œ÷Úl(ŽPíø9Iueé&¾åk)«­‰W1Éþ¬Ñ=k˜Z¦b;8˜zBÈC޳ùƒû4›«Ú•a¾vþÀ?àsuú¯UuPÝæŠ\Vf»>+›5Ð2ÁŽj&…PØ6»šuR»É®Ô!
+aÑ+árù‘Òkô!¹o©4 í«É`¡ÌÜ—ëP)fíô%ù¬VñœôQñ‡T™èFÂ(ZÖ]sŠö‹,”ÔTR‡|O)O?‡¨hŸoÑ# ]¿6yÄл£)ôéOçU ,FœéÙ ¯´›ÑE})gŽsá©ÚÙ´GðyÖÒ˜p/kw~Ë–»¶I'Y¿Ù;TdÀÜg‚YÉUž•Ò'áñƒ‹I8ÿ@Fâ*ð‹ ”8 $¸I¸à+A2((²+yª8O¬æÏ‹W‰»È—EÄ {Õ¢ýbÚWÕ"@œ}@‰C׌Štâe®Y¢¢gv…Öˆœ•üHáÅùoÂfE`áea±<o§V¸ µFÍ CÐÅk2…ÿ”ýèê* ]v%ë…¦LËy|è–KJ‚´ú3…®PºnWê(¤ý<Š
+²°2«G®’2Ñ›Bjx$Z-äúµ¢aV#s¨/³h"õ¬ÂK6WZ,–ɳ–¢ñÈLü9ˆ0Ã~GÞ"~4äƒ5"[à»âKåè4Ôh¹öû’q§¹‘É•áÕÿŽ÷Fˆªãa¥Ò1U¸_´K¥Äªá)„âP åÁ ?Q΢9–Þ’"ÙîzÜ®i$ׯ5I¥Æ0ü]xÆðoܬ¿UJ&²]ñ:QS{%KÖßz³øÒ×<° –~m댋„/7®Æ£ðëi_‰ä˜y
+ I<G®dk~°÷xÕkÛÛ’øÝ1¥e'w¦7¼¯˜Û²ÄòàüÔU·y­·Êª¯ý•¤\‰ŸöP”ÊxÛäV©©ªnL"Ð N6tÖĦ>±gbN›_¥ÁÇÌ4Ê!/¼e%¥³ÁÐO;m¯ÅÈht§ÄWv6¨º·ú›´[©ÛÌiô¹˜¦åÌÅÁyF˜yÃËÓ4Ê”aêt V„]ÑÍ auãƒõ ¬#ùëÌÎ쪭QÏYm3rMè/q{Ž~äÁtjÈüD»¢ ×7†Š•fàiÓƒ¾É· ‹Á„èºÀê ¶. =[ôn`4æŠêÈqSháæBéžrÐx_º5wÔ Ï%ÇènÀQˆ…ó‚ðš~x“'G[ ×`+qb«]Q›2Ô¸²iÃ=UGnŽ~»òç¤Ú‹JÏ(¬†£à¬ýAݪÈôd«Ç ȉE7ªÍKÊz +MÿÊ]¡ã’ˆš!}ïæ j–nh-CÕ¦_èé­º¦Ùç䃊ûíŠò›a¦Ù­¬DÙfrÎjï6–$-Ó4ôn®UõZ×F)¯Zpï•×ux'º@ü]U•ïÏ/ìÙ³¢Û¿Ø3íUg`iUì¡}…åÚ°ßÛUÖL¶»W©–Îuœ¯òù+ëÕSïØUå‰î[¸Ýû;u•¯ÓÌû¨„žXJP“v§åOâÅ€©–{$•KFÛ,qƒQÏÕÞr¨uÏHìÝ.ûúÈ}iÞmõ”×ÛfZšh~a¢æåtŸ€ÛM—訅BÇb0*€ÛiW­ÎÌCä¶§µ‚¯jZ®×mÙÆn¬›[nKfi c¬+.º&oV.£ô
+Z§Ä>øÿó\ßoMŸkÀCZ8à)* µ€b
+êðùå~®ë~ï7¹<Nözƒ©QöÒÉTî!wò¾ýðÅB,þùC)kªÝòE‡¼+ç½ ÷+ŸÌ#¾6å-÷œ)£çpê*ãªA õè…™¨Ao…˜cŠªc~­\_xbï5Ó¼þ÷Àž²z¾gQªïuÒ}Þµ®zãGeª¾X~ >TÕSîs¦‚V6·÷¸ÉÏØØiŒ1=2J眆Jhê@ UÍ•æu¤;·¶7!³0
+ß½Ù\Ó醮õý¢ý¬—«±ƒ%-;Øþ=.ÒÜeÏ/©TÖ7DŒ$v°{·.Ê«“‚–µÂ|ÃZÑ®³mcDÖ²+-C¤Åu_Ïï{¯>ë1H±1]ÿ"—D^n×R¾ ÞÙº“:êëÖÜE3ƒ[²h9¶»¬ÏèÂÅ ‰Ù7–TJOW+3 v«œLimc @ï6'³é[Öcì`Ǧ8v§­ª!“ó÷b‘RÝ{•Ð1Ï_Óµåòu‘¶Íoù³PµE°2Øð\@;4"›mOÕÙ §Óÿüm{·Ä ߺE1Ñd‘ÄA‘¡’}C=W²¼®¾B¶}[3']\PJäG5ØV¯ò‡mnY¡G XyÊahdÃ'J„[U~ v¯óWæÛ…WoÌ]¼Wn÷G¥n½R°¶²9H7ÑѨÔ¡Au Ú1¢v£ö‚ëÛZmˆ]lU§rñÏTØV¨A
+sjÊ6lŠhm,M†Ôy•4Aˆ–À’*0¤æ¯ÂÛv”J–²RÈ?¨ï ˜Ä´>ëîÄ2C!*Ë#±q0êMJ!:ÅÖCö‰ÜÂÀRÇ|ÿdüF¿a?Àµ2ÎÝ‚ÅcÙh3¡«dBš‘z¨ŸSIÎt?¸%‚Lý›mïëF[ÁAÚãxÁY‘GâÒ0mê;GY1űh%[Ÿsጒ@×9é q·_8ùG>r˜ íWó¾nêå¦Ü)jod€E“…z¤Cö.qúJviN&¶IfÅÔð8b›øgŸ
+ ñŸv|ïs÷‰d¡ò©%:uTÉÓÛfü‘û—è&òL®ˆ0Ñç~–pï¢èÝË.(R½ŸâU
+; _)§w%$ò‹Õ/à÷ Ýt#é«
+~ö#œu`u[ëwø.–¥qÜsï“¿Æ‹ååYìü_•-þ‡Ç*'̳ɩåßk/§––)2* iø9¤¼$é°7fU‘zÏfl¡“×cô9}£]´
+ú</ÇÓBིY%õV(ê¿q/ܦ,Ç— ‡9(B¿è0jtI4 ÓS:²q¡Ô™ì1×Köh^š”ChhCȽFÈõÇæ( ¬|¿|y0%o:TA— BîQ–ÉyPI\¹;hVÊ“×ã2J
+Ä¥[‘Qºr…º_k`ÞÇ9h”ˆ "?7æ<18ϲŸ”ª~éþˆÐ©þ t]Z*7Ö¡Ò^™¸%A™ÇŒ#òµ¨ðÒ7L{Lu‰7Â7]ÄŒY LêŸîL¹;
+ô i›\WkIý±Sœ•¡©y!KC:›31hZÅPf:¶ß„ùŠ„0’ƒC>C¨{Þ¶$tR:(Ï­uO¹ÖR4›ÞÜâ$=ðjl¥÷¡Äuq—1?פþç9î”ùSèi|cåõqÎF!¨_z^žSÂêK}¡Ÿ`d¢%DÎT íwVŸ>Å;
+5€'”\ãGBŠ „ć…ÓÉÎdƒ^u•ò¿x +•¢ë[¨^Úá%e¸‰ ¨Öªúp»«xEÀò
+À œ6…%!ItãÞ¨@ò•Ò¾
+nñÖ#ܼ°ë(a¡«ºFìqì&mqü3%—\g?ñ%ÙÓ†ñÙM5«Ÿ¸”ø¶X‹D3‘b$ÊW×ö Ÿ‹¡öÆ¿‹5&Í”D4Ó®Kƒ°ãc²ØáŠ ˆ.
+úë1Zëšo
+^`ú~¿`Ç6z ëq¯©Â „ãaX‘Œø½Ç°ø)±ýӼ܄'À¹84 n"Db.ƒâÊ
+Ú¸»hè>¼¹ôwMv^ cö¦8ÞIÆ»õ(~óÆj?¾’¥
+eðo²®y†¨l/—‚Dl5¯€úžãŻרãpùy1œùÎܣܵü‡«´Ü^004€{ ñØÃïí.™·%CÔA2¼ù2ë¡dÿWuìõ’šœQö>‰oÂkL<ÒÍßòò‚Å5.ſȠiñ”f¦¯fh³7S­ñ-ßÉ|¥^–³Š…T¡jX[•wCòY©*¸sG^1­VeÖ—ä+À˃€˜…åó·L³²·3“ —â/2y†èà{Œ†¹ì+ò½.Å;Cçt‚äJ ¢}¢þ Ðáô-³¯>Ù«y6q<ƒÁ³Í ý–<bØsä
+—bk*ĉÔª_ÖVƒè¡âêˆú޵«Tÿm Œ}£ÎÇD5ï1o¾øU³
+0Cî¤l…ËçÓ];¤S¥k)ÃÍ=RZ@Õ[É·ô5…ù‘¾JBe„Ç$ÆÿNi"ã0 -úìR4HœÅ~€9.☫|DϸahÑ
+QD~Ÿ0Täæ.¾Œ>"x*õO¼>é…§…ã.Ey+HVy55R”ƒ•Ws•éùÑÐEk*êPxEGBü¦ß;(J³ Xâéµ(8h©iqãmh¹‘^™ Í0`óö€}ú´”_APWµDèÒüLü£Z¬‹]Š<–·4âÁzGÖ¦`Œíoy©üZÃR|½u^ŠŸg™ôCð˜F#ÌnŸr)ÜÌVaåô5ëÆªwˆ–Ššó9³­nój®yIÃÁÅtó¶Æ
+xI«®1bªIyâ>}•‚›-A–ðÚ¯ÑÁæõOShãšìKF¦xµŸá6xq†qQ
+Ž3“°SóU„‘›\kaÆþ椚̩ÌDi©~ ·?{‘>¾J¯3mátßÈðZƒ„øtªÝÔ»]ªY §‘N¡ju]äÉ’ÍÚï©QYílZZÂsˆNÑ´Ñ·À“´Wé>Sã§Ý¥0ÕBÌÂj»æëËâõ+Ö7ëºÔqìÒ„fU7û»ð™ªmì ¸·ºœÝ:Õ8ŠÁé¿^Ó;õ#ðeÕ¿Õ+ ‚â*,Ä_ç©ì†ôñCÊY3ì®â´õMåÿßSšªU*™¶ªLùXÆ.jQ†È–g²_I¯ÅWËJ5a"9Rª†'ÈC¥\y׳qH)ÒåVÍUü¢-Z®Ö.Î\+ѤÆ/—aÕÃenš/|F¥[•¯?S âPk
+^”YÊ×>”•˜ÝVŽH9 &yaŠòùI‚ÖxýQÕ<èzU1\–µ¥
+Ù½þl§*7ù}ûô±hu;Twfa^Çåúcn²Îü†YS q;)oZ¹uÞG!öuTÕU¬Orþ§›lk;ÛHF´-Àx/,uþ sÔm¸í>fdêä}é+]
+U×.oÊ.ýÕˆù=·Ûq»-y¶]†[õÌv¿iáR»ßçîgëÄÿk*ï`/p©êþ¶LBuéû±+çºÍAº²ßÚ@‰Â[»)‡î·&P®¸ÇYQÃ?æù˜iåm/¤‹K,¶Y
+Çàâ¦Åå&wó«òïa7W”ÆAÆ«X…æÏU÷î²Ûr8„Î+}E)ÆoV¦ÓƒIÃŒ}ÀqZlúÛhˆ<Ëgw®
+àˆ¹Aæ°ø?ï=‹ê$ù6-ÁÞ¡|,)!<¥*‘ǘ*z!­•³8í߀ϸu”ùPpÕ
+E© ºÖ»qd{qÕ׉Ï;’
+²N­Âæô‘–¥„ÇYH‹d×fttcâ– Çì»â#&®vŠPótQÖÚjd¡©‹ 1ùo˜ øù­ìR)ʽ@}”Áï<7õ ç³&—ßè8³w¯àyóyÒb¢ÌH¢¦04·èôÍ‚@>ð
+¥EJ…7ˆTöÏæ¶Ù|4ëê'¥r41—œ“„ãæžâ¬ nÏsÆÌ#?ÊCÅ›hkòº6¤/Ï»¿¬——7›n Œ áHIT¶ƒ¡‚c6èß±=ðzz!ñ?z·¸_)þºc¼)¯uÚe‰T»®}nSê3ÒØ&iq4r¾‘¬ŒE¥UÂYÑX\ùKÔ¼ì—88IXú`}7yIi¦×
+¢ôSëÕiG›¼‹r3æ8EiÖ…ú$f ú·ÚÌl+n º.¶’ð®*d}0QeÊó:FލœœSµ¯S”í†Éô°Ö¯äSȺ–8æÔ~‹%;
+$ mþ:ð¤è@®•Ïöl¡ÆÚ(蕉9 Ea,·_ÜŽiàÇ€/O IýÂ4ÂaÜßS8!ûÙ%UJ’õÍý«à8C¢(”&2¥J:ð;{»ÊMጫ~J¸ÂÃådméâ«rFW øjg°Yý?ªë;®©s8Ò€€Ò´Š€Z6\Fq€D,²ÃH Y'{“ÍÉ$a@QÜÞkÕ^½÷ºÐ¶ŠÕrõªh¥Ò"jÅ}+ˆmÁjÂôßóy¾ŸsÎû¾Ÿçù½Ó™Mµƒ°D]Å_CJQw’)Áê/ʹÀ5õ®¬×´2µsµ7§v†½¡}§Þå7Ä TëVÊÓøS¯†€6~Gú„”oLF¢ø YHÀ©²ŠKmÖkÂv»õ¿~öUÝô.+ýjpâo8Ɖ$Â5û)Ô\R‹‘¹²´¾(ùàgÝ–H¾m}ìw”5jœN)
++¶¢£‰›„ÛïüŒp·vO[n™ŽHj©ÍŒ%=hº• ’Ï4‡^"‹ýl’q:iƒ“Òç%S,˜ŒPîÀûË=ªÆ‹äG°±›²Tü²åï” ‡B†ULR8LW÷˜äêÒÏ$GÅxLcLSƒÀ},ù?þEÑ;ð8Q¿±Eª$?\.”e£!&¹õ¬KKûç¥øJ¬øÛéG„‹È7°ˆ|_ ÿÒžl8!>Ë8Žè“(Y«BæIúY·`[å}ì®.úB”Áõb ѼT–²ú$¿=U8OÉŧ yˆPá¿-x¯ð$¶]ú¥0Æý_Âù¬
+ájûˆ(³“sO¼¤À‹ûH|/=wKRŸØÁ—ƒÁ•¼ç`ì p±túl>f*ºÓ¡œþuU‰þ<‰=¬T­§s®(·&zpK”½A?sLÊo`N0M¥q+~*m¥î-¥‡~F7‚^ÈôÓ5惬H]ò${žö|›-Ò¶9Y&í=X'¸V£u+^°Ï–Eñ m®ÿ²
+¤·Y/ç0ŽX® c˜ÍïøAæùdPcÜü_X˜ª ™VŸRx­6²ŽÔb¯|Cã6‡å‘éÚÆî´^FeC]¼…¡oÀ-¾F?f7Á˜ Q3éVÌ>Íy­Fsõ•£y]ŸÝ¯M©F®½¥íÁÍŠ¾£kã^NÕ¶¾¿I¥’¹öë#F›Z‡.À7¨¿Æ†–Qêfã®eÏ«CJ×n®;AjˆB JFÀ€æw
+îýmÔ—îê6êÕt¢”Žã(úI5¥beE‘•¨l ²×X•ôè¨Q•…´¨ÍŒôõ™û i,)…6QS ƒ1zJŠezVBfžì Û¹ú¬œÊ¹•/¿Ëí HQ8¸9ê…SnE%Šoòþ‡-“4ðÇŠ±`¤`,£Ü)ü~u¸tŽÈyQŠ“Nˆº]çvØ­
+ ‚Y¦ÞMÐä• N­|ÙØÏÒ _ÏTd'wa£}†Ó0Zð„÷“ãî¦ùü<ûÑä9|•Ý3Áµ¯†Ãé–—3~o=§ØY>Ûú¬l0Wb=P1‰ÜjmE ‚¬XòR[lÍo¾uv:.öC=æú;.Îa.©œBÅ™á˜S[þnºWƒJ3ÇŸNì1='\XrÉü–8á»Û²Ÿè:KìX÷»j¶6â‰e †g΀aépÂ
+áéS­Ð§|NüWP´ !€ì¢o-t_àš Šƒn…y­úV|¾Ø¤ö“è³êe`HÊêE =>\½
+­@Ô?¦ý^f›E¿ko¦oó\fyÉÜíJ×Óó«8Åz‘‰ÊΰÒXmi Ëö¿– -NwØ}‹…‹„OYÏpz&˜@>ÚÕg¼ÝªHcþ‡ .«Ã «×]7MŠü£¦z‘#Œf¢ŠàÃæe"“g¦æÎ\ÃÂaã°ì@Ö\ãqyºJc™\„¨3Ü”·‡Á Íò½ðý†r«'W§QVüEá Dè|¦ÉP½L½­‡sõ\h_h—í…#ôÐ9ÏZ-ÍTúdóÊL´>˼!õ„îWS/bžn­iAè3­ÖÑ.1Fx@Ç §3UNåN^nýPOZñ¦dÚ t<¦jGLyÁâs=‚г>»vW¾O&-8ךs§»hvöö‡eSȉî§’•`wPU_´cÛaÌéÅ™Ý=Õ·}öm`«<<ìƒ$•+­»UV×6Ì6dòÖìÁ¤oº88{ÔÝηµçºzkG}Ú»®Š<<ê7Ó\õÒÓjvg!5M×Á!®wž&—GmpfS¬ÙÎg€·O3x?Ì÷Ø
+wZs«LRûqê/ë~lŸK]QÓVÉØ¹¿õ:ÓoÑïmùÌ<Që'ÌÃÆ R]çAMXyÀu¢¨ º^¨ƒÈ©¢€œá ä$÷}ß!ä 9LHÈÁaH8«µh¥¥Ê¡rTtD-Å*ÐífY]]wu¤®Š¢²®u[bgÚÿßg޼ߛ÷û¾Ÿ"î÷ÕȹÚÔ I7ù•ŒØH”R·7ŒH§B—Hðu»£d¿të ÷*µá²²=e¢ Jޝt­jŽ|먾 á¹#T­¾•ÎIõ/¨¥W?þ{úÎ¥¯O»^ëŸÆ'º`ú·‘£õv½'$»^®óEÖ=7¨IÉÜßÖTF×2˵”­õ7-¯â^Ô'Zú"[x ;ìü“Åæ[U‹7ã–,©”þQyWrrÛ9†õE†—6cày'I§ ÀgIR“mŠ 2›¹¯
+ç{0K”,è^ÊH©/>€>G’@l`á—Tîë=FZ‹àÓðnZ¢H ¨Ñ³$mžÂ¯鵩K¬AÝ3D;w³¨Å×7⾎ԫÅw·‘^J<á«È`i$ M_¤xÐò8w¿ˆU-,ì/h!Ó»pbPê÷ 1²ª|*¾k _U¾;ì
+¸àØA™>Ê¡ <¿*Ùgü!½°r)JÌü‹¼;ȳ–&x§K0äNÁ\“úB¼&æ­£Ô³¶‡ùï$¥bÔ7fùp¹è¤t(Å0ú±HÉ2Š€—ª3¤Ó²G1d?źµûè
+ùbßVÖ†|­°®ÿ­\[æ†w+™tj§j?b7–¤öÏhÄ«§wJñCmÐmê#Íb.^V÷äBõ–ÂDR¹bÑ8¥¡E]4œJ… 7LGÏc.Xd£°à¼/ÅaÏ&ÚŽè ù@›é¡¢«˜zQuÖˆ4TÒq¨i˽èèbË• 4ù3˽Ê~,ãyýmoθ³[ü 0þ ˜
+€l«} ûÞTóCÚuäLõBt 2¿úZÐò¬ã°WÙ>¤´EhÃ@+[‚Řüê¦yеÈÝ0=
+ósç¦U"r]á;îû]÷(üºÌ{…Ó‚®¬eù ÃE=œm˜òa^2þ'FKv¥~.ôОmŸµ0O­j(›eñs¶ßº´€ ÕPk*!“þ3ùIËBЦŠñs4å{^§|{6ÏÆk\* à}XÌYž¥Ç å©ú‰DÆ=A %ŒÍ$’hǹW‹øŒÇ‚ORV°Ž‰ü· ²ÜâÖUB›øê¯ª¢¦rÏç+C°a6¯‰ ¯ ‘ãK‡ÀÔ£eàä :ZÚ¿u6&»à?ÀWË&ïk‹).”Ù]%¯ä]Ü,l”b7MX]Þ[H"}ªìØWL)RI·rÉfõr?Aâ­ÆYÈ&íI¹~_ÂIBåë$Ø{XŒîlÿZ·°·—X¯E¾&|¯wø#ùºßþ؆û`ˆ¥_vºß¢fã–õØu¨·æ3ù—°füÞõømæ¨È8—9Ç?ÿ˜9ð£
+ÌŸ¯ë”NûÕŽ²º`çjz1*äÛêöüíè.Ç@’ë爎܋¹ì`øoåÙ’¼J_™·±™+-
+‰‰ÿQ¥ÁAÕÉÜÓ%ǃÄ$Ìû[H~‡}{†1Øf½KÔÙ²Â:HmWS
+µ¥Ã«d}Ð2w7ñÔõá j¾¾‡­<ø ½O¯›Œ­Æ7ÖiÂ2‰GêÖ;SW•Ý’Ê!Ø@YâsœZ~ß*âî¦ÞPÆ6îx¤Qþܡާ/9iø7cñGªöHV‹fÑ3ÌáR>K•ÌüŸí2jêZãxH"Z‚"²)ûvH€Dö} ‚@ز} YÈJ64ˆT(P_Ý(*¨C]žŽ¢mi­öéSžJq·OZÑg­Aåé(nœy8}wν3÷Îÿ7÷œï;ó?߇‹*x"Dü’Ö6²Hae¢Z
+Ä5Kü e
+tE=H÷º·—±ÿš\Æ’·W¢8 Žù7¹¡2ƒy÷èm¡ü]L´ËŠyô‹ÚÄ ªß1N<å8͸@û±û±“ù:ï> Å>›Ë6p·„ü›ãÈÓ¹à¹<ÞAМš*ìµ®‹5M
+àåN5Ðøƒåg¨Á³Ü¿ÑÝ]7ïcðDþ!\Æ÷¢ÖO¢Û6Nê(©·•bž¯‡H´õ‰éÖ²r¯t1gž‚“u¡ÿNÝ'w„ߢËeÎGrºÍuŒbÞJ1îÖaÔ#øgøiõ|™ÑGcŸƒ"½ÐÀÐ á'xù¾&ÚYH;£¡Ùª4›T1}‹Ï®;cÚV*¯‡™Ü²w¦f”™´Æ|œH2;ÛR±Æl›Õ{í‡ÝKÿ²*²óGÜùZPgìÖ׸z *Û¿ù©?Ê—ÖÁ
+¦×“:ÌN£”Íæ9u¼ö”•¢L7Ûo©­¹Û|jëBñjEV—QRªî!Ū¢Ú;~;ÔZüZûËÚÝxÙ²ž
+ì(Åñ†œ¦qìƒê’ïªÁE蠟ëV¥‡Éñ7å:b¹Ÿg3ìh\yˆœ²¬gå.:OšîÇé«ðÜ0ˆ7ºÒ¢›¦bÞ4=¢Nø¶Hdto{¥<ƒî¾¬çWZgHåNR•üRZ\á¨às†W*Zxˆù Æ»QÍ®ñ™l<ÆI³‘Ts|—õâX)‚…c?®€p \BÁ —ÉK‰àuðç¢eüÁ|0|/aì½hBÈ|樕ª—ÐsEG±¶ÌMâÁüs¬Mâdû‚Ä?ê*G,õ°ïHÞÁæ…ýRÀg®[)|å3…¬CëQÌå{0b•‰Iט%9*–ùXÙáÝÌôR‚MñÇU)+ë^Ü ¹P,¤põy*šŸ~uR½Hw,r„~L×îõ;í_º“°vN§øˆ"¹í¿—“‡Û¿Ê]¤ðÌ//S{Ì‘‘êͶË^¨Áæ0X 7Õ4¼’ªžÓ㊈Å#úÞ’{Y´<mg¨$…Œµ5`·ÊVs-*l¬Û»¬gÆùr¯’iŠË•[ šÉÊöL¹–X#DEhgq!N-_׸Îêc YP‹a ˆ!wÖú¯Ê|Emí±"¬ÒŸž¡PþDŽOUHSðr5¸a»+[ý– m×þ•
+äWh¥*Â¥{I/1YwûôJyõ׸jùkú@p[³†ñÈåzó3*RÝeñŸñµRXwq3¯0´©Œu%­BÍAÅ’%\NÐC)šWí’'5ð꡵
+&ð™F+U,ŒŒd5ò…gR "JãrØVDB‹SDO]V‰[EסdyVÓƒ•1,+åIevù"þŒì`Ê‚WrwÄKa¯œ¸Gø…|—óÞÆ`¹Ú%+TˆVR¥C¸Fœ÷œ{Ys*¥š÷Zó5â
+ÅÉöjáQA¬4Ó‹<>ÇÆô$ù.7Ã$C$p¯ç¹ãçõ¼Aã)h©ì„îéJªè¹ewáTÑÂ*Fmž´èK™g-‹ØlïŠm„Ê*{íö{•¯–ºÑv\ܲžsØJa>3_*Ý‘¹ÙüÛ>ÑîVµ5ü|»¦æœWG_Ç>ÁâR÷óR_YL¢!ì´R…Fã›jz†§ Sû5fÚO›2Í’<ï¶Þ `¿¶}I\:ÁõXêi Zk³R–—H*‰Ñ4„‡Ùè[(ßxúÔX›á$u|I9̺TkV—ÿ…z––œ×l£_‰×¼g¢C½µ%¬*íw¬XR ‘õnY)
+¿SU÷.nkç.ü«°“„ƒÞômâc‡Å®)RxØbÛ
+ΆÓBL¡ûÃ.„?Ž\ÃD˜‘C±ãq÷ãߢ7nŽO(Mä&™“¿JO½Ÿ¶á˜š•™ËiÊÝ–w0ÿ¿
+    üóæØÌÖÞááßÛÖ Ñ!Ë"Ä#¾$¸%³&®'ª(¦)¢* +ž,-š.•/‘01‰2†3ƒ45678€9‚:;~<|=|>|?}@A‚B…C‰DE’F˜GšH›IJŸK£L¦MªN¯O´PºQÀRÇSÎTÕU×VÙWÛXÞYàZã[ç\ê]í^ñ_õ`øaüc
+“ú”é•Ù–É—º˜§™“š€›mœZHž6Ÿ% ¡¡õ¢æ£Ø¤Ê¥½¦±§¦¨›©‘ª‡«¬w­o®g¯`°Z±T²O³L´IµF¶E·D¸E¹FºH»J¼N½R¾W¿]ÀcÁjÂrÃ{ĄŊÆÇ–Èɥʭ˶̿ÍÈÎÒÏÜÐæÑñÒûÔÕÖ×*Ø7ÙDÚQÛ^ÜkÝyކߔàœá¢â¨ã­ä²å¶æºç½èÀéÃêÔëåìõîïð ñ,ò8óCôNõYöc÷jønùoúlûdüVýDþ/ÿÿÿ
+R e r xzzzyuph^RE7)4=@?:4 ,!#"#$$ù%î&ã'Ø(Ì)Á*¶+«,Ÿ-“.‡/|0p1d2Y3M4A566+7!89 :
+¤ Ÿ “ ‚p_L7! ðÖ»Ÿƒ}tfUA,
+bÊ€ä}àS™÷ÛWôö¼?»®ò™æ€üCN÷<üÞ-Yô4ˆÈû³£ ²ŒÊB€Ûé‘ÊYp«z–S–)j"¢Ø{%î>6ìÏ4àø;ç>ý¿FVWíñÑm¬Ž
+Sý^ÖD±iéÀ*÷¬bPk‡ÙÙà†ÔÀpØ©È?³%"È1ˆ#!ϼK`ùµŽL±<ín-eª2*þÀÊ+Ý) X䥂C@µv2l Q?ñþ‹í(=ì0Éqåç½ Mzï«ÇƒIãz·¶¹¤7MEïY;ª¦ä Yµ¥@K (×-ì
+ÉXÞùJ4†^¿Æ¬w¤ï¤/€ûI!5
+Šbnñ#ÙF§Œ‰Oìüý
+)ýÝ¥q A߀«½O’°kðVÂô¬Ã^B™Ûï ™f8KĈza>p-Fð„þOÚJ*€ŠÆÉYr(º—¨".À°Ož˜'š±ýºqÚ䦨ÚéfÕårC¡üRJ'–dc~¤‹híÕ!€Î?‰`ƒÔÆ}WÑzBd;…hѲG¯„ϲåm½T •ÖSAi©j9š<î „
+ߍ׸%É@`æÅ8xL¢®T­qÄ—=¬,Mk $hJÔü®ìöd˜ôÓþ€x_§r¢Ì°gʱÀ­ht­G,ÒK±ÝytomVK0õ¥¢¼XÇ?R<¬J%žämq<UG]IÀX©½'tè9E¯Ç
+$VtÙÖ
+ÝíF§øgml0L×1¥, ©œ—¯Ïy¤+•HuÑÌ2‘‹f;Ë[¸T½0BEÍÊ{Ž:‘¯Åqn¶ø´àõtoÑT†]o®´”•–úkI,
+ „îLgV_R:­KÏ‹0¢dêP?= ËÆv¿ÀEÌ·Öƒ(M4èm”‹šÖ\—ùT·k׉o–ƒ´”‰,ôHœ§=³Ãù”‡¦Þïé¤Zµ»w/³®E½Iñ-LQ¦ÿµ¢[ú©ùÇ ¾8¦F/˜gÖ–'$‚?‡[òçu~fÄgõÔîhÜXÒjšñÝš-³ VIñmíK˜ÂÕ€,%išbÁŒQŽþç*e9à7‡·ˆƒÀ—®ÏèWK«‹üÜM™YiHtÑXÆT÷¤ÅBUD”ÚúwÜ-Å4ïþ9#i¯Ô—/ñr]£hGÞˆÕ/¤
+
+lD2 h§Â‘Œ%TTòT*FÞÃédw">ö­âGüðY?é"ì°Ï[f r5ˆÌÊŠ4€Û`ÝùTAoº‡4H5”rWôS¨8ÂÖXy˜ ;$YrŒÿÜ'‚q ævUPñåV&Š×çèÌ4mù/5LJ¸òEÊ:•ªSË7Æì’HvŸy..ø› ø
+Ȩ,eª‘: EÁ$@B’KrÉå².!{A$AÂ,ÈCåY[EA;|Š TJkU>°ý¸4í1aÕÆœdžcT.Ð×ÿéîUs Rã&ùBc±h»R¢)¿‰’¥Ä
+† Pd;‘ÆÝÊŸH«b¯‡ŸÄl?1;_Ñ:i»^«½mùMh9Óœ+,—xê+(‘‡j3ö=P6uü>§¯a}Ú&¨‰¿bß (ƒúÒ0ô€®=ªá.À<ø’2&îmÎ%ë…u9¶’_ª~z¼Œƒ•­L½å!îS`(ð6ÍŸ>Õ©Váú”l¼ûW䨸¤m5y‡pgó!ì2¾·<îÉ PêR%wCý>èÅuþb€v…Õï«bšFñ.0¬¯ÃUKÒ$áK¾;¤Ø‚ÿPà,ž!ôrAÔ5­Ñ%\×ÚvüÈò"Ú
+[2Ég”wãõdx©ŽJú±æ“:”_Ú'ßEÚš_+^CÚ˜ÌÜ íŽÞÂI! vüÎ,­¶V7—2UJ§¦÷Œ÷ìL­‚N¨ITUÒK×ÉŽIy/R+“=+(Ö¨§°Œ²vÇð6™×â°Èø·!Mܨ²ñ ¼‹@P¶B‘%…R-›—-â3’ì|4Ý-ÊÆþ”ï˜)#Í¯Í Òwòƒ¨á.ËܘŒ<Ç;”ü b»ä#‡’;*œ…>$eó†˜G£Èì
+º>à3ÆêïÍñæ"íÙˆ~A•ÉZ¡±Ë$xOU¼¬x›ä äf𜓜x;Ù¥QùÐ h XÅ(îZ”¦·–ã²x=É`dÂšàÆ 8…b‘†i¿•dç, ÏÉÄ!îenZ
+b°èî /šÞ²ÔÔІ‘þ ßÜ2ÁôPß0~‡ “+µ›1ÙbâaŽŠkÜþtT
+$Iâ·±d­`UÄ+¸ƒíæßzÓ3„Ó 8"}×åY\úE^û\Qñ¼íܵ‚³¦)<&uZ«!FM)V×…"çÚŸ}ÎÃçˆ&aÌ€/éíÎå Ķ» ‹­¶5ÄÏ Öÿ¬›O546ƒPWä눤0ð ÒfŸGlEýbd¼c ­´'œÆªžrûŠÓ¬[©ò{K(ÔÔ"M/y%•“Á0=²Ÿœâ»zFŽBx}霚{w6{Y5¹í0Ëóº%,ËÇ4ù0œR}Ô“´vTôp>¬K@¥fùR¡$Ê7HìU( ¸ÞÅàªÃ/1¹¯0’f<,È÷1äB§S>¨ý˜Ù¨RI3#&ì&ÿÿ´pßaÖ5j¬àé•Å1€Ð9#yTõ®õ±H†9cùIÀÏû[ßÎÕ§Œ©jÙ¨’UÌŸÃ~á¡?óýÄÊÖ À+Ý7ÀÞNŒzMÕ`k†|-ækËqJÞ}(Ò˜2¨œçþÊSØÁaÓ¼Gài¿ æ;ຠíÊbË:`uǤayU“}T“€ 2÷ÉûòFtmâÌ”%OpuDìÁ‰›ãU0m~L-_°¨·±µ:ÛqÞÒW¥²gÜ0~æhuwÞ-] NVº‰rPå =<×]x;Y×ýÎ1Ëiw@á­í8,áÝÐnœù™«\(zºÆq²¸ö‹ø˜„b“ !$zB³§&5d©øníÈ6ç1Q&ß &ô «ÊC¯Àu÷ÐŽy#«À†­c%$7ý]w'z\½‘×0Lük{8<Oz †ßð¤o%4üP¨Ë—õ
+¨9hz»‚ÍŠ°ž$ßLØmxEFGOª¾$»…DcõóÛíðnò:0td#û*¼v–ÕTöâ2¼.q0û³6·Òævêg€ûÑï•AÔ¡n‘3埯ØlŠ'¸í¿±H:<@âIËžõ¨8!èmR¶SÖÆæ–sÂ^½°×U¦g„¼f
+~”t-"XÏnÃv&Ãô­°ç„?úøÍÀVAìG³˜+¿‚™¢Ï7¢WÑûÑh¤KüµDâYùèZÄ%4YF¶g£[öÐ äÄC_ÔMæ6Ð…LJ®¥!˜¨)¯9ÍÕ!6ÔÛ-²‡l?¯@œ±W …Ÿ³?‹t`k¦;Ýé½ýæpŸö}ý
+P»ç†À>Šf†{Ë’i8DbÒÃ̯Mp/Ų›FñÊ_<`w½[ßUèq®°. ÊÏËY!é'i7ÓL'‰ ƒRüz$û‡§v]²cÁ-äÞ©%HYÙ~“Ù•­ªç Ï鞀ws{)WðaÖñ˹ÐúÔ‘`è¬ÊùØ{¨[zÀÎ ö¥ä“Ï¡•ö½Á¢Z¨& ­èËz
+-ù† ƒòU‰Ï@uÖBP.8›jz …Bä£{G tϤ1˜Þ•q# Üßò¸^×oÍ2†ªN*`®D—èZçm錞cß@QYîÕ@ÕÌðÄOàÉÁùžy`þ†Å•^ £Í)ÓH¬?€?˜Ês %Àý¢ðÀJð@öf»à-ëÞHˆ%ª{#}Å™ìPK
+­¦Æàè‹ÏÉ'ðÏ玣· +¸ÅvO@¾Ù%ö7_ß*Z-r*Çé~z Õ‘ê 4ˆŽ!ñøwÄBp‰°ÂõG-q.Öa+Ãî¡cÃÃ"wmqk=½¨ÞWfB +k^ƒ²«0‡ø¤>n‰puŸ5à
+¹1ë°Í9ö~±(Vì¶ýZQjsf¶ü•b÷5~Nlï, $LAE ¾±Æ\ù–ÝûY†vÄø¡‡3kŽô"³*ÆÆIežö´¦.²gj4uœä¼D•’k"Õ*TÜ~~ˆ¬œgˆ^– ò›~½¼<|ƒ1â­c©Px7 í‚kFÔ84‘K(/AIƒ\
+Ôè¸Z¯J¤äAÔFqµŒKšqÓ$5„GT´è#.öa<±ñC]”s¶zOŒ£9ÇÔÕÄ@yQÑ’í-–D¥RPÒÇÉßGªD{˜Ÿnš¦ªùoˆ«^Â7é°PµëvU±vºƒ²ôT_F"éT‹Øeâ Šñˆ^‘9ùì¡^‹9®¯É(Zi•í*³¢”JŽ|1¿"Ë]º˜ýœ.˜g:±ÆE¼TIÄFzNõË÷%T„|ƒk&ñJ´lÅÇãIùõ3ÊŒª+År^¹qfŠ”SNM‰‹òX»™eÝÆ $’vî{ò…ꢫ?I¬Bð-”º-Oi×0iX’oª È葚T\£¬Å.%kR&-?(ümš¥=ó€Ôþ?~3ÂJncu ¦/·mS<ìøÃ,»ÙLeØH:ëi¢_+MSVüaù kò@
+ô4€–9"(hÂd}+É—½7Œ°)T« Þ b¹ÇªÔªhmõê`Y­Õ»÷ÿ ís<ª†-´,@˜­Í‹ÀŸ‘þ¸ÏSCg—ZwÓñN² CUëcªÜWš’š¤Ê-JGm\Q²ìA]Q†]Æh&D‹¶0±íÐZvnw %’õÎá0†°zºÊMT“XÈI¶ÂÉò:¤Ól¿ŒÅ<›7åaÍ` çù™5æP` ±Ý°Dh“k?Ø(ªbþø Ÿü?²`Fº‚gÝÏ—ñÖűàîy¬¸À=î‹4EJ‹í­ú0 GvQ³O4À©W²‘Šd SAšnMÿCäÄí€
+Ò±|`ïŠ7ø_·ÎvY—£¦Io©«ÙRÅyYqE¨,Dò;É&Q‰\éBÑxWì¡rÀ‡­v tÑ Û[€¶Ë\"]£º©ËU¬ÐØË‹¥ ʤÅâwÒ髱<¶ |Mc+…éÂåç ÛZê¬9Ú(WZ¦Ê³Í°Âäf“ïø¨t‘Ž~X)†Uái—á?¤m±<P€¼Á– ¦;×ëG½ï-_k&ÚžI&”ÓMƒL¥<º:žŠ:͹‡7Š·éÓ°KÆ’ÀÍâKX² ¸ÙѪçöW[’5P7W’«ttbþE6Õ¼šz ý»ýÁ¡‰~0¢iÑðWêűÛi4 [$¨k›üšûi´lç$öÙ×VRc4Ó«–ÆêGk~Ê¿¤YQ·:Ù®HbÔFßG™X ~ÏNuã›.±kf÷™c`´m ³·f¥é4óê±³ºqVP~š:‚U›#/bûE7K*ØW0ûá]œqÇ=$Ü­ù¼ñ‘ X×òÿQM52ymÇB´S<lÞU
+÷ZÒ¸LÌeD׊=Ü B¸ç°kLBR³¿Y„^Ëä#z«A¡%¥Ú¥`r^Œ’'UIoîFgˆ>ð;1 pš't.t-SSUn;Qñ›à¹Y(sÑžè§Ð*ÊMº8=÷œâ
+BHZ# ƒ–GcDáSŒ¬ä{d'Ù,UÚt†£‚lÓ=í´,}¼*–¯vËcrÓ+Ð]‰(_ý1rœÃ˜@?A[ã˜ã±K£Dl¡v'”oî>=¦ëÔ’‡ÕÔÓ•[‹µ¨?úQÔ ¤ô
+]è·7E9!À¡7ùëÂ;aˆ—¢ÕuŒ*Àñ­8Y?ÃäÒ¢Úã#ö
+Ç"ôŸàù8Ûº?pGªZ:ÜOÈ¿´ú7òÃä†ÂT•ÅÔIÐéÔ ¹¸J±A¼éºÈÜð‹xß¹rxýŸn?(ªŠØ
+•«–æ>~÷oîß\Ƙa3Ƹ2Œ«è@e»ÔæU*Tl©çm«cÕÓ±½ý¾Ÿï
+Fvz|"ïÆž¯¦Ò©n2=µ3[ï°"s¬¥ŸÙtÕ ù}Nâ8«”ûž`†ùñÒÌ1`X¼iý]àx¬£½(®U2\o[v1·d%JOO&rØ—´ß0ܵ9³YxÅݨ \–´€¼D‡m§¹ÖÄ]=Hßjíovfd—¥˜)Ì:ÓtíbèdH9\”ØÏ›®YÉ?«8°Þ <%þ}ÈçdV‹Û•hšÁnuŒÓ»kV˜¶3+ª¥(+µ$ŠÞÁ-¼›¸Š‹ë¼#¯ð£Õ‹®‚QÒ°ÁØì·ÚåЖžÙãX@ßâh-NdðëÜ$Ìa«š>•M3¿¡q—œ"yÿÕè$À[YÚ@S¶õšgÏÓ=ÝŽÆÚP‡³±˜1·)L<Æd×"´döìrÕ•sÎÈÝñžW¬í ˆìDJÿ
+tÃýbêÆ µcèj¥è*º!®Aq
+hGÐ|î3j zŸO‰ôÀ¶
+»°*ªÏ€› òÂÓðÔ%2#ÐÇ“™H¼7!¹ ;v¤¡‡
+ÿAr3vŸ*>¼õö§ð>É„½x\œ+¨0lk¤àïôçÍOðÔ¼`Å
+û‘kH¦éÍ>*çÚ²éÈ1ùö£ ¥wÏp5õ´Èê…êQº¶àªÝŒß$Š;¬ÌÛòLvvJ1ƒóf¯›ÔÊ3nž*ÙäøTŠg@oO#9|}•¯ª˜?V0Mù5.‘Û€zÆ{" NÖK?C”_$ PÑ&B̆e>ˆ(q´ªIÁu›‹ò¨`«þ£|oùb|ã_²0l2©Wê‚ßsCü¦Ü´LT÷Iæa?µf(/+¬ŠP¹Iw‘¸B WÊhÿg°ÌšH
+¢€®‹¢" À
+"(®H2™Ì™™dfrß $ˆxàZEP>ŠÅ£ŠºµúéÖóC¬ÔÇÿáû~ì׋EÎF½:}< ï\{
+š„‚ƒ¢%“àø± rHÐ6N$(ß«Ná·˜_%ó1š¢«ƒÑ]²2Î:$ŸÈÜØoƯ-†8È¥ I-qt;ù'kŠã¾TjJW^Â}ôkÀôŽfQUr\ùuî¦lôƒNkHná«‚æH¾‚÷õ*‰W¶ã¯ø¦˜Æd6™¸M2 *{¶®¼`ÌV%¥…VR£oJJ‰²À÷`+"èyO|s8ÙÎÃñ®6ÅVÌËy8‡ µ:+¿‡˜;ÄÐ9Ýî´ɨÒ=Ú.q“qŸ‚›Ñ=¸ôàÉ¥­ñ¨^Óöw÷ld¯‹ÞGÁ­å;<¡bShÚ ¨ˆ®
+ë(¯ÿ$Ëp\)9D$ZYÙr|§¾öçç(¨4DÜOÊH¶»äʳ ;Ü«òévÛ±j‡xÒLr_rû µµ©;Wi·Ô nV¨|ªRé›üu«ÊdܦÂ
+éÌM]4ãǽ•íAî…ß—n„×°Q‰ÔˆXÝtGdñƇšD¡\wQy­R¤>Ál-ÌQôÈïnJ¦úȲ¸cä/øÓðËø14™µÑC:Ã'K£&‡ŒÛÌ•BOÝJ{ß´z‘fŒsWñƒÊ|F-§Ùq2˜ì ?НÁ}Y[pXdY<\‰ävÃ+ÇM{Á¶i©rš¼8µÌ~í£LJÞ¯´ÇŨ ávlL:ÀÚ ?@Ö¢ÌoÓ[gŒ`¶²}ý>?Ur‹Ç›I2¾ËL•k.Æ}GpI8‰QäRV%Ü‚L0/PU³ ûEÕñ ?´É¹TÙÂcÍÛ¼Ûf¢HÓësÖÄ^QMò½C!º¤)Ã$ ‚; Òej¸ íuIy õWïêîË6„ÏÏ#LÜÆMi9ºÄ¦Í±嫿šPœ*ŒøHˆÊ˜…¢FÉgˆ]Ÿm¯¾ßnÝ+|“·ÄX$Z6K'Ú»OñQÔJÊq Üþømì(B~—ljçÀSu‡Z áÅ¥–bhW®ÓP"¹žz@³U’»V±¼JÈ΂\ÿ,<\H»¿Až ö·5’çë¶
+ES!.?Ó”ø¡#ÔðCí:[˜6¨$ß´Rµ2G [ÍD§ÜU…ÈcbÆé™Dx(M<Åù¢}°‡|ÜyÚá%î¶Úµ³­}¦Ñ’¿íº©œHm‹zyŠEy€)Œ9Didh(Cp½ÿKôoð¹ÊYÓ9_\n¯i¿QsÆš×úÓ®kÆX3¶í£6R7‘ÂU&ª"c´T4E„ʈ0éXÀ,t ’Ÿ:žTÙÉ/>UÝ®¸xà7€óÌõªF›õÜ>QöóL‹ª~:Ê¥ÿ#ï]ð[eTSì2»Œ%…Ác• žüÔÆ~EWמgØ9ÌÌiíå÷%¾3W4ÞŠ: }Þ¼0Ä_ÁX|¡¢-ðƣ鵼‡„‚ÿV”u×8H{»àßYêF¸"qÄ”-´F95E!L¸èŽ/ŸÈÁ3z„—óLw@éå“"‰œÒF‚Ÿ²Rm¨O´·éQ&õ[Ð#¤ŠÎ¿ö–ZÅO¡î/©xðºË¤Öró~9žT0ä0b³³Ü¬ ò4±ÁPß‹bÔ>_nMFY%÷ý<ñip
+Ê;R¯xÑðá@ý¤|‹ëŠüqj#ßlºjù?Ape¢
+ÑsäHýt-bM—#¢²¥t8fÆÜß5ó}sßcÌÁ0†f0 CcB¤KèRê•mc³EÑíµÕÖk÷ýÿ×¥ÿJH×[ÅۢŚ<!)´Nñ‰oòOCÜç¼ùöI}5£ûôâ2—‚å Áš”œÝæ0•9¥Ü¤tMب÷’Œ©ûÄ7B»ùÂ0¾Ø‹×ηç×ø0äÍzÃDþ KƒÉYe²è¼RˆU§J›–«”‰Ñ:N::,#ùÛD~6·Å¾²žË4o1ÊlÙ`ÌÏq5­¨X–’U5Gû"Á³ì«êZ4¯tXÞú»Ü ¶øßøàò áÄcV÷u–{«_<9·£Äà“¬©S¸Nó}hýµª%Ì{ÿnÔ¾‚"V}V”"Càw}ùt¡¼ë)ýbî3ÇzFj¦oKÓ¨ÑÄýj2€'¢þQ9
+]òÐ\BÈ«r1.8C|œÎ.x8÷~ûßpG¦§m<ϰŒ"i¸©SÈ˨µ¬Ú  u[æë)«E• k¯+WE8x–ÃéÊ5µ qæg<k~€™ñ[ëÛ0n²& ËŠÚ®·cÙAúÒaŒí Jú€gçþ¿"Ä:©"].fïæfô5 Rñ¡uõüBÜTu6Ïu¸|ˆ;4Rú ÷'ßûÒhNpõ\@I!´£Bq"—Ö:*«ÈpX'%Oð¾µýâ$ÜK#(¼•}ìvW¥ñ&ýü$§¹§€Æn®ˆàï˜Öxä·®T•eØ­k”‘øUµ^²IÜûªkK”¦l·ˆYÒ+Hõ;"ÙÌË”Ý^†\‚¯#G_KmáèR2Úyêø•fbIîª-òب:Ý$;8³$N(ñÓ‰ÇxŸ¸Ëa
+ ž™1–æ"¶¿*.d8>éžãýMOÔa±î†N¥$ʡ˖~ &(Ÿ‹$~çÅ>ø°žü–tÕBM%^i¦Õï3®¢Ï§Ef8UB
+†ˆÓ¯_–ùw•CÕ„YK/¼–Ô¨ 佨/²“êY0ƒ¼£ý–yŸÕ̸7Í.]Â*ѳ°þÚa Œ!ëd™[ëm ˜9­„Â#{-ô;˜Wà[¡¸Ìï ·›UŽ$‡mb?ciŸ3¸Ø˜s’™Œqê6ÿÄ‚TÀ tù
+`,ä g6ô$cNúŠæ!LÄp©<Šb<ïÀ™Nz;¶0y%€ )°è"Î «DÜB@Ž ;€.aÿÒ$G=ØW+²ØïØvÑFöv-­‹ý…Ý|d%ºŠÝ¿ÇîgOŨÐÔ;$KF“»î²lbšu#«^$(ŸbÕ ¥‚,‡ ”jdMð[sÃÙkx÷vÿ͆¹ïc–³?sÝB–¡fl¶S ¥ÿkø€ ªMe͈Ly˜i’Ã3òTZuøgÖvñàî#¬áøV'{ÿFð,ºË8+„5Žå%\©ŸÑY`kY o¾­ÙC¾ˆ,VQÕ"˜B½k”5WjØšÃê aëx·­ÐhÝkË"è© ÕA³Æ›Ü/ðŠŠd˜XöÃ!xJ±ë(}¶~ÏÚ&¹|–ÊÿËé íh‰®?í·¾Ò.„òê¸ñøÿÁXS
+jkÅuÖjµ­³ˆV´JE)ž­£Çý¿swmºü^ ´«VQe¢’^*ð…¡7”cô¸·Su›5Ô.›:’Õ5!{3°A/—ýu@vÉ?êaD§åUNCþkùŒH2)ú 1¡z%­9ì­Ué±›ý»éÐÊçvò¹°—Ë!ÆkšÕ0>][#À?¬wÏsb‚÷Ä,4U²á¤_ fà)…ýEêÄÛ* uä6Go7°6ɵêèù¦{¹¿ Èæ'C¶É‚Ga‡â+Á¯ðRUõA=±@ù¾5_ªrgs1OU¸ŠGÿ£*ŠƒÊšëO¨&øÂQÍ¡Á4ÒÜ%¸¥©×€°nl¡c=ú%Z¨ ÷v¥Y ÿZe‚ëà¨4í‹? eC‹ƒÁ—`…àØ Þ_‚ªÙwvĦ10áKÌB/§ê*BrâvÎ4ÏŒwMèñ  0ù›à¸rë“ ó³–`ø¤ñ$½Ðï’÷C‘ÜŠGàa6;g-N_&ɰ .` ¸`ß0üØùM/s\¢Pœ’MƒfóªŠ`p‰¾3ÛìåÖÆå
+TYRJ¦€3éÝøO )*+
+¾«8"ӽ΃ǫöLúÁ¥ì"êHï”Úü°k›è:^Ö–T¢Ïm©©éÈÆøÏÂ
+›ÁKX_ÿt+ ¬ €=‘çÐ#¬Ø«Líø³
+u¶uWçKìñ¢ØÌ¹– u)±F£@Œ†´ó»ïû…jÔRÎý_$ Ð®ÝYuBÅ›öGõbQl+$Ö»·,o8“qÜl¸Åègš!) ÷ÛÁˆnî2QùãήÒßUÎ>YtÍwŽ(Ùþ¢^ëù'Y¦ÝòÑØºŸ!…Ú %à—GËU¸û9¼·ù
+qÑÔAN?!9NxlÅbÀO°¹Î{ìeÙi‡Y“Q½Ì¶>S¸Òô€ZŸîª‡ .&ŒsÃÞ¿búÑj?1_ñ¡Ç¡P´ÃékœåÙŸè¿°x²`дY»!í¸n”ù6f®VJ•?ffãoýš‰n”ð0¡‰°šÒ6l¿)ó±7ØB¹u‰yMAƒÑ¢˜&×mæ>>ÃNjÔÁ#4J%ûÄ&|žÇòE]ÛŠ:íÒiû2g¢0ÀÒio*ð6zXŠÓÂø†¬h +æÒ‚÷Ò3;üšñ1"Æã2ÒÔ+Oô?©ˆ¨Kܳja½÷´ðÿ
+w%5»”,xë+ zî!Ί}|%­¿w¬pÈ©™xe¥Xx|Y üy$Mœ}yòA—Üzè5À“{ù+=Ž}5"6‰~‹{άq~„p^¨Q~Md¨¤~*XŸÄ~,L¹›U~S@é–Æ~“5½’ ~å+f2T"ˆPÒ{p«U…Ip
+‘®zÄ W‹W} q(·¢vsf€²ÊvÉ[º­°wLP̨OxE뢳y
+;NœÞz11c–Ò{v(_•|è ׊b~mq¶C|ÏfL±o|¢[y¬[|™P£§|ÃEÒ¡t};O›²}›1…•¼~/(¦~ä!E‰§pÞµƒf&°B‚p[Q«/àPt¥ÛgEÁ S;Pš¦€è1£”Æ€Ã(ãŽÄ€·!£ˆÞ€ºp¸´,‰Rf¯Oˆ6[Bª5‡Pc¤á…õE°Ÿ`„ð;U™·„1¾“ëƒ*)Ž‚^!ðˆH§p—³y`f ®‹È[>©fŒ P[¤ŠAE©ž—ˆŒ;Z˜ø†é1Ù“0…U)FjƒÒ"0‡Î‚op~²õ•7f ­ð“![B¨¼ÀPY£_ŽEEŸé‹Ü;T˜\‰…1æ’ ‡C)kŒç…"d‡jƒpm²–šÙf­r˜=[M¨,•1P\¢Ç‘üE£SŽÜ;`—ЋÔ1ð’'ˆå)}Œ„†"‡ƒ›Î¡mfú»ènìi=¨µp­kq”êr^mœ€µtoÎlVuÚrX w¿tDyßw'0É|Yz>̾j´qźjlÁrä§`n­t“¾p‹u0¤rnvgkbtgw³WIv~yCtxÏz›0b{x|bÊùh§|~¸´jÒ|¥èlä|’^næ|¶~ppò|ój\s}AVtu[}–B×wÚ}û0z¬~lÉ;fׇ ¶øi†
+¤9kD…ëmh„5})oœƒviNqê‚¿U¥tX‚BEvÿ=/·y÷€VǧeP‘{µqg¦‹¢¶iåž|l‹»{õno‰èhLpãˆ(Tâsu†SAÁv@„Z/ryX‚Æ_d›Ö´2f}™¡}hÆ–ŽMk“/zámtLgdoþjT3r²ŠxAKuœ‡I/8xσ´Å[c&¦³5e¢[ }gÜžrQj.šxyýl¥–†f¥oD’ˆSŸr Žd@êu‰þ/x\…Ä™bp° ²vdÜ«Ÿ²g%¦”Œ€iw¡„y3køœyeøn¬—bSq‰’@—t Œo.àwý†UÄa÷¹ë±ôd`´RŸfŸ®W‹Øhè¨-x‘kk¢emn)›ÐR§q•\@?t@ŽŒ.Àw°‡ZÀ‚tÿf4¯uþh¦åvÿjõ‹xxm0xŠyosekz.qÐR|{itP?Ä|Öw-Ó~zK¿'rÈp{®sÿqÔœãu#sŠ„vFtgw£wtu½dx¾w*QÏz%x®?E{Äzb-Ž}¯|X½¯pÑzŸ¬êr'zò›ssj{@‰(t¯{™vxv|c£wy|Qy
+}>ºzÓ}œ-R|ä~H¼(o„™«Yp‚ƒþ™éq݃^‡Ûs=‚ÁuPtº‚;b°vS¼PPx <>Ayû€¶-|0€º»mŸŽ{©øo˜p‹‹z†r
+“o¤_ý%ÄÅ{"bHkí&9q\¥œW:Æêˆîr™ò‘&ÏGˆ%2.i6þD±1ô%~Fù‡¿+~ZýÉC„?‡¶8‚ŸV†Ø
+ð2•#ƒâ®ª“ŠÓØ ui^#v
+ðNLÁƒ¡ØàS¿ÛØEèñ„Z„Ln©Ã–`®áè=l‡vaò;(‹>̽…èp ʠÿ†¶£½~íè[ô‡Ë@›šQd€³F!•œ7•ŒHÙÙ$‰ÌùŒäÐÑ Žˆ…Ú#—Èd¿Ltæ!BOáKš*áŸÌG-i°ÙäC–ÃrƒBú.ÒUålmO¼>Æ †,Bæ2Wü<+Õ3¡6¹7¡›µß›íÐ@µÑ ´)¤Û &KO¶ 0Þû
+/—hw{ìûV³\lÞs—ýT¾jçgæ?Ò›¾ú¼â©ÛŸÔu «ê¹®¨D­}û”.ƒ5ïʺÄ(wM’ °ýÒ±=ÉL·üÄÎjúe³o(u\ý›» yÚPµXû³üžæƒÆ¢8ùp2¥¶¤©23Î2"uhì0
+¿P7÷½DšgÛÄ3Iœ33D_Âè«ôºâ)ÔJœQNdOÌm2‘tØa':‰=ÂûÄJÜê.ÆÛ±¡
+sÞ`d³é+úuu—-Š Çµ‰i—ȵ\L
+±kw„á/iƒð®&GÜ1|91É:Hÿ^ÙÈ£gWü¢ú@-µ
+‡â}~s¬¥<xÑi'¡Ôy?^WžLy¹SVš¢zOH-–Ü{=:’þ{Ö2î|¼)pŠý}Á!g‡ ~Ós@£ö|hÀ —g]ðORí™r:Gî•¶A=‘ï^2쎅)–Š$¾!µ†Uÿrá¢û†hnŸ ……]£œ„ÏR£˜y„G¯”ƒZ<øÿ‚¸2ì3‚ )´‰hŽ!÷…¼r’¢7Œh/žÛ‹u]j›RŠRh—ªˆ¦Gv“ú‡:<ÙA…Ù2ôŒv„ƒ)҈ƃ*"-…=árY¡™’Ågûž<‘"]2š¯$R1—ŒöGD“TŠÏ<©«ˆ¯2å‹ä†¡)ðˆ?„"Y„ׂ™r+¡0˜ÕgýÈ–½]/š&”R–`üG;’¦Ž <´‹42ö‹^ˆu*‡Ú…¹"|„…ƒ/j ²×cj_ü®èe©U᪹h K°¦$j›A€¡+mX7‰›èpM.=–tsk%½ÙvÓ ‹\z`i˜±òj@_×­ÒkËUÖ©im‰Kº¤±oxAœŸ®q›7¹štsó.…•vp&(y(6Š0{õi[°„pÂ_“¬`q»U¬§òrÛK™£5t7A’ž<uÁ7Ë™wz.¸“ÇyQ&‚Žc{V·‰+}di$®ùw_OªÛwƒU_¦vxKk¡ÈxßAvœáyÐ7Í—Òzã.Ý’Ÿ|&Ì\}[ 'ˆK~ªhê­¢}9_©‹}AU-¥)}TK4 }wAd›¨}Ä7Ζ°~..ü‘—~§' Œv6 …‡Êh·¬™ƒc_
+v|@Uvõx±.Áz={9¿)bØv®"e†v·œ•hwlŠEj›x6wm0yd±oæyüRrÄz÷?Ìuç|.ye}S½i`°€Y¬jcƒ€šêf6̈Ýhã–vRk©yc®ngQNq¡Y?KtøS.Jx¥L»Ï^ÙŠŒªæaɉY™qd›ˆ%‡vgf†ôu/jO…Îb¼mf„»P›p¥ƒ>Ùt(‚o.wþº]^”«©©`c’—˜CcHl†Vf+Ž;t)i0Œaèld‰çOþȯµ>tsw…[-òwn‚¿¹w\-ž¦¨®_A›®—Mb0˜‰…ke#•SsShA’!a7k‘ŽçO|o‹’>#rሠ-Ðvö„0¸±[D¨n§ð^a¤Œ–‹aS h„ªdLœ%r›g{—ç`˜j哟On‚-=ÜrfŠv-µv’…m¸3Z¤±ê§p]­ •û`®§Ü„c¤¢r fØC` jR—ëNŸn’b=‘qþŒŽ-ŸvB†wµ~o®`^¥£q&cc•rfBƒÇti qõu£kÚ_ôwInÅN1yqØ<¸{u8,-}py´mjy¤”nÎlp”ptnR‚Þrp/qsÏr_;u¨tMžw§v2<Tyéx‰,|~{6²™j¿tn¢ül¦uf’­ntvT‰pEwFoür+xF^Jt8yZLâvkz;Ûxà{Å+à{¦}6± h¯~4¡lj¶~F‘(l¦~L€Fn™~Qnßp°~e]erê~†L:uM~­;swô~ß+Àz毛fê‡á  i ‡Òk†<~ÿm*…RmÞo_„n\qǃ˜K tU‚º;w&Ï+¥z@€É®oe~‘žêg²à޼iÑŽ}økùŒ:lðnHŠ[[Õpʈ~Ks€†™:Ævw„+Žy²‚Q­dXšùžfš˜×h•Ç}jù’ð Õ‡_S
+§žˆx§ç+Úûþ‰O±¾pOé¢T±ò£Ý+ì˜æg‹ŸŸxjZà@›™Žc}9`k`cTnwó?í+ò^5ú¢²‚¯j³ä¤¢¹’“T&ïo
+ Öl±×øEùæx9ø {óXföPg@C_®[ÇG›=/¬5éÏgš4ÓêÊ¥^E×*ízªþ¡´ÂÏ ø³5È#´p½&ŸåX÷ÿs–Y>€ãû>ƒŸ@?ÈnÀíòC)…HKÑ#èEu·ä$þ%à펭û¡`^>[»
+ø(?`~¤^x
+±ÒÓþ5ç£2[{Fõ;‹On·–çÛúݦÛà *Cæª{2H<¡è¬N-ßS´º2ÍUråÓu÷UKm¹³5ųBuœ•
+}£·ñÏÄ3À!ãJ÷H$‡œ#h^Qv¾0ªqÓUô·ºY:‘Ê«ªa¯Z=V.ˆ¿ }VÒZ¦Ÿfsôy îå½ÝÃÖ§¹MîßÑP¬8Û:Æx-kîÕ¶(+’rÞ©ãªGãSµIå²Ð­€¥øºO ø¹ùw÷ðiιþ9јy»¸—€³Ÿ’—&÷†z8,kÊ.$†äxì=rmRMêï¼ÎRŽ€Øíu×Mb;d—íw0yÁ â2Ö¤˜Tý{¢œ¾§—¨WÆŠñ6m|ˆ+±8êEC`ð òGd]íÄMm²µ"ˆæWürS禜D¿ò²~AêÿSƒ‹‚»° ¨Ä+ïG6W#èÿjnA>p€Õ
+
+èshø]T4ÚIÏÌ ƒÿDµG§Ã£TDÐ(2BNlz9“eB_ Ý«.ë#­J³UÿÒbG޳ºÐôà¿É°€Œ¯¢ú¡ îPÌc36õ ûèß…!Ø3Ьü•?ào
+¨0”\C"‚°ÉdhK>Ù…¶‹ÈŒ©Ù¸Æ:IF“qž\BVhFˆ'$—[Iœ&3’—äæ°B¢™tÙâK\ DåÄ'â`;™I [¿"–ð%„#þÖôNã‡÷š\÷¾ÀëI
+|?·a×ü8+ñÅŸ3"-AûöשÕ_ZZ„K±O%u6`·îÄÎX{cçͯw1Ø ÞÙÿ$¾Ñè+ÊÁO±M{›'±øšEØ],¸j¹z6¶¸¼+§ý°~¢ ›Qœ½k a=â_ˆµò/E ¸—q¦ÀŒ¿bVškÐÞ&S ß7<R5 |m[NšSµ<ñ T[Æï¼‡ö¿öcžÖ"16`²ªñfˆi+mÈÔ‚Ò§ìEžýhîöÌë6©Ì=¶Ï2%w ýÉ\W´Íw3/VWxúG€­kÌÜ (œ+îÌK6\°Gå³òk6ygf¨¥«p86×,W C ~­¦Â×Ù8 ¸y†q' Skî±õˆ3½ÑvIùýˆÕª*•/°ÄkffÔ›]´æ˜_MãÀÊÐ5ÆŸô·$Ϲv°Ñ3š­‡ë\—2èe*Ëò^\?ÃüÐÀ|Í_Óÿcj†„1ñÆxÚ¶+œžœemÈ[Ï8¦›ëš@ßŃÈr³1¼ ×ä3¦4¼6=Ø‹ãvã×¶Á¬+©fЉ5žRú‘æj¢WµÄïËuêÚM!\„ì7c
+¨ —È}ÈýsüÜ‚â-j©³»Öü–5+Ú²kE™³Z5׺ֵÒÚÞ›c„Á¨–[GS±J–©oÓŤÞ—¯
+»Ùñ9y<‹i;M½›¼ÏB¯]
+—šüYÒ5SúA*bPW#ÊXš~$u2Ðξ/1×a£¾+ºßÀ` ³¾µ®cv&ÛL/Ù0¸Ì°›û;t)HÜ„|©E×òß9ï1îÑÝŽçÌjÂɆ'u/ŠðÖqvC¦Ï|ˆ›™\l´ðWÃÙúja©ËçCšm’^~nc÷=í¾=…7E˜m+àO^± |™Óvádò—†,ñ<8L«O…j³%=ŸÔ Y0o—}·Ä¶ÅvCBÀW[ÞK&šçI>füg\"Å&]ÐÇKOÑ:´ %ije¿B>ä%¼à™j†:l=®ºT1e~ ª/ðߪg¸ ‹”ƒI0^YÕV) <õ^ÅÏ‘ë%
+·Õ¦ÕQS-WõÓGpaθDû8¥ß 9•DÖ‘ÕƒðXM'Ð
+ÕóUÊÈJ•]Iå"­—mtª¿euuE²)ÓÌ-3`ŒÒŒù SoOŠÓÕý
+ßÔ¼‚Ñ{Ä!`åD}°üÂK_á4ˆ
+‰!ƒ¶ªQ\üîHÒ½¼ÏzÈ”ôHçNÚ>«uA-^á²°²bg¦¹%+kÌ5‚ž°8ýWâ #ýwi+‹¯öqéÇ0ÞîŸk…hcûuTT™[ÀÖÁÎ`î5ØZ[›`ËJ¯ &º¶œ-áêvÍ**–öcs0”®Î
+Rr_´ñT—Ó'›Uät£Kyγ•zØa“L™= z”s#k–)Æð|O¿ŠèÄ€ŠÜ‡­ý:aúxÓim&ò™ã¼&^c½®†ÂŽoIÑ“`
+‘¦îWÊñ8Ï2K/ÖױϬ˽ßòŸñ^º¯i©ÁùÊáp‘u£€óÔO£ßè:JD­:WtG<Z2µë]uyy±+–ü–0¬¾H5W²?16× ~u´[4')BÝæ—rjâXâ‚GØÔê‚>±Š<Ü8YJ]
+ Õ„Ñy¬iZPí-|xm«4×Är¦Qe¼`þdZ´H ;4°SÉìí¹X1Ìšå`¯±wªŠªŸp¼u>Ê7<dE,]Ìÿ5çJÝ!ù˜óT¼%"Œ•ø7%:ã -+Ç6\Àw‹Š÷•Oêç1=ä’zsùê[ITNXí‰)bOIUq~‚JYéþì÷À鮦Òt ͆/?¦•wãU[j«Ï+Ï–Qz•UÙRÆÅ'd(Û_ÑûƒX±qïÃBŸy ³g5m³¢ÊVÆeüq}£»J©ûsg[\[Ÿõš>¢ Hô²2
+à%ª;†8;2¶/+BöM˜ƒqÿvs$`Ù‰öY4/á\ssi iUcdæõ”U›øóˆa¬û§fhð6uaÉPëÚÇòõ&>ã%¡C‚d>«zES³?«É+éæ&ÓÎeð{™\ÄQ>‡Ý+)ú ”^T9ZîPàäåF­ê†ëü«‰
+r‡3„„“L2÷$“™$ €x ¸¨ë‚Õ*,^-àÚ·[]ëù<**è–R®ÚíßðýôIˆ<E‹|Nù
+!fÓç¥üÜ#ƒ ÞäA¶É§oÖ(u´
+gį›Vgæ”·ÔÏçE)Õ?nî/Ø  bd£éSûu3ßQ·¼Q¸–ùI»B`×\áC!ž’ÄdÑ
+ÙPÜ,ÎÀˆ2QC³—ë[PŸ¶å‘Ò»µnª`±žRŸ”ºƒ£šXYUé^í”Ò'ý,|Y5G 4-}Ä<¸Ÿí«kÀC÷ûÈfÞŽoÊÛ†3
+¬wo²¦ß¿“ÙD[D$ú”<ÏÞHÜ×½k”¨ÕUÞÒ*Iï#;He6Åi*µø80C¥¤
+î”P&ÒÍÇàÓh_2ˆnÁnm½‡MsC¯?’™¬…ºÿê¾wø
+õ¿Â/8*ê;,Óï‰aøÍ8sÀk¨ ïÖ~!¯!Ö@)Å¢Ö§ï&Ñs<-ùsZ«•œ2ýEbÆõTçb~AãðA¤}Däʺ0ˆ¸S”‰{‘ñ{GðaòoqBÍ$ëF8¥ºûz±ÿ*ˆòd¼á¦¾ƒ¨™:ٟЕLza:JÏìeaûéæø¼æq— ³œTÝ*Ã1ÿ"}µ!‡@¸ýk˜ÇÚ˳á߬ÎB/älEyÚ~ôeKüìŸæ¥\Ÿ5¾ä$ÓçOè=h­áßZ¬îÔí©Í“Þ‚9 œðíªæ´`ä_ö¦¸‡˜»ÕÉÝŽç[ê9±Ä¤‰ô¯×u2Bƒ\s¦ù ®u5=•–@¹ÎŒ‚õp@™z tˆ;Œ*+¿ã.ÆîZæ°‹ùƒÿa]²1ÀlùRÕSëó‚¢o¬á‚ÀÜÉ
+‡ÐšrÕòHòYìK„¬gÓ<³`ÖÏ7/Õ|ë+4A÷àû*òKÖ¨8–‹Â1‘»%¤ Ï-1w”‡¤”˜Ë?Æ–šª€éˆ)ÓrÍÒõ~ÆK0é;h¬Ã±+fé€g:"&›6)†rúÃÀpòcúyìB£›6 b”„Íë#˜&lõ:w¦†¸>fިÕyL/Ø\ZÌüŸáúðjò@
+Öÿ$f»‡~ ^¬Í„:¡)É]È•}‚pèæA(žŸ+…R ©´X¨
+*XcÝZ3}E®Ù“DÅsj…d9FªKŒä›‰JÈ š ¤I1{ŒV!xnK®-e>³ÆëÒ7n·×…¬§á ô_Qg1ÃR2Ǽĸ:@“nè\ÖKžXù¦å°Ÿ)Â'€WIØC0hݤù!XL}†¾»É4œ‰l5É Vh2–°,ú–?ƒžbLb#(sÀyÌtk]:ibP _"ÏÈ2S&‚FþŒ ߆*:Ü/~‰·Ù5ülÒ6ÄÄþfÝ»¡ ²¯Ó óð¯©vÍ<NÎE¶¡×"A¢MûBÛáˆI܆=ôžäÓ°D!5¹ŽoÈG³]ëÒMl[UŠôZ~$g"þØHäUø(ú>ô(l1‘Êïu’™;8ãíŸqžµ·ŒóÒi¿7mœL[Ú@¨ÅWxlâ<R‰s\Î]1Ç^¢>äg™ÑñË íY<€#nMDý«yYùZOÃEÑXê;/C<_í•IfG‰ªuÛúR›äÄOè…M­++cØ7šS«ò
+4ÆŠaZ²÷é­ÔƒîŠœøÍÎuª¤í¶ Mߊ]–>]o°¶ÓÐ/ôm^&=ÂNhÌ•.®g*>d_ò$«¥
+]ókÜñßÙoj-¤½Ö]ºŽwðzýí`ùg`¹@ÝXRËê¯ïÖÐèžSZ^6Ãu¸VŸîø^×ïo«×ÿÔg±„~X—ñQðô Èæ¿®¡ïa%{s Tú·Æp4{®³ÖHLydWû)YUÀ&R?î
+âS0᪄ïg :¤poÊ›)·-.XF:€eÙæ
+t&8…3µ¯Bõã-ã(;û^S‚éeÚÙúdSyÂ7yG^íH@¶Es­¹7<ëA–³Q|•áh“[ß\j‰²–eÿÚZŠÒŽy1|iÂ-—Þ<C^ow¡³Ñ5–Ãè¢pcŠ ÆP_¤ªâ<.&nýŽ;ËŒpa‚…[y«yŸ$Û«j„ãì!É Ñ«Ü$E³42éËšvå‰?Ý´öé^ãwnYg?GÎç9c¹÷ËKŒ+%¢<qWÅ”˜Æz^}NV–ã”]QvS{Ôqš8â‡Ú·u)? WVÁí³Ì1lâü%õWy¿ÈÑjpÿÿ‹0#º©>M»']£ý‚š¦|§k!¾¨õ3h{ß&«Õm¹›5&[K¨iªƒK%Ú}UEüÞÚkÌ€Êåu hTñô[Ÿ*îF‰÷´k
+?µí;”A¯34VfO× –ý–ßæ 5âœÄ*DvƒÿUe_ÑRqr…_ê°ü¨«pMv–‡]€ö½{ûÒ›[;¶õfÕ4¯( Ìc5Ú‘Gìùõ÷d›xþµ±…»EÒjOª˜®ü-n
+|âš ë†g8žî KƒöٶŲˆ]ï{r3ÚJ¨(°ù?Ò±’µq…lá÷u;S7¢êÇqWA}Åǰ³†ÃÎé=·Åo
+¢nÉœxgº|ŒGCT˜pŽT§aHžìÓÍ—O¥0áU`llÚ¤»Cçltîî0Ájþhý~±¯pÚ±Yú_é,xü'ù,ÉIU·jnÁ\Í[Møæ z©ÎóÆÈDBéb<Ô¾Ó]ò«ÜT7S0CùoÛ}ñ2õ%ýsÙF͘¸MQ«óÏ Å›¡!¤7füSÑ•&.!m›àôéFk´¼õ†(+OÐ ãºóâOÈ¢í@ ê•Wª1ù¦f­ÙôG Û0J¸èZ¬’âî-#¢=¾ÅýÄqËb>@@Ñg¤IxFzŸ|˜û½âÅÞ´\“E=ˆY®g6ÒatÀÒº*ÿ¡SYõŒÝçô5û—T9vh÷ øŽ Æ%2{}n€ü´}Iò90Ýv zRf‹¢›Ð8kOËʼ™¨jV‘oõ:*¡xÚýH3Ê_ 6ˆWWxç4Ç\;“5juÙêèK:«ã:iì²7‹rʶ‡»YAd§«~XÃâõ×:J1<Œœ¥²;îäeªî
+(ú;èMÙsºÅÈrlÚªÜU[y5ÿ»¶ýävw‹(k
+-Í×Olí—ÖHº«¢WeGã£ü݆L9s›¸ªè‚ŠFp6§ i&xëÁÙИp0üC2}ÉTxmÌCûÀôH#îѽZy—Ú‡†m{ìç+ËEŠAaî­ö‘ìWÏÓd¼V¨Sy%ÛÛ®™8bôש"SLL„¶òé1å4Ø$B
+]õ@‘äV9È9'ç¹Ò3Õ«Êè ¡Pç?#Ü0eDãaã¶ÃÕSœº†#Ö[%ëÀó™µLYŸø”‡Æ‰U‡U•_n¸¢¥ÊFÀ†η4FÙ/V[ØäŽ&+‡;Õk¨+å4e[D7\ì~YCm[†D¹¯úÄ·æÏÊó»ë4MÑn/ýé«9Ó0ÌÑéÆ:/[Òo¬¥`ŸK½Ó˜Ž(Öá
+™ƒT™Þo…Ír M­8«ÍæFECw…LhT¯–l¿…ç¢ÂíÈ ¥;=í×ÑL×t¹Ì^r%´¢Ë|ÊTXN©£dñR3O>¢Ö«Ä^º°¹àž¡Gû)­ y®·¯ýÛ‚„2ãbþ+žPgˆDWŒB¤Á;TäÑ+ô4Q”v—¡§€¡{ˆ§…À9è¼µ;Œ¯°!f»é6~/Ä—|@Ír~’½EÃMíŠ$,<ª²`ÿ2Á‹¦+ù´oM­Ò¿$ȵk뤆éÄÆ)<$‚\nnu|”LX
+$EVDĶ¡¥Ôt o§Á \~6-s‡ð/€/²›‰EÊÔä 2<뤪tñ ‘Åù:¨ÝÚmb“Õp®´V÷±ÊçÈn(Q®7: ÏziZ¢“îàN½l*é3ÄÔmôiИ¥‹`® ß sµ‘ÒnX
+ýUœ‚\ПÈb¡i0™¤Ä^Kcà=Ÿ!!{pwp y€ÄK´™H´ö&ôȘ/ùU¯DµáÆgõ#M@’êÁ1“¦¢“¡&®ÓÁyf_s‘IrÈÚùÚÅ”\ ýBcŸ×¨7¬H˜eßxÔXl¶Åtbuæ!†ÿhI
+&)¥å Ö©êršbp›s³˜Ô;î²Cu GFæqÝ~~c6RbOŒñ 'l—"<Í–¼‹üÃz [ª”TÿÏ0}°5y
+¨åÅAK]מ ñq\k‹P­µžU´"ÎJÑ»÷?¼Wåù¶{¥¯j#¬'rG”^–‚ô$U¤Ù)~VÓHDäÎTup©áéæ7©ðe’ÖÊ⚊–˜R³"«I˜›^Çw0Ó^Ô+m¯âO©ïX¾‹ÖiM£Ái†-T‡ß5Ȱ©'ˆ†œNÙ]¢~{e‘œ
+ÂC²½ÙlÄ]œÂEškoÆ% ¾õݼ§è„¦™“‰õ
+ü‚CÁ_"ý>“h0š[[¡ÑãˆtVÛƒw,ÑíÇU™£À^|}²X?†½Šõ4:a<X… sšá%˜Ã×Ù‡U)¦Õ<‹@Z§Q›/ç[µ6ÓóÌ Ý.ú ÿ0A=¹fˆ œxãIÒ—³’Q¾lä3\¨P©BoJ]Ô´ã\>[3Ô²õ­ö‚­Œ?,·ôÆЛMOyIÆOi>§ œ'»|“àÄ2Óökޤx¥Ío»ç6‡–<Jßc¹^|5±Ü²Zz;*Õ,”1¹—M;]¬åôU÷º «¸¦DQ$6*Ú“óÈXz#ÝËÔUáèfZ,kŒâÐuŠõÜ)jF¹ÀR6í:ƒò*‹hyYÍ”­pÊI ~©êK³SpMnÂQjm-+òy²žÉ½MÊ4q,œÜt0L$t®h!f«³ ‡‰_åyb qº¶Xð– •@ÂFb«ª/r„àhf¹ÄZ`;kð4<aÜ%â KùEìb}°ä9v_Õ“ý ö³ÆU‹kñw±3À¢Hõÿ/us¿ÆþkÌ`Ç™;3—Ã÷Êÿмæ$B4\+;ÝjèKDóŒñ Z FFŠÐvˆÁE§áu¬{qcʰS¨«TK¸àkø
+rS4‚,FæSˆúQ¼?’‰†D »Ñ"îuä%ºíŒZÑiæ^,[U6Pw ª¡ãÔ°¨š§S7ž$¿ Î!îE.…ÏÜãHþ˜ý7ä^üˆžÀ”u‚ö¿¬Çm[E^Ÿõï)@Yæ¡MSP‡)'b^E»qÂäc¶?¢'F™¿¡¹xûŽûmñä¶îÖuym.͇$|¯¦O‹Kbˆ´1ôcû¥ªwWlnŠÝ~B ª¢½XfO yDzæù2çMÓ…£B QWê´ù¨ci¹2æ’Ý [êb},ÿ1ðµEª¼éwÈô^»Òk˜¾bÈ)Ìk¬,]—ÃwH¥ß¦Ï4ÄU’›£mϪKc`«C!åÍZ¸ÊŽ 5¦IõC¿Wô ÷fQýÆ9ÉQû|U«¸Ëάþ2=Éæ-ŸIºd™¯‹ˆ 2_ªïåM˜º4Ü /Ú¨»åB†>oy
+çaG»ð•˜-?”J1§ˆpŠmšÈÓòÄ"âõ2ºŸ§Vñzð|Raw‰~ÿlÌ8 áëÅÓÿ4‰ý„öÎüÜøó»™Ã‡‹ÎE°ZßKݸÅÍáUGÙ|‡B1ŸÙ&T6›è;wÆæW§ßÙ]8ÈÏÙXâ÷yÛWÒîðG-««ª¸%…r9›¶ŸPvø”X7iÞx*ÌLƒ(Þu¶¸==½ãϲüÍ“m qqZe¢ð¡&¹ƒ›ß°§nž=jc¨iŸoÌÓ@ŒçýÌx/{Y‡¨¢/írÛpÕ“Í-×k²b§›(n‡«™J;7ÃöN]Äž²Ó |ט 9^Rê”,²¶qäýi-Çj5I›~WfÇ’Ž;*~x†ý˜FÊMµÚuûÙoÌ€~Æ7“ÎS.0.rm«ú¿4¯P¿MÚÙX¦Ä
+dºðõ6±žÁ͵¤N|b*¥¾4uvõv#3‘Ö¬Þ¦q½V`i2d%‰Æ5±žö0ãµðÖð7Óœ õÄÐjxÀ÷9‡ôx磨!KÐhFÜ bLb4¬EzcláÈXØ>ò’eÒ¢ü€(j =äû™‰~ðn$¢± Y9P²KàÚÐC‰/íq䢘Ö¢&lÁŒâr³èSøÁ
+-ÀbY@ªX?(Ûe±92í"Õ¯)fÃmˆ€6@>íÄ_æ|ÓXȼ ½ÿæL N§+VJ2¥v&¿Áœ’Ç‚•ÙgÕa:yÊ*=>ˆC¦,ê½……zq¨¡©øwΣa™çáVîbP$©àßÔ†3H¦ ‰ìµ*
+|™tšc^Œ7½Cûv›ó¼fÝC¦›ïºUʆNì\ƒAœ X)ŒM¢×ÈŠ¬ÅQËr’K{¥ãÄFÛeÍ"jùó%§§h·¥C¿ŠùÀªi¸24¿Ô.$ÛÒ²–ɹ™DÑüÓ®Â?2ª»ðÏ]HMtî—Ôa¡¥»PZé¸+CØÍ9èJ*™á_r%QNÊH®4Òrõ{W)þ |Õem}˜^…e «ÎÙ»
+¶.év–¬ö_.e'TÞ)ñV4û¦(FéïoÉÿUgzfðÓ0Õ=ÛrûºÆ£Õ[(hÃØGjKÝÝÒ¢žÚy‚}¬÷¡%ë€]ÁÊŸ%ö€(yì­¬À¿0Üóæ·àÒL1Á½™sÚR1w^N³JÛO÷7ö „ÌÙ†yÅoxõ™”OÅãÕ`òi0) Â¿6Tù„­@JŸ­ŽJL#†Ù×´©²C
+ê5\1(ù¢â¡<U~¼¨
+ÖBÚ2iªNÿø…í›ÄŸ¡'Øï¸:dÈa­¥Ö9Ò<oé­=’Âð:ÚO8Ç
+ÑcÞEÃNéO‚ri@ñDüœ¡Ž“ï¤úu{Aî6.ѱ>1óáë_Ù:,¡ ƒûJf?È/•Lèí¢CòNÉNÙç*E]Ù­!ç½×ø ämqú=pñ)²¹¿Ýî
+œc«F±M¼Hõ´Œ?¨£b;t•%› 7órÁ~ãû´£ÎL&ž3>µÅï°žÁ~6slD'9ã?´ÿ6š‰T­æÏ™‡¥^ 5÷;Õ
+k[ }€Î»ágX0^h™q$WâKJm3ÖqV/fÔÌ”¿Æ&„í|ª}3ãø¶½1—ªèsÌO[­9ï¨"6Á‘εñ6 Ç9àK¦ÿæ+´|dÔøæjÒ8aÞ&Ák ËɈŒž=9wåUžÍ©ÏÔ?º¯ƒ®Û¿Ó|ÍáµÒÔ0,Œ’«¡lug™³z®
+ÓC‚B¦´'ÌøL†HÉ?— 6¦û¶ÇZWzªÍjxAñ|õ+cs£hi„#aÝ43¾ü ËKÑZr?Ú'H:‚µmø2ÇèAŒÄ½ ÙešÐ­d€cÐM^ðkÃ^þCÖj#,®¤ž@DòL2Ëâ
+„"pÄpß„CH„æ I&Çd2÷LÂ)x‚ʪ­°*j±XEtJ×J]«EZ_=@±XžºY¥¨íî÷#>§(§Uûª÷TªÊ#tgåÛE Àíª¬ÌU¦O4E±Á]cD®•ix`FfÁ¬åøw0b(½”ÑU
+Õ•Yâ®ù]sAˆêvjßfh€w@A,búÆx#‚iù”ðu+•ÝE¹†Ì_÷X™¯x˼U-ÆEŸW'šÒ_ë½@ÿØ
+¬`V´[@-k…¤b½’°nÎ_PëáÅéeÚ:øÿ60ŽŒ„š³Ðñ€èl–u-'\õåj|Dþme;±®tHÞGDæËª&ˆø´‡Õ¡D!ߪ¿@Ðá—M…Ä?Bâ=ër›äéΕt<‘GHß8šJd öÿlûM¹×žiÜlv>Swžo2î§‹ŽYê©!Ö;DL…é¢Å¾]²¡ò®†’±ŸÖ˶íRÔÖþœ¬f;Ë·¾Ò-rü®0ÌÛ첸ÛR}Ö"‹¨?5#ðÍmk+3§(¸¸­(.ÏõÇRÄÝÉxP{K$ ‘™ê~?uXÅ m‹ÁµÒÂ(U$C[Kží·–Il9v˜®ŒLæ"çF¿À«]C2qƒ.ÍOI®6Ž1žQÐçªxã É1i™QÑZxlµe_³Ì)O&uZõê±èCÈj7$6ôøÕã} âA~««8zXþ°ÜÓÀ¯âm¿b|®nÍ^i>¢]œäfQêë¢ùÖBchÈJDj^ðû k‚æ½]èr¥ou‡ö#I¸î”îšh
+8ЂTc£1)±Ã¼ßüWÞ+Ë-Ë*îkxƒuße´I÷~€ßÁPEŒ:À¼±LR] &‹‚t-¬^à*$MŒ4-µb¼› îãÎB 迟c˜œé޳ñA9ZµuÿKíDÛ„§«§Ñù¬T}–•pËp;dz›…x0w
+ú Þï7 ? r™©ØâŽlJÁ§ûÄÑU/è3B®¥K3hÿ f@ jçm1©¨Rî×–D*Øï¿pù–©ÁÖ“ƒ2ÃÿOö(VvÔÚ
+n¨‹œd¦³úmMAOßëˆá¹;í1ÜS`§Mà÷Òú-a6 —)²ÂNßË›,ž£_
+ßl[có.HûÑ–%¶Å–Ø´+#]l‡åcÙ¶ŸÕã¾ö$ ÒsÔ~’&ªÂÞbâ~In^™Yö6-Šìª¸ÊŸ£–É/FŒRˆêa` ýŽçÛEi|à•o Îò$Ô³¬¥h:)=’kÇZvö¥6ãÁŠg|V'E´Úë;ÈRèûˆÊ¥^ýt\"ZWÉÊ
+Y°»ânN'⢒¤ü†LiøKÑ[ºž!6bؘƒÿjnf¯$= + *™.Óƒ¹ÆöK›vIöchP÷*%Õz˜ÙÚ‰,1-ŠpGsÀ¨ÀDì8D±C7x†¡&µXš¸Ó8e¸!j5kLÌì4ð–Y›ü Ä·&Xqˆ¾
+ 7épÊL=º³#.´®[ÑÌCjÏ¨Ž†^wÖUOl’Õè€Tv·CeúÚû¤½Êöè¶Íÿ]òŠj2Íã0–uuFfÆÕ±®ŒÊªè:„AƆ"E©*‚±S¤¤'_
+
+•B©PëàT;üê}ú *~>22
+EOL¼€º_½~ýæÍ[¨ýý‡““Íý g ,vó˜,òcy]âzFÞþlç(§‘}FVÏPÕq㫪¹ØÚJ6©AÅ$µ*ŠH¹·$ÒÏΡ™ŠÙÁì`v0;˜Ìf³Ã—9¸zL2¼‚ÆÙžQC|¼QàM5ð±›x§¤zA¨R+Œ­Ô” k*x½ŠGj‘sH%Ť^VaÒݼ¼´û¦r~…LÈ¡Ø3hœã5$Ø‹»#òÈìú2›…'$ ’ó
+,ª¼F‘P].ÁÖˤV!‡ØÉfoÒèDc&2`* ÷_'ǹ{# ݰw%{2º¥>ŒÙaQ“*Xª¯ SV*â5r1V/‚¸\‚¡ˆôä2·Òèd£©L9¸x~dºŸœà»E ‹]0ý
+çôÕ^z[ÙAKm€°º<¬L!¯‰± |±¥˜tä³®,&p#…Aºe* ×aʽ'pBàré±Ì }¯Ü1µ·zw¾AçÁÒj$ª²°
+±,¾Ž9°KÍ…| -—‹7\cÝÉLâ Sy r฾íRꈼ_½blØžÝÒ°—U¡KTáU|YB[ŒÓ3„@S¾
+æ Ðz•Oh'óˆ¦ò7Ã@önOPàðJ¾-äyõæ¸áV;Lg·]f¹Î“'¨R±”‘j†^[(ÅëòÄ€ž
+y¤C”B I@l3o?å ÜqìM™Ãù±zÛ¨{·6ÀõöT©Ö‡Ï*?]FW^VÓˆš|¾.Gè2 T¡!YHl" Iͦ
+~œ]ž tË‘·Õ¶Á:6^ìüm5ºÂàÃÕúò +Ï)ó”ÑU¹
+¤&GŽ¯Í’êÒ¥ÝU ¡>ILÔŤSKì` `›;¨‚m\¬oÀêœöÞjœ¨c{.]wˆ{]}Až][žUЬÎTà5×䄚T9±–"#Ö‘¥$-QJÖ™
+(²…R;×7µë¼îõ®òn^¢°§ìœ†£¬ÌÚaº:VžVT¥•áËST@e²’ &—«‰
+’PkL”l‚ëvw¬ØÙ6´Ô·ì¹U8{`>§Û5#½õ8-EʦhcÉ5Iƒ–jŒ ¯ÆÉ±Uxº(EU•¦Ê×ÃÆ–Ùu=X°U=±ôï¹uxî}{tjŸG
+¹ó•Øš4Çaã™(=œ¨Gràõ(n‚Ë×âøqZ¼ÀTÞi–­ííÿvUíè‚Âɇ F7†¤ :&|ðØ¾“ôÐôĮȬ8CLNlG\nt{BÞåvx~T2?²]ÑŠ¡™Êíî¹?Öÿþí:éäB':¸ÚÿÚ û³¤§n‘èA¿øýSè+ý€Ëw.‘"nG%…÷ÅPBûâRBz®^ìML½p‘zþ&*ÍT@æ–õ ÝmHÛïÿ®hÿÙæØ‡¬ƒDcññì΢³¬&Z¤T_
+ Ç ÇÒ»Eèâž/Åì`v0;˜Ìf‡ÿË¡pŸß;†Ï™Ö¾ØA΂‘}„ÚUÌl¦‡Kôô8¥¶SQC#kÔדtY”FUÚÕVErºAF̾!ÆÝâb7¿”ÏEŸ|üÞ{ŸeûÄ òöw¸þYÝ쓌æE‘¾8¦TË@VÔ4U4²®<7©I™Ii—¥¦öˆ“²û¸š‘A(êýRþí@÷öûƒý: j:¿ã8þ¸öÑÎvÚî´®ug–±«Ö*t´âE@EQ‹€*—r 럄B’î;ä !„rI†C@V@Å]Ý_³¶µÓ‡äQøàýü5ßù<ùnˆÒ.>—¥UßWÃ/)³aÅYØ/-Ry%•F·¤Þ2"Æ  InK/iÜÈ"‡tôY£{¦p—8dþ|´Q\Äx
+–ö`'ªyM>¤ a¬ATçm#GÑ’ZVZãÆËªÝ¼ÊETDèÿÃÖõ‰_l ®}²mð»Ï’do¿8Ëz¶»”¶–PÝÎÂcò)¨ÙÒVdøjGÍT ³*„ì¾:YϪ ¢z*‚Mìò
+yOC¼Å»Øæ”ÃRéeb &Ùìl§[GhmÃÃÍÔí¶b9£ƒMð˜%ó”>]8îóˆ!pÒ€Ÿ~æ{áñ’g¢¬ëkâlÂ’ðBÇ4ï2?È©VŒ±õžn¢ÙÙI³Ž´Òí6
+Ãe%2G-8¶o
+Ÿ²Nç–aìÈaâ½Zfk@ÞÎ é¼0›,œî"IBL’tr³AlÄ øˆ N9õVžrñ…:õÚ#ÝQôª1ƒºhÌaÎ x!c¹ÄoDªÜjÔ€E‚éã½òš_ÀdLqœi&]8ÓN“LµS¥“›íƒNI’äSÊä‹/µ)WÒï˜ÓÉK–lÆœ5==\[jTv]½Ù¨@(…WØKñsØm!fwO¸)œii“LÑÚ¤¡Íö?é‘“ª#¯tÉ•OLÔ=ë?Ù¯“¨¦ï€ã‡9Íë,oÚ9̳³´tž‡Ú2ÏâÀ¨UAÙÅPÙ×@C6²‡¬„-!d!ûž@ BB6²BØÄ‚QDÁŽˆk«§ãop¦Ï9ÿç4Ÿû÷úM§Üre²—½9ó¾*¡ßÓš›†ûŒ—RMð‹dÔ0WÜ:ËrçBÙ5*G1GëRÎBd; âib"P'dþh8^øý`Bõ–5yÏ•J\ ž¡¯L¶Î„*nWÎ2Ò bÞß­¦L¶)˜3tç*E&' ”sd¾r* ‹Þiˆ²„@sìì?ûâ/=:Vùh,~ß—‚½;{–´ôu15®àÛÆkµ}6¸EnA;øýxo‡bhéS$uÇ,N%œÃÉ•³8©jª· òã'€îè™×q/¬qåO=Ç`›S)ˆÛËçг ù,÷T™Àì­×¨ísïÖÞ=@o5’-zê$^ËšFk¸3(l¥UAä?5(ã€!ö4°ÎÛv¹ò(_uw1­fáf:Þw-»}hâŠXë©ÓKvz†q¶®A²›ÙOñQŒÔ NÏœ@ë:&‘zá$B/† Èÿ$Gc€*î8è?”ñÚz0û;ß——î]/ZZ‡…V’É#sY]°X&q×zlK×NC»™ìd P<dÕ‹ïcø1ƶ
+[°†ÁÜÆâòoðy«DÈÂs?ÿ™íÎ{hÀowóÁÇ1,°÷8 Äf L?ßðCVAÕ÷yE%Ï
+K.?)-¿ô¨²²àamUÞÃúš [5¹[ðÚœˆºœȺìMtýùMì0¨€ðo»
+ËÛy0Á£wõA6kÀÇloáŒzˆ=vVätbd.RãCô{,“Ç„4¹¡
+”)ÙÛšô+Ou§`÷õ™Ø;ú\ú mqׂªZ4+‡+'Å8ƒ¿‡bqu2Ǭ¬NÏ ­ÇßG‘†t$í„§â ÞèƒôFïÂ7 ÉG€,)¨’³·µ©ÅO 'ë6ûÎbîögSoö¶/+øWu²°£÷ˆˆ–Q.ml°½Íc`rjš($oQM
+ˆš0¨rIF?ÿiˆ¢¤#@ž˜ÔIç_éSŠžõ¥×>8Z7g‘W-[—ÌåÜ«½ J?&Á[Ìü–1Ck\ÕÖ°B†"ÌmÑ„;[ô¡Žƒ
+ þ©¡7q§áD
+Ð$fýØtù;SjÍ–%qÏzޏfÏg,ÛË;¦-°^¡Q`-}"Ò˜ŠG÷HºÚüvÞ—- ·35Slº.ÔJ7 øoÉÃ@‘ þÅ5pŸNgÚmwº³ÓÙ±•Ù™mu»£ë±*ꊸ‚€Š¹/#7H äNH  Ž@HB€„\ä$77!áP¡ˆŠx
+ʽÅm°Šôzw…,³»FžÑÅp h9àäûvoZþñíÂͯwÝx´ëpçËïüoÇð—=3éÎb¨³—‹
+ï$”çßʨÁŽåÔ!G‘lø0–›3DhÌvøYv² s°¤)ÝF¦ÚhÍ)Õ-®
+Õ`®FmË5©{òÌÛökï ÿ· ôÿ5„ÿ·!>s™™Œ^ ÃÈsUXÚt™9UJ厓«šÞ7ŒàYΆ™-PØß7 $*gƒz0W«î‡›Ô]yÛ÷¡él`\:XA>€s9ø7<î5'&c™E=ffÎÑÓ•DædyiÃx M8ZHë¥<WaÇT6T‹Ú†”hò”º¸AgEt€º LÈ1Àô ÁAïCc×#ÓV¸ñˆEvrÁ}fNùt –1I#7ܤ– G‹«Û†
+˜r;£´aùšt«®)ëèChôH½^ê0¨ÑÛê uΆº€c€pð.]ü˜ ~xê ^LÞ“(qžUv‰¬ª&roÒ(ÂJe›ƒÌP Ùê<_ׇé{Q
+c7Rg4 tFZg”a¶ïCC½³¡þâQÀ½xð! )(ú04eµ)
+þTÿ 12ÃÎ¥ßfà8ãÕE‘òò6G ½ÝVÀÒôøú^ŒØØƒR™(E…Ö˜¥­Y„Õ™¶ °>6°.¼à"4F½m IzÙžó¬9ÿ°)©d¶1»ú źŠF+)mjuû@a½¶7Ÿg°âDfFiUcÔR¬ÚÚŠÓX„x­i»>6|XþGÏß/@@+$âßâ k¯ÄaÙÏÅÑØQbÑœ0ƒö/¯n‚MàÒ‹Åí]%µ:c!רËZTxY§ §êjq4ÖF‚Ö¼]XÎöywÀ?çþÃ=5a'
+oPùE¼i„­·„ ×Ô‘9Ôq‹Í©¬[ üq)Q<\UØh.gêºY}‰°WS(35Qû•EJÖYj)zS µÝh/PókƒÈë¨ó<”^~ É'ü­Ú?aS„|¢‹ ¬´AÉ :8}F/¿RŸ+¶ˆ¨Ý|š®­Œcha
+Œ 4±Y^¬´H‹´ÖjªÖZU 7
+Ûûìõ[C­ç1 ?w¨<}A‹wØ{­_ÜKýyÄ“ŽðŒû]Pâm¢p\‹+¿Ø-¶ T酟ˠäðŒRVYÄ[…tõ
+šÝ½Ö#ø]‡wÌ‹ž€¤Usri07mN¥ w¢ËŒ|­®!W¥’Q´RQI§°Šcä fWlerUôòÖ:Gg¡³õ&{ýÖ q?
+”n. ÅÍ ´¹½éòŠ|fò0–rg$uÍšB¶ö 8†6œ¨¥9»A®$µVËŠ:¹bVoi ÏLŠ,EUŠ‚Jí@!Og)ªÐ›íõ±áÌ@êv4œ>4§=A‡[À+£gøÚ $fy4"nv,9r1™ÔgJcë:±5J-AYÛLÔð
+:J匞Y¹‰*©îÏ—È­y­5¿Zg!WêÍöµ¿6ÈήÎ@çâºÎ@6,ÁGüÃîÎD†OMBÓ†F`ù+¢´Ù˜^-íÄ+*u¹j¶š¢/i¢u‘UcnC¥9K)ëÇ7hú³êÚúsÅz³½€ØÖ ²5È]ÍÎN ÝÙí­ÑÅûÙ°;äÁT
+g 9yNºnZÏ®^sw¿sâ9µqq.Þ5ŸÛ4–̨½œ&¨ÂJ邊lÊ×d‹Ú2zYéÝü.d—¸ÑÕØ“j0t£´=ö’#Û€Øå{ :y´?ö¶ï¸ÓÚˆ“óâ-w§©»ž~—îB =wBÐêÙ(¢ôz?ç1GÓE—²äÄAR#ÁR¤Æ™™Úô¾Š6TŸPŸf¬kG5ú”^C‡½€ÔÖ sÚš€Î#?­9zïú ‡é%g×á· žû^‰ê»œt!„Ì›a2¦Rx…c˜šü‘,iöp¾?T¨Â ЛÐVN ÒÂW#úEšÔ~¥Þßj7 sØêïZ‡½oûî_;°wfÑÁáÒê ÷žÇ.áêUÉ/¾™ÜÅ`
+cÊ(¸¯ N`+³G³Dø+ùÌå‚:ô%Z}ÚE–2u¨¼>T©J”4& *íêo‡w‚Î}»Ÿ ïÙuóö?¿»¼zðXï‹cÞêçΑ’ggS¹+~™ô¥P2u.ž–÷3‚M˜ÆVà&³éãÄ*ä¥1Z,‚_eˆ“¯°%I#\iâˆP–pYRg/Pphý×ö×ýßm¿sãYÙý}ïÆ~'õkGˆä•s4÷ù¹Túj`ùÞ…¼ÜX~>­3‹e¢n؈™¼2ø4¹"yª ›¤ñ'™‚ø‰²Ê¸q~t¼Zh/ Úû5èÚùõƒkùòêƒoþfÜØµOýa¿‹8ús߸FÓ_ø$“‡¦ç¬@3q˰<ôR ±€¡Âïd%Íç'ÎåÓnQéq·Š±³4fÌM+úçòÒ¨|¶½€îÛÏ׆·n›û|‹qý›íj°ëpå‚ChïÝ#È/ⳟ_€c×bQˆG0lÊ
+û%=+á>>'n9;7ö^^^Ì=1ú.5?j©ˆ¹D'_X,¥D,–QíµnúòÓÑñ?ýÁtÿ‹/ÔàïßJÀžÃ\pø œò&ï ìw!‘˜×‘±©±0Ø‹¤”øgˆTèStZô*j€Ž|œ“ñ˜ˆ D„=¢bCáBÒ3‚WYx{oùÄt}Ë5›[ÿ,ÛwòÀéÀñ$ ¸Í4LBÞA# oaQQÿ…Ç\x„†¿ÂÄ…½Êˆ}IHÙÌNÞÌK ÚȇߠÀ× “ÖK’íeºúÙ'­sŸ}*_}Å;vÐÁ¾½$pâ¸;¡€ï™$pþ\,÷Ž1~ Á?$Äù €
+
+˜à
+9¡~òÂ|?Â}ÞSíØ·‚R—€éwpƒÿøá^@òž—·YH{ÊÆVDòrqQÉ"…Åž'VpoåTUÞ$ÔVÏdÈD×±Jñtz“t
+Ý*›Bµ×M"{ä“i–ú1äÅúËèa¹=~oø½áÿض†R[ßÖ ó•Q!ïñq/¹ð´§e˜ÌUV.y™VH[(`²çIåÜÙÜʪY¢š¼L<SJ§0Mu“èVùª£~Ù«˜@(® ‡Céö[ÇÖÀñ=ç€0Ä#‚?¡±/0ÄS.2s•C -3)´…bgŽÂáß$ò…395µÓ©t2C!ŸÀ6+ÆÓµÊkhCÃ5”Iuý?Fç;ªé;
+DF$L„@ Ù;½Ç/‰d:!á¢Zu]õ{©øwþøüÿT •*Üï
+hc“^ë3~1¦Ü3ÁJŒE¨i¢é¢¸†ÒÏÆr!:Aj$“Uê†^Nà›ÅMr›
+Am-[[‹Óêþám@¼¨ChØ[kdì†+>é~rÚÂ`vöä¹Sˆ!ËœCƒìÐËkØÝB¬DÈÅ+Y]d=­“a&Óø¶JD ˜„;Dlw؛ɛ7
+ y툈zÜŸ°6š’tk4 6Ö—è³7Z åí*Š-–ÕKØ¢&%ת#qèfB׆cÊ¡¦Â2 GƒìMT‡ÍÂC?€žƒ».ÈX¿è [Ž‹ZžH™¹œ‘5:˜Wît6è­¥d™¾’ÅU¡EôîFIҬŋ(¦ËZÇ—ØkxZ¨šÙÑ,‡­žé´z0=¦ç >ø=PØŒ~„?‹Y˜9=1žžy~4§ÔÚ«t–V$“a¨¶i°Ýx%Aé!jäLs¥LdEÈÔ¶r™ÃV!tZQ<§Å Ës`ú
+gèSDFîd{Wöƒ5ÁãdŽË¸ã:nú8ä ð”¾ÿ
+oÕ´äßĶå^Ƿî‘:²f©ÔŒ&íä Ÿž6-ïLÖzH|ÿ ›ÿúbð«?ÍÝùâÓ‘uŸ¯¡—[}U¯ö
+^ÆÐ^…¦_Æåb6’‹QëYˆÒûù¨âU8úÔ2“¿Tݘ³ˆi‚-4á3ï4Òo“‰'çiÍ©·ºZRn
+Z“oÊHÞ
+‚bˆï#’1¯e¡ž§”nÀŠ ”æ?‚#s"*a÷Q¨Ì{u5ék µi«xtêJK}Ê
+“¼LÿËjHºË÷
+0}ùÉì…Ïþ0:÷Çÿ³[gAM vÇít»v§3ítÛÑ™vZuÕ®]ïÕu‘Cî;r†CDD @ !`BBHHBî„r;€ \BñÀêZP´X‹ŠnÅ‹uåß Ý÷øØ‡}ø¾f~/¿Ÿ÷¾ùô76ذQ Û÷ˆá@BbhŸ\ñ™Y²œu¬èun^þR!êø lQöwLÖs6ó—±H-M{ÊÀ#ŸÖRŸpËRžÊ’KÊ“ÄéÖ7k׌MürMßô'¿°Áï?ÕÂg›Ä°kúS!"œ q8@&¡ ™ûþxæ±w¨¬¬ï‹³3–KsÒÞ–G¾!禼®:‘üš–—ôŠ‘Ÿø²® á%§àð’
+(Dh>¤FåÀ‘Ø,ÈA¤C~ÐI)€óžo|J"”§&
+¯8ç-» ý‚QMxFeU>¦ðiHRæ|™Šý
+‚„½ N ÉÑŸH]iDç¼åâÑ/XÂ"IY¨æÑÿE–°æ‰Mœo®Ô(¸‡µ
+g‹]âYt·d¦¨_6]8ìí‚|ªpR~ =)›ÀLÊ}¬UÃz¨ÚÂÃ{@† yf4HsRAŒ:öVPRô¢¾‚°X[CY qè«Du*ܹr­à.Þ šÅY%3X—lsZ~= œ*UN^išÄ\U^,¾Út{ÅgPç5Ôy ÜÈ- AìEr
+ÉI§¼I0k&K;tWp}­ØQýhéý þ‚¡§ì¼¡³ü¼ÁEôp¼Ž× Û ªèÐë­ˆ0Ð'#@Ÿ‘þ®%'÷¥U¼¨Ä‰ÉÔû<FÝt]ÿ]"½JQ«ÆI†æóeöV¡ÛèÆŸ5 ÆL§ËÇÚ:ˆcmvÒ˜ÉRá»U×o=HB?Mäh‰>
+žR¤M¥L”pü'ʸ~ãµ<ÄxNPm¤¯¹µñ‹Žö zÍc×\ýcÇæÊÓ]ªOfyŸìlÅŸ]]¹à‹H€àêG$ÿ;…þ‹zO'D¼Mˆ {“BYL§½Ñ²èþóy Ä\Qœ¯FÅôÑ”Å{ÏV%è²Ü°I¯½e½ÞÝ'ëõê_eP†[OûsÁÊ,ŽrÁÝ1Fà
+ú+&àwÊo™F|ÈÄø,ea½—rq^ï‹ðžïTwg‰ºtÜ\«w»Õ@¯~`Ý`óúSðÃ6ìß%‚#ÿ䀣ÜmIàëˆ`×(@z†Ê;¾@ñóF
+;»œ("½Ëa¼¡)^STÉó±çSæ• ¾–7‹½’ö
+Ó˜>ƒ¾ÉŸAµdL£ ¦búcè!Á3쨠÷bUÿoø³¥m`kRŒ´S2i€ÈÂí@Šò
+1¡+¹dÔ)“ôVÀaÎsÅìYV.o*¾X0N?'£TgÄÖå<'ÞTôZsŸ{óZI=yw)=ù?SŸ4О\¥¯¸ÚžÕÛ …ÎÆpÊã
+Q#eÇMeX¿qåiÃÉJѳøRéSF…¼‹z9·ÜXÐFR¶’º”w©O”M´nåUzwÑOqÝÊKqOV<mCš¶!Ûv9î•Û~(õ>güþKx}E5åqcuÇî(ƒ:ãŠÊ¢2à‚ R¥‡^Œ´P)R @JHƒôC¨"BE0 A\а ¸êèg–ñnÖ‡}ŒŸ÷ï9÷üï9? ÷^Çÿ!HŒyY“‘ô”ŽÉz@-ÄüF*#ÜÂ1KÇóøcH9}¡b_ÉR×h2/s/gÜàõfÝ䩼 97y¹7¹ HóþßPå´ðÝö‰ça ÷±Š
+ràì|ôh%‰x…\Né/bÒzð|VVÎièø-¨¡
+5(n@éÅ
+Œ^$͉°¡k˜
+÷­$»€ub»î
+wk <sôS£ß‰÷MAn¯š#¼ž6ÇÝkH‰ž”fÃǸÔƒH¼TA¦t•VUµÕ°› b®²@)¬Å¶‰eØK ö†D˜¯“ð ôb.n\ÄÁ ÍùÜPbj¨þßîu±µn@ãiÛÅf‡?Z_üõxÜçw»))J_›‘zUˆB]ªÁ;éÅ”ÖòŠªF2‹]Kð¤D…HHh‘ðð½R~DVMÐIÙD„IÔ‹™Äq‘9 ÈÔ@>¶ÔßdÎßz×ý ÉÝf¡ÍËîú¬ãóî0×û]1>“­ç£F•é)Ò\d7KheR«¨UŠr:[Dáòx%2Q5I%euÊè¤aYI+£‘tÒJ’^ü%Ÿ(G-
+q˜êôëJˆhMIîlÈÊm–£ðµ"YÄ+¦qªËÙ &‹WQ%‘+ë¤Å•mò¢ÊËòÂʲ¢
+­”T®“Êõbs@±©¡ÂÖ¶‚º»@³ÓžEµËoûÜ­Ÿùº-Ð~b0üÌÕÞøð‹ê䤶挥2'‘ç—rÄ…• >•UÎepKªyÂB–B‚c^å3ûäX–VVÀÐIqUz1þ >7Ðþ±ˆí·
+Ó?Õl·u©ëØö·ûíÏô?ovµÔßô9ªÑ†¸wŽD«†Î%HúS2ª{31ÈÂâö|
+®µˆ†n)c!š˜5é*!/µQ)Hjì&ª†…I AªÂ €Ë |s
+vŽ0™š†žÈÎÉ2`òRõüy=¹0^G/‰Öñɺz*T×N Ók¨¡ã·©a#3åÐá™
+s
+¨­V¾»ºeùÌÝ Ë´Ï÷¬œ?²¥sÎiŸê™Ç1ñß“¬ÇA¾Ô‡a„ñqèûÉI™w3ÓSY*v†œ5Ã(€Îð¡³õ„àY51讆toŠ4ý¨0xòQ‘9€ûírÐlµ|±ÓWÓ†uís‹¯^ïY§~m»³á•ãÑKwæ¿|NQ^ùáç#B‘óqÑés‰ç’æÒ“æi1s¸Ìˆ¹’ìÐ9ZnÐ0/GûÏ·`üæ{ò|ç®åùÎßÏó{úcn[:6-›º¶2vk-äò»oV´÷ZÔm-…FÇC £ûq²Ñ÷Þâƒ4F¥cÉÑqÆ´ø(c jÄ&†‹Rß•¥ž}ËLó{#Èð}£Èôù½9Ëû÷®,¯×W²Ï¼3 ¨, Sý«!×ÇVCúהּ}ؼªîþMüG¬è‹N‡K?z8à–üOæ.{—Âý`Ÿbc?ø[BD/b¢>býÿSç»P™àýoö9¯¿„°3)Î{þ©J<ý¾#Éã}Yw:W@F4« ÇWAZï­Y¦
+]
+­àÕzÜHûMQà´õ xì¿Ýþ²ÇÂözAÔ¾“€ßïDkW p†¤ƒNÀ8t8‡@`s$¶Ç@fkèa;P±­Ylµèöë€én "¿b HäQƺˆê<v°R# 9÷ªžø;¹´’PNù@<G{w>ù¡…±Œoc.᮳cºÙ‹Ñ÷9 Ñýܹè1î¯1“ܸ—ÜÑØ³þ?ö` vë€5ˆ}w
+È+¤³Ô÷ñ5ôwqŒåØæ¡½ˆ»ÁYˆéæ.Äôðæ£‡x¯cÆy/q“¼1Â4o(v†7÷¿kHÆx®– õÞAûAn8ìx‘|Aœ
+<A$°ÓñŸ“5ñ©y‰KiïªËÄFÖb\ gÐÆ]ÀßàÍãn§¼yÀŸyÊŸÁMÆ Süḗü‡Äi9ÀÄXßÅd^Û Ü™gìAãñ¾F /¸þS•°B3QßSJèËä*æbBgx‘7Û–ò–Ð)˜ÃßÎá
+_áž‹&¢â”ðQüKQO”Ø`}¿˜vëAäl žÛ!˨OÛ*Ê2ã| v$ðS ŸØ™¤•d}Ò»¤"ÆRb%{TÏ›¿ÈŸ‹kÎÆÞÍâï‰ßà%S„1É(ñ…äqü¤¤7a2õù…Ô`c ,¬Aâø (=¶æÔ  =ºH'P¼AI#DB§” Ò–6i™^À\¤–sçÉuü¹„‹ÂYb»øMì­Ôׄ^é aTúŒ8.}œð"­‡4Ü!OÈnQÆe]f|i`Yi'¾l÷m㻌Á¶` w=ÎÔ A ¤‡Dxø÷"„´ÈSÑÞ²L¬7ôRÞ+jp†Ü$y™ð“t2¾3m‚x#>‘õ“Æe=ä1ùíıô.êóŒŽ$ó€³{5p°¹Ã× qÝ
+&+ȶü0È‹9 †ø€55l eãæÔ„ßÄJÚtJ{’UÈK®?Mj>¡´"ƒäkò>ÒíŒG¤>EOâsE7åÚ™ôý+í©ò2Ý<àb )‡×ƒâØ×ãòä{ÅA¡øÌ1(Šrû˜ë·hH<=«fDÏ ÂøH:í™XÃNÉô±KS2jd¿Ðš2îSBïQº”w)½™ÝÔÑÌë´‘¬6úHV+c$«‰ið°í:ÈÄþÕFç-Pä½Ê¬¡,ÔþSY„Ër Þg¶€2m¤GŽkøq#(BíC²8Äzáý”Bé]NEz7³½•Ü’ÕI¿¦ºN¿£î ©/3†ÕÍÌau#kX]Ëy¢2x»VƒèÐ:ȶßyΛ¡Ôk”ŸÚ•Á‡?T†9¾­ˆ>ùk1`¢0)쉑Kx¤’P{
+Î]D#ú›Ø„t
+J2:xÕ™íœ&ÕV›¦•ù³ö"ó_ºóìëê8CÚjî¶‚7¬1R°ÉÁuØÓ²
+œ6ÁYÏPå»÷sM ÍÒ¹½>÷×Éj‚ïèYÊéÇù,BOŽz;[ÆéRd‰:½¬MR h–g7óë5¼V]=÷†¾–{__ÍЗósʃºbÁÖàc ÒkA‡í½ENßBv?k¼÷|¬ó?0ßj7Óá8ÞH89PE
+í-aàoç¦Poꤜ«Y¨ø²B#k–å¢ ©¥ÙçÄ5Ú*a³¾\pÕP&¸k,ö
+Eú|ñ€>O<¨3çKb½ôXC‰ãŸ¡Êm+Ôyî~Þoß›æ`›©–°££b<Ö&„ü£œŽ»UÈ¥\5Š9—´é¢¦¬lY€VÉ‹TgÓ*uÅ©ç ’6cž¤ÛdJí3¥ýFƒt@Ÿ“6˜cv®ùþµ`°Åö^GK¨qÝ;—š}¬^]
+´žh;c;ØåÞÛH é®NÂ]/eS®ä Ù ˆ¨V«”UfëÐRe¾ª ã¬Î$¯7èe—MZYW®Fþ0W-ï3ªåý•|@oΗ† lÇ1a ØœªuÞü©ÉmÛü%¯]ÓíþVÏ;B=ºévïB\pW-%¦½‚™Ø\Ìg×åKE•R¢S£y*“ÊUªÓ(ϲѓíÌEÑ_0}&í7¢Š½9 ÄÐÖ@žÍŸ 
+›SõŽß®\tùn¶ÍcûÔu¿O:ƒ>hpý{+!èZ#9úRM2¹¡œÇª* KòåHž)ÍÑT*m‘N©®6¤«šM2ÕµÜ4Õý\DÕgB²û²¬½9_2÷®›?Bõñ pÞÁòÝ%ÇM¿u¸mžøÙwËÀ­€ÿ¼îÒuØÖLŠºÐ@#ÕÔp˜¥åBA^ ’–S¨Tü—Ï:§:ßã8þ³·iQªi£mŠŠlY"ŽÝYÇYœÅ9Ž}Ï^ãPd9(RÒ6¡Å ê–D©)L¥í†I3ƒ %Í8‡îÜ)Ûûž¿îüñüûóúãóø|¿'r2$E9)yçó¥W âro•¶?(}S—Ó‘ŸÕ) âÖ© CO§¶ÏÇ¥]‹ðãî%c7M–5Y,íi±Yñê!iËãF²yÃ-_RM-Ï»¢RÌ?{9,¤èRlŒô|RRF©$5õtÖÑäâYqE¥ÒèÂËù…7  )¼É<Þ‘×) â Y4P¢¸SFš“5;Êî/x´W³û©åòg-ŽÍÍ^f7ï2œ®ÝâÒ.ÔŠ¸ÅU!AyW££2*R/}8éBfzì¹üc¥%9¡gÊ¥AgjóÄ¥:ó‚NwJC‹ºrgƒECîzuœÛ6W·ÏûzsëüáæmsûÚw©¿~a®ÝòäÀ¦†Ç¦5ÍôeJÉmNÁ qæõȈԪ¸¸Äk‡Rb®‘„WH•Ÿ:&*»”í_V“Ë/»ŸË+ÿwŽÿÅ_rDgßfÏIkU¯«†‹[4Pe 1vGO}°MOµ»Ó@íÙ›½K›_Øo¼ùÔmÏ•Úþ³Yž'î ˜Yw…’úðÐÄÅFÇÜHN«M?x=Gâ_sò·úb§º:“Uݔɬy•É®|—É­˜¯RA‘b/+ôÕ¦êtUÿ|¸J¥÷Í
+¥W¿mR}Üm²øN§ÝúªW®ÆÛ)6'Ÿøºå´ð|Òð’„ŠcîÇ„…6%ňÓâw³“ýŠ3ï\HeÜ©’Ð%Þw_Jè·{Òµ¿§1®Ï GV(£d2*uTþnÐVîy¾€xÕ«E´ôé«5öš.¬ýÝvmy·³áéN²¥ô5ƒ”þÒ’Ü.bÆ< á‡>‰üœÊo“DrZ³c}[‹âè-¨­U‰”ÖÆ$rÛËDòãþ$jÓû”Ù {™.¬TBí2¥·æ/^Ï#.SjПS3°giù{Ý’>'ÃüOó£¿Ñ’ßqÜbº…´Ð_B˜]Ñ\~g¢Ý‘&ftä{wœ £t\ ÷ꨎðèlŠtïzáÞ9éÑþ)z68±D W–oæÏZç?uÎ#ꇗ«T ,¸ôiÏÊÓCÖzÒNFé<,’iöQL— ¾—°?ЛÛéËìO`ÓûS¹Ôþ,W¡Ð}àœÈuàšØeð¶Øy°ULúÐ+véûSìÒ;3œÕ$~­S'žÞÓ šŸj·ÞÍ#*eß©]”o^T,7Y+·ÑO“;'Ê=Ì#e4Û@Ó‘/ã¹rdbO†,œB“ô&ËùxÈúºÊóY¤Ñ¶ãh™ŸÃØu®ýX#wÿ—vžÝ—
+C3ê¹¢L<»­FÜ{¨FÜx©A\ÖT:÷Ï¢¯Û´²¿š®‘|³ÕO'íŒw7S­xã [Ö×>!r¢L„:{NF»ºN&º“&%ž¦²ÉöS…»é³T›éË´}P<4ÓMt«éç
+/fô¢V™¸wW•¨kS%*º4ˆÒ©ÅÊÇ¡;÷ µRa¾:6Ãp`«®F~ ›0àcFÇÒþÖn´uF¸#ÚÛ#ÅÁG÷!×É
+E$Kœs¶@…‹9ª]ÍÐ0«û·”‰ÚÊDù âT‘eÊØ8çv,ŽÉÊ`X­`¿ ¤ÍÞðØîÚN70Iàî>
+¬WSà ã— $xé9À[ßLkp·XB¸ÕAÛ̾ݱ†{´c7$;ŸC#äí@±±!ÎïšMùOŠù/ âX/A¤bÒAh)c¥Š
+ãEî0Ó"ÁZÛ+làò<W™ƒºz/kLÀ^»üuÆé!Xw"u ·~R6lEÚÆÍ8¶Ñ
+…¿'jïp1ÿƘ0oÐE¸×¨*ü«¨Fⲓ<‹è£Ì ~¥L!f(#Q¿Q‡P/iý1sôkè9ú•Ø9†= nÀmqœíŸ©ÿbø5Ð_ 4!Û€ú¼?PÄìÿ(Æ}ÏcžyËJ MKB¾$¥¡gãsâf°ü³Ø2ÒdLeÕNµE÷ÒF£ïÓ£žÑc~cÜDϲº1³¬ì vKœ}
+Q§˜o\J¸˜hæ”KóÙ²ÖU¹‘}_žÉ“¸6HϵIÓ¹ã{
+lLjüÉwxÇѬwÆížÀòÝ‚Å{ÿ«"ÐûYMвÖž\;TêwÓ}°Ë„ nʦDÖ¤³cËtB¢5YN7)S¸92½ Cš'NÃE¢EЍC™,PªGI‰£ò¤Ä1YR¢ ²PàÑJ [ïr¿¬»çÿYöý¹š}‹'}ùKý±5Uçvö” ´Y°§/ç‘Ηg1c­é|‚I'¥SÔCRšà‚Â(N‘Yd*¨R!‡Z•2¨_Éž*!hTAc2™ÔÙ¨pƒx¹3H]çò}=@‘ŸçÛÊ]ó_ÔïY0Ö¸è^ÓÑÕ}µgwt•# cOU™ E¹ttAVÁ¨Óô©JŽN«¨S²ÄrµY&U•+UÍJ¡ê†Jô¾ŽÊEÊ1™Haƒì´UÎ@ú5¼÷ÂóiÞêþ¿Òí¯kwxN·ìö|Ò¹Çk°5ÈçzCÈö¶êóûëÊÐ'K­ñáÍÔ˜œ<^Ÿ-¢j3äì$½†/K5ˆu&ˆ¯-Qp5 J¶æš’­Qr4£rnò˜,AmƒìŒ•Î@7dÀïìK[Ü>TluÙ°Õ}¢s‡ûëþ oýtxÕ•–`ߦËû*«P'¬¥¸°¼B2Êp‘¯5 (ª\ˆÕ<afº(Á+eé‹ät}‚ªïVR ´ôQ9#uLÆÔÙ {
+§OU+æÚ¼Gû½†î®r꽳ݳ­ÿÒšÇ7õüèŸÛy(½¬n‰€)(A=Ç®’¨52:¡ZÃÆVf$ +Ì‚èŠJ²¢]„¨¼#Ь˜EO‰PÖ)¡=@ò…Ð/qÖ¯ßÕ/qxÜãépoÈÓ¡ÿÞrÇΟ}=ê¼KŽ®+èñ3ôF¦ô NȺãÎ :ÉVMiçãð­ Ó’LC5˜ˆævDS7¬©Ú<
+È*›¬²dý’/ûFI¡
+¢ ÖâÂŒb­Ö#ngT-.uGÜ·ºÎ0µn€
+£BŠ€„
+eq¤åqýu1ÛSÀÛË
+c‚rY3aÒaH @FVÒ–ECþm<$›’ ü1Ôn&xÙ k¹àž&Áiàƒ}ƒV3æ ëêš#¬~Á
+ä›™üÄhÂÃÔ'ê¸à#¿NôwY<LÞ[ȧ’ò¤ì—¼¥Ì’vÁ ô±È"k[ä6ü™A>ŸŸ3ò‚Çaù¢)X‘<Åä,“úa©&Fc42‹Q–ò)mkD±’í¦,’B³øgñ€ð´¤_Ð íÜ’½£ZäÝT§ü OÖ.Pü&6+%ƒª_eÕ- Ÿã
+‚Ô Ò›R éÉ—’ ÐIh’
+8ÙáïæLAöȲ5ƒ…Ç,á2§õ_*\7ÿ»Â{û|ù޳%a?<Ë=õˆ?ÿ òJI ¾ÃäEÞ¢Ĩȥ˜¢Û…oÄwà:ˆ­Øäfì0E¦4à(²¤7”*ü²„°HÅÿw #­•jcç\ —Ø[À•§¾Y’ºX-Tylý­Ê×fº"Øn¢(êäƒ<¬Û=Å·?|#åjd7+'æT”ÐJ­À5‘k‰Är¡‡ZC¸K«&<¦K‰:Z%IO‘¨¥ä7Ù8é+ê6¤9‘³(8°.?b¾\å°ú­ì̺ù:·M/ê¼·ý, °Õ”]r`\ûsH¾×3A]iœÈ¶+üØ&vAB£,IF¯"I©rj%¥‹^N€Ê(ãP)õ5TLÕÑ…4=M@[ ~̦#½™¾Ãî3…+™}‘·Xlp\óJá¼~ZîþÝ#™ÿÁ‘ʈê’¸sªB<º+‡Ôš™)¿šSËÉM¨J)ÆW²Å¤2f=µê€P£
+”0"Ĺ©1eY¼af>?½ŒœVMËä63®rz™\Î?Yî BËàr´—££s9zš!p2;iÎk#¸|·ñrÝ“ß[þaò[×!Óg=Ç,Æ®¹l¹×‚Þw³1øXWÍeçf Æ«VDÓÃË)ÑÂütL^Nn?«„”ΓÒ8™rFJF7‹q—ÅÊxŠ˜g°3´PrºŽž|UO3& ³S¿5‚ó·¢`±Ñ—ƽÆúŽ}Æ/®0~¬²_5ªtÞ<Ðá³··9ðh[C䙆šxO©$É_TN –²£r‹Ò0<AvRZ¾t%OLcå4@Pv'“–­fQr³¨9Z-K Ñù::Ä×Ó IÈ=¤mBÁ‚Í(¸j;j©y—ÑkåNÔômÔxßó¡ëg¾íSzíél8¢hŠ8]ÓëQ.Ãù «È¡¹bæåÌrN<·ŒK..$2ŠÊ©TATØÁÀö3“Z¡@‘òô4R®a09ƒ +\Œì!ÛŠzÛnzycøY½ õ ÿ™ºÏɪ÷ºÇîVåÅÃuíaŽâ–èóÅòÄ yõÄàLÉ­¾Ë–d$Ò+óñäŠ22^$£%ŠÚ LY/C¼¦'”êiØ"=+4 †¬÷¸‹6‚?­€^¹ürnj߳F ݵ[Ù§>µ¡ó–»\åg'í9YÚåšßŽAóZð-œÕ˜M«¿O¬ÍÁád%LM59¦º…U}ƒv¹ú!â5Jº@©X Ä–¿1f¯GÁyëP°døÐ²<S­÷Vû#›ÁÀðAÓž!‡u̓nÛeý¾¶¢¾àãªKÎüž8/NWÒEÖ5JµIhãÆ$¶dcâš‹°QMR|¤¢‰¦P‘B›F/‰¡òEbD-BfÌY‹‚…kÁ'Ùj0uÍŒ÷™ƒÑa3pG³¨4ûLÚÿ¦~ÄźrÈg¿`0ðh–:‰{;ö<«ëC½IòÇ«! ×9‘1=üèKݸ°nIB°R‘¨ìÅtßGÌaý»ÞáÛÞâ‚Z ƒ3×
+úM@ϘhŸZ Ùi$:d^üÔicÎ÷iý°&Cì)Q'q㘳ñ?Ü¢~‚¼"Æ® C4<¿
+ûËcKà_¾Ûüà >pÄ.O8öûó0vï9˜´ßf< smOÁÙvÿc»ÎÃbÞ÷8€fZˆpµ(%-Ú$TÔ,ÍÚ,5ÓìK3Í4íëHDuQ¤P"KÙ—déZN<¸9–ÇÅå\—ãv²updi“ä}Ïý{žïóú>¿ç÷Ïçýûþþù¾QãІ€ œ– `´á>á7«ZN‘HM‰ùí$RåC©¸Ã†´ãGdaªî”øÙ+2Z‚ˆ±BÐ'pÁ™Ä‚p2ñS£ š¾úáHr]ŒŒ™¡°Ì
+Áj· ¬›€yhðôCÓ_œšãƒK^Þh³êàybþ5bþ=©è‰´lО”†±# p¶QÁ•,Á[üí8XàG*èc–‚íÁ¸E_
+bƒí u€:Ò ZŽ34b(µ¥ø@¼,
+À{*DÌ¿Dà[‘!Þ‡·-Ló쑾h ²h‘Å›ôøÙHÖú 1%:Kè°º8|H±!rPº…6 ÙÉèïcöÆa‰=Íù,ºÀý(¼Îëþ“÷^ø‚×%ìæ½~åwÇBx/¢[bëëE†Ü‹=!ÕÏ9¡£a‰š
+Ñ;óæÚ¢0xŠÃÇ¡„á<TãÚ[(ñìÎ×Ì{›“ò*#7ü”UÔßMk™O UÜGº:þÍnÑ=uSÜ]å É-ÅyéMÅ5Y»â¾ìšâüŠ¢GqI9¤8§R6k†”'4C
+k %º•蜄åD×+t@Ùâ1Ãë¨ûÖr¦w—ƺw­VútƒÏË {”¹œz?µ„u7iC̯ÆÑ? õâë ñWµ‡åmšÓŠKšËª ê[êVõ곚êfí æ˜nPs8aPݤ·râ L®dä½³ÈSæo ¡Ž_+—ŽÿXÉœüçzkg™lΊôA
+RÃîäåÑÚ³V±¯¥—ñÛR6Æ^JÚÿwcƒü\â~åýqusB«öd Ýñ„'º#ú÷ºCúݾÄÝ^ã€n·ñ«Ö¨fÙ ™è{¢ëÌ&c½¯íð¦‘=ÕáNïªé_læN{Z!ñ¼¿Fx«Èv½ ‹Öf)`_È)œË\×’¶Yz*e»ò¸y¯úˆé°î ±EÀxŰÏxßÐhì2ì1õv&õéëÍý uÉÖA3Ó©Ól°œÈQêfƒJoÊ`MýÇÚEŽoê"Ç=߯šü°*ÖãözUÀµ5ÆÅ—‹Ò©­+,ì–e«'sËÄG³*e3¶ªö§5hS$ìN>™Ø|Ѹ#ùŽi{òKcmJqkjobMZŸ¡:­Oo tÄ÷gw%Ñ;×yª¼È}µþ¶ïw,pèÜ>ú·zºóÝ­B÷›äþ¿”ë/M¡6å°­\!8´¼D¼ß²^¾7§ZÕU¯«ÏüÙP›q̸%½5©:½=iszGRUÆgÓÆÌcefobEf¯Áâ
+bƒ,g¬":ïz×ÄÕÃæË_Jמ Ûç¡#ì¢Nº¹ïv¥F:¯u£náérsÔ±ÒLvSQ¾ ±pµxWÁZy}þ&õ6K­®&w¡*ç°©2û¬yCöõäòìgæu9ÝIër¿˜ÖæöËr{­A"ñrˆ¾YLtÞŠ#¾os³éÞåeórŸùÉ`‡»{£&^ßÉ›u±6ÞïLµfáñJSdSy:«qM¿¡¤P\·ºT¶ee…ºª`KBE~Cbù²¦¤2Ëis©åjr‰åqòÿؤϷ&Ó,
+ãö"g«f(š`¾’–*`ŠžTÝ=ª»ðœš.În×Áe‘ä4.ýÛ
+†ðÇÍà‰ò„3ªö¨)fº”½hH1E3ªZQõÑN|:‰ë »¸6úоÃ@š&†ÓÔ1FB “$T0®ŒùtE,£+`¤¶HÊc‡HèdHA'S*z|,hègKÇ
+|5;ÀGk ¤:{ðnq
+Ÿ,
+(wúĬÁMÒ›\?ÐÚñþ'÷¾ó?ëö–zÙí uðš:Lów¥~v¡Î{ SÀó‘?xýJ¯;ôoe;ºêºÔ5C¶B„ý"ˆÁ/ƒoSˆ¢¯ŸlþKlñYk3)N¶Ÿd;¾åáÆ9•®¯Øuøÿ´ºtž3{ÜŸ1þN|ʸIü/ã‰ÇïŒWŸ¼ÐçI÷èàs‹ >@þe@³Ð>ê™AængkJ—¥ ôX©ÛO]û%–³i2BúÓŸ¡‘Ö¯eìǤ鎣â—§Â2üøÕ„!nñ §Ýó1»Ûë!«Ïûk€tõÀçkÌçkÒ÷:Kç7ÀJ?(}\ œ[
+h¯eó-/©ƒìú’#{(ܺÉžq™äö ¥5ªŒ~4¢& A~”]ÞÉ­ ;˯
+»&¨ hÃ'ø‡Ãçøeòy^©|ž[ñ-¬ÓƒHÔ7“PïÌ\¯§ÓlÒ›(Ý¢÷¢ÂJÿÊ]‹î•»w£ˆºár>×¢/Kj{FîÜ•·÷XR¢gk‚šÜ´?—ZWL¯‰©dUE7p´QÇøå‘=Â’È_DÅ‘E…Qo……Q3‚CÑ:~A´ŽW=ÿ ¢ü1¨ï%£Þ™³û«höF»IïiÍV½ÖØ Vº™\-ó[¡ýSO¾xgWV¨S{zôÞæT¥gƒ*•|$1‹Z•pˆqXq˜U_Ë-ŠkhbO‰òc/‰scˆsâ^‹r⦅Ùñs‚ìx!„Þ!n꽫QßZƒM•}½üy6Tÿv¿n§áõjüŠþ
+òºÞÒ€­' …;ŽåË›³#ñõû=ªÓT>)éÔ2U>£(±”¥I¨ææ*›ÙŠ.Q†â¢$]qW¢V¾«•S¢4åœá§)u߀`”_vP¯Â@cŠMjM±çµ¦Øã¦õØ`£áÕ:×åç«IkOk[Ž—
+lZ
+÷í®Ï—ï©Î‰#j3I%éi”Ci¹ô¼ÔbVvr/]Õ$8˜Ô)NI</Q%JTIc•ê³H•4+T%Í þ× ½CÂJ 2‘¢•ØD• 6Ò¸{в»Ñlep©·¬·Öëû®ŸiæmZžuC©Ô¡º(ÌU«‰!–ä%49©”ÜÌ,zfF!+M]ÁMNk$ì'¤ž“(Ro"£ÅÁ"ÅÁY¡2uN°£$~‡AŽ ö©t6td)ö¨yv·Í hÛfp¾ÅyééFO³cµþ›Vs¬ŽTHvU”‡¸””D¹k
+Þ9šd¿ôü Zj®& )û07!«NŸÙ.ŠÍè•Dg\—DgŽ “¢èÌYaLúœ v¹ýG ò–c£ZcìqÝì^ëìf‡)v©ÃB¿·ÝqɉâêÖ&Êuu¬m•5"ûÒ*N£pË.÷J/MòM)Vû'æ1šRNlÁ~T~›PžwF–7€ #ïEáy³ByΜ
+®U†Q¬£P°È (
+n Å PÄ µî#¨ŽöŒÆZ§*‚K•MžyOçNzñ;ßåÿùÞåâÕºTÔÄxl¯6yo©š°q×Bi~ÅWŠ5å+Ôy;ÖÍÎ)-4,Ý^»h[E|fI­)£øxÒœâöä9Å?“ï[bØ”Q4’˜±u,̱,°„§%4xPɄ۵L¸RÏ„¶ã6pê„¥þ˜³æ{é„ÊzµSÙ{ëÁaamš_þ¾ÌàÕ{—Ês«–«²w¯ÕfUnˆ\ð¢èŒåÆôûãSÊ“Ë/˜’Êÿ•˜TÞ“˜ôݧ„”²Ñø”Ò±0Ïú6ÑáQ þ¹‡AÞž 8×H‡¦&+hhq¡h2ªNÛì8¡r(jŒr+lˆãå×§ŠV™¸üP–tÙÁÅâÚÕá™5söm‰L­Þa0UWÇ&ìm0Æí=gÜ{Ÿè6÷ ãv£1n×X¸† ϶Ü« ’{@ƒóõ4h:A†Vl›{Úx´ò³ã‹Ï(&njÑM]ßë±êd²ðëãs}—[$^ؘ-×°24ýh¾*¹~³6¡®Lg¬Ûsä¨ÞP×fÐ×uôGº Q‡ Q‡Ð ?8ÖPÀ£m
+T^e@é­ PtËRxˇ±î¦ÄzÅð‰Ù×õ΋¯%LŸÿC7ýj¦ ùÊRŸ„˹~ÆËë 훂#Û·KuíU2M{\ÝÞÞ~—x%W]ìW(/`˜ò<*¿ôï€ëU
+8ÁÚ|Zîƒ Ö²ûa¶ ïÏž”qÏè”Ú™2ÍÔ9בÅéXÎêøÆK×Q(Òvló ïØí¯ì¨ ëh TtÞ Pt¼
+½Û$¿3,»ƒcÜ®
+ aÈPÁ” ÆBŒzV
+¨<Q=0€ÊƪÊi®¨¢¹`Í 4L OÂtº=.¦Ûa.ÃÿʰÁ"¢œaÕDíìCE©ˆ†Š™4TùÓQÂDU8 Ãc¬P™f‡Š¬É([åˆÒ .RìŠÁn(®žŽA‡ØÔÀÆÀSîxÖÛÉX·9ðøxG r09£Äçÿã¢ç—þ˜AæCú„Ú—ŽZ1µJj Ö¨I±Gբɖç„ò‚©8«hJ¿›Ž’*²µî\ïÁ'8(>M\䢸ñ£'ŠŸo<GÅžÃâQî§`䊑Gðûÿ†BÒ't>tŒ b`”‚…‘‘Ö8ÛdÚLTíŒÊõ®¨ø;YúR6Ê*ÝqÖ~œu˜ƒÒF.J›=Qrއ’ˆNÞ¨äþ°¤›?(éãõKGy½Rä” àƒ…ï$èõ%”zQQÅ£¢ŽGC½Ž1þ Œ•±0VgÑñö•်¯œQ»f†Î@e‰;†íä b/CxbèQÞ¨ü$DÞ*ø,¿,’ß È ûä]Âß彂wòá9zõÈЧ[†¢—ò±0”OE-‡Šz cÉ ¾ L”Z`¢Æ
+ãcì16Í£³\0j…ê
+Ø#Ú­œaM™çzo0|?@uDЧjö*[¼>*/x}PÞô~¯|èÝ£|éÝ¥úèõBùYôß0œù< }Ÿ…¡ßc%ú •\*ÎfS1–¼wM\¦’ýH ¶Àt¥¦èíÐdrÏtŠÍqˆþƽ7j÷Cd ï½n§à]į7¼{´G}^kNútiÚD/5×D/4D¿j=Õ|ðýfØÿ‘~Rcà5ÝýÛuÕÔ™ÇqüIDÅQ‹¸âŠˆȾCHrsH Ä’
+
+#x Z¤Ê´êiëÒZëÔ±u´ÕZÛ>sûxñ9ç¾|Îïß|ƒ–ƒûñеnø.<ŒÙo
+àilƒ8¬÷¸Ø;2-
+^S3R2Y db`Ýû‡Ô<)2÷µkÀÝjcp³~#˜ªß‡©µ7¬òÚÐ]NÜÖ¢ Ù]_B?TUo«-à8”(x.…r‘‡"'×'GZ-ÑEY ˆ@܉òÅC4žhš–.þó35]üš.†ž"KÁlƒ ð:×|£2 å«Á\Ý0£7ãú=¸¡F;£¾:õmÕ‹Æ
+Ú®ê²ÈƒZuì±û¤²(Í9O™é!Í—údå)2åeAüœ:rš¬åÊ.R“¥S4Žì>æ5YöM–AJŠ"KA¶ €Â5à©î© Áí*#p£ÑL6¯#-»pͶ†z7Ó¦ú
+n˜
+®\÷+‚k—{'à«B
+× ‘Z×À‘V7á«n7áË÷ìÅïp;¾ƒ¬8¿á]~QB–‡™úiÈ8êŠ c>H7Æ'´ã""zBJ*'T¤ü­–"}k „CŠ]dRƒ!—EöBXd/€•4øÂß8p¡Ü‘Ž~pÖ‘ ÍŽ,xðžñÜx¡5—qþuœoC(ç'u"4cŒŠ´à‚¢Á )…d $L„‚„‚ÄË.9t¤?$’\0Qü ›Â‚ŠÌ·£C™|nç ›©ðÕPÝ «}Ïfü>­Çù×Þågµ#”Ð…f
+
+WÞ(Ï
+ÌWR`H€ýR~“ç«E$ÄbºP
+èev0CK‘q'й@Ì7ˆþ«'¨þæ Ê- ˆÜîŠr\Ã>&ȳ@~ «aƒüÖÌ—ÛØ£òö+ù
+<<©r€ûU˜Ûò`³E)Õz®ì¤ÃLiâî Å,·a>Ûc €çÕË)õëÌ“´å*‚u9‚6»¨Éˆ¬Ïœ ×e^£*3W¨ŠÌuZMÖ?©ÕY¿Pª³2Š´r}ÿìm€ð¬àob øZfkÕVðPaå~ÌRM é|%Qz|Ǹ$~÷(ŵO˜íÞ%ànãûê
+%ZnU°šSOP嵕¹½¤jö8¥’=G«`ß¡ËØ_Ò¥¹¯hÒÜŸ)Ò<„üÒfA®%¼˜Ãz¹)¬UšÃ#¥%Ü«³ƒe•f¡îˆÉeE䶉ª˜íò3»úÊ.â ÷ÖÒ¼ÃMB¾¯Z Pñ+‚•¼Ú°ê¦9·›$åŽP%œ+´2Î-ºˆ³%âþ&âþDqЍ
+BV­–˜®ì&¥*Æ)LÅU*CqÆP|CeÔü›Â¬AÈ¿!m†p
+oá€s¹+» kå †µ„c®L“V¢-ãïÆáãî&Ûž¼Ë²?f`;Dx;£ e»¨†jg²A»'Òз—h˜v 7|âfø½køg/] ·ÿëz Ù¿Õ¯{x¥`ÝÁ®)
+Avd“¸à‚qÇÚ£cÕâR·¡BQ,r° î€V+Å]ZÆ¥Ö­âˆ+‚ È&•g.gúA8ýð;ç~ûß÷Þçýp+Y²'õ,YÇU–GGËãñ;–´}„-mÿhö©Cc3X °ó$Àrþ+Ï“~ éß
+ÅÀlŠÂ¥àzb'±¨&jÆA©'…^R
+½¥4TxÑP®¤£—†ŽžQ ôHÕGÉ</c¢h­ºm6F·Ý&èVjŠÂrâ
+µlž&š‰âñœèe
+‡Ù#nÈ#DÎÐÿ™
+e¤¯SèëNC¥áCCß@:*"è=S½æ ,ߥkŒPºÉ%;LQRÌB÷ýlt¯$jÌÐý„Šˆs_%nsFÅœ÷ÎûÙ€ÍÞ»£9ÁísG^Ÿx<”“¾RH¡?1YLÃ)Þ„šŽþÓõÐ/A}u¨øÜ½W™ ×W¦èµ…žElôÜk†²”UGÉ·Ô›ÊΘ”]2‘ÝàÉþkÞ/ë6ë9hÞã‰Ü72´è–!¿K†–¡ÏXß™B51UHC'áOÇÀªcõqJ:ý¡ß2ST®e¡o¡ÙGŸœ?}ŠÍ?(ös‡•Ü!E oPq’7 hä½W4óú­¼^Å#^·â5ï•πŠZ>W ÕSo<U u‡÷x¨ëO¢Pã@aY‡’¹%3¡ÆÀàH}Ô$31p®ñÈÔŬá€ÕfS6pÞOÞÆíó/â½õ/åõªX¼QUñ»UÇø]ªSüNÕ9þ+U ÿ…ê!¿Ã¿“ÿXÕoÕ¦Bë‡~hûÀíîwýÐþS8e¬oKúÖFu”ˆÂhrQ Õÿ0=ž9 Í2~œÃê ZÎéÒ¬åvN+ä½ Üañ|j1ÿÙÔ2Ëu¥Õõa«Çê:«võYA›úŠà¡ú¾õ]õ+ëßÔýv×Õ£­8é×
+ÈlÆûÑg齎bvE¦¿ Ïf?›¾˜ó$t5¯=ä+~[ðVË?>Û-¸Tj}'¨Ü润Êö–æ¸Ý M£ýuÍyûVÍM‡– g—ƒú/hF›5èÒDœÓ ëDdEa0–˜$
+æÊ©ßgêý[at5#‰}!UgÑ”¼Pp6i‰íé„Õ ñëêã6»ÔÆîžˆ-‹)‰>$©‰®“VG7yTE_—UF?—UÄôËÊcPö=±LìxIÚ ¦€dsØ0<Ÿ¯Z@{Ž-ÜΑR¿.˜Â¸œjøóì8ö]ºECF¶ .-×îDÊ
+Ç£É_:NÚ ¬NÜ&ú!¡È½2~Ÿ´"¾RVwÌsÜ^ûâZä¥qOä%ñïäÅñ(ß“€òoÐk"Œ!ídc@1ŒÌ3ÞE¦ð4—wóøÐºXD]Êõcœ[lظ †]—lq|ö,ëú…öÕ™ùŽ3
+\+ÒÖ¹•§º—¥ìîM.‘}7ó€×ž¤ÃÞEIÿRìNºì³+©ÍgÇÌ^Ÿí3?*¾IÆ1Þ„üS8ÄÌ!9&¼Î1€¶<&Ü_b7–ráÊ2Wªi©½1_ìÍdÍIäUÏÏTfgÛ˜“ëT6k¹ë^ÝQIæÉ·Û<þ™^äµ3­Ì{{j•϶Ô:ß-©ç•…©w•_§u+7¥ønJGŸé¨˜ãÈ=èCÿ<´çÒáÎR}¸¹ÂZVr y•œ^)§×.ŸjpdI«*/ŽWñyš`ÿÂÙv¥ósœŠç-q-š[ Ú5gdû¬BÙVÝ.¯ÍY¥ŠMY•¾2O(×g6©þ‘yKµ.«ÓomÖåZú>a"“Ü^.ø#ŸNúzðK\ü‚ g×8@ýž´£U+´¦Ëb¹eùÉV%y:»¢Eówåä¹n_°B´uþÿÈ.Ϩ¨Î<ÿîÌPDš ½H)#LQA£, "  t¤ÃІFpFš ‚RDŠŠ *‚1XÝQp]÷¸fƒ%'nìf‹‰=Gß}ýæÃsîÇçþÛ=÷WâR™¤à*êxåñ-^òønAIܰ8î¼·4î†wQüSQQü;aQPø_B6ª€Ä1xüCÂÂTßÉT0^ª…¯Ë,p¢Ì•Õ_â­Ú-]©ÙQ¬×šnÜ”m^Ÿ`U“™fW™žë¤H+v)O­à–m«áÉRš½Š’»I¢¼¤³Þ’¤)ŸÜ¤ÇÞ¹ÉoE¹ÉD˜›BþLH$ oA÷€æ­26.Ê98»]æ¨pfõnªt–.ŸÛ*[;¯©h½Q]Á&³ê¼8+eî6»òœlDz¬BY¦œ[”QÅ+Hoò’¤u²SûE™©g|2R¯ù¤§>ôÉH{-ÊH#BŠàKÈ&àE20\—ß–±p¾‚QÅ )qXéÄt)ø*û+ü4öÈWëÖ•†VË"ÍÒ­ ¶&Û–äg:Jóò $¥\IîN^vNƒWFv» -ëˆh[ÖiŸ”¬ Ÿä¬Q^‰R²ˆ"ø<LnÑÜyµø3ýí:£dp¢R ýÕè®¶gÚ«<9Í;—ΩW¬Ò©.1PÈ7šÊËb,e%‰6EÅéyR‰sN‘Ì-³PážVPï™’¿_”wH” ñŽ“\ö‰“üDùM/!BŠàKH,ðSC!pYœW#4{ ԲѻKu¶LË.wvC­¯zMu€¶¢2X_®Ü`,Û±Ù¢°"ÞZ²=Õ>Kž³0½T꺭¤bQ’¬Ö#¾¸•¿UÚ+Œ‘‹¢¥ñÞ,½ï-})Š.ú$Œ)"
+&¯€{ÙÀd1pqð5õ¯£¹k7ÐÙ¨‹Ö&+46rÙ5 ÞjŠúšò:±^qí:£üê(³œªX«Ì)¶©Ê,ÇdE¡Kü9wkE5/º¼ÙsÓönA¤ü„p£ü¢h£ü}þOQöQQFø_ò,…ÞÕ2ÚƒJàõ÷Óüw ™fÏm4¶š£¶Õ…¥lªÈ›ý5¤{Vëæ5…d7D˜¤ïÞb‘RŸd¸+Ã>¶6Ï)¦¦ÔuSu墈ª&Þ†Ê.Ïu•Cü°Êq~hå÷üÐÿá‡)÷
+S’ÏxÎägz“7è.^ú܃Zàõ÷ìZiÜÝ>5ÆPt:2e^iûRuIÛ*­¬ýÒKÝn”Ôm¿7ÞrKsªÍæ=¹ö‘M2§ J—°ÆnHC§{pà OÜpÁCÜpËC¼ûßâúâ:ÂïšÍ=zW?÷ 
+¦þÃ-4ûµ ]@e*Ê{磤ϖ)ìsgçö.VÍèY97¥[¬“pp½þÖ®(£è±f‘)–áÙÖë:¤v!í;ƒÚê®ikwýªí¨ÛªýçÜÛnºî{áØúžØBþÀßé.^RÐÔÓ=¤õlöRMPz”æ¿A]H-‘uÌ•IrýÕbVÏ>ªu4BcÿÃuG’LBŽdš.X°æP¹õW‡vÙÚï°¢¯ßqyß7Nþ}7ý{Ÿ;úõ¼sòë& ýÎæíÁ9:ƒtþ½´þ}ºC@Å
+dŸ.‘W€à)6nªcémm,¹m
+ßï°øŸ<xÿ°¢é
+¬¸ÅÀgZü‡Zð|hÞC+¸?r÷±'ÜûÂåÉJ8?3NO×3ŽO£ûgÉ,Ûg–Íór–ÕóFö‚Ýl‹§Øæ¿LP°Íž¿d›={?‹6ºÿu´ÿòÓ@îõGýS€ø&àÝ=`Áí¥\^êÃéWs8üj»ß\`óÊÖ¯¼±àµ?,^¯†Ù›P˜¼bŒÞ&2ïr™ùïËý÷MŒÞ‡Ö¼#,Ý߯PîSžÍb÷ PNë—Pí}$­}í-Àoàÿ¸þ 8¼ì>iÀšèb1„91‡ ±q‚áBŸð¡G|¡KV@›ˆ¡EÂ1—ÄaÉ‚:‘3j¤Q!9N¹À°ÉÄ,vP>õ'SÔߨŸÖ¾ŒÖîù
+Š(‚
P눕¢Ï±´Úê+•ꇧ€â
+8b¯µ3þ7*ðý5€®3â5Á.ClU4°-¥§B 8Û°::pöaÍØQpÚ±³X'v“1Îée|â 2F9ï#¶ôa[ †lA{ˆÌ·S
+"_ †h€@ª |9 fæÑ€W¬¼2:p·b5 àîa|áî'&¸Gˆqî bŒ{šøÌ½D|ä^'F¹‰aî
+¸áoqŸ‰áy¸-¤‚ žÇüpÍ1q¼Ö‡oÒéïçä3†go7j¿qÞÂ|í\Ã|åT¯ó©IgÐéˆÎ€Së¹Ó9Ö3§«¬~çVŸó3^ç¡iœÆõî;þ¯N0½Ç ~™âŸþ.ÆÜ,²À+çâ‹wÓ{>uÜË[cÔ}™æ[ŒÖk×ú —•ÄÀµö³ùk™ýó*tžÎ«f=ïbõ‰tÿÔýS|lÚù§ÙÌídߟ{‡Ý#î×»-š~C<fx] FÝb0þY &]SüÓßc:ÌßÁŒOù8›2¼x!õ•o€æ¼#´ú%0ú<Óµ{=òtº¯fÝ_¸aÚon›Ù=nÛõîºîÖ¿íÚ4ý–ëƒ.í×]: ¯¹\7úٵרÓõµñ%×O¦®`vÞÌÏag§€…¼Ùôãz¹9 ±CCË…”Áày”§K}4z—Ó~÷aôø%3ïø*uoú¨Ø×½Kô»m4èòª4¼êµÃ¨Ó³Þø²ç“‹­¦<N›÷øÉì¬Ç¯æ§<Ìðµló«ãØ1¬u
+ð½ýX¸¿.‚P\G¢‘H 41=w¢üêN½²„vcY$ÑϺºDÁ¾­Ñ_mر¸Ôèœ_¹É¿­¦§|kÍOú6X´û²lói·:îsÁºÕç–u‹O¿Í!ßN³/pöû‚m>§Ü;çÞP‘,ôIÂF/£ PŸÄÝ“Ð-‰ µ;j1­3"”¸Ã:’¤wjY†ÁÉà<ã¶ U¦Ç—®3k]RaÑXmu8 Îú`À~ÎÿVÛýþgíý»íÙï xk¿;`bÆ®@°ÿjç°÷ƹ7‚ÞGhPªþ–²ÐýXCt3އºbçR/IÑÎF?®ˆÒm‹Œ×o —¶„)M…ªÌšCŠ-š–o°Ú·¬Òfopm}Pƒ]]Ðá;ƒNrkƒ~âíú·-è¯*xŒ·5x[—wË@à; ¡Mô4Vý;A Ý“è¶Lu%Ù¡ IÎÔÓ2Z{| Ñ* Ó=ó­~st’QãŠt“†¨óúµU]x©Íΰïmw„UÛo ÝÍ­
+iæm ião¹ä°)ä‡ïCÊC?ñË€¿1 fNKhø(h?/½2*êI¡¡[©tÔÊF6è´\DiKuÕlIò#šeËYñý=±ñFuR¹imŒÒbGtUµd5gëŠ v›£*gTDÖòÊ#ù"Ž ÖGœ'×FÜ"K#Ÿ“k"?J¢Àáÿø“A0M¬@èiB÷SpîUh îtº’®‹ÎdX¢¶L’Ò’>_³YáÍØ—ÄÚ©W›(5ÜždZ—nQ›cU!-ä”[j¿!¦‚».zûÌ5Ñ{Š%-d‘äŒp•ä_ŽjI¿£:ú½Päw1 À§Ãd_ŸØáh„áÌwWеL*º¢ÔDç•:臕fèèJ>¥Y)ÖhÈô¢×¥êÔ(ÂôªSc +“e&Iró2™Òj}‚ŠS_l_WÆ-Š­âÆÖ TÒCÂ|é)Ç<i·(WúD”;☠dn¦‚P„¤=Äwp3¡«+)¨#‡ŠNåèXž1:”Ç£4æÎÑØíNß¡ôgVe†°7¥¯0(WÄ™¬—§˜—¦fZ'çqŠ’ŠìՉ빉[øy²:AvB³P™pR”•Ð%ÊLx$Ê” ;fʾ3e@b‚É ¡gx¿ÉqÞR"tgÏ3ÔV@G‡U†¨é;{T¯r¦Öä»iUåú17eO+SFN_—%5.ÉH4+JO³T§åØ(Ôv¹òµÜìÔÍü¬”Z2#e¿cZr»HžÜ9Kžüç,yÊ[‘<eB(OR‘‚ɾD!ôD†ç€sgWÎ|*œ7ÔUk¢…úhï*[´s•ˆZ­v¡mRùho,Xª»6/\¿8'ƨ0;ÁL¥”[æe)m²3UvYk¸é|Ez 5­Ñ19í„(Qqy–,íöF”˜6.LLaR“½Ã»ðW2žCBWòñàß®8{\MA Ål´«Äm+R7ϧ•-"Ö²V«CõÕ*‰Q~A¼iN^Š…27Ó:#'ßV‘]ÌM]Y>3I¹] Ü'ŒË:&ŠÍº(’fÝÃ^ãzL›dÜ/ñ.<Às¸–ý?¶ë=Ê|ø÷¹ ƸŽËLÌŒË3bf0F‘QœPI…’.Š“­])’ˆ)¤èB[(%]ä²Û•êìn‰v·­Dm¯¶vŽT[ÑUJ—ßù:ûê¼´¯óÇû5ó×|žßç÷{žy¾
+É%Ž!9ŸkųxÏá)-v€ùû¶bE8{⺮`u©¬,“ÀÒ=
+*¡ÌƒŽ/óãÅ–éÅ”„ f—L7ŽÚ=[¹+Î"|W‚hÊW+­Bw¦K'çÙÛý­¸’ ,:Áýˆº¹€í}ò€mD°õs—±ƒsë±ì¾×^RŒà(’shRÀ’X\)‚øƒwPCÅTŽffUò¢*'êEL;mV1ß$´b¡Ù„ýË-‚ö¥‰÷m´W¾Câ_^!S~\æ[~ uI}÷¼’ù–ŸÒÿ²ý¤q-v P…ëß»`G)@N9@ΡK
+³km ºN3ê<©ˆ:fjm0orm˜îÄÚüÚ¹‚ñ5ñ†5KŒÇÖ¤˜úUkÍ|ª·YxWï³ü¢ú‘WõE‘×ÑNѨª—âQ‡?ó<D¬†:‹{P‡û_ù_a~þ~€ŒC8âúå×8ÿá,~Ú
+n Ç7Ld¦±c¢uübu}øÞ ɯúõžõF#ë÷¨¯5v¯¿`¬©o7Öœ~aâvò½©Û "DfŸ/
+®=á@ fG|rÀïG<¯òaÄ5Spo‘€¦ÅÜ®k@}Ý\[À¥u¨Ú"AÙC9ÝXD9ÞXE9ÜÐÒò›E´ýÍC´Ý/gPm{ã1mÛöš±i%ìPå˜_„ù9˜Ÿz ±`Þw˜Ù!?
+¶£À¦Ód@úïptÍë®Å`u/Ä÷r)Qw ey¿†²¸ž2ð;eÖý”2ëøÌNÌÏÃü4Ì_r`þyÌ¿|À·`Ä
+B_ ¾ |ozrü3Ä^AÎá¬k«¢ˆDC+_ŠˆC(b9&æ±41[Ba
+ÊBùh;Úö¢JT…¾F§Ñ·èt“þ 즄Ïé~á
+`ÉTÀRi€¥°àyòFxºsî³.îq|þãr·‡Ûµ„÷¶ÕyÜßÖêyo«Õë­}Þw8Ã>·9û|nrŽol™ö½¾å„ïÊ–Òµ-ˤeÎ=òUΧ䋜o)‹Ìƒœç`ó¯À¶£ì"Ê&¡#Þ3©ð<#¾äÆÂ“´ÍðQZ2î½Ô\ü½äbÂ$•ÇÍD½×õÞ+ mÄk ݾKñvÒÕxùrü~¿Kq”·âf(âÎø/Ä]˜»p6âŸQÇÿ4ÿñ3ë` (;Å eû
+Wã})­†¸˜j&-¤ZÈó)=”³Éýþg’GN% <‘4IK:4›t!x&i5äXÒé䧡G“HÆÂ\È‘5)XèKX’7`\wÀrÜáß>xÃ> ÞçÁ]á¸.äà–©øKü<÷ ¹Å^ó9e>gx:ÒÉ,£ßñÌfÿÙŒŽ€™ õMî`Ð×ü‡tWèDúL˜+ýípúí`úýðé_„ïãþ+ÂÉÅÂÇÑ5ÿ‡¥ |¾ ¾Uˆƒ‡"wxPä w‹`EÄ„+â͸EQ"þ\!ÏýdA¡×œ°”8#P“§ó ”É܆€‰œVê‘kð¡ì=!ã¼½aûyã4gÖTøhÖ©È‘¬ËtGÖ]úïú ï;z6¶&Òþ
+,}²sQçDŸö‡%¨óIÝà¶ÔV%~pYJ‡ÙFÜii~NÂu?V,ðš•'Š”äÃZÿñüÚÀýBsS` ô„9øƒ´¡¼}yt{Þ\T_Þbôî¼›ŒÞ¼¿1zøßDwó±5Q]‚ŸÑ_Zûµ|#x„~s÷Kn)ñ°¢$ÀU9 ”48UöÌ*·â§å©„‰Ò\¯CÒ"â’R²³Xí?"֋ꃋZBû ­´¾‚=¶‚zOþᨮüFgþyfGþ*Ë’ÿV{Á3f{ÁOŒ¶BlMôzà 4ƒe¨ë Îµ¢FO‡ å>pZ³`JýÞUžD/ãy:ùĽr y¨´Ì¿_ª Ü#© ¶•˜C{Š-4k±-¢C<L·ˆÆ£ÛDÓÌÑYV“èÛ,úmÿù‘e*Ƙc½Ÿ×A ð@på/U¢Þ«8[é ³Z*LéØàÒnÆW&Æ*2<j¾Ï€JLê+“SzÝrCpgiC¨¥´•Ö&ëŽl‘D™¥û’IV½ä4»N²c”¼S+}Šü‡U+Ř¿`¼ô‚ö¢íÀ*Ê¿¬G½×
+¥S­
+´¨tA­*chsY3ͤìŒlPØ£ŒŠ1Fü(Ë ?£—_‰­’ßÕÉ¿Š©RüÀ®R`,„¹Þ?ó>FûàêË(±õ^Ô½fjpp´–Q0f|7\»ÍÍ^âa3d{wUåûîÒ•øµi•M Õ¤© ©¯0ÑŒê]‘Õå}Qúò¦NåbWªŽÇT¨.ŪU÷¿ÇV¨^°+Ê1Â\ïË"€”hÐü¯ üù:Ôùеg=Õ“ÁÙŽÆ×pö¾·>ÉÝjÌô²Ô
+ˆ-Õb²Ù ÷oЫ©Æ*CHµ®¦×µGhµ¶(M¥ƒ¡®<ÌRifc”š‹±
+Í;Èç±JÍ¿ÙJ ÆZS¦Á˜/}ŽÎÃtÝZE3¸ˆrÏ î7ƒ®€.ôÜg&£) ú›7€­é÷x«9Ðnâz67äMõEäº:™±œª¯­
+ÖÕÔ…ijZ#Ê«»ée†!†BUªŸ‰‘êc%úÛÈgèý{¶´
+cɪ0æzO¤
+m1«hfZ<‘f À4K8›j‰eÉ–)B’e†8Þ2WkI—N*Zd]´\Y´N^´EZøzBáA»àÂjrÉ.xÿ» }Ï5AùÜ~¸JÚ(?wjÅ6å
+à]ÊžQKù @Ä Ê?›ÜÏI¡oUÂ鲎Wôp¼ê‡«ã »6DC{=ö7fÁþf:47—BsëÏPßÞ uÛ~¨Úª`ÛvÊÛ?ÁöÖ lo>månV•
+¤ §äÜkSþRÚsïÓš¿Esž@Ùa”=î2`¸8·öí”ÝȨlñ3€q}ÛðJCè÷Hb >$˜L$)„^Šœ>Š8ͧÍÇéÐqZt^ñwKÆÐ-¨×uô·÷×D‚«'3®šÅ¸2qŲ’¬'›Év’A²—ç‘BpùaRNªH^É›ñB~ CòôËðXÎñHÁñPÁÙCåÿãnžànðÑDOµŒð£ZÂÀu TÏ Æ5ï2®^H–“ÉF²•ì"™$—°Wêì…º„=WÅ3užª›Ð¯¾ŽGên<P÷£WÍYgÝÿ¥á¬kîáîáJÓHÜ܉ÍËš—ªgãŽswøqÝ2²Š½Òm`/t_°gÚílH›Ážj³Ø€6õk‹Øm1{¬­`µ5ìßÚFö“ö
+ëÒu²uý¬Ý³»\¸ãø?mÃp/ð±#ÀÇ8{QciMƘ¨&3Õ“ >j6{é’ΆF.fƒ#W°'úµì‘~#{¨ßÊúô;ÙýÖ«Ïe=úÖ¥?Ä:õå¬CœÝןd÷ô­ìöÈNvÃ¥_¸âÂ…VW.ºD.ºrñ…a¸åûêh+iÁýœ ­‰ß8ú†—ÞñôJc=ßa} X¯aë2¬b†õì¾ak7lcwÝ¿fwܳØm÷<vËÝÂnJÙ5C•pÅðƒÐj¸(œ÷èZ<žˆN{rñ)O.iôäÒ¼ûQ¶¿¸ICÛÙмyá™É„GþQèõ›‚ß9ìžo:kóYÌnú,g׌kØeã§B«ñKá¢q‡pÞ¸[ôOcŽè¬±@Ôl<,:m¬5ëÄÆfñIŸ»â߇Ò:ßW²_nsügÇ|¹üÜDùAJðPx8Õሡ°Ñè 1¢#8mAq¸”†ÖÀwعÀB‹i©pÆ´RÔdZ+j øL|2`‹øDÀNÉ÷{$ {¥õEÒÚ€RiMÀ1Ùñ€FY•é¦ì¨éÜjz®(3qeÉÏŽ Ãm)_
+%§#m‹—ÑôE9¢= ×#ÆãBdZ"§°S³Ù‰ˆùBCøBQ}Øïĵa”T‡®“ Ý(û.t«ìÛ]6•!YòŠ|yyÈ!EiH¥²$¤AYrIy0´Û¶(ô©ª0”NPWï{“›ðÅIÐ'ÇýIj\™è„³1ÞhŒ Å÷± ¬66M8ó¶èÛIˆ+'.––Gÿ^Vý'›’¨õòâ¨MŠCæmʃæ [‹9WUh.RD–©÷™kìòÌ-v{Í?j²£úí³¢¸ý7$ó <’Q>ЗH=_2p5Y‚‹Ir4'jqb²j“Q•<‰ULž*”%Í'¾'9”°@f‰ÿȦ0n…¢ n2?öÛ¼Ø/T{cvØåÄdj²bòí3cе{bª´1§t¹£ÛûØaG,wøŠlãºáx0”
+•K»<×­E¦÷hJ3y5¤YxS}k¥]|‹tDP%VHçEféu±Iúc‰Qú™Ø˜úo‘1•}¼ä‹ºêÁc%õ€¼ïª–ÞÙ9ôÎUs0“í…cšÓlÆfÓ§çveÅÙ·©¤ŽÍ™.J[}FG]z©—%½’W¥¨÷­PtðÍò!A™|BT*ŸË—$ùã
+ŸìÂ~VA— ³`T”¡;!Vè.J亻rÝ'Ä?$
+Kß±¢åü–îá!Ýýu=Í"åŸ!ÿ<J йÃd&³7–¯€¥|#*Ê·1ÆòPn‰9Ú^oJt,0ÉœóŒ*WmY®Gv™Þ+«ÔÌS–ôK/i÷—¦O‹¥† ’ÃûÄÇÄßèóER=ûáK>¤{¸Ks°HÏ_[p¢«"çª$ï¬bP[í‰*‹æšõ(« b –ÝœBË~»üêxmuªSv•Ò%«2ÇMYYè™^aô–•×ø¤–·òS̃‚$ó¤0ÁôŽ(Þôž8Îôq¼éKQ¼ñ[Q‚‘&”½â'à]êÁ%3í£…¼³¬Z‰Z:WtCYƒ?Š֠Ⱥ…ÑYwr´ÖpnvCŒ}V}²£²^á¬8¨v•Ìw—Ö•z&×Vók›}ãjúù15þ,ó‚(Ë-a”åW¨ê/ÑUÏ V]Éú¿äÍÿuºÿóTÿ©zò>+ÐC4å¤Å-Î(lõA~Û*hÛ7#»};£jåd´Eqm‰öim2Gi«Ê9©%×5¡Åà×\áy ÙêÕÔ볿é¨_xã,?¬ñ:_ã/øaÖÏùa ÿá‡Õ³ßãõ`ú>GùSÍ´ -ä]­ä=mä]]@^7š^/dõ‰‘Ù¿éýAõ‡0©ýœäþXnbŸÔ>¾/Ã!¦WãÝ[èÙcv ï©÷Ø×Ýå¹·û°÷îî³¼î%Þ®îŸòB:ŸòB:žù„´³ß㦉f‘z~š²tÐ.tVÊ.ïòÉ=ŠQ7¤ñ![äñ@$ŒoGÜx(3͉OäFŽÉì"ƲìÃÆòöŽ–9í­uÞ5ÚáºctÔmûÈi÷m#‹Á#?r>ô÷àáo<‚YÏå,Ð ÌQý“”}¨hï§Pv e猲c@¤b¦¼=%FÔô:ìŸÞŠˆ™]› gBgb˜=3)œ™ ÎΙîÛÓ»mÓUö?˜nqxkzØqëô §7§/œÞœú³Sàñ¯'X—Àc¯¸@
+v’ê§ì>ʵ’šº sH:D’&îuÃî9_ìš[‰s°c>oχ`Û|‚mq²¥2oÙTÌ›Žh3q6٬ܶ~î¶I»õ¶‹vël÷íÖÍ?á®ýÊní9Ö~íÙWœ¥ücÀÐíÂÀ2 N
+ˆï®ƒèîïí€à^üïÅ‚_¿û9ð}PŸVð Áûá)x=¼χÂëÁ_áuï¼î²ß1>F»H}o ÚM³´€ô+@ܰïå¿ lzxý> þ×õÕ¹Åüï¶°lˆe£ DË‚((`¤W%ÑÄØ 1`A,H @DņŠ(Š< âàC‰-ŠO1D!âŒODQDÄ‚ŠÑïgã0¿¹³;Ë=ç;{÷ûÎ9ÍÁ¢F Õ9ÌêÔPÔY“Þ0­s C ¯Y½Ò¿#ÉDHþN„øb:Dó ¼X
+AC¸†FrŸ<û(g3Jßù,Zû”=ÀXŠ=æOZÕÿ8àðÐû `] hêÅ%Ò+"ˆ6ª!¸j°€&;àÚ@à:5¨Í£©1Š
+}k7¡Ýj æíöGK(þœ]À4ªû8Zw }Äë0ø`Wè.
+:U3ñP•„ªÅ¸§Ê@»ê7ÜQå¢M•‡Ven)wà¦rš•{q]yMÊüWÕŒõCÔkºp^øZsÆ{¯æ=fK±m•`½‰­éf£ë9/ºÇcë
+·®Üõ[¾Ìu_êò£ Äe–p§K²°Ø**2,m7¬Ö›l1l5)0ü.É7TJ6ª¥ W¥ë\:dk\^ËV»2YîgØ`ÚÚ(vËšùFµ#9œ.Æ % ï‰}#úcÏw”ðåJFsÅÃcøíž…žS…[†Å‹6›-Î÷X Þè‘f’ç‘%Yç±ZºÖ=_¶Ú½X¾Êýßò•U¦9 ¦¿ »§ÈÖ¥ÈÆþÏtÙ'˜+m¿¿‘¶÷Z#PMŽúñ88Ê>Ö(óÕc§ŸE¾ÞÜVß
+™$Y'MN”'-PÌ J7›˜£JÜ ž¸Ã<>°ÂâÇÀj‹¸ fòÄ<.ˆiÞSÐæGßCp"
+80(§Ù¯˜®[èu^”¹ÑÖȉé‡Ì˜AȈñäÒ¢}ùÅQ‚…‘¢äˆXqRÄ÷’9áÓ¤³Â–'„ÍSÌ ûE–­Š ]¯™ºÝ|jè‹)¡Ç»Mk"-&‡1s¢ùT‹?pžjPEq÷§Ðì·®ÇÒ¼3NŽå±Z,û¦Ò¿qBj¬;·(vŸ<.@0wl˜pvLŒ8!f‚ÉÌè)Òøèxy\ÔÅô¨åÔ¨Lõäȵš" ÿÇuyGEu¦qøwgfd`€™ÁÅ.誈Ši*e60ƒ U‚RUZbì2ÇXYKˆ(nŒQ@M\)GOÔ-–h²®kÁ²nŒQ¹û“ã9ùã9÷Þ9s¿ç{ßû}÷¾¯ÖÙìbˆ<Õ31òŸ=uO\u]ÚD¨1èDõ[~:s[<ס='ËyÜÄëµú¨ÖkPž0e £P”0I(ÐûHòôÒœøy2sœÎ&3.Nž›Ô#56CiŠÉuXSìhŒ©vNŒÞ ÖGïÔÆGp‰>Ñ36úoäÏ_kã¢E Q¿åŸÃYÆ|Œñ7³ìjdÿYÏc¯?4Øb¥Ñ¥IýQ˜4ùI„\ãtÁlœ-É4„ÈÒ 6)‰1òäDƒ")!ÍÎcŸ /TÅë+bõëÔÑñÛµ ã›\¢âÛzêâ/“Nž¿ÒFÅ‹¢~Ë>‡Ó\Ÿ3þ¦T`'Ù”¬âuy²%&G,5õE^ŠrRÆ!+ešaò—¤š‚¤ÉÉa6IÉѶ†E
+ý¢»¸$³}LÒRÕ¤r'±Vaܦ 7îw 3wY`¼ä²ÀðÐ%ÌðRf5áQý–‹|'€Ã&`_°Ô¥³ïå<JÒ€‚täfô‚9c(23=–9Y0eøJ’2æJ óeúô(Û¸ôxyLZr…i‹•‘©ùá©+¤¬už—bQ‡¦ìÓ†¤|¡ NùNlº¯ 1½Ô„˜Duhò;¾æZleÌ鵚¹²Ø÷.f½ŸI?9YJ,6k‘–=¦œQX”ã%rfúœÙ’¸ìitv„MTv¬m¤Ù¨7§ÛÍÏʵÍ*U/^å¸x«óÜÌ=ê€Ì£š9™ß{š€Œ_Õ¢: ]t~ËY¹‰î]°ï%Õ9@!ÉáyFž¦%ÎHÊïÄwè Æ#v©·]à/ÑI#
+ÂdaùÑ6óóå¡KRAKrìææÛÏÉûP5+w³£®ÕÉ/÷ˆ³on‡³ßwÉ/Î~9¢“ö;N1‡óÞ\ö,¿k–
+]c‹À'ÔöÅœÚa˜Uç™u“áW矺9Âûuó„éu:É´ºÉ”ºTé¤Ú\ÙÄÚå6ãkkl=k·ËÇÖ4Ë=j¾"·äkž)<V‹
+UÝôxÃAæÜJïÖ
+îGRRÉÐLwL {¯õÀìM€ïV{ÌØªÅôú˜VOL±LÅ$‹¼,˜h Æ[bOË"ÁÃb–Œ¶IFYVIGZ,Ò–&™»å¤Ì­þ†ÔmËS™Ûæ×6n›ÄßÙÇØ·Ó»žmX½ùkØûÕ2€ˆ-À\¶ª>;€I 2ŒÛ­‚çî^Û8cGbtã8Œ²NÅŸ¬þi ÂkÜ­z ·¦a˜5_b­[7 ÿ¼W`=.¸Z¯
+®׆—×]âï4T[è]MoÉ:À¼‘ë`+×ÀÇ@È.Àß
+LÞ Œýp? ÇÐf' iî…A̓0ðà 8è ׃SÑÿú
+Æ{‡tè{È€>-fôn)EÏ–:¸´4@Ûrš–o¡9tšæÐ…·XÖr0ߌy ÛÑTÆ»˜Oï¬&`ÛRÏÀí3`àQ ÷19´­*hZ] nu…sÛp2Nm“àÔî U{0‰‚C{2ìOäAy¢
+víõP´7AÞþ%¹ÉóÇP´ýJ^u³qW×ó}@w&cNÜ„7sÞGè§×í80 èu
+p>-ÀþœŠó*ÈÏka{¾lÎ…ìÂ(H/xArÁ‡A$üupÓupÃ}ÍDwPÐÁA;Îñú6yDžt³Š±3çæ=€ñSîþ-€ÞiôŽ¥w8½ýO.¼Õ±°û]’°è°#ŽÀ÷Zàr_àÊ`àêHD€¼)PY^ãKø:_À×KôõFà»ÁÁnÜ ·»)kàûp°ˆ9×}Î5ØÊøOô= ¼G¯æ;Àá ¿ÿ„·ã¦ø¡pKŦ@ üØ‹…©+pÇ ¸ëÉBy:ð€óxÀy<äK°“¾“Iïd²;ÿB¾$gºÉcîM|Þ ¿
+íO4~‘¥à%—ï¸<¼àJðLR§’j|%©ÅcI=r øׂÜ&|ÉíÀ\þÎÄ]î8îp½¸%½‰²'¸.ÿW.+\òÒçE¦‚0TI¹ðÄ
+8Kœ!N˜×>ÞC”ÇðøÙ¾<O}'ãá iøÇ dÜ”Ž[ƒ¸á[„k¾å¸â;—ü¡Ï¯Ÿø­ÆE¿f\¼ ½ƒwâüàNœrg†œÅ©!×pò/Ñ3ô¿Ì94L`º½ô"Œ"}*/¿–â§@%žSüßÂçþãpkØû¸6\‹Kø8†ÞNœQ‚³³p: 
+§jp2`9N¬Å±€õè ØÂÙÎy€éy’9ØÇt>`:G}ËvŒúƒm›Þ(/»½cH?ˆ®5•¸'ã©ôIp?P;¾¸2z4.Ž™„óc§á´: 'Õi8¦¶ã¨ÚÃêèVW0Õó™.õbf¿z³WÝÈtª[Ùõ¶=h/ÛÔÃî
+ºÀî ºÇn{ç¹h˸_E›Æ â¼´zy¥ÿ’ÊÉ“ÉTb5À­É ®OàÐ7®?ÎO‚ßEOH…hq`âtì ÉDgˆƒé)`ÚCʘÝ!³Ù]ÁÕìŽàZv{ð*vkp³hsðѦàÅ­!‡ÄCΉׇÜ7OüZÒ4ñgIÃ{ÂÿYûÂ*w¤}? ¸Aåµ/‚zýPg&óè™2ÝSÕØ:¡Qh MÂÎP3³}ªÙ:5Ù<µˆý@SÎnÔTŠ6hŠZ4uâušq“¦UÒ Ù%Y£éâVkNq+5W¥õSŸH—OýQZ*H—yYêå‡ITꩤމ&ý8šùbiÞ¡'çH¸ûÃ}Ñ1
+»"'b{d86GjÑi`6D¤3-vv]„‹m /­ Ÿ%^^%^¶DR¶’[ÖÂ- Û&­ Û+[v\^Þ'_þH¾ â{BWG²7ù&”ž3Ò¿|œœ º)Þ-F{ô
+[–jfKS­¢âÔ\q¡©Ë7•K]¦*™Ó´Ì'×Ô¤È1nåíÆ}ª,Ó)âF?›é?ôý•Ï2 J/ŠWü<¿h¦³Hú]ÙÔçSÛ³žâ5iÔ禋P“ÞÕþ˜›1³3߃'# e1LIF"[”nd Ò3Dîô±Ó’/ɵ”Is,sevK­O–¥AaMÛÂg¦uªÒ-õK·\'žQü ŸnøŒ4AùšÛ´½´îÒï̶Ø&ŠW‹)—ù6s²†`VÖÛ˜™ŒÒ¬PeE1ùY ŒËf`ól‘Ö-¶Û\’l[)g³Î‘eZû¤[×(Ò¬›xsæUªõ„Êd½ÚÏdý·Ê”ù3Ÿš)¼BùšÏÈó³¤uØ´çÑÌ™KP\Kí×|ÊgvŽå9~(uŒD‘c<
+r5påF2y-ãp¤°v‡Y”å°‰­9y’ŒœbÎ’S!3ç,’›ì«F{«Ò`ÿ×Û©Rì—‰§*½ý'^Ÿ-(_aðr…<?Eë>袙“h!êÔkS.s(§™N9Š]Qà—;yîIÈq‡#ÛËØÜIl¦ÛĦ»3Ein‡8ÕU(1º<Rƒ«Z–â\á“ìÜ Ht¶)uΣ|‚óñ¯ËûQ©Ë”º\A‘èåòü8éí/ ûH4Kóiæs“>}‹
+$pöGn‘?ìÅcUkÉûÈ(‰b,% Œ¹ÄÀšJ,¢éÅv±¾Ø-I..㋪¤ Euòø¢Ÿ¸ÂÝŠØÂ#ʘÂO‰Çʘ‚—ÊØA›ÿ'“GHo­ÅtˆEÄ,¢þ9g0°—ñ°ÎŒŒò@X<ãaöh`òDÂèÑ2O
+“â1³I›HçqŠãËK%qå•\LùRiÔÿÈ.ó°(¯+ÿ¾Ù„afØÔÔaQA¢â׺<ØTµ5Ú¨E£A¶ ¢ÀŒEE†qA&.TD0j]S4‰VmšX×Zc4DMòõ'á±>éï3ÌÂ}ï9÷ÞóÝ“Tà84ɬŒOÚOZ”qIÿPÆ/yªŒ_,*ã‰Ê·º8Âì¥o{PÄë÷z’NóýÜØ÷-gß‘¢ÄÔn˜’Ú“Òô˜ñé1—>cÓÇ £Ó§#Ó% iïJ‡¥-”¾•–,‹KÍRĦæw‹I­pˆ^±—4;D­¸éòÄ!:ùg‡èåâk˜{ëRž‡eÜ‹$“$Ó»0˜•
+Lg«6)CŽ·×8cl¦7FgaTV?ŒÈ„„ìx Ï…¡Ù„øìéB\ö,IlÖ|ÉଥҨ¬Õ²Ye‘YÛ䙵ŠðÌ&rM¾æ±"<ã'"*ÂWýB[ +X—IYIoRðºÙ¢M^ËÞs=£ÂÐ\wÄçõD\žCŒáˆ5Æ`ã0DÇ"Ê83…ƹB„q±fL—ô7æJú·HCóvKCòŽviHÎ#iȆe!ëÅ×Ô¦°.e°.¯²I
+ÿ^˜ ̦{êö~F`8[Ö˜B99c@±"‹uˆ(AxI$ %±è_2ýJÞF_ÓT„šf!Ø´
+º»PrQÐ?
+_H
+Ä×ì\ la –›dð5i÷!݉tOÜÌø‹ØR r+ZáˆàJWô©ìŽÞ•èeE9æXèÌ 0‡¿eü,sÐò¾–Õð±äÃÛb—å ´–3КïB[Ñm¹(xuQù!ëbÏ#IÉåYØÄ}X
+0^-½z»½ò~I8m|E¾&ߦwŽÍœ<P]€GžÀcæä;=ð=›–Îã>Ÿ³>çáÁ½ñ‚‰ɤ¿¬ëDtò…Ø­;D¼¢éM"ÉP2ž$’ydIÆOÈÀK¬Ã äáä£Åøex†J<E¾ƒߢÿÁ'x„“xˆËxÀÉÞã7wñ3ÿQã Qé Qðà;þ@b CÈ2ÞßÓ¹€¾¥t¥q´L<Áz<†‘®ºLtm£ËB×.Ž»‡Öz¦ê8SvŽ©û'nñ×79û›ñFïBtq‡¨v…¨p(á\àGBHI {ãû-c›G×"º–ã>VÒ“…;Ø€ÛØDW!-¥tUÐUÅñ­¸†Ü2Gq§ñ?¹Œãžã"G½@οèêÌ\¨!ªˆ“Û/ë"éÅo™ã8Æ9ŽÎiôͦë=z–ГÂQ3pk¹Mrpq«ÓUF™ŽjF¿g¹§ÑŠSÜÄmå$3Ù‘O"š%ÿCôpb.Ä‘h8®‰ÒÏ!x ‹Â×ÒÜ’NÂuY"®Êæào²…¸$[Š òTœ“¯ÆYù:œ‘çáSùfœ’›Ð*/G‹|š6üEÑ€&E Ž:´ãˆã=Ø~@ƒJDý¯½dœ j¥|å<\5x¢ñÄ]•7•\u‚Kª18§ú ΨÞÁ)õ<´ªßG‹zšÕi8®^ƒ&͇8¦Ùˆ#š"Ö”Á®ÙŽFõÎõ8è|\Ú±Ïõln/`uQã.b÷ˆ>ôûòØ<Þ·=¸Í||©ñÂE×>8í6­îÃpÜc<ŽyLÇaÏÙ°{þ ž‹Pï¹ =Óq@›‰ýÚ Ø§ý{´&ؼ*`õªÆnï:T{7a§÷ETùÜŧ•¾¢PÑEy"rËÊ]–¸½Y‚t.ú:ଗ+Z|p¬GØ{Æâß(ÔùMÆ^¿DØüÞ…Õÿ=Ôø/Fµ2vù¯ÄÿlTùçb{@,e¨ ¨ByÀa›î°P¦;+”~#˜‚ž ÅA¢PD
+ß@ dü!ôXjYî?e©=¡—¢)PÆ ÔõÆž>QÓgvéÇ¡J?ÛõïÀ¬Ÿ‹
+ý”ë—`«>eú ¡T¿NøX¿I(Ñ›„¢`³Pl67HòCNI6…Ü’CŸHrCEIίx̲\fimc™?Æ×Æpöž}aëçêþ¨2„ÁlˆE¹a$ÊÂ&¢4lL†Y(6ÌC¡áOBa©oø/ÓåÕ}†ñç\öì)A55ŠJ ²Â.à®{]8ÀËîÂ.Ë.·]n r[‘ÛŠ(—­Â%Œ¦*X“èèÔ±mSÓ¦i3¶Î´vò!m¦Ó´“t2M3Mš¶il¶/2füð›ÿ9çËó¼ïùßž>f&m˜™J›`O¥fO¦-±'ö_æŽï¿É…÷¿É¥¿Ë¤ ©¿"¢Ü±Çø˜jÿ}6p/¸{ˆòVp¼\ÉàqéÀzœÏÜŠ5)˜Óhð¼FÓ+" ÓšjLjÜÌIMs"³•ÏìfÙ!v43ÌgNsC™óÜ æ"Ò\ç4wýY¿Sôe}¬èÉ~@DùÇùPCÇéÿÔ
+§xJpˆsÊjqYeoªªÄŸÇT‰ïŸÓsT¹ŠMŒ
+«Ü§9ð:éÞ¨¢ÿ@WYºöL’—1êÇQòÒ[‹`Ù´KÛRÐ,e QʃO2¢^*bê¤rÆ%ÙÙZ©ŽuJÍœC
+òÕÒQE•4!ÈÒ¬²Bº¨*—¾#Io"þIïQeyÙC„UîQßïîÕjàœîùäåy9F¾zÉK°"­• h‘·¢Q~^Y |nù0je ã”ˇlcªek“9Yîà+äE¹<.”Ég•¥ò•(ß ÞŒ)–ÿ#ÊŸ©Äʨr•’ʨ°ÊϨÖ×HûŠXtÐ<´SÈO?ù’Ÿ€M@Su<¼Õ›á±ï‚Ëž†G6ª°9JÙ!3•޶ÜácËìm\©½íaE‘ý´`µ¿¤´Ø¯«
+í?&Þ#>UVG•–5„U~BµÞ$íåZÊœ5”·ˆ!òÓM^Zé{£“ƒ§&®ÚMpÖî€Ýµ6W&dw*Ý&”»‹É]Á”º¬è®g‹]Îêêá ]£
+³+"˜\ç”F×5¥ÁuWep½«2ÔþCi¨ýJi¬‰
+øÕzÃE{Rp†âÀqb€ÞÛÉS#uôÝé‰EuýS¨ònC…wÊ}j”ù¢Ôw¢Ï‚b_cõÙ˜BŸ›5ûšY“¯‹3ø†x½wZ¡ó. ‡¼¯
+ùÞוyÞw”ùõŸùõÿy¢_s‡j½J`‰˜&FˆÂOÔy]Ë妔7¯Giófˆ-»PìO…ÕŸ‹ÿ
+f˜"Œ™1j½¿‘=äïdóüƒ\®’?è_Tä´¼¬Ènù"Ûÿ[ÿ®Èn~ ÈiŠ~Í-ªûJ#0GLÞQ;‰ºŠ;Z€Š
+dÄ!st#2ƶãÀؤ¥c8ia=RÃ|+,a_؉”pö„ƒH0»Ã§™]á ÌÎðM&)|IûIù7“4e“†Ö¸LZ³Äq¢h릶´Åc€qÈ=dL
+П²fIH^¶6]àp)ñË D"ž\Þ¸•½„ßXÑ"vÅŒu+žX©EÌJ
+à}œ£óæ@»›R;àNlÌØ_¦ËlL:X;¸ù:8Ñ ²ƒu'íäF7yÛ€´Ü‹û€o
+¹*_õ¾6®™ßX°A¶æE—•Ûl–ïÐÇ÷<˜ï²ðßÍâŠsÒÉDwî' ®óL7Œ;á
+<'b¡g$ò<c뙈ÏTažW–í•'dy•™^kÄ ¯Jq¶÷q–÷)Íûœ”:úŽ”2ú‘Åw¹N½³Ôo ö
+Ñ¡IBTh†0=4WŒ -‘¦†®•¦„mSL
+«'-Šˆ°kzaøù–ÈR“ÈYÎA#[Œ]dóû¼ëÐK½ä3Ùô’f€´ ¦Hž0‰$h\§ñF¬FhM0¢4ᘮ™ŒiáÑBdx¢0%|–09<GŒ/’>_#Müë2‹ºÌãøç73Š¢(à‘Bj‚+Š7&‚‚Š
+"Ç
+Å0=}ÞíÉ6ÑOÖ¼@¥åuRʤîJÎÔ¸ü(%æÓXg‚Æ8“5Ú™©QN‡F:§ñÎYFœs¡i˜³Üë\oŠqî2u1G;Ï™£ó¿4ÍÿÞ<t’ÏÓHžÏ|íyÒZÆ€2X
+úkdA´F¸F(Þ•¨8Wº†»r뚬W¡íšo q•ƒ]U¦(×NÓ ×!8cTpÓ4hÚwà3EM}È~Zÿ-謄%P3 2þOE¿€¹‹Q)a†¿FÌ R\aˆ††)¶¨bŠ¢4Ô=LÑî q×ãn›»'i{ººKŒþî§~î5F_÷v£û€é~ψ,ºnDþÓÔg¦ïj 7 UOÁl¾€ÃÅÌ3CW(š% ›ÛLÑÅmôxI .骨y=5p^ ˜7Dý=ñêçIT_OºúxìŠôLSoÏEx¼êéY¥žj#ܳNáó®á% Fx±ÏŸûø¸æ Îð@!ß'£=¡ˆ¹o¶4šñh¸‡ÙkÔ‘¿ú”)²´“z/ŽÔsñ@ýÊ£ÞQ
+÷&+ÌkSwo¾óªÛ’…겤Rz_P¨wŸB¼'á²BJï+tÑO
+}Êg4² ­g¡怋Ñp"Úh'2Å3®YŒþR)¢Ì¬°gôXy°º•‡¨Ky˜­ˆThÅ …TĪsÅhuªLÓ#•¹êXéR‡Êyj_Y¦àÊõ
+ªÜ£ Š7Xñ±‚ž¹« å¼œi¢Ì§ª¹Ôâi>ïEà,‘²Ñö¨%ÒÐeÒ
+¬Š‚áj[5Vm8ìªòպʭVU‹å_µZ-Öm—ߺCò[[7ä·¦A-Vÿôsèy¡\ÄÛ¾HJÇç1eRl¥4Ý^kЯBƒÔn£Ôfs µªn£–ÕíÔ¢:D~Õaj^)Kõ`™«ãeÚÊổÃf+]M"«Ë·ìƒ“4(…Ûð-ÒwZ^ÊÝ…Ä;ÿiöâr) Ÿãž“¢ÐínwFÔÎ[¥àèï’Zîi&sMk.:ƒšâ½B8ô‘jiÊj¹pk¹xjÙäµu-‹×âHíN~sÎñÜp¾ÑRb]ŒÏÓ05g9à§#ž§Ñ܆þ‹èïA¿V
+xEò{M\0pȰãvÆŽÃ4äGÂhˆ±ã á1.ÿ:û:6zI®Ã¹:bQG,޽Á ¸¥…Ïr/¬’òÖ±Ë>¨¦Ðí[#…½Œ>šA‡¤VG%ËëhŸ€·š8Ùœ¨Íh[ém†“ÓiÊhÊßmlÒ‰GccPÏ¡_ONêIt=‰­ß.ýœâ󸤹ë9_ ñ9é%Îtz²ÎuR šþ¤Ðà½gàl€óf1Ñ3¨] &;Jw•>é%}Š—iB®PWf2ÄPäWIðU„®Ì«8rõ´f`ÖDböª4ò05€nt;¡xZjñ~“ÞyÀlý þ—€ÒÒ§p>³HŸûK×o“[ÔÈékòrfä>Ï7vßü’Ý@a5쇃L½í ¨‰Ð úB,$B&äé'fÝš¥ïåÑ¿´Hßêi5¨\÷µB÷˜u¿Öz}¥Mº£íº­=ºÅ¼û¥êtCïê:Æ~ή±Â5Vûìÿð)Ú@
+ð£b4èoØñ‰–‘š
+R´
+*Öߨ´ÔíÑ9½¦z×û¼¾Ë“ïµÓxö6*§àí&|ææ¼ÐøÞšXáoýC½ñ1ßHw*>M@'_\h¸Ñ(Ac«{)ÓezO•è<‡ÆzÖ߬“ÄâM½¬7tT¯£~ kâÍaý
+~ÁºŸ³î‚(ëzBðÔÇEtäIÛä2µöStÌ,R–CÛ‚5˜ÎǤ 9ØUP„Q¡ #ÂJ «±ChÁ °ýB7ú„>„¶÷ ¸}=ÛÇÑ]8ƒÎÂc,PxuÝb¾¢Ì'úký…µ‰¾&Ñÿùˆj~GIÿ¿‚F²(£}‹²LÓs™1ì.ŠÃ¨( Qvˆ 0 .EŸ¸!q‚bzŠëÐ]ìBg±âNt÷ÁW2ж’)ÖZ²À¼¥çXKéMæ–¼C~Êš%Ÿ‘¯X“$º¢™|@5ï©—É)p¸œêS–=eÀå‹”®Ç d#ú$éJrÑ#¡K*C@Z¿Ô
+ °WzPWÙ [å
+`5”À\­@mµ¦j#Œ5u¨©iBuc:ãNVeœfZãq¦1^áTïr*LÉ#N…ñKúåTÖ¬`o+&à™&£¤ŸîuÒóh¥<Í”«ÑƒzS<l¦-0›²`ªÍ‡±VŒêZ æ
+èÍèÌVTYœÐZÚ ±ô ÒajË~¦²,²rË%¦´Þá(¬? ¿ã(-I”)ÍQVNîZ¨/™#d’DH´S–fZèse5ÌÖ8m›QmË€ÞÆ‡®®Ú: 4u*TÖëPQ_ u½*»åö.(ìCÛ'™Ì¾ÀÊì˜Ô~›Iìï“OèóLZeÒº(+#¯ÙhÕ!sd7$]ÄC«A]m4š›ì Ç ¨jH„¦!•<¨  r£Ü©€Ò©Âi„ÜU™Ë ©Ë‰k
+ÒöX”ú6¢¸# âD(ò£Ð¯Àv¿B¿ ‚€ù/ò½È ì?0‹œÀ2²¯€x€mŸƒçÿ¼Ž¯Y¶/ºâýsdœêõ?q»éð
+¼e* {a˜Ü
+#åZØ~/cÑ`¹Å˹h²œŠfɱ¨L…52î–}Á'2|#ÜïåÿLyV-J¡! ’‰wìdb€ö›éR,)r¦–'¢?WòXÀXb''‹³-&ð–ƒ%PKGÙ[̲³ô–­¥¿ZZF¨…%Y6– 5³Ì'áÕRévvÌ’+4Ä»ðxªB✓ÐM˜JÌÞÎ&øl.@ÝÀ…è—¢¿ýå’c…­l+eSÉ0º’w¥/qÁƪxø«hòU4–jŠ«:•¿q¢jŸÙû>ç¼€ÍÍi06“»@¼ÿ0›{€Ï]1½ºK%ï’‰5Õe•dX+µ¨aXo/m` ßàÆ£ËP¼;61”Ö2üÔòð×Ñèë†@Ì:X»vò¹ãp ¾ã{”•KO"Þ£ó¥˜br€Ï¯.–ÂË¥ *ôÑ4­—œqÁ~³d³%dÔ³Õ³|à$í$;‰GƒñØ=€‡g~IÞƒs{ˆEc¿ÿÄïÓpnk*>'Í“†•|þ]%5ˆ¹!èúðqw4ê¥V èîBÉB%}lÀN:èÈPÎBpØ$aIø˜¡øvg9Îãs‚Ë~‚DŸDè$Á<É'ø¢ =óâóëïQµÜÁ÷%_BfjD¿d{
+²¾ä4cÇþi3TÃÚûBl)Æ–žá¼
+ 6oRŒ¹Q1ѧš ŠþkP´õÌVbÜÙ
+UPs ›Qlz4³>¶¤DÙ(9ÊI‰Q&1·S¼9DqæpÅš»jdt/ î«aÑ44zˆ†tÓà®cÓ-Mƒºåh`÷yн\ÿgºÌ£¢:Ï0þŒ+*"àŽEP”E†a†eXT7Æ(
+.Çšb´¥š&Zš¤q_cÆŒ5Ö¥1Ñx¬ñXÍÒ¨ÑZMÒäD§? æøÇï\†{ï÷¼ßû¾÷Þ÷É0¾¬tã^ƒÍø>|ß‚ûšñ3ÖÁs°æ1þTG9y™%ÑÝ41ÆW…1~¨ü˜PåÅŒV®1VÙÆ$Ù6es”[ ŒØ2¥ÇÍ”-îWJ3-Wª©AÖøMJ‰Ý`‰?f°˜¯r¼îŸ1¹ 6Ó Œk`TÃþ7ƒ8¦“bŽEqgò–ÃÔO9¦Á²›†+Ë®ŒøhÙâÍ`Uš9K©æq²&”(%a†,‰ó•Ìpœ”¸F‰IÍJHÚc0'…+ð ¸ ‰?ó:cx#Ô%S˜3ù=•|K!1å'tTN¢—쉽•‘è/[ÒP¥%”5)J)Iq²$[”œœ¡¤d‡-“”`™.sÊ\ŧ<+SJ½â¬Mеî2ÄZŒÖËïbS‚»•žIÆð•°ªÀ ¥ÄQÎg[Ú)#ÅSi)¾²Zýd±Q²5D‰©‘JH5Êœš¨ø4›Li¹ŠK+R¬mªŒ¶9б-QtzƦoÔ˜ôТ¨ôKïjŒí0­¥¹µÕÊ;1Uªe ¯†
+˜Êï‰üßv~§Û˜7m]•œî­ÄŒ~2g V|F°â2Û-c¦Y1YVEgÙ56«@cì“e¯Ðh»K‘ö•аoPxöv…e†
+³ÿGáö
+Ïr+^eý†ti)̇Œâ¥ ÀÎß¶L<G3·½“LÙ^ŠÍé­˜Eçiln¨¢r£4:/N‘yEäe*Ü‘¯0G‰F9žÖHGµB+’ÿ’Fäo…CžN#w4"ï'…ä¹’ëV3u°J˜ŽE›Hg$OÉ•ÌØ¥Øüv;ÎSQã}9¾¿"
+V0\£
+Â5²0F¡4NHašFæjxÑD•kXÑ -ªQPÑ
+,Ú¬!Eá,ÜV`á
+,`r…õè¬È¡0 &£É2ÊF75ŸýC¿ß1A
+Ÿä¡Ðâ
+)î­á%þ
+. Ò°’P -‰RPiœKS4¤Ô®€ÒB .›ªAeUzªl©üË~§e¯j@Ù~ù•~ ¥75 ä ,v·²–±)̃r4‹!ÝÌñRr¾ Û:¶ïW†÷™Ò^AÓ<5dº¦÷Ó òÁzª<XþåaÈËd€3A~N›ú;êç,U_g…ú8—¨·³^½œ›ÔÓù¦|§ä[~]=§¯^ÓÜ­Ô¡µK8¦°ß"ÈE; ms‰=Š˜&(—ŸÆUtVÿ9^ê7§§úTú©we€zUŽ€õ¬Œ•o•E>UvyWMP*§¼ªªÕ½j4ʳrºUS·9×ä9û¾ºÏ~ înÕ¢µ
+Â.úÏ•ú.|«»¨‡«‡¼\½`€º»åé
+Q7W”ººâÕÅe“‡+_]“ÕÑ5W\ËÕÞõ¢ ‹0¯‹ËP}A†…·Ônáwð“ž)¥a¹ž9Sè´ãÙsÔl)´
+}tŸªFÿ×è?‹\ÖI]j<Õ±ÆGíkú¨]¿ 5|èkøØÖòr¯åÅRKS?Ú`íLàÆÚ5œ…¡`?àu—]§ ¾´<Ï„2G¾3ÐND;zž4j¡4t1úèö].ù<‡þJÉ£®ƒÚÕ3
+ÇG fó:GMÎÑhçIòù ÀâçÞ†ã* ß¿ç]ÐÈ{€Ý.ù¡ëCûvy¬ùHç8œZZgÚ8 ”Uçàø|J}®Ð'×|¤/ü¤¯‚¥ëFé„›ÔäkŠ~‹»E’o#v{n¯;tkÃü`Œ3¤ãóõ?M—NñÎÒ=üå]üå|îm-Ó-­ÐM<æ ­ÕW8Ë/ñ™Ÿãt?ÓºªÃºBà—9sIߦ»•‹m|Òêy= Câé­‡
+ÐÉFý¯{ùoèk•ê:þòKüåçøËkÄñOâ¸B—õ<õº u:¯ú˜æ=KÑ[Dp\§¹êÑŸDáÄœlAOЕýúpå@öÌÞF½=+ä¡1ɤÞI *ô!q|@#ŽÓÄq’8þªÕ´Ç‹”¯™–ÙB)÷’‰½Ão“±ƒdõ
+$¡&!Is±MÒ4I›&kÔf]zd=Uu«¢®«¶µÓŽvÕ6µÕ¤ýQ©ÒºNÝ:mk·UíŽvÞ·d­¬Þ÷÷Úz¿ß÷ðó{>ÔI}ÎRþ’/Öÿ‘xW¼%^Õ½ø¡îÅ‹º7ÈÓZÊ¥c×:š´ŸæïÖü\—Gåãù¸&ßdQû¤qXóžÔÍ<¯pz…ËúÕ}šá^íÖÝóÚés:ñóÒþ’DÁïv)^‰VˆY =·HÓ¨5¥K¯H:Ò©Õ »¥Ñ. fƨ4&¹$åã^íÇöKcEÿ’»ô¹Äi¹;¥:©Ûp\'zL»}Lçñ5iÉÞÞTx{Y<¯ðò”ž¯+ì='Í$éeK«D³Y¥ãäž5ίéàî¨ g£85Êé¨0w®½•“k8¾vÇ¢±}'G×ÝÃáuW9¸þIîXÿûc~ξ[Þc_ìÇ,oˆ°÷ÿ¼£ÐúS…ùçÅã)z­(Ä]Qxùº!šË·¸'&™s±yÜ[Æ© ÕœÜØÀñ-ÛØÉÊÆŽÆ p8nŒCqSÜ7ϸEöö³l8ÆÃyn7<ÈRüìŠÛâ_gaÓûì4~Êcä+ÞTh)çæ«æê_ÊÔë4î’·3 1œŠ7r|S+Æ"-4:8àbBË ~ö&ô²;aˆÛÆYLœfWâN÷²#é(Û“Î2—t…ÙÍ×™I¾Átò«L§¼Ç¶”O˜J|ÅkzýÂÃJA.©=«çyJ-ä爼J6p 9™å”\v§˜XJ­b1µŽÛR›YHõ²#-Àö´si#̦M2“>ÇtúSé gœb"ó㙲5ë;Œeý„Ñìß2’ó±ˆ|Å+
+çO›ôúçÄ =.VÊ%?{åewV,‹Y ,de°#»ùl3³ÙÕÌäÔ3ãa*§pN7“9Œçnekî4£y Œäíg(ÿƒù(¸FÁsôþˆPá;„Šþ."7)ŒðR¹î .‹ÓJÅŽ¨].ƒ%ùY—ë˜Ëg¦ …m¹„ K˜(¬d¼ÐÁX¡‹Ñ¢V†‹ü õ2X4Lq˜¾âí„¶ì¥GIP°äݦ‡è2=K ôeñ6þÒ¿‰ˆú7¹Q×¾H¿”úÔ_ÒØNy›•—™’5„Kâ/Ib¬$“SC¦rM6úMNúJÝô–úè)í&X:@wÙ8]e³øËo§³üæs´[®â³<ƒ·ââ7âCñ_Ák‰ðœÒ«6Ý¥Gı ±Yy™Ò¾Lh_Fͱ ™ ˜Óè3çÑk6´TÒmqÐeiÄoi¥Óâ§£"D{Å(¾Ê¼U»h­:D‹õ,ëxlOá¶}ŸfÛ¯Å_Õÿ\DVyFšW”fœ•/ªÛäk\û2,ªÖªŠ'X•L—5¿µ˜«™v« ŸÕI›ÕM«­[žêaÜÕS4×,Ðd¿—ý Ž+48ž/QïxKüEýÏDd•'¤{©VwQiÆ>± fô<¡ñaùë¯QÍS³–@M5‰øìxí´ÚKñØ+qÛ4;\49ÚpÕvÑX;HCÝ$õu;p:÷Sç<EmýýâqõßÃ^ÿ†Ú?‹ÿàpF¨ÕÝLÅWêuJ…·‹°ú£ë— üøõ_ÝÚœF<ÎTš¹49·Ðè´ÐP_C}}ΆêüÔ6ôãhÇ®¥ÆµLµë$ÕM—±5} kÓwůğĿ±¹"«\“æ¹FýÅ¢Ky¾ƒzÊK§¾÷ªïqÅÐ䊧ѕL}SΦBj›Êp4[±7×QÓì¦ÚÝÍÝ‹Õ=F•g–JÏ*ZŽci¹O<†¹åÊ[~¡öñ/,žÈ*JïŒRðÍ:±M%Ò¨Ú>Ñ©ñ6µn5z¢q¶¨mIÄÞšNuk>¶VÖ¶JªÚT¶¹¨ðz±xƒ˜½#”ûf(ó-Qê[Áä»—’öGÙÒ~C¼Îß(ñý“7²Êý_”eJ¿÷¶À¼˜ƒ"(¼¢YåRƒÒâZŸòþö X;ŒTv¤PÑ™ƒ¹³˜òN3eÕ”úë1ù[(ñؤ80EQ`…#.ßõ°xž¼®×È ¼O~àS
+ü‘U.¶ê.HgIÜ*­1Ñ'üÒô´ký*•j;Á€Šîʃñ”“(éÉ`KO>Å=&Šzª(ì­¥ ·™üÞòzûÈ MÚIvè Y¡»É =DFèÛ¤‡~&~OFï'döDÈwKë ´Ä”ô†DP´K·IºN•®ÕAé÷‚©?Š¢ ÉL!w(‡œ¡"²‡Ìd U“9Ü@ÆpéÃAÒ†GI™#yd›GΈ«$ ?KâðÅ»$ }ÌæÁÈ*wJkY©ÿœôÆE¿H·M%ZcØU¶V¨ö‚¢QÕ[cȘ06‘HêD:)“y$O–°y²‚¤ÉZ'Ý$„ýbcxšMáÝćOb_O7ù²xÃÄGÆ?#~<Š´µÎi1"ÍÑ¡5»UžÕI»JåQÙ˜ôUžä†!}z I³±$ÌmÂ8·™Ms™ÄÏ`˜/6âæØ8ïeÃ|/±óÄÌ߯úù£¬›¿HôÜu¢æ^díìDÏ~@ô­ÿcºL ¢<¯0ü"‹"2 
+*ƒ ²ˆ5®"(Ȧ((ˆ² (8*
+Ž+¢ŽË©â‚©1n&ÑDŒQc“Ô%§iŒ[5±Ç¦­MjÔ¨IÛ4©Ñ4>99=žÇ™ÿgþûÞïÞû}ÿ½ßÁTÇ:+Ñ+„HÏ–s©Ã<r0KŠœÍì5}ÆÅ€y’o•Ô£ÚMƒºYŒêjñ“«%H.–r¶D©‹…CÖÂf²\ °”Á2`]x
+½"˜éÒ„9Ôa 9¨ Hx< ý:ôW0ƒ®–\h„×Ñt6Ò|6Òˆ®ï)mÀ4yéÛxéÙ8Øl›…Ù0fãa[oå·çàÏ=‚oU†fdïIÄ;¶’}ÈšÃMƒÑ àQŸµ’'Ëèf“ºRšgxšðmîÒvühÂø±?váÇîp©™—MÇ᳇BÛC,š1ØÌ¬ÛÜÂßÉÇnò±ësøZsI×tâJ¼ãkج9r9úkÐ_/7K4Ýv »' >Í'ì'- <hiÊùH‡ýiŠð£?:^ˆmä¤k#m«
+õy…d_µA½zNÁyeçrü·Ã÷r±^_Öj`}ÎíÍs‹Ã _â2|WáÜ€›NÒ'ÔërÓ1¼|Ì GÞã¥ð›ïþ|`Áö
+û㱟ŒýLV˜ý<½HîÁfüØ…;ñ£I+µÚøÿoå—[°²‰ˆm${•»ž
+_v£ƒ'ý l©SŽ#§º8ÈiaìÓ
+ö3¨¾l4ò´?6ᇼl >UG$Öòo‹Vãá*V°‚Ì[Éèrª¹žª«cÇÕ£ßÁ_ºKïs¬œ4 Íg [{/Ç^³º£g¤ºÐ A'
+áhŒEc)hd²²ÑÈcå³Ñ)E£
+»‹µŒ«ZV¾„È,""[Ã.¨vº­ê._iós°wr#í ÇkGý>>wp½•ãe³‡³l®­wòUƒS Vw ÕJçhYG¨Þ%Vu.‰ZꚢZ×L-qÍÑ"×|YÜŠTãV®j·ÍïZ¯ª®ªèÖ¤yî-*s?¦R÷wTÒýc•x<Q±á9Ø;¹Ä‘zœã½…WMظnà¸
+Vz¹iew/Õw÷ÓRþZì1HCŒFªÚ§†$Í÷LU¥çUxæjžgÊ<KTâ5_Å^K5Ç«AE^ÛTØc¿
+¼_Ó,ïw5Óû¶òÿ€ÁÞÉÛ}yñJì'm‚Õ|¯ç^-þ,â¨]dt×Bo£x¨ÒÛ¤yÆ•‡ªÔ8Z%Æx'jŽOšŠ|²Tè“§Ÿ"Íô­P¾ïbÍè¹JÓ{nUN¯½ÊîõЦùS–ßMeõþ
+~{''y­0Ñ
+@Cm ŸŽøøS‰/óü]TÞËK%~½4ׯŸŠüÌ*ô¬‚ÞÃ5³÷Xå÷NPž²¦ûg(×?GÙþšPª¬€MécUfßMÊà%˜ئÔÀ³J º®ÉAá°sm×±´¡Ä€6lEëæ³Šërü)Æ—9ø2;ÐC³úú(?°fš”®œÀeTVÐ8M JÔ” TeôËRz¿<¥õŸ«Éýç+e@’lФàÝJ2…ÓJ4]QbÈ—ðì\ÛÕ†î.ÚÆiTC×s¸_€/ùø2#¸«rƒ{hZ°Ÿ¦÷Sfp¨2LQJ7 Sªi´&›Æ+Å4IÉ!𒫤³•8°RB—j¼¹QñæŠ ;§4.ì²Æ zÏønçž]‡ÑÜF«±f°´*`núÜË£5Ì!.Yfe†z*=ÔW©æ>J1›”l×Dó%™G(1,V a‰–¦øAÙŠ /иðrÅF,ÖØˆ‰lÒèÈCuR#£~÷ùþ=Ø;iAo mÆ
+Úá…1Ä
+ùžÇ½lü˜Šé09ÂC“"ŒJŠè­ ý•ªøˆ(ÅEÓ¸ÈÑŠ¯±Q)5U£çkTt©FF[4bȽ0d»†Ç¼¤a1íó|Á÷§`×ð!víòS\7”yŠa&äpo
+K#.Éø“ÝM Ñÿ£ºÌ£¢>¯0üdQv•MvD}†}GEepcÄ `Ôˆ\
+`²’Ò§)1Ý®„ŒEŠÏX©‰gÞ­Xóš`>·xÿ7Åe8Ù†V7ºm\›3¤™0*ù»òùÌÊë¤ôÇ”‘á.S†R3Æ(Å<NIæ(%š'*!3Uñ™™š˜™§¸IeP§XËM°,TŒ¥Sã³Ö+:k—¢²+2ë,Üàý=E[ƒl"þ*3ûkÔ6®Á5¼–C!X!“ÿ¥Yð7%ey)ÁjP¼5PqÖpÅZ'hBv’b²Ó5>;[Ñ9%ŠÊ©VdîLEä¶(<w¹ÂrŸWhÞËpH!y§áºBsïÂC…å8´§ÑhEcÖhTB ä‚Ù*™¸'cWòœ—|_/ðWTAˆ" ¢Q¯ðB£Â
+-
+-,THQ•Æ5(¸¨YAE
+,zNÅ;àuù¿§±E×ä_ôµ
+¿Q`¡CßE§ùèÌĪÖB9‚Ýô<îü\‰ã±*1ÅÃU2Rá¥Þ
++3(¤,HãÊ"\« òd–›Pž'ÿò
+­˜¦1M]Ñ&CÅ3ò«xQ£*öÃIùV|ªQåwäWþ@†2‡ºÑz­¹hÕçKUPy0 íÔ"Ö_‚÷Ä:FbUB*]4ÅSþÕ¾[=VcªC4º:Z†êxù1È£j¬ò­)ZùÔ4Ê»v±¼j»äY³~ šð‘<«¿‚ÉkŠCè,d³¡½
+(„ìbjP*%•ã;&KQS¤Ð)°N2L!ßz/ùØü @Þ¶0yq yÚ’!S¶¹Ûª4Ò6Cn¶¹Ú:åb땳m¯†ÛÞÒðú÷å\ÿ%Ü•ËôjGëqÖÙ€^5”Bkž„vj%Þ{]+…amÒè’ÏlyØ=4Òî£öÑr³ËÕ)gûD ·›äd·j˜ v~dŸí°VjĨ6bØÏ3|øÝ9wàZ„V#5ž“YoX«Øh'²æ¬QXúØÅÑè7IžÍÃå¶À]N ¸ì´øú–P›ƒ½•­•ÖJ[)^ëlX =€ßmÁ¨¶œâ|!ü3ü]óùÚ ¨rÈ£Þæ©ìƒzf`¦>}t Íè·à±j®Kœ4l)w)·‹W—že 7Úv2ílí {Emgíó¡6Ãk|÷m~snãžæ²Öé0Š(…z§²æ8»E B×°Hò~Rr_&¹,çÒK8­p.£+©Å*.]«¹ðtqáèæÒæ‡Ã­‡Aïaa=°§ èGϾÓÏw1«]_ð»¯5 Í©PFŸs([Ú\ö!kŽ\ˆ>ºZéõiÄJÉ©mì*si-ñµä±Ž<ÖS Qòè zt!êãaØGOú¸>jÑÇbúº`;Ÿ’6úÑ{[ èV‘fõ6S²DÖEÊÁèV¡¿Fr{Íõ° ¶À6xh¯¾ç$íà2þóÞÒ.æc}ÙËÅc³±žìcÈöÑÔ}ü¿¾ǹÀ}
+ûá
+ü‚þ\eN>aF®Ñ—ß2£×éÉglø ÛM†ëæVéżu§õØ ïuÈ <ÀÆÁ=”wš'-Õ]UëŽêõ'üÔmüÔðu¿Ç×}›úœr Çuvxaù”Æ}‚kü˜‚}¤Ó¸Ý_ëC<ÞωüÁá <Òÿ74}t÷WEé/JD3½|t*ШÅ-6¿‘ø¿•øKˆßAüNâwë}üÝeâE|æy\ëY"ƒ·qyW(ãõ®Ð2Ç9ö?9<„{Î=õ;ô™Bõ¼î¯¨ÅDz¢QDüJÊ^GùtŽ<ÎÇ)òø)y¼‹³|‡<NàBi~Ìê×NªðCÚÙÛ<Ï8Ý$£û´Øñ 蜚ÔSú%^÷
+^÷µ8×=£t2ÐÈa$JÁ*"Öéy¼I_k±[ˆ½X¯“Ç­ kã z õ*C½—o¼B„ݺª](íDu€—‡x8¤}h¤ÎÈ™Êy²kÑŠ!~2ùf²óˆ]ʶ¬$v±mÄž­=j"öâ.f ·k‡Vj;Ù‹ôd*[Èj37Ñ¥^}©ú':Ù<¹¨ù£ÑØZG·áH´|è¦?ñÉG|#ñ-t9إĮÔVæ£9ݬYÄžK¼Ä]B'žÒóêbB×éY¾ý ‘z¨Z7“ÑÅD­fÚV£ý-7м8´Í=»¨Åvj±=²EÁäEü⛈jaêò‰_BüJâ×RùzâÏ"~1[¨ÂR:Ò©§ùÆr²|Š.t}«lcŠ—ê+ø78¹Âñvœce¿+Ú¼¾ÀßQ蹡åMEÇ ‚F ñµJéÄÏB!_¸I4&£QKêјÆ<vìT£C‹Èê "µRÁú±€iOÇ›ÙÕÍìøfô8Í‘vˆãu¿>XËû5î<Ò\‡iÕ0w´|ÑòG'X4’ÑHG# |4Jô’Ë=¶Í« ãOÓÜ»¶[;iì$Nb'Ž'±sqb×±sq“ØiRçÒÄMš‹Ó6½¥Zï:íÒum·U¥]K+T(L\¤Mh  B€6þá&B Ć m0Ú‰3?Ú?~:ßwü}~Î{ÎùÎû¼˜ýŒcûc/ßËÔVˆ0³î¬–Ö]ÔbÎ -ä|Qóë_‡75—ûŽæòþ©¹üO «op”}ãý©æœãú$Çì1Æs´4WG
+ :œkÑ¡\»öçÕi5Ï«½ù~íÎj%?¢LÁ€– F´X0¦…Âií*œ×\ÑŠv­i¶ø´fŠ/hGÉ‹š*¹§É’×5Qú&¼£”á>dò
+éö³e¤E¬ÐãïÇi׸?€-Ye,«Æí-5j¥ÔªåÒJ-–ºµËЬyC»æ =ÚiˆjÖ׌!©Æ”¦iM—5a<¨íNjÌÄ®1]ר鞒¦×”0¿¥ó»ð‘F,YÚ¬^F÷ºƒz¯’ø«Ð§ÝË}†c~‘±,lZ§s±æLf¥Íåš1;5mnД¹E“æ¥Ìam·ôiÜ2¤1˘F-ÓJn\ÐÈÆU c>¶nzRqëU ZïjÀöuõÛ~¤þ²?ÂÈ>ä.š/8™lجÇ>Xæz{˜f,3ŒqÚ–¯)«Q)ë&Û³ÕiÔæUÒÖ¦„-¨‘²ˆ†Ê´µ,¡xù„Êwªónõm~LÑŠÇÕk¿¢ˆýsÚâxUaÇà
+WÞ§ÍÒÇy…ÞEÒüi—tÈ-­À<×3ôM2¶s3îÈÑ6{©’f ÛË5dw*n¯× Ý§G»ú=Š9bŠV©·r\‘ªYm©Ê(T½¦žêO©Ûù¼‚5wÔUóŠ:k¾oÇÜgòZçIñǘìè"Ìr=Iß8ce^F˳HƒÕÔ_mUÌéPÔY§^g£¶8[®éR¨&¢žšAu׎*X;­®ºEuÔT»ëŒîËò»o«Í͉[ÿ=µÖÿ–ö¥?«kh=îRü˜‡io¤Þ¡˜yd,}®<E]E\…]›ÕãªVÐU¯.W³:Ýíêp‡Ô^ß§@}Bþ†Iµ5ìR«g¿ZOÉ×xQÍ·Ôäýš¼ÞïÂo¸þ;|BVWÐ9‡æ4ãc@
+F¹jÂgò{¶xrÔã)QÐcR§Ç¦vCOÚ<jmlS«7¨oT¾¦!57¥ÔÔ<'oóª}'äñ]Pƒï¦ê[¾"wËwà×\
+ZU´«¦»VÎnª»ÛTÕTeOLŽžÙCSª-isè°ÊCO¨,| ¾$[ø[²†&[è=•…>Vy¹ý54–ИÁVˆA: …RÅÛ#Õ‡¨}Â…rn1ª*bQe¤\ŽHµì½nUôú´¹·Ce½Ù¢C²FSÚ]€CÚ;'KìªÌ±{ðM™b?‘9úWY¢ÿ’¥7«3hïÇzÏ£3N Að£Û„%nˆà»£RU_®*úKT>`RÙ€UÖA»6 Öjã`#øe É)>s2Æ÷Ãâ/À]•Æ_SIüÇ´ïÊ0ø
+Emæ$PÀf>ϳ‡Ë?åÝ¿À}í"Î ô´JH†ï%æ:bvÌ ®™RѰ$®Hë÷®ã@ÁøíÃpíÇìÀhĈ²ó‘‘`Ö¼>¶5&ym xyõX{†g(b᜾âÝ5‹Þ s/w¦¤æ)ö
+ž£Oán1°¼Ã.¶þ̓¾ÙŠ×¼
+¨‹U2 E|ñÄÏ$¾—èEÄ/×"|,ÀÇ||ÌÃÇ\òq7õÑ˺½‹§³øÕLÜOgTÓáT2ÝCe÷èOð…ºÑà8[ÚS6óú[÷³Õ.çÙR^‹Ù/i8:4ðìD# <4ŠÐ(G£J3ð1y™J}ôP§Ý\M曓´„»ÕTÌ÷p÷$™:¤ª½ƒ¤ùè`vàá [Ù6¶÷ ¼jV˜Ïý^?½á!š=8-:qèØÑHCÃÍhóÐ(f…–£S­‰wÔ«óŽMÔ¡öAS4>ä. Y¤ÖÁ«Ô2øa Ý©æÐƒj
+=©Æ!çÔv®A@{ÐÜÄql5[ê=0f±ÍOã5Ü3\ê6„ª;Ü IC¢tg˜UaÉšæR{X¶ÆõiÜе ­Tkøhµ„7ª9|œš†u©qØ 5D,P}ÄJÕEnÔèȪ‰<¨jÃIø\ëÐhn8zŒ`üÇfB7×]<ëÄK^:ŒCÕn0jœ!F­†x58ÔlÈP“!G†1”ªÞX¥:c½j­mœ¨êáÓUe𝑦ûTiÚ¨
+ó•™ÂI•šß‡ª4êkîÙ#Ð[c#;zyÕöÀ\·s,kã³¼4G‡¨Ù¡F“I æ8Õ™íª5§j´9KÕf¯ªÌÅU¡‘Q£UÕ¬òè•E÷È3G%1ËTlùŽŠ,ÛT{
+íFùìÑʳÛäµ'+מ®œ¤le'ùäI.“;¹FY)ÍÊLéT†c†Ò‹”æ|P©ÎÍr:Ÿ–#õx>“3õÏzÅhÎL§9þµeHcø_#yV†—òRùŽ!ò: Êq˜åqÄ)Ë‘¨L‡SÎ,¹œyJO-QZZ•RÓåLï#}šR\ ”ìz@I®GeÏØÇ•˜ñ6|ÊõW<h5ú Ñ›–É<ÀXŽuPÅu9~Šù¬
+Æ*º KQ½2.‡ïÊT¸ ž—±ð—^pQ¦‚C@KП‰ÞDÚ‘±P •P>È…LÚÇÔ|ô}ô^…ƒd+
+W\±Q–âhÅ”X]’¤¨’tÈ‘Ù_$“4ÉèŸ(cé,J—*²t=ìT„ÿ°†ùOÃÇ\ÿK‘%7´±ME£F4ª¡ Š gžÿR]æAQßg¸Ø]w‘EЪ(*
+È-,»¸‡«» ˆ
+BÁŒmªÄƒÄD3iÔFƒ¹¦Ui;1&ͤšÄNk’NÇcšÌ´éL56iµÓÖ£­c³ý,îdÒ?žY–ýýÞç½¾ï÷}øÌF¦L+ƒ¿‚Ý©`«ŠWJµQ‡Ødv|dÉäÈ•ÑQ ìJvú”ä\*ƒ³C Î~Å;·+ιO±ÎcŠq¼§XÇïç¸þ¥^8V±„ÕÛœ`>(ù §Ý÷”*i¢ýådÿ_+³;IFYIžñJôdÈàÉT‚'[ñžÅy*ãu)Ú‹€õ®ˆ'/Ñ‹pð| <è<Ï_h¾Ûê&¾åðáq;(ó@1Ï„;³~$Jš ~d›Ù«äʼnJð›°**À@0àô
+PHÌ9ÄœEÌ“}èO?üµð/¿QŠ_ÆÂµœEgKO ËF+—k+:w%—KƒµÃÖF.Ú(\/¶Q¶u`;ϰ-®D¸µþ°M·ÜT\~àvPBÌsˆy&1O Â_?¼æf4`‹‡9†‹V§AZM.ºð£?ÖP“Ãpxøs {ÈEÆz–êÑ3ÈïÏñr [d÷§àïªç¨!måÄ\@̳p}jƒ”Ϋ)ðšÚ$C‡Ý7¦XÓlàaüX‡ëñ£?X†6N`àÂÝLM6s°6“‹Íôæ¦U|RM»y†žØðSp‰w®+§TQç"Í¡|™ÄÌÕ«^3uK a·A?À ¹XWY<Á£Ôe0þþ":„ÛÈÇò1Ì%7ÌÀ¦é‡ p£;q|'õØq¥…žØöøL~xÔ¹”|çÑÂÓ‰9c5ü=’‘Æmˆp>
+¶‚í‘Åûqð„î/âO=äärò, êwéÓ}øq€Kù
+~±O
+y‰À.þ…¡¯!˜M÷ÐwÿAÏÜQ‰þ¡jÝ@Ç|ν†:¹Šª+¥OQ8¿ç|‚¢»Œ»HÓü–â§I~E?"ˆsp‡Ïè×hºë¸Bå„p;ÄC¤-ô>ÜU”n¢3o ï>×$}†ž¹ª|ýQúX5º€Î=¯:ì7j öÀ~ö{°¿û›°=Hj¶“¢=¤k?
+óUxŽ“Â3z]WÀ]R¢tè+päk>ü\P\fýFi¤3®Ò[G¥P'Ø^„ÿuz›|¼…Â:êzö:~üùCüE©Õ0-³¥{/ŽÑJ'iÙ : ÓŒ‡Aøó ¸ï߯¸E¶¤Ÿ+Z§d$†üŸˆý,¢˜ƒß%ض㷋–\D›ÔêôåK¨ÉèË”à!ØAüøž6“­Ÿ]Täyï«xs‚cuåù9ú/Ÿ¡¯pÞ#­ôN¤õŽÈÀ[f|·a
+Gböó±]Šm;V]Øöa;ˆíl/Ãn«vãÇSø±‹º<Φ7vÀ²·#‚­dr ´E·Aè+|2»ðòþ‘;ú1Ø7bÛŠítìf‚ÙØ.Àv#ÊŽíØ^ˆí
+êrz³™SÓLõ›9ñÍð‡q,ŠøãǬƒ`#£v£¿—ÿ÷pFØH±oÅ~:ö§b&'2ŽB".ƒ£
+Ž8¼p°Ù@w¬àÔtÒ±kµ„ªÔÓuÔ£–jéö §.¨¿‚»€3ÂHÝÃxßÂU3
+DeË=G‹£‹´(º\¾˜jycÜòÄøåŽm+¶U5qÝrÆ È?¤êø½²'¼¬ª„ªL8®€›ª4ÜÓ^ø¶°zô¥¿íþJÖÄßKXkñ'€/S¼üIF-6Xå3¤Ék˜,!K.Ãl-HÌWMb±œ‰óåHªQu’Oö¤zU&¯Ðüä.UûUfR©q¯JL/«ØtBE¦Àp |©'áú6¼½\³pÅ-õÀÏõïãÿ^~w[¢å6ä2™UcJ‘Ó”®jÓTÙM3TeÊÕ|S¡*Ìå*7W«ÌìU©¹VÅã–«hÜjͳô©À²Uù–½šk}Es¬o*Ïú!¸îð=¤pmÌ`­cmázk
+‹Må–I*µNS‰5[ÅÖ¹*²«ÐZ©‚—òSš;¾IyãW)7urR¿£Ù©O+Ûö’fÙÞÐLÛ9pÜá{HCð÷ÁÙÁ
+ØÌúU |ÀÅwä“*ñ¥_ÊñµÔ– ’T“ŠRST˜:A©“575Ksl¹Ê³*×V®DÐì´EÊž°T³ÒÛ5#}­²2˜.»5-cD™Ç55ã,øß! ¿Îö°4`õò$“|ÿÓeÔä}Çño…Jx  b I€ðä5AI*‘ˆˆà;–ùœ¢ÎVÛYKífµn¾”Vúb»›UQ«óÚy­UÛÎÎÛº›uÕ¶»]ï¶¹—ž]·›×énÝÖ9ö d½÷½'yÂóûþ^ÿÏïÛÀýz|©Á—*|­ÌW…%Ye–ty-Y*±ä©ÈR O®GîÜr¹òjU˜×(§µEk‡
+&-V¾­_vÛÙl»4É~PVû÷•g¿~«<ÛmYm£ÚÇj8! :œh0 LõÜ«æ·Jü)çÿJA±Í Í(·Í¤B[Ž6p©ÀîU¾½JöüÙò§kRA»¬Ž…Ês¬R®ã~Yœ;•ã|Vfç)e;ßוí¸%³ã?ÚÇ
+øzXûÚ]ô ›p €îUò[(Áp9&ÊéHQ#]vG¶lN«¬N(V^¡O¹ˆS‹kšr\m2»{”í^¡,Ï&ezvÈäyFž“à¢Ò=¿V†û¯2¹ïhöï…¯‹•oh*¢ÿ¸ÖÉøSJøÝœ|ÎwÇÉæI’ÕcTn‘I–"‹rŠòe.r+»¸\YŵÊ, ÊTÒªŒ’ùÊðö)Ý»QiÞoÊè'”ê}üÜ”±ä ­ÅöRøæSp’ Àµø@)>”߬È8Ki‚Ì¥©Ê*ËPf™Y¦²IÊ(/Tz¹T) ±j¬hQjE'èUŠo@ɾA%ù†”è;ªÄŠs2T\åú©’*>W?öa;‚íæR©ÔƒÉ|ǤJ€ pÏŠ|³THÙ¾x™&')½Ê¨´ªLiØÔªP¤”*Ÿ’«ýJªnRbuD†êÅšX³Fw×lU|õ^ÅU¿¨ Õ¯+|þDñU·µûݬ¼mØo  ø¸W
+<ÀÉw›íƒLÈF¾™jâ”V—¨ª’üJô›eðÛ”àwi¢¿LñþÅùƒš@ÀØ£+¢,€Xð#ü,ç~o?Ûcý-õb¿“Øfªä6WBQ9(.îåÃW -üõRZ N)S 24¦êî`ºâ¢‡Xƒ$HÓ)ZBh«B6„€ ¡)B›
+<|.¨¡HF3ńٴF4P(N†iÅOç;J˜N3‡X3ÃÛ.ká¡–&€˜lAĵ )š³0‚(Ìö4ýà–æÃ1‹ø¦
+Á‘­Òa:tüPu¸V·ƒŸrŸ4ò›°%Æû-°ì‹ñ|Éé4.ÎF
+ô4æGªÖ5tÌ»jÂÒLý?~„ʹˆòy -óJò<Cs?ÎÒD¯¢P@“¾LÜÖó:Û ’t k#DwÆÃQ}‚;1î?ƒëà¹ø™Rà1‡<eد%íSueô2~œÆS(­“X'P–Ç84Fðã~¼ˆßÓ6ÚfÐs0>Cá†ÑxЙOEµØö‚ÛðÞã±—öh­4¼7ÓjvP„}¶ëhÅ©øß„ÿ3ô,jñiÔÞ0Úî)á“(Ò'´
+ÛØÞL5¶ÓÒ{ðä #uŠÌ¼M›ß`”ïpEW€íàãhë€óàT¬ÕŸW<~§`;Û¹Øv0‚Å´i%ëômüx}¹ %ùêrǘªí¢ as9ÇÆjÆj=ÇÈ7ø{L`e º¬RÒ5ã:ú%.ÇZ;ÚöÏÅÆ}÷ØQ`ÀO#6³€ÕZˆm/®¤ÚuØž‚í{°ÝŒíVlE°Ý¥ø±º è«TeÇê Ÿöе/àÙi²ôŽVÒ›«àŽbõXŽø÷öØ1¸™\ÜG.¾®tìš±k.l{±]‰íZŽÐ쇰ÆV+¶#é]LÊb¼ø
+YÇä<H×îäÎ0ÞgšÞ×Á?ø>:†ã1~6âžÍktv Ø5bׄE ÈgÜØ÷ª?–áÇRüX¢ aì¶2)sèÒnþ–áM?Ó³‰ŽÙ®:e•Ž05&9¢ÏÀ(÷F©ýøñ»•ãdè}¼{¹·”\,æ¼XH. &=ôFóÚóñc~tªŽ
+Û§áÜÐ:ÛÐ\G8N
+6
+CÜ÷C7Ç]'±´c«aV«£DY»]-v§ÒöJ¥ì~%íuJØ£j²§w´)æèUÔ±@ Ž1EÊ×(llV±G!ã~Õ*h<§ãG
+W¹^‡kú›Ð[í›KÍBzøßN<YÞµK3±¤œ…JV% ‡â†K1çF#¨#¢ˆÑ¤°3£zg·êœC
+¹–(èZ¥÷çpïVµû>UyŽÉç9-¯ç²¼îßÃuùÜ7µ ;Ð\MZLÊ3 ÿ¹v@–giÞ%‰§‰r1OÝ5¸m
+» Õ»+rW«Ö]§ 'ªO³òWäT]¹HU•+äóÞ©Jï.UøØq}Èíû¶\¾Kry§áïr{oj'“蓎@.Èpmƒ4ñ$x#–FÊEˆ¥Þ[¨×ª ×¡€×¥j¯WU¾ Dä«JÊ[Õ¦Êê~UTÈãŸÛ¿A®Àçå |EFàpJå‹r®ÈáÿPåþOtkÑ#ÝZý!ú€kRÄç]„‰'DÙü~‹ªvy†*ªøå©©—»&.W0#g°GÎÚ…2j—«<4)Gh§ì¡/Ãò…žRYè¼–­öcm¦þÕè݆]‚hƒ44O#ïÂ"ž¨âYe¨PžP™\u9ëÜrÖWɨ¯Uy}£á4tÊž'[d©Ê"kdlUidJ%‘#²DNÀ *Žü\Åád ¤Ô¿½EسAè‚,$!†=ˆð®‚P •<wGÌr6”¨¼Ñ.GÔ){´R¶hÊ¢aY£ •ÆÚT%¶Hű•*ŠoRaü^™ã‡dŠ?¡‚Ø*ëû*ˆÎèvtÆ#ì ¤œýÐihâY#Ô£Y ~î}<÷`áœ1³Êã%²%m²& •$=²$«Uœ¬SQ2&sªE¦T
+Rø«^³³Ò¼›{õÔ7ä;IÆ”|f´–º—Rïü(ã
+l‚3AþŸ4ËÞlQi‹MŇ̗
+²³ ™ÁÊh6x¼VLk+yt+^³oÓJf%—Îb2¯
+Ö3ë¶oíc”e}~–Œzõ‡ÊÑÎ.hE7Á5LØÚ\=5Ð-C·h,¯I5$sÔëo%{sIߌËF¯Mı…8¶17v0&;XÔÛØíTºàï¢ð¶½ÒVæÄæåfÖç¦ÔßÏô¤H3׆Aæ
+ñk”yRÚ{žòÓ꣯³ŒsŒ"!Úì¥ÍíµRÜt{^sk^ën¸h
+Æ‚zóÉ8K!LŽt?Iñ!ÒÃÄq”8fe‘ep¢ò#Tx÷p˜lþöªC¯©ƒþN.a0ÆU´ÙX‡þRÁÖ|ûîÉëíÏë¼e€ø>oHº–Ô]Ø)aþ0K$æÇ›“¬—S¬•gX'Ï0ÙŸ¦“Ÿ¢qßÚ%=y„2¬ãçÕ²œ1@Û¶k#ãÏVR°+¯{ ¯ù@^ëëy3òxޔͺT'áé¼Y{.o¿?$Ž ôÇ%ýKŒÉE&Üúâ<{Å‹ÌÍsì™gŸWí Ú´mlé¦}y³õ`¾}ò{ÞÍš¢çó:Lmý`VKs†‘%OÊ,ý~¿€·ˆcš½cšú·l:Wè‹ß ú{Å›4ì“d÷ºÅ§pþƒ·úOó/yÉþCú/s ü/dì"oGñGçÛ,’ßá~®à§~ͤy“IúKÚëL–WéÀW°ã^/ë¡}_%Ô¿é,jgàœ€'à¿èÞ€ üUf½§yw?ó6sZ)ý
+—ð:â§Äñ2q¼„¹„Óº€{‘Esx–ÉûqœÁ­~—Éó&̳ÿg¹ÌcÛ¼Ë8þõmÇ~í×v|Åñ;Ž׉8i§¹ê´Mš£é‘¶ëµµ¬´)ëF‡=¦±C¨¨Ý Ó$@ •c“ÇL †´ILHC‰Cô©ˆCêÚÁ„Ð$PøäPõéë÷}ó~Ÿç÷{žßï÷<$ÈËé%<ù¦þÈt~´nû+ký|>Äî¸ ¿Ýœ¾7å¡3 0†(_fôѯ¢=„öÚM´gP^ <ûÐ?ˆþQRã$úgH“GH—‹X†ô¹¡xú< rõ/0Òç°{ >OÃß°y Þ†×áûë!·ã»Ý$ð=‡~7óVÃÿ!Rd ÝíŒcºsŒe Ýe:Ê#hŸ$…Ïêª>‰+xñyÒëKÌÊ+Dègtž·á°Š—«ÌØ*s½‘B?ØLm* ô×Òу®_×éu¯ÑÛ]Sµ2º5t·âÿ¨ž¤›ü,Ýßôv“#WÈ‘Ëøq?>­2ãSü{Rý#Œâaø¼ÞÕCdÚCØþ¬À/6Óük›Ëà*< 3—™‹‹ô˜ÉÏ‹J³\;Ñ-£[Cw‘6Ð@wÍY4Ù:0 G‰ÈÇtš7òå)T 2÷ÅdÔ ýV×9ßÝûµÍ-hm[¸°¹=c¬0g‰ÉÅ!´ËX¨¢=ÀˆGÐG¿‰æ š‹x°Ìl'[Ïp¼<ÊÝSxö<üÙó:GÏŸá#~¯®Ã)Ê|¯{Ã6Û¾o 'äàhò¢€VtaEÐ.“‰UôÐF ½&™1KT–ðä0{Šl9O¤.óô:¾H½¿æøÿ€ëê:7°ÅŽ¥µ­ñãëv9Vá ì—]7Šº&DÐnƒ,úÍãÇnõ¢_G½q´§É9þßGÇ5Å,NÁ 2iœxŒù1öŠ1vœ1v¥q|X‹ÿc3g9ò޳¥-s]‚yŽãÝlû³äÅ.µ íG;Qô“ègYl”±QÅÆ
+6Ž`o‘Rc%ñ A÷=øRæ] _ºL‹:M»ò~·ò†O9_@Y_X_\i#¥”‘W»QVÒèW›¿¡¸Z1ÿ’¢cŠÎ)l^QÈ|NAóð#™_Á_ üK§±q{sQrr¸P後/EÞuâK²!›ÒA§R¦Gí¦¡dÀTÂŒ(n&3;5KŠkj Ž(l*ZT0tDfhEfø’üágᦌðk2BoÉzë?u
+°7K©3A)<}Pᾄ/¼ë€ þ´·ZÕ¶+r*ò*ò«5R8W(œQ0Ü¥`kÌÖ­
+D¦äÌË=$#zZ¾ØcòÆ®ª%öUy¢ß“;ú&Ü’'rW÷£¿{;iÇ`R¸Êµ |Éñ.íø“ˆZ‹Ú‰ÚŽzŒ2cAbQùcíòÇó2âeùuØ&o;KÛ~yÚNÊ•¼ gò9’_–½í;²µý\¶ÄŸdOÜÑ1ô±×¤5j@*P„<þdy——6ˆÅ-ŠÄ­
+%ì2“.ù“>í¦|íayÛjiÏÈ“*ʪʕ‘3Ý”=½$[愬™‡eÉpf(HÓiúÍÔàŽcc{S”àÃPƒ2(ÿ:xž†$¾Ä!š´(œ´*Øn—?í’/ãUKÖ/w6$gGLŽŽ”ì²æ*²äè)r“ „F%Oƒ’§9Ès*ä© r¯
+AÞhߌ‚M-]N9K-²u³Éma©°À*$MG+],ljèjùž@ÓÔÃiÜCeP¡–®P où)àCù®Ðê$PƒnèÄNÏS€÷!ž6Jô@e«œ‡¬½4U6ó]?MÅS'hu>ªÓ[Õéwë4quš˜:Õ@z¾ŸJ¥Ÿ†¤Ê±†µ{š-´hAz Äï<Ï2ØLrr®YÆ~û´°ÎšEÖ~ÖÚAœ!–aüh„66˜“×@¬A<ã0Ç€fq„&mø&Ð ýŽឦ ]ú` ta/Çç)®qîà ÇÄ®Q“<HrÈ2Ä?B;ê¶q¸Ž3Ädйh’Mæ¢Éä5ù°I<š4”Ûɉíçø¨Iš´ š£ñwáž&ÛV¨B{Æ›ášè!©‰]»îaÉÞÀö6˜€)hâÇüØéÞ8ðwû7¾6òb, ðŒxÌïe’säånÓYúÝ™w¤]w4ÊøêÕ*a7Ç5I{áS“ñØu“ÞÖÉ5›@ŠQ4¡L±)tö—½Äe?~,ãÇabrÊaòâ0sqÇÍq8S¥$'–ÉˬÏ}¿„¿j¸Nk»®)ÆeÌA>óa×E{HY´a“¥Na·V䡇6 ¿ÿ3]®ÁQÕg6{ËîžÝì9»›½d7›l²’˜²„[ˆ Pb F
+*Ãe(P*ˆS­S
+J‘Ò/PÊ¥j-2È Ê´:ÓÚ±jõ 3öú¥H«m-Óéø¡ÛßÉžét˜‡äœÍ¾Ïûoÿ÷¡ýµ’el?VáÇ~¬å‚YÇpYKb×r 5ã c̉ջø»c,NÔÑÕÏY{àÄÏ&ΜâÌ&¡38¯Ïæ]ìp~ÙᣴX¢±á,¿kÀ:gÞHL6³˜}?¶Ñ¯Ò'ÒÔÛ ðv ?€±m÷I[ÉÇýÌË-¨š¯¿¥~x;g’rœ!Öq68¯w©sΕΒmó­QQ]·Ú˨#r–s[ ì 7»é—Çðc?}²&{Œ€ï‡€¹‡Ïî—v±ÍïdVí¸¬ÉÄ»H˜²Œ³Ø"òÏ8ñÜíœs­ÃIKiø&xÄA;Á.GÙâd¿#’#¶h¡^Ÿe~œ WN0?H“ç°ÇHæ3î(ýqe3~VípçáNÀÁEïj'®[œóÙœ;ÀnGˆØ<¶$”¬êضH9®ªX< žWU¸Ò~ºH<^£W^ef\¢.‹ ÷euîqDæIàNÁ…Û¿Á‰ïÃï^pÐá³¹ŽýÏU‰6×Kà<`ìˆ,®D$ô+ð5ò>õñµñ>ïÉ¿JŸ¾Kbß&¿>Âf-ý |
+þþ‰¶º…Îü”ÝùïìïOl¿ÝúÛûïØð¯£.> I®Q¬¿Aa½M¡¾…2zõ÷ øüó2zâ’àÖQÜ{uõºÎ¢k~Šž8Ž‚Ãà p Þ¿‚àÏà÷àºz]uq-óÛù›lð¯£$.ãÇ%ü¸Èî~Åuž¦9Gñže‡m÷~<GÁž¡HOQ0'HÜqþožÂûqtæx€=à;àÆÿxÅy¤ŸƒWÐVÐwçÑ3çÐv/¡!^D-<‡š8~8‰Ú9ÁÀ:ΰz–Aõ4
+ö)šwœ&ú>~|?žDë~—bz‚":À©÷é'¤ö—ð~BiUhŸ
+^VˆXe‚û]ðš“ÚSNÚŸ&ãhÝq4æ8Zæj%vآljÇ~†ö>ö^çôån†×.×NÝC)­¦u¾†Ž}€vÝ×!¸~D‰_Òvý·<¬ÐâmvÙ\p¸Ç²ß;Ñvâd`ÁIЈÝ"v;±;»eZu&v‡°;»#”óbl/£"VÒÎc(¼Íp<¤ 4ïz"µŽŒ­%³kЙcp¯_/;muÐi÷Gœ–Ü:ÑžµØŠ
+®”š]9x
+ðLÂf·]eå\s”u¨Áu§2®U`‹Ò®tøð<xüÜÒ}FuaÝ⪈V¥BÏí¼/|)n5…|j
+”÷jôEÔè5•õ&ÔàÍ(ã̓¢ÒÞN¥¼}Jú¨ß|°T ÿ½Šû7+æT–ÿ°,ßYÞ˲<×e¹ÿ¦{áXß|xgqÍ÷ƒÉ ”xW
+* +ˆ*°TH*È*((lW,8EVp†¬Ð<™¡%ŠLc³"Æ£à°Â¡3àU…ƒï›Z‰ýEð Ûò„5´ÜÚxná}dñ#­QªÎ£úˆ_‰H@ñpH1#,ˈ‚¸L##3ܤ(B¬1‰ÌT82,‘Q7ªPÝ&ë¾­@Ý“à´j#—ÀUù#7´Ž/À7ï4äIhEÐÌ»Ÿe@Ò¬QÂô*õËŠdÖpE©³ަ@£Œht)d–4oWÀ\¨Zënù­ òYß’×:(uRnópE5æ -Ãþ|ƒ¬9ýH“.Ð
+<7ò¾¤ð%a¹³¼²L¿¢f@ufPa3$Ê(d™
+Z b   ÚX‡üñ>yãƒò$î;±B5‰ur%X|Ü –±øE€ÎŠ}¤¥ṗs&ëo/èEÐÌ»,HãK}Ü¥xÌ#+æ“iÕªDbñ ‚‰°jë£òÕÇä­OË“ÌËlSMªG®;lq’fú¦¹Ò܆i–ßÔ €I|HÞÔbx†SÈGdQh- Ï»|Iñy"Q£XÂ+3îSD~õµ
+&ƒò§ y2Õ4Xr5ðÇ 9œg—Ï¢)²ˆ¥K{¡caÍq+æX³ÜÔY–φ«$ú/ú"\C¬Þedj(fžsø‘I>'ݲê=Š&=Š
+æÜòç½r73D ÓV£:HJW §ÛØãÛÐxmˆÇ¶A€ÖlcŸoC•¸%Kˆ"›K+>´|¢¹pNç+=`hi¤y×€)üˆóÓÞ…ÿË~™F5}faüX5 àZÜëZ«"ˆ
+nUƱ®3hÝ:£ÕŠ­Z+neœ¶J;š=ˆ²HYB6BIH€@€°„PÙ !Ѻ/ÓÅNíT;Õ¶ÏK2çÌx:Ûɇù0pÎ=!üïóüÞû¾ï½ø?}fÀé^”÷LÌR³pÐÏ†Ž¹`@Vr¸¡€ñ…@¬GæÝ ôòAÞw"0<¢ŸŸ‡A!
+€ÔYð;¯ãày8ûÂï0ä´Ï_õw97º= Ec‰g»@Ò€î“=Ð Ñбõ¹¿QøR$Ή ´{P»1¨îj§æÃëÜù`€× ð<z ö
+€š¢F¤Ø³Ù¨ )êSÃ"ÌŒ ¨tNÃT—ª¤f`Ç#÷Hä¦Ï8Î00â˜;çIw>Bà΃í…VÝ•‹ ‰Y”k`Õˆö¢°õ(,7U
+UØ+Ø'F|`—€…
+ ^›EMBîÑðÍ@n/âù›+iúQ¾ýÃ'B/ p¼aT‚/DŽ;—¡C!ÈàXލBÔ"È0‹cˆr¢>ºQ8ô8/l0ÝŠûÃMÄÐ?þxy~nÈ0áë7|ä¨ÑcüÇŽ›0qÒä)S§MŸùâ¬ÙsæÍ^¸(dñ’¥Ë^^±2lÕ/W¯Y»nýÆ_ýzÓæW·lÛ¾ãµßîܵ{Ïï"¢ÞˆÞûÖ¾ýo|çÐá#G½ûû?¼÷þ§â>üãé3L6‡ËÄŸ=w>1)åBêÇé™Y"±D–-ÏÉUæ©5ùڂ¢âCi¹±¢²ÚTSûÉź†Æ¦f‹µµÝfïpvv]êé½|åê§×nܼuçî½ûŸ}þÅ—_ýùëoþòí£ïþúý“~üOüÿŸû÷D%–}ày1ý<\'¶_ ¾gÀøKÄù<b}¼‡óˉû_Àþ+Äÿ
+§°zûÌÞ»»Ÿ}Ã_>xðŠä뇿ïo=züø;xÿþÉ“§Oðßk¡M¼×ê·ü¾uDT_“_²¥ba~CÙ–sñªJÝÉ:CÏR¬àÚ ³¹Z ¯G#â÷©³â¯æe&\Wfœ¿©HK¾ãi hÐ0 a@À† 4ü·Ñ wÛ|—Ýk…†fßdKe°¦¡üUhØ_ݯAųès¹ö94H¡AÌïSe ®*…g¯) A–žxËÓ€† · án›_To‹O²¥*HSoÜTb.ÙW]¡û Þ âZŠ A ùР†%4(„ñײ3nˆ¡ÃÓ膆ëí>Ën·ûEõ´ø$YLó !Ü`*Ù_mÔ¬/Qs­…Р… 4ä‰y—"þy–àSIfüuaƹžÑÐgc,»ióê¶0’,5šºŠpCuñ~S9Ñ riȇ•”Û+á]–‹xW¤Yü«"¡àZ:êÂÓ¸d¥Mèµ3–^µûDv æ9ùŸ7•V0•éN6CC4¨åœN¥ŒÛ#áöÊÄÜ>1tdBG*êÂÓè²ÒÆ÷@C¯Ã'Òie$5›çhÍÆÍ¥Åo×t§ôРÍåØUrŽS!ã\Ê–rz$N¯:Ò #{ÄÓ脆îÆ’n#¢ÃÊHi¬ Кʷ”õkJ´§
+¡A“˱)sØNy6»K"cwgIÙ=éÐq:E‚+ž†³•6¾ ºœŒ›…žZg
+ÔU•o-/Ó4CƒTРÈa;¤rV§(›Õ•©Ð‘„u9'æ_ö4­ôqÆb§“Ùf¡¥Õš‚ *ʶ—ô‡Ì…Ú¸Æ|5Ç¢T²Û³,»8‡åÈ„Ž¡#Eƾt^Ê鎗ò{<Ž6úX‡“jwУ-VZ†©zQQYÙkF}QL­V× ††œ<V›Dɲ ÌŽ´¦#EÎr&ÊÙg³9]|ï’§ak§ûÛŒ%mú[ - ‘±j©¾¤tg…¶èp­Z× Ô°›e*–5+Ù–¦d¶§ä2í‰
+VG‚‚íäp\9Ïéi´ÙèÏ·9éË[ôæ¦á2CeX‰Ö°»RUt¤6· ®N®e5Š4Ì–t5Ó’¢b¶&‚I‚’Õ.Àúps¹6v.ÏîiXm´q-NzX½ƒS^ï¯Ð×”†ˆJ¹þ¨YVwQ¢cÕgh™òY͉VK‚šm x*Ž•­â¶2óøG“6±¾“¾ºÆA?^T÷‚RaÜX*+ª5‰Šâj……¬‹©¬º$«!AËnh9¼|N'ŸÛÄÒð›ÏhG]ǰ©æ.Ú†r'ý=UûØt±i½.³4ºâ'öë$¸­ú€ãøl'Nb;niPÚà
+˱lpËûð’WDLÒ|rÊÍáV\„`Ɖ‰çÈмVÖêmÓ°iØ4l6 ÿ†m†Î{†Pdžºg0|ÇûÖ0þ­aÚÍåV]Ö‰ç¡F!ʺ[KnÛ¹–lï¼{ž`¬¿¥²¦?Ã)`èŠo”èh@†½l‚îǧÜ"¢êäsf\Þ¼-R¸tÉŒ)êíN²¹m-¹³ónü‘ëá=Àð
+•5¿çeŒ'#1=0h±tH…ŽùhÉ#EËîA¬âìÇg"²F ¸‹V®hÅ•­èIÅb½mî0;;×6 ‘ŸÃËö¬ùÀð7`ø2>¬ÇÒA :æS¢ã´vI±‡¯QÄ¢UÌ]1‹„W Bé% O±Xo·™æ¶ÛL[çÄn`ø «`ËQ/c×ñˆÍµhÞ«F'ÜJ´â”cs”_´‘+æ!Þ£DtU3(½¤Ë—êí³µívjÃððáÕáÇYyÛ`xN
+¦Çª°ž¨»ÚhC˵ÂÖ}×ò;Þ¼–kaOÙFƒ¯º#£}š±ƒ‰‚óT”YØ¥A {\,|4É1Áe¼¼W؈×ÝÜXÃŽ•RÓϾ*m;øÏBóÅRôq }¦ýŠZ鳌ÉÙÑ9 œ‚bIlì¼ØV¨ÈµB%Ò—0 2Ž˜ÑqÈŒOÔ[u¬aûÜDãÓµé¦7æ&›¾(¥Ú-ÞÈk~«ïÃýiRëîN©œPF
+ývhT@Ac<
+Ê“6¸€ƒ;BÁ?[±"ÛŠ×ÝT¡a[¥ÜødµÚôúädcW(¾Çi  i}EUžóq%Ý”»¡Ô pP–ç€s¤Îávdµ£c…ŽA>ÖGùz›(6lœjÜ;^i<”)7²m©}UðÜÿɰÌw>&õtÇ%4”»a†ï‚Sœ&œHs¢ĉe!–e;ˆl¯ƒÌÕ[±Ô°¥0ݸ/Wi<ê™kêQ•ž– %ߤCüç£ý¾®X¿Ž <p‚C#I‚FÌ2ˆc`7žb»‰TŸ‹Lõº8u—›xp{¦Òðlh¾ñ=ÝW-—Ôöà‚‰ß ñrïë¹É“wä͉ôúð;ˆá0@"l?:ÜçÇ¢àëHéö‘±./§î˜òí#3>o¾Ñü¶üÆ®O„W÷}Ž/€áê>{âcI_ᜲkôsíÅ,Ëp!Ýmú,Õg9Ï@ÖOØv.‰Rg“u6AØÏ$Ⱥ[µ5¯‡éX÷=õÚšëWǖͯðsòé´ül€‘w‡rl$&ÅÑ"’Ù—;$øù
+¨e;©Ž²5+ƒ»j¦K×´4Å’’ª^S5óx{z¶µ~q_=°a8ˆÞ £ie‡/©éÅ´„xX§ýj(ïQ@Ó¨š³‹áʨ€V³ò«f÷ =[rSÉPÔ¤tuE‚hÊx{~¶õñ {¯»¶/¼ÉÍ©0ƒêª/©½Šé‡â!=”ñk¡¼[Í8åPÙ&…­ÚªYÌü âÝV $7¥yMÄRU„LMoþgn`†#˜á#4­n÷%uŸ…b†DÈ@ÉøtPÁ­f*haTI­™åÈš^ʸ¥’°þ,—Šo‰„òe>OUåqÕ xà -"ßÝ÷8¸çøºgÿƒ3h.ù’†ÎPÌÔ—™€ŒÏMºôЬ] U,*¸¦WPWÕ2ÚM©”~K(ãÝÈd+\‘²Ê¨+x{ÙòÄÚ^o_=ŒN£iÝoÒØŠYz!ËpÆg†'ئ³b£
+ZÒ(¡e™^Êik{£¬°dÊE–DUÁÛ†áaä…}B»Ž­{_k_¶ý”—ÓœB3†sÞ”åJ(6úy<d#dÜØž²À’I .h5`U¡—ÄJ¨ÆUPW˜
+]É_f¨•‹t¥ª‚·Ñ--¢Ïï}Æ ¾¶×l?áå´mhÆtÆ›²]ÆÑk1¿m(ë´@SV#8«×Sæ•ZÊ‚DC©òÕà" » ¢Bj°Š]ƒÕâ*¬QTðv?ÚÚr?¶mïÃÈÎcëþ´×oðÇõ¿G³#'=cî3þ°«#î² åF-à¤ÑD™Q€’LÌ u@™ƒY°BÕP« †Q4¼
+ -àí^¬uó½øÖ½÷£/[|¯½æ<Ä7ÿÆ‘sp%'ôZÒf#ä,ØÆÕš¢ÂDžɳ\ÌÂÔSæ8ëà2 CÊÃ:v™¤ãáîëDËæoßÙs/þÒ{롽íK®ý‚¼ãmÛxô¨Ãïû8dG»R!k¡L¨¬ä‚ÄBžâ›ÉE¶ ˜¦)3Tì}@ì¯Z‰d`”ˆîî&›7ÿ+õÜžõä‹ï}}¥}Éó}áTäUK8ü®Ûî=1£Ÿ% vBFcr²Qò„p„œçXÓL"Êl‹ *’ÍÔ"ÉDŸ&š¸[K76•Ù²ûŸémï~Ü~y5µ••Lìu?ô=ç£Z×ç µ“8¦°‘ Èò0 k§cw¡Ž€y‹b…ód+µ@²"¢•Ž»¥LCÓ‰–]î¿g·^š)5Ahì€Ýø]@å½Q¸»ã2””8ߌ±”4ÃNÉÐì`¶YÈe)68G¶Qs$’#Žâ¯œmh\œlÚùåtËÛÕró™èB3lH¿a”ûO$Þ+a‘çFTè&Ä.J‚ƒR’L,Ä ¦¨N(;¡1Ð QÔ1²ƒ–v iÒ·¨4Þ°inºé¥ò|óÑÔbó¹‘Õ-dYñÇ~ú·FN좃ºîaûƒ/aºÁÝÆi.(»àˆÂI
+JM(-9Œ"IJO¿EÅ|Ã3ÅÙ¦íÙJÓ/i=«¸½½‡½ô#)}ȧÎÈál‡ë1S#ä8ÁNŒ’œ„Œ
+&dp$&¢Å#|$b32A:3ç‡9>
+¿à%‹¦<$iÑCPM»‡ôÓöAcqdÐ<‰§ºk[cÝœûÔGÞÄ ¿ê«ÈÛ¤Yѧ®´¤Û—’‚ 9J©‰°6ä"Y?“1ᣱ&=·è„3.’tÖET•ì]ÉJ0L ¦)<Õ݆˜áuÌp¤í®á—} ñ'®´ôº/% Æ”hT'Ãbj&À§ûØHÁCgÝ0g¥æœdé¼sX]¶’´sF’aFG2Má©îÞÚP÷`3Œn»c8Þ¿ ?)ËJ®¸Òò._JÑŒ«È±ˆN…¤pÖ/¤æ½\Ú”›IŸAi¬9Ä_°S¤Õ²ªb$kçµdýŒŠlœÂSݳax3¼öÄ`<Ö_Vœ”夗]iÅ5_JÕûovç+¸éÃ€ãøµ 5R!¬äÚƒBÒ¦nñu\¹#ô.Ò@33lpŒ1–%Y²þKÛÖ´¬½÷´öÖ_Ë[¶lË6˜$„Ù4é¥ Žö_1®}Eo}ÈÃ÷å÷ð»O(®ìˆE”"9_72èáÓò®NƘƒÉž°Ñx%+,™ÑCò’Tå ¶ õ#•„yž{hXûÈ`{ë‰a¿4'ùÄ–7øPeK(®"ÇÂ*$Ã}ÞxØ-B
+N½hã°J&.¹ªFä%9¢*J`mA ëG*é‘ÁûâZÌýó‡Ü]ýŽÇi­;­8ëEUÍ¡˜škàt@ ÷{dðˆKŒŒÙ…´) 9kìä~¦a÷ÌÉ™ò醪(¢k Bš~¤’þgx¥lØ‚»kØN(Ê?åd'Üiåi/ªi
+Å´„xX ¦ýjx W:epÑÚC›1u3¯éD¼r~Ïœ¸K6-ä('ø,ÍeS—¯$̻챡·l°o~lPì•åäÇÜuÕÅôøxXOÍøuð`¯.8”ð¤YN›ÕJ×dbö=ñ5P:Ãã)'»º4c]¾’|kˆ—ŒÛEåû²œâˆ+£ýØ‹êƒ1SK<l¢¤}FdÐ¥ƒ 5<©S 3rrETvp%¼9®T:ËéVL²ê1O;ZIÿ5xÊÇ›¸;Æ­„¢ú=iN…seôÇ<¨ùT0nkŠ…íÄ´Ç ÚtPÁ †ŠJ%4%V@3\í
+Kƾ”wÏ0åŠ)Fzœ.Ò* ó-ù×” k0ç&ÜÓÛÄ¢ö]iŸögÖ|°7å8áOôžú섌Ó™ôШF ŽKÕÀ„@Nqð4]Á˜ÜiD%DªqXª-TÒcÃ굘wCÙðîŽùwÄ ãI¿q·#çÜëÎxƒ½õq—˜µš¡AÌ+ô@¡[ Œq5@‘©†&}T±'©já UŒjM¡’0ÿ«°Àª5˜o} æz wÇòÒ¤õ×âÛ[ð{4p àvŸKXí¤¬Ñ¨LÀ°Ä@Í ôÔQޏL×Bc§j˜ãM×8Y+#ëU…JÂÏVaÁ•k0ÿº¬·wÛVC*yª…C×Mhômg¯ïpÈêjL줌Ö
+öÉ,Ô‘‰:Ä5#,§ QPŒvèå?v¤ç =ùJ‚Kª°ÐŠ5Xð§5˜÷UÜŸz…¿’x©³?õ£7´ÓcñèÝM ƒ”RÚ¬ØJíã[€~Ž`˜ÁAÄ &d¸ÃH&Y#$c×0Á$¬¨áÅU"ËWÿ;üÒ[˜ÿeÜ7ÑuæÆCÁä+fsp·Oë­(ÝârW;*q
+ãÑŽîd„,A#$9Z6¤ÂmÚtoJ‡Zmio«=õ´a–²Áò³˜qS5¦Ý¾ëž|OííîÃíÓ‚S‚aþc†Oò¤0ƒÉ.v8ÎêŒÆhÜxâ'#TQ*LéI‡H²Lˆ¨Ê†ÚtÙ Þœõâ­YÞ–~Ú0˪²aý
+Ìôаm×=ÅćÈÓ“Â!A£)# xÒBª1B 3ã°ãQFg2óR!@˜ RÄÙ`», ªú}¿oîw¶YrÖ6[æiì+çaÖuÏc¦7ª1ÝÖ'†ƒ”R÷ áðœ9#lõ¤D²›úãõÕä}Çq¼N¤Ýܱó‚­ëqZÛÓNÅU»zœuu«k«@ÕzW¼¡ÈEAAP ÷äIž\ÈBx $„ÜóäFî$@BÈ…„„pAEV×Ôº9;¢mý-ʶ¿óÇûÿ×ùœï?ß««»Žíõ°¹½ôú€‹*9ÉMý‚8bÇÉ¢v¬:jÂh"(VVãÐ@ªýß ÝøÂõTòeÁ´ði¢ù4EJtäªÕßLpø„°»avwñk}n§ÏÅä…œ4~¿ÜµÄ1+¾=nÆ*ã(NUã´ý
+<Lµ—ý;såö¬YINÁ´èiBx‰6£æ
+«_ˆsøZ¨îîfFGÀìíäÕœµÜ°®Z)HÜB':ò!¯TãUr‚¦_JDƒ©ôoÎt$ ŸÌÄH¢\$Ò\ˆ„åV¿¨ÆéQÜÝ"Faø]|VÈÁaG­ ^¼j2“Ä#(Q6¢"*r¢*&!i"­$]0Õæ kæ ª¤Aš]0ݺ4!>‚DZòÑ€è¢Õ/®rz[Iž®Vº×-¤÷9Œ~ë`q‡L4Á5Ôz]E–޶“åCmdULDÑD„](Õ€þÀðÂðû¤áOICVátÛ^ÒDÛA$"ÊCâ V[¥Ó+Á{º$°¯SL:šèQ Ÿ1hâ°GõŒ† 5M|CF•^k¥Ê‡Z¨Êx3UA¨ºPªÃi/ è ²¬ÂénÒ„di=‰ÛЬ~i¹Ó+ÃzºdÏ%C6ë@èÃ>k\ËáOÉX¢["†d¬™Þ>ŒÐ•q]i ëB©6gx;iذ¨·% »
+gd9Ä Ù$*ÉMM¾Å//uxU‚Üë”Ãa«Ž›Äôkºfæ¤á×R×2ÙÈ•\o`·Ô×*ëXê(© §0,OƤAÿÂðqÖÙÎÂù.â„<§1*;¬ ÊO[úTů¦ÂãÑà{j8lQƒ¨ ¾.3&…Bö×aËí:AÛ8—/áð W=ÀæhûS ÿgÈÜ
+Zr-½Žs.‡éj¯)ùkjT´A©AdÐGOÐÛ¸7`©p–HGáùܤŒÃM$Õ€é¥!Öo
+…juÔ~X G(ZF”¨­ÀixX­ Z£Eªuò”–ÅiÀúÖ2`y/7eÿäZ{ü~dUÑðÔb‚kà·"UW¶YâÈs‰­e]-Ø¡ò5˜ ?×H 0 ´ M‡((#DDYa<Ê cÑú0EÂÕúÖPªÛ¯æûŠeÀöîz`]·óQ`Õþ±É%y¾©¥$4–)lóîÖ ùÄVîn´b»”ž:3ÕÇ6Óz&ØO3Òý#£ddõ ì
+dwÖàÇ6³¹ñÝMµáÓf_©’é«ÒÂ=8=ÔM4‘<d3Á YpnªÓ Ûj\ [µ‹e¿êbÛ¯8¹Ž
+'?ånu§ÿlÖ»hñ?‹ÖDo.Ì´Ï.ÎVü{ådf]%{j ß⌜ª'^@±Ë-ØèUquäj'­ Ú/Éòò
+mBS )`B€àŒmh!1·Á„Rc£Ó’eY’uß·dIÖ¹Òz%!ë°v¥]iu Æöpd0!L˜–ÌÄ$Í̯ۚ>ð¦? ß÷ÏÓ·mÿýHÓëè¾ozŽíýº÷øÞ¯'ö|%:ùÁWÒS»Ÿ¨Úë 8þ#à^½87Ѐí÷ñ`óSÍÉÓTç”]·=Ær‘{L&‡Êu”ë…~C*ßgNç{­i‚ë%8® Áòdp¦?ƒ3 §£ø¥x¥×׆ Ï\”aheøeh¤ šŸjOœy¤é`~¦î”ÜR²M×OU!ƒKrUœ”h“¡>•˜ÒD¯u”àÚ38ljâ,J0œÆpzÃè—ÑTÍ~¹¸Þ¡{ã&`jm™Óë˜Õµ³îkÎ˦Õt˸Šï«("¤R™(HÕɼX—&ÆQœoAq®ËqœÙË“Í2¹,Îaôh6EeãŒËX=7eð¼NÞ¦ ;63e0´uÌêO°îëÎɧ4]¶«jn ¬ÅHµ"Q«RÄ€vÐߌ帶l–ãÌa,/Ž1 £Gð#‚ÇÑÂŒaõ†žÜ”Áñ>ehi3~Ø1k<Êž1tȧ´±iØPQÓ'5²D^©LRMé±lŸ)‡ñl8Æq(Ë›G¡|šäã „ˆ0#x˜ËÖeX
+%–Ðàh¿>Ÿéµi޳”fÊqf¨Œ°àbˆ5\ð³ÂÉæêéߊyƒsÛ&`Ý`ÎÖÚ1kû{ÆÚ¦˜4ŸvTá¢/û“„AšÉ©åYLª"2B™î5—“=Î+ v°:Ì‚*;\òq`r¨!œ=Ñ\=ï ËÏÚyƒ­éÀœ£¹cÖÑÊšq’OZO:ªæóᢉ/˜ù)Â,Édu²ªPÒbM9É7ÕFz\W¶ÿj¬z{Be7&¹HÞ΋æê ø(ƒÿÿ†­ À¾¯un°é̬³™ußyP>é8â¨Z?­]ñ‚µ'…[E(f”à£jY1)QUããµoh"Àõ^âjN^¨lï…‹Ö^$oæGsõ4o -ÞßÒ€‹28ö´>uí9óȽyh¿lÊuØ^s´‡KöOâ3…;(jçÓzIyD. “A¾sÚÅ÷\·ó5KT1÷ÁE£
+]QIÂ%¥d¸@­•¨§ç [€»±å[ߎöA;.}ï߆w™®…[|•pRBÎ\ÎG;ÓÙA¢š×-œÒÙ¤÷”û¹Þ}S¦ñKUÁªT*Kå0)“!D=
+É×Ó¼áµg†w€oëþ‡¶œø2º¹óoÉMü{Ù Êir³e¬¸Ó‡¢Å¢#ç3P„S
+ ®ü}ãðfïjª×;8Áwy¯ñm±>3tE`—úuÃ…zAÊ
+1*Sï¤öÐ]ÀñÁ¾S¡ûwB?6oí?¬¿†¶>Oo~~´ú¯˜ŠúÕÔZÜJ^]ÍrùrjùªÆÒ[µôâEóÛ›bÌ.¸Aå\¸ÞÔ’wÉͽÆáåÌ xYóbþNºëÞ²8¼ww`î—{-CÃÔàÃpŒlI¤nFœ'¼LÈǬŸ/F>Í-/û,zT‚B¬^ªÍÿ¾“÷›ûO>{¥¾.ë™ù€Vþ~1ã“”v¯…tnYHÞI×½{Bc{C¬·ö„ªßü: >g‚ÏŽPÀ‘¨:}¦$%_z–^´‘UðS~nÞË¢üìeˆÌç…Ϫ‹ÒÖÑÅçÖq%©k„Ò”IeÉ?RË’ž6^L|Ê,OxÒri' î ±ÎîÞ¥^y/ïÿŽ >9Ø
+²R7
+×øE¨UneÃ2Ç^dQDóL†r¦‘Ó9Iå÷Œ‘[M#D©%@P؆ñš~?Vçô£N­ÍéÁØ8 ÆÞoÂö @°A$o á@ ŒÔ (§pMˆ@­ò/6,qkÙ‹¢xŽÕ¤šf°uã¼Þ*d %v½ÂéÇk\CØn׺ÏåÅØ\ý›Ó‚µ9X»£gïh… Ò?…)lÅ„(9
+ˆÒR7D™…k­P«PqÃ¿Š³ÀÅKf9Tõ$«Y?Êàö. íñcˆ(w á5îA¬Þ3€¶x‹ÛŒµº{±6—gsváíŽ`ý× ;òƒèl¤¤lHÓk’lÔª¨ a *ç,ðÑ’Y.I3Á¡÷Œ°8–aº ßO¹‰rÏ
+á¾6q[“Í·—µO¶ÕèG$$‹_ÄpyØ¿"žì!hgTÄîñ6’! !›E f_+ÅâRl.ÅîÖ[ƒbÛðuÂÅ©ã@ø¦36÷….®ü}"~¥'¥ñ®>‹]WØ6£­èSãLÃ
+ªÃ 1¦fñŒ’¢™Su<*šqˆOëóñh—fuqi6g°€ôA±m8ñ%h?ñw =™
+Á?Nû¿Ù­ó·¤<€ãø´ÛèöT3ÍîÎT¶ÏTNóÔÔØ”ŽfšWZi[¢y噡r‰x Š((¨x¡à‰J¹š ¨¥!x$š)¦™æef5­•5M§Ÿýî>ósüûÃû÷×oÄP‰Ê~™Ã¨w8 Jÿ÷W1¯4G£—u'¨‹CžôÙñ0ÆàVЮ#ÕÔ«š%JFWV]Ö
+”œ¼& 7·Ym((C Uÿ5XšB¥ý~¸dgr[7h±õ~¯± }ÑëùhÄ87w*A?åËÕ‡‰ä:’LÚž¤6³4œ^or]ÑÍÄúÚþäªú¾ÔÒ†¦XÑ&jìL/RiY‚¦vCAù©…)TÙþu6‡Ae}ì£Ú
+½¢³ñ{4â8=ç„Ó?p¥kï¢ùòÁéõ¨ÚbÄPÐÂVg(3»éò]¢¼BGi¸¬K”É{h•ŠnzicgªX©eˆTCýÏPý½ T!Ù¡} ·´†ÖƒN¯¯t[¶>59wèìÀ#;’vÞ‰)¿ã‘/)+F š$ev+«•ÕÌÓ¤(ó»’”’.ŠRÖ«PtSj]ÔêÆÎ¤
+eGr©Jc(¨D 5f&P}Àj-~„&s‹wûlŸèÍ&çÌÝn.Y„hžÚÄ)æYÒQtÁ…вüÒE~­£Nkb´r[iWrÔÔa;¥¥¢=¶¹VCjRic.«´ñ—TÚ„j•ÆP E ²&Pó³)4üô´í6_¾±ëàÔÌ.«›K{OkVœW<>D‘Î:±Ew¼òóôa’¬>rç:­–ÙÁlHÖd(ÔÙÍqm‚–˜6Ér[uKT[ý|ks+Au¥•tÙpP.í0™ùVhúaÇëîí»f'¶™ ,í´Õ¬ìñR¼:!]:œ œseåŒûäp‡1"ö@L)ãF¢”¦K•%t³þÛ•YÝ‘×@ìÉ Ú29¾ã¢Û!—‡kZÃÛÔb¨ýÎ.ïúæ£zÛ–…‘¯7.nÞ­YÙá¨ø´Ç[úÊ"BôÄ>žß‘1íÏcŽEäÑG¢‹¨C â¸z)¹?­"êWŠïãWc{5:± £«Dª•ïUÕ„ö¨/
+j¿DAÝ?Œ¡iÛWÏú7lZØð7íÊ·û«;œ¥°Ï[ôú†ÿÌ%š½ˆNJd&ÍDrâ'IYäñø\âhRî6£0r˜-ÄÜʺ6”WªŠCô¥’`}UI¾¾$Pß,1ÔoDAÖ/V:×þe­öņ-ŠÕÍû¥`æ,„žÙoíƒY/ݱ)Ï|ÈÔÇ! 1ÃiÄûÄTܽشˆÙDöù™ιét^È73h’Ÿ0!àûß-Îñ/Ïñ—åúŒ]6(Ö£àê†5ú~c#턉‰üÓ—¦R05Ânûl°:™¾êâCçLyí!½ Áa—1Q˜_ñäsOÉq!)” %5à#Ñ‘ä÷03Ùg!—îý 0å̃âTÏûå©èû2†¡^6£†:×¢4C_Éç¿XW ›L‹à»³ÀÜ: ìœipÂ=<ÑÄU_ßðAçÞþð[T˜ßëXŒÏ+j„÷Jr¤×K&ý‚óx‘‰?ý<—pj¹xrYåþï
+’¡FÚÖ ÚuF5Œ¯1ªx»n]!üýÛL03c‚…y8Z‘áø,œvïcApÖÝBÿéá^€G£äubÏœ‚ï“«É>'>±ýŽäùûsÖõCa€Ëûâ@Ci4kP z#Tù=”‘
+|G§F¼¢1£—©<êSJ^ÊR¬0ý!Y’±@*çÝ'VeÏ.åÜÃÕçÎaù³‘ͳ×
+f»3áý‚éðaÁíÈÛCØÏ÷Ã6bÈ@ ÄÀF LÄÀr†Œp`E½eR°+)©1Ïhœ¤ÇÔÆ"¥0ãAlqæ<¹ŒT•;K¼˜?C¨LãESØáT¤Z8q]41(ÅÞ ân oào‰>dl2.bà"† ÄÀ>c ¼@gàaЫ\Bðï¬XÜK&=î×”Œä%?}*àÎÇ_àÏÅ–æÍ¥‚iÒÅ¢IbhßX<»"¾‹Õ õJF±ƒ’Aü ¸—0(é&~>à ÞöµÀC ×ïëe ÙÎÀC¯fáƒßpcð/Ø´ø§LVÊbJû-?k.A”7_"˜Š©NDËŠÇ£ê%c„Æ’1üÕÒ;¸Ž²;¸þ²AÂͲ^bYgT™†ôù€‹2Cbà!†,OKÈ=ë yçЫ¹Øà7ÙÑøçÜDÊvZêC&3OÏãÏ&L%H„qâ1²¬ä©®|4JYq›ÐZy›Ð-Õû¤½¤>i'’:ºOÚJþ|ÀCÞ> 1d#†Lă¶Ÿ3‚Ñ«ùáÁor‰„çÙ”„ÇÜTæ›Ã›cääM'M$ŠÅc”òÒÑØšŠr]Õ0IU=u­fˆØ#ëî©é ÷È®ÅôÈZTÿ!¼Î¢š<Ð0ŽãŒZO¶sj­Óžq©kuÜQj-Š‚¸€"eA‚‚€` @XB$d%!@VI ûö%$lj@A¨˜A‹ VªÖGíè;ß™éuzñ¿ÿݼç</¶Wæ-¨D 4ÔPµå  ¢Æ‘-ÀŽÜ ìaX‰ñ¯kSÒžÔdá§hù¤»äÊ[djíÍÂZÞ "OxÐ Âɤ×Î+›3 -ç:—³ºÎó]­VlW«1»«UƒëV´áº[¼TÔP…ªQ-h%°B·
+o††ƒþТ𘷢èÓÏ›Nb
+“ îÕeRÆYD¦‡^Z?D©–^)æª.ç‹uñZB°˜5y³‚h5K/X¢ 6Cc¢Úuõ…­· úwC-j`ï]õA›@ü=HÙÑèw²ÌKYLÖ#IbþdSjÙO‚lÆ?9…ü¡Š´¿Œ¥î-;rµ6MžÉÖœo¶Š ,æ†B«©¾Èfä!†:¢ç‘ì:oýÏÀøÝÀ X {7‚$pȃ€âpÄZç•QéOZãIÅwd4Oc÷
+‡,é¤Õ´™És{¡Ò.»`p4šÉb©+¶š¸Å6#§1°K=«Ä®óÔ †ZÔÀD ü=Ë iÏìEà~PÿÐ?­ Oy¬ÁNóÿ¥9WzµÏîJŒLJ{+•m•É:ø%:'‡l´³JÍVf©Õ\[j32JmF)¢ÿ£€þÙ `¢jø/±ÿzhÙ½Ô  :úÖp(æ™åXÒ”ýxÚD;lN/éUg³ÌR¢XÉ'·72vu“³†¦sUUU•&[Åb®¢XMU6cUb@Ó{ëÿ†Å³„»¾éwë@¹Ë4»À¸ïð4r â±+,vòRLâèÅS™}ŽÔb»>‹Ù¦$ˆDbR«žf©äÖw9Wi­ÁQ^c²UT›-U3…f5URmÆJ*¢÷0P 5p¶Ì‡ÆK@¾ý Þ± »üCö?ï
+ê ;61|âøµþ„Œ®î3$½-ƒ!ÓášxÊUF1‰xöÜ&µó‚Àà(â‘b¦ÙJfX,¥t«©´Úf(«FôÞ‚ÚOg
+sªÞjÎl±˜³ÅV^h3æò‘k×åsZo 5pϾï ßøwÐl\öÙ°úÙ¥Íëîì\ë¹½{Ç•ûÁ‡;ï„ÆnE¦ËGãˆu?&S¨ƒì‚>|Sv/¹%½³V}Æ)Ö$;ÍšÓF».MeדÛuçEmvC‡'èh÷°QCÝ¢Yаésh]÷×,šî^½trxÝ77nû®í›úÎß9µ7Lwÿ@¢ìî‘,ÞDdu,BºžÆÌ ²Ü%â³kä§{êÉ=ê–Äîe’Õ¥:­u©S•.uºÜ¥Êt*½ôß ÉÚ¿BÛ·ó³/ûÛÔÀ’7&V,tOmÜäxº}Ÿöñîéà o*ä<í^DAñxbyÞôì0“>P,8ÓOkĸ¹¢D·Tœà6ˆã.÷HO:{剿9FÛ#OV{x¨AˆšW~Æ%Ÿ=½´à3Ï­/?uÿ²l©ýåúmšWÛ‚%/¾æ=J¦=:’U|?šHœÀ”deP2<øê”ëELÌ…“pY?ØÀhœ´ñ¸1û…±} ñÖ¾Foà/>дp¨Ïyãœ7gÔ3w¦{jþ<äÕÒUíïÖm—¼ÛvûÎ?š6œTòäXq*6;‰ÉϸAJ¹•S†+¤$Œ–Óâ<ôê˜uôè #j¤½6rÄÁˆ`D 3£ÜÃ,op®HÎÓçéûøOî±¹³m¯¾\ †ekİ~'üÐ ¢äÍá“Ä‘g²ŸÄg¤=Ä`O?H'œšÄOÞ½P{‡\3A+‰ú‰Eޏ%,ûa\^~l\ƒf/¿ZvllÌk úØÚæÍéœãsipŽíÞÜ9*XðU,_Æ۩°#°‚Ž߇EcßDǧMÇ'%¿HNIxšž÷+ö\Ì£¼¬¨‡$lÄ/¸ptl…ýÌ%ý¹17ô~K^È} 1dÒN<<yÑk ûÈgÔ:Ó§·w¶åúG3”/?™Û_Í‚UßV‚ïVøûçÁÁýYš
+Qá§ÞÇý—ù:‹júÀâ8@ϙΔž©ÕZt´V[p´Š
+JŠÈ•‚,Ê’…,$@²“…DÀL„$ €ŽÐQ¬£U†"{A”Ê(‚ˆ"¢ "Z¬ŠÞùŸÎ3yž‡ïûçíþntÂ;bìñ7´„è×i¸£ó\Bø+11쥂txNEÁ¼Ð%…Ψ!³fZðs=èycràÌvi@¡Ú~Z‚jê^‚ª½»ÔÑý‹֬̆M$°k ¾Û•A>d8⇃ȀXˆ ‰<&(G¾‡dl±Â1À?’È ʨÀ÷¹1òù¿3?ðÎë÷Ö÷ÝÛ3vë¼à€jjqBÙn8 J¦–:j`ÙG'`Ý21|»š ;7Ð`ß&øo‹ƒPÏhß1ÞG a}C )Èeðž¿dD#O«/¨‚ö6ØŠƒ½Áâ ÖP{]¸äˆªéF¡ Ã(”þä €åKù°öc¸.#»K<x­ßõáàz0C |s
+æ B û9þtú ®‘;“ÐÄ{–p™?ß*˜ŽëNÅÞNÄߎ&Œ nã
+nÙéÿÇðåàlw–Ÿ pޏ?Æ8_àQC€Í<
+LqÜ;FVâkú)ê+ª>eŽRÆœ%Y8ωÕÜg„zþ4¾Q0…kMa?M¸–ñ4¾7ãqüŒ1콌!Ü}ñ ÂXÆu;1pC:b`û­Þ!7Ey‚‹!%x©‘ØÂ„·Liž¡¦¿¤¤ÍRKØ33ošT-˜"Ö‹ž2žà›$“¸+ÒIl›l2¡_þ
+N¹‚*ÌÔ1hÈÅb ‡½ dàç¥|Ú Q&ëO%xÂΗL¤–(Æ“+²ÐjU÷) êQÒÅS÷ˆ×4w‰75}”M{Ò€¶™6 ½DÐ6%hÏ'h ĈAˆDˆAºœ pÍá ‹òm|(œ"F-œ¤ãç•lú¬Tže '¹Zù8«8kŒaVÒm§F’µÃäËùwIÝýI7
+Úiýú«ô~ýÅä~ý¹”~}ãFÁY;AÆ2ýÍ ÄîΠðq¼ƒß@Á¡íP±ôÇB ù^“„›W¥Ñf•ΔT!žªÒ sF™¦¼‘›î.õýJsѯ”¾âz¯áJr¯á£×ИÚk8“ÖW\Çì+ªe^_4 1b †,oÐøŠB·!Ì ‘APwô}A"öµ†NUqØSJ©xBrR9ÆÏÏaµCŒšÂÛ´s%ƒIme]ôž²+ŒnãùÔnãYf·±žÕSVÃî)µ²)©äô ¤ˆ!cµHÃɽ_€Þo”ºCÆŒáP%Øøß ÉI/´ Ö”J z¤P*ï‹5êaniÁíÔš’›ô¦ò>Z§ù*£Ó|ŽÙa9Ãê°Ø8fkz—ÉÂí.7q{Êʹ=¥‹²O@‚dˆA½û (F¯£ÿ0ïËa°DY0ÅÿÍH ÍÓR'uÁ˜Jª¸£P©¯‹
+ ;Ó-ÆÖ´³WS›­çXmÖzN›µŠÛfµðÚ+Êù–2~§©DÐ]ntëƒ1ȃÆk%”ú~f¿o¡2À ª0~PŽy]ýÜŠÅ?¶é÷K™é·
+ÄòÕ‰¼ÖLMÑeQ©©‰k«jä\´Õr¶Uð[kÊתK…mVƒ¨½²HÔaÑ‹ºLzqWùb1ÈCæÖO@·ës0ú¬ƒ
+ô&¨ö÷„Úß·uaÁsõQáOëããÔ‘Éÿ©Leõ”ðå-ZyÞÅUqƒ¼ÐR+®¬±
+ÿYg¶Ô•ˆ[j‹2Zkô’kUù’6«NÒQ¡•tšµ’.ÓbA&b#åVgÐïüL{×BÕþPë·ÎzÏ7òöÃÑCÎÅF7’ð}µÉÌV3Gv±HœwV“e¨ÎÑU”+ÊmŲœ.5ŸÖÉ[ê4òŸm§ä­Õyò6kž¼½R-ï4Ûë†UNµÅŠ<W€eϨñq…Óh÷wwÏõ}ò¯ˆ€±+ÿå»Î¡NÜ
+RÐÞ9(1ÙBìN¨7׆Ký/ív¦o»œÍÿîó¶šêóuí¤Q{Ú8‘7#ëªNg••&åeTdäÖ¦äT7&æt6%e÷6$gõÕ¥döפf ŠRyCÂ4®¸%ÒІT´!KOJÑ¿·ú5h:¬ñã†Åþå.›ƒ¯úqƳ#^؉a¢ë`o ¥£ƒÑt=ì\EÃÉÌË¢³™‚ åIWóªãŠ… 1Å·c îÖÇçöÕ&äôWŸË¬JäUžÏ þHGÒІ‹û7ƒÀXjMv@«é®o¿Ö\ê³Ô™sÒyöÈíÐÈ(ÁùÎ`€ÿõ;Œ0QÇñøâ¶\~S\~r]Ji¬(§*J(¨ þV!è«.é¯9U0 ŠÍž¹4\Q,ˆ¿x¯\¸€6¤«®ƒ\MPi¸ ”¡ÝPõSÉϯF,~þë©Ã®ñ)Ó o§[#d¿ú¡ Ò^ΙKii7Oçĵ%G¶d ‚›Ëª˜Ím"Vàè¸p°*¼lHU2\](Ääß+;•_"àn•.ÚP - ¢òТ«ø½KWéX_iz«ðhÖVýÞ_ÎF·§¼Zù+ǨìËbV o ,)¡7&óD÷ùü.~ ý÷Ò²ÀÎë唎{åôkb»F,©¼WZv¿,âÊÈՈɀ‡6d  W´6@ݾMЮ)÷a`ÜÌcmÙñY£mÃó–ÚÝsŽ¿´Í¸;Ô<'z—<¡ñ'‘I„ÆÇŒžL ½Ÿ˜EósÄ¥~âë…$ñƒBÿ®±âÀ¶Ñ’ ÆÑfÍX G8VÌ©+’2·HA–Ê:h¬‡k{d¾v«ÈÌ+I?œU—\4Pí|e¡Ûº`o.zé‚+þ¯'‘?C¢'½ †~|*l*úc215`’Ï#M–ež´f&Çø>#rH½¹äŽñü€¶ñ|jÓx>­A2࣠9Û1PÞRíÊë¦îoÆ<œÝ‚x­.ßñNWóÖ@øÆÆ²è5Î…ÿʃ˜ü‘vz!0$lÅœŠ œKHð›ÉH"N¥â_Ô¤y=ïH÷zñgþá—04™Aìy’éÛñŸ,¿›’ÁEyŠÐÝÙ¬,õ´G} ‹ô¿Þ"}ó³ºbãg}ÊOX£Âe«¬eœKò²'!v™D _¦0YïÙÇ©o£"È‹ñÑÄ…ôÓø¿sã¼^–Ÿõ˜kJ86×zšà99“àõ`æ¼÷Ðt2~@2È“C ByvK¹ß¿éEÚ?)ÈÔ¯ìÞ&XÕ׺¼Š5Î\µµL^sv:³æå±F"±¾ÐþeùŠd?œ Æ¿K õ\⇻¿)Št{]uÂe±5Úyñn4nq2Úuîe´ûäüI÷Ç 1’@á&dº ƒŒt®Gº‡e¶ÉRµðÓÆRÐØžZ`n˜væ±àbžÎ, ºS!OþÁ"ú¬†“½¿Ÿ
+ðøšHuû rùœKÇ}*e:.×°Ž~¼Ávøx—cÿñOÎÑ¥Žãë$Z"ÈØoäöi¤eLMoºʲ—@[™ F{áˆ^ Ø™„ΜîÖð±÷Š˜.žzìD{¸@œ—ú…,‚ýËDÛµr’ÍZ¯õê ?«Õ²ÕÊ8Ùrå™DÍÒÝA® bÊǤðËfL6¨lLÅ0V;fšÁ`¥Kƒ£†dp3%
+œ, =‚j]Ìຠî¸b¡O¢žrí‚TŒ!HÞ4‚d€¼T2¨­-¹pÐSb‚±j
+—ÍL ÔÜDFÐrÄÚ$jí@ç#Hî$‚¤½C´!×±AU&46Aw«'˜(¹‚ÅvG°UµÜNkpßõ+àÕÍÁo5SÓB´Œ!JÛb÷„D}àîÓƒœýºP¤»*ôöC•D•}èüqI™E3€ a°QŠòÚá*ëq°GÆt7Zƒñ¦#`&gV[ÃVpV0wEÀÿ¤¾Jz@Ù¦ å}²}/DîІØš¨¢.£Ý£¶r%‚`U 0µ¤j(6
+ÀpUa/S Í1f´-ÐÏá€ÆuÊ%oð/&¹Ü|Eþ@j ü ¶R×|Úi«„ÛA«„ú
+~ŒñÍû9ã_ïyÆü"ã á-cžøŽ>CzÏBT0ÀBh2@EØÎjp¿BŒ „eœ(;`žuzºP/€Rè þeþ@®¢¬ùÖSWI-A+ÄvúwŸÛÌï>¬o„¬/øìeü<{Ég‘½@|Ëšõ]b=ó{Ç–Ž£ lMi2Ø
+øïÝ€èçñžÀÝë¼µÀö…xr Ð9{€"=qéáŸðHÔ{\vÌ[laSBzYF¯¢½
+»Hz1ÒÊ|ÒÇ`öD>eØ1ƒÌ¦¨¡øëXç 5Q5P7y
+­Gî,`Ow&ºã˜ËÝ iÍ× Þ8 ÿ…`Ú¹Ìû} 3Ô Ñ»ÞkˆÁãJ&ö¹DDâ§0³ ¼Gôñ’MÖI¨T:ð ©wc;Óê‰éçÈíªrj»ª”Þ‘nct¦3:S ˜])ÎúlˆG ìen GïÞ Ÿ™`Þ2²w,‡ì o°o~g‰üå•1öàˆ–ŠAg[ÜÃ$³K åÛÙi½PyƒR¦úƒT«©'5kk¨ÝºCWÂlÓÇ·iòYíê<V‡*‡Ý™æ,àüm˜ç
+ ^S uåW€øÌ€ì-ó wÛRÈÛ½öCÞßW¹a#ÙÑA&RXoïH•0š’Ò×HòeNNZ-³D[C¯ÒWÒ‘ÓŒäx¼ÝPÀnÕçrºln›6‹Û®¶$t¨œÜi.ÀB ‚ÅS@…Þ½æõÓ w³',†¢_V½)Þ·a´è°ÿãBÌÎÞ<â¡V33æ–NÀ¸š"Ö&iåÕB‹ª<¡0£”]n´±êM…œfS.÷žÑÊkA,|»ÞÄwèŒü6-"hW; P{®+$þw
+h— Vïo ý7Šý½·mûߨ‰=ëOòë;±õ~1þàí#´˜+™ Œß4Rá¯)iŠÒ$D},1×pTPj:«µdñïZLÂf"ºgÔ‹ì† Q«^'rè´¢ûg}6pPƒ½q2–ÿ r¼= èÇY`Û<ÿMéV¯ggv®ê¯8°¾»<Ìÿî Ü?
+ȸZ+›Qˆ„'5
+EaŠNsDfELÒヸƪ“4ei¥wÍjis¦ZÚ‚¨¤v½JâÐ¥KÚ4Îz[pQCò‚²ì 8ºÎŽm˜ñ©Ô×óe¹ÿ¢ÁªËú.ì[ÕZuxË2ìþߣ«Ž2é¥Y|A!’,ÏÖª5HšÑ(‹,©Š_³Ê[V¥âŽE©h6)å-F¥ÌnPÊZ3”ÉmZgÿoƒ§+È¿› ¦¥S¡à‡¯ Ä{Ú»3gVÿ4w nÛÂîK»WÞ¹pè竘}çJ زc4Zq—ŸmËÌTµJoDäÚ|‹D]ni®[Ū;Iz³IšÚbLJ±’”­ÉŠ6­³@€PCÊÜÉååÅ«¿„Sk<Æ+½§ÕnšÕ×°u¶£aÇŠÆ‹ª«‰Ø[q±•R(¹ÇX<c¾(YuD¡’Y2 ÂÌ\3×x:‹e¼jå w-¼Œf“@Ó‚Õvƒ(Ý‘‘˜Ö¦uÖg5¤£Y2l+¾øT¾òß/.¬þzàª{O“ßÌ;Û—_¹²ß·¦.lwi ."ÿ,‰d>Åäjl|©¬(9U˜¯Ñ±r³´œ“frÎ% 5»ÙÄ0Ý3²„£oÕ'è:ž¦Mã,þÇs&ö[W8ŠnºR¯)UK¦]^6õaÓ·Ö– Ó›—Ö6ýX~%dgñEl¨õ|AWIg)ÊE§¤
+Ö •šb³è ÇKŒ1¶‹F|q B̳(ÖV=ÝìÐ1ŒmÚxC»ÚYˆÄè†0 ¡;âÌ‚Écµó&÷ßüε³k±ëíŽõß\¶,®º³ÇçÄàí¹ ˜`ärlLj•žxžÃcW‹¥”Ê´|…Yuö¸6¢¢NQîÐaK:|a›6.÷¾†”Ý®¦X:ÒbMŸfôí*™7éCõÌIC×<\:»ÜÿÑôç’ÉW{}ÜÏwù/<íØµ®àÞÁ
+á.…r=3þ¿'CúM*${yæCô½
+„É8ÆüHè—ÇA³6 ªM©PnK‡|
+g#/Î …Ÿù¡HˆB5ù™ÑÈ]Â…eE*LëÐo–@»Cõ^Tßi <¡ƒ¢N?"»–1,ýÙøVÚa÷;¨×ÆVõ€é>ý&ó¦fÐ|U7h¾ì
+c½P’ì‹2A J•¢±$7‹?OCÖj!Lä0lSA»Gú°~DuÜøAYgV\7¿•?²¼Q¼´tR½–6º?«Iû:û?úœK†œ 9õÀÌ4d0 ³\`å°Qå‰rî$T¦OC…l6Vh£ñ¹%<XËÅȪV´…†a·þƒö é=}ÜüNUŸm§þ›3¨|œÛ£êÊm×¾ÊmÒ÷Y¯úò~2öåýhêË;“Ù—wÊX&9Á4Íæ,5+ÃÇbu¼Ö¦MÅZI(VÓQ¨0qQjå¡°Dü>o eÏÙÄLp»ñqŸ¥__›Ó«ýÁÚ£¹’×I7ç?ÑþŸíúŒkòÚã
+·eûì¡âèz¨ðv„©A;¡{à³€àþžMóyKçÍPäa“Ä,ܾˆ`ˆªŽ¸BÑ…ß > ¢ b'iÝ‘/éí¸Œfü F}ÌKF-á%½:v2±2v’fŒAçi:OÇ¡s=½_Hœì`úž0ãÈZ¨:±*¶J?³÷,Úm:™ìý‚Ë 20%áà Êè§qùqC„²„hmâݨ6foT?«'jœÝ†Ÿà4Æ9uÄ NUÜ»<ÞÄ*‰ÁÐ^š _b`4šâ0ò6YAù÷¶PõÓ2˜í²æß4—ã·í]fˆó”2Òe\w|DÌ xŒÃ˜Š˜~j&¹‡T˜x‹XÍú…p%©=æ6òsŒŽ×@4ðjâ ¼r’ÑÇ“
+)㜠[`b™cí1ï` ië,¡p£<»Õf;/êÃ_ý·à˜Ã
+|§ÔA;ÇsÃ÷ëT!5஘‹íáI 7XçÚó˜- eI$-¿>¾CX?˜\Ij(c‚ªžŸO3 ¹4#7›6ÎÉNœ0 ÿŸ±ÆŠÑZdü{Ìß½\ù±ÄmÍl©×·¦ÿm#Å¡ÎC¹Ñîýä€.9 Û!ƶðÔËœLV£©¢]–Q[Åj¿¤6,ÎKeÓÇ„™ =ÿ<Àd0ŒÜ Æx’9hËÇ@ZöjK˜²Þf¡{£p—Ý?¥û–½«pùj²ú˜ƒ¾ÆwË“òS?Þ+ˆ<Ú•çß¡¤c›S‘X­8…Z-P²K¹j~!»B”Ïl’æ0{R2Y:©Š=,V²G“ÏrÆé=/mDP\s`z¿Š[k “ÐsBŽfÉýÎjv,œ«t^<{ñÀŠ ­Ûj]½×7ªwÿZ‚uë¼@ð¿šE מãÄVÊET$£æ²x¥b%RŸ’ν)S ¦È$¥"£B¢ç˸DÆ5rÍñhÚ·xèþHGû¸z³,߾ோ;í^5ìY¢ovYñ¸ÉcSÿ%ÿ]7+ÃŽ´jð~õjrXU“P¬Pó©l•,S.)’ÈDu2qrgªHô\&餢äa±H8š,Œ DOÄ7"æ@š‚Ö@¸Ò*Ñ,…ßXþSµÕúÖi©y—­®}ï’-îz.ûíh¯ um¬ÀùÖǟѨé1yÙH‚J%e+Îfð%Š b~jŒ›Ú–Ê‘ÿ!K’餈tXŒHF“yâ1_dàñ“ ˆ9´iè9%FŸçWa`ÉF‹¹ÚÍ󦛭ôíNÖoü¸¸¿ÝÍáÆUß®6œ>\w1Ò§¬"6T­¡âUŠ"OÄeå!ª<3£2%QÙ*£f<”%žÕIi#b–|TÈ– 8)GjDÌdtm†½”-ÇÀôÿ,_‡ù«ÞÁÂtmƒ…®k³Åƒ®]_t_w]×ÖæíÔÐ|ê`eöDAmLpf—VÆ"‰Š…tn‚KÏÏRòÊ%q¹Í)±y¿I㲇%dÕˆˆznLHK×óé
+B—¹æ@
+º6¥X†êå
+›TšÉ#””$ãJšÄ‘%ý"\ÑhrLþ˜˜­çÇŸ7ðÈJ#—rn<ɘ€®Ï[ŠÊ%
+t·YŽkÐ2´4ŒÙcªÇ­‹G—f»®Mûó„cò³Àݬ?ÂQáÅ ‘}#Ù!ƒâÀ‡á'Gz6⼆úpǧq^½o Þ7Þ}¯½Žó»ò:Îÿò¬Y0y!˜Î`°º¯X‚¶NKÐØg jŸ,e¦ULÿ²9or^!Ÿp]/?îÈ4úí$BöâõQ.X=Éã´žå0&òõÕ+¼ô…Ažm°‡±;ØÝð2Ø]÷>ÄãÁ‡0ÏÞØã·ÞGzýbLà©€;Z hkµ
+.,* ‹l!!!{ I „7Šk¬KGw©KUªŒ#-êX+:–uŽíA=zÎL[·ê8¸ ‚
+B|潚«ô|çwñ]=Ïû¿““ÿhi²l´*5{¬.]0ÖÀáºwp3ÜG²ØN RqU˜‚ç"Ö˜[”>
+Ê@qH*œó’±tA<Ö†Åâ³ðEø<"Ž\ˆ‹‹Âq+: }Ñ¡@Ìï9ÛFQ_^$ùW)jÓmŠªHQµ`ReðgÅ ¯|2 Âé2ÄúˆÁšÎ$„SÒ ócA3-†éq°Îˆ&È"QëŽ5 °uvšæ£en .ÎEWÐ\ôÏA¿GÇÉì%w°‰ÜAÝ}Šª¤(h”Çi0uœþ^BÓ¸÷f#†ÎB#lŸX𘋠fF@>> ÚñóaœÛÄ@TNš•“ý±qÊ ìòýGý¦ãë©Óq‰¸2Í“ý$¿›äß¡(çŠ2»)*%`Ò8¦z¥ d±¥E!Ê; qÞó‘L›.}6DŒ
+ø§R‘u– ÞÅtð:3À½ÆAæÝLd>Íç 1Êy™ïà
+‘ùJ ®'ÿï J¦CšÊ€‚ÄFâ ­ÖKTaPÔ-‚´!’ÆDˆ›XK…à/lðÏd€ßÎAV'ɺÆï.ÜgÄ Ivg‘|ž›Þ[xýbd=“xM°ä‘4d'Ñ¡`1 IgB'öƒ^
+ñÑtˆNf@x&‚v.Yàwg!ë>Y½ð†·
+A¹1ö’Å(ªIy †-|èv‹‘{8šTgP^R¹WÕï¿hÞÊŸkï4OÔïµw´£yÿÌÓý¨wë:ó?è.<C>Ù)´á4˜Â½a#*¢}P›2Ëø3±LŒ}*‹ÃQ™Š’ºÌÖáhA£dÄÐ$§?¦ÒµjÞäžÏ}­íÌëÓÜÔõjžèz´oõ¿æºõïW £¦Ë¦QÓ…‚Qã9³g0"7ŒËÊx£&ŠºäÉXÅ›UÒ@|œŽe渱jGʰs)w°tèuñVé+Ëne¯ù°ö©±E×c8“ÿ8ÿ²á‘¾Ûx_ÿØø³~° Ëð®ðï¦aK»yØr®pØÒf.lµŽ˜=‘tГ|Ù{]ÄŠ:Ö&NÄ'œéØ=ëÕ¡#«1ƒ+m¬WµU™Ï]õ¢žò²GöFÍýâ&ÝK³ñ_æ¯
+n´[nš®Z¯›ýd(î0ÙÎ[mmEC¶SÅCÅ-¶·Å_–¼³zÓl/çyÁA¾‹²o®"÷ñ‡øñØ”î‡-ÂY#›óô‹z×Y’þ[ïäü¶|¹è×%ëå·+¶æÞpì2\+9TøcqKÑ?¬gK:-ßÛ;,·J/Y_8þZ4àhµ 8NØJ•¾±),9ä²y‚ÒÁL~#œd+È·±.”†Í±LlK2ÚÈŸ9Ð( êý£váãÍ‹ïmp°o¯ª]_±JùSMƒîŠk{A‡s_Ñ%ÇQû{kÙ7%íγ¶®Š¶’žŠ“ö¾ŠfGŸópYùò×eMÎÇ>ç»'( »¦•d»È¾WOz4Ì£a[4;Y‡öp§½Ø›=ëñnÕü»†˜[›mì® .Ñ•Õ+”+×ëÛ—n)ü¦z§ítåAG«³¥âdÙ9׉²+ÕÍe¿Uá|YõyÅ+׾ʾŠÝ®~ç.×ë²®‡'0“ÓFfQCö½5¤Ï&2“‘Þï÷%Žï?1åÉAáŒd·÷袻¶[Ó~ØìýmC­êüšÕù§?Þh9µ|{ɉÚýåǪ›]‡]mK¸:jš\¿Ôî­ê]²³úEõŸ–¼tm_ÒW±íl—y8ÕyÆŸ³8–¥H%C¥E¡S‰²f_Ë–¥‡ã88‹­9v!Œ5d¡l!‘Ò´ÕôÖeFišìK4ÓLóÌï÷ýóüñùû¾¯ûþ^ßë¹ÃXYá‹A¢@ÂƒÐæ»3A…„™êÄîü‚²Z¾—6]M—ú©ÖDî»j+µ§¥.;øìÍfšw¥‡9´&G»7Æ%úÖÆžgTF².ò«8¸MÜ<îm^÷?›7ÅÍäO‡gðg9éü9v:>$·,
+ô%tƒ”IIl¾b󿍒þ¾¨I^¬Ö¥NÔï{å ô‹:‹+vô—xÑo˜·f‡:4¤ ÜkR„¾e iŒ"anH^lYXVt/#ê¦à\Ôˆ 5ú=/%z*"9z&,9j649j>$)j%Š=°Ö“0æ¿{‹È"_…ô©Bƒ4{Y›üS“.u´EÍp£™ê@­Ãöî
+Ãk%§ÍóYö5_qO–fÄúžKf|•”’‘Pžw™— ¼.ˆF
+ã&ùBáWøåLxÜÙYN\ì;.v%
+ô#<°ÿ¿{Ï+­‡•UÒäÕͤ·íÛHß´ï“zÔrL¥¯ÁNûú¥“M~ÇjK‚ìÊ
+ÂOæDûdg%2Ò22YIçŠÂ„)—xÑÉí‚È䇑‚” ~dÒ7*a:<:~–7ÇŽ.°DþDaDñr€Ùò€¥ò°xY~nS‚Ñ›j¤á®½í&Ê·šmµZÜö_¾äs´¢2ÐæÂÅP×ì"wZ~Ü™„œôàØìŽ ³Š‘Ñ*àd 8çᇧOq#RgÂy)³~Ò[¸Àž&<påH˜, ˜' +•2ðóUi»! Ï{Táa÷nñÞ®#ë;Ú­5›]ôª½‹ëŽçT‡8§•ó<.žõ)Jeò
+sC9ùᬼ&3¯Ÿ˜ÿ#/8g:"$s&,4c6”“6ÏK]`‰ˆ·)K¼øý¯•€W­’ð¼[
+†î(Ãݾ´=‡›oZm¹Ôá´çb«çáܦӖé ÁŽ uî151¾¼ª$»<›Å,+åœ)½îWÚá[ú.Ü¿x&, –ÃÌ™ce͇°Î/‹ƒˆb¥3¥à]^6ˆÃH‡<î•€ûŠÐ}_‡Úv—.Wßg¾©¼ÛA7ÿ†;=£Ã×4±iÓÌqå^òb7&øÖg2üëJ‚½ëØžu·C=êÆÙž5sìSeó!~Å ¬Ó‹Á¹‹LQ ›è!^ó¨0ZE‘&<è¢A¿tÊAûà6Jã#™Ê¯M7Þ³Óɼã¶?éö)ãØ[ Kî ¶ûºÀÑïå×~ÞÏëZñ™“mõ®m=L—ko]š—˜'./1=ª—˜^å˧J–¢ÀÀT*¼-xVGmTè½E…®ûbÐöTŸm&W=Õ“*6VÊ´ÖL~ä²ûì×^tÞÀ™£ì{!VŒ~¾½ï¡«g_†»[o‘·So½Co¯}ßÛž~«þÎM«þ®õ«þ'.‰†˜!0 ð²àÑ2ôv¡³— MiPûR
+*GUIE£º´ìW‡äSþc©~öGmÞ3½§þtÆp°‰ïž…Ç¡µÛP†½ãP‘³ÝP½«ÍP›õ“qW«Á'ŽßûËæûOO»Î?½Z?‰
+*ø«¢2~Z'ŠêëDÿw ý
+à ¢VÇ渶(.€–@ ÁB€
+Üå¶£QÁbØÍ´'Äž8 3ÁVÏCXÖ|°Š#È⌀.`ì]ÿOWÁï¨+üN¸ƒ~n5|¯w<àóÌ>ý„ÕÞø `À㞣A  OžË_Bý¨'=¸Áöà®q
+žQÞvˆñ·Glˆ„üYˆMtBtÊ"ðsÁ+]ð*W„5¬Fh+ !‡½À:æÖi__¤#è;?=öGÐ+b„ÀŸ¬¡ðà à¿á`>ã‚ù˜7ø¤ÃÒO=íà3â@$ñfAœà„õb³–"ºh%ø[WƒWG·Ùámtp:ýÀéf€}6
+±›¤žS g8@> J¡dòÅHJ[Q¾+âÊ<³Í‚:ø­ D¶3q,¼ÓÁà]bûkœû,d„;2È{Çêç!´—ö8?Dƒs3œë“€ÀÙüU¶HXA…ŒP»ÛAKŸ
+}Ø,èNÐ&-†*ÅòunH2Ñ ²Ð_Çx+l´Æ´Ew„ŒºÃ†ç؃Q7ÙýQ9/¯9}‚ñð§pïÅ€w3Wâq>ç&Ò!†ü?“È{¨ÉÖÔ¯²ÃZ{d±>@V¤#ÖŠA¯rAr†»UUà=,7ûJ¶$6°^‹ZÂ^%´s^Ä[Ó÷%÷¹ð*·7î!ïqÜ+ÞC¡•;Q× 8/†àËDN&AÐ-™±d_Æ“|ҶЙä-Ö‘{äÎ@w. Â…ÖlÙ²Át½[¿ÞàýR[Âøª"¸WQöT¶;ü±¤ûKRgÄñIþO¢‹‚»¢{‚[âÑ7Åc±—!ì‘ î„qÇe•#¶C1„$WLhÙb-‘»Œ
+#¹G‘ÿ4³g æ_4®¿egy?Y»ñH_κŸ¼s[½+â–²%êßòö˜²ãÂkÒžø+’o.J{ÎI‡E§eW@Ü©‚è°¢ƒj$´©ÿ>Ä‘|)ÉÖ‘‘³Àä6¦UT”ùN-gÍì7G8õ•Æ/yZ¤\ùИN»³n}À÷™%¡7Ò,Ü«ºíQ—´ÿˆ=¯ÙVÝ!þJy2é´ò²ä„òɪ7Ò£ê·ÒvÍ[Éí»¤V-÷¥@¼gˆ_Bî°Àidg¬'=Š—ØÀ¼Üö­…6åM%sz_eøœ_-Ñ ~6K]n™t^7
+r˜— …ìó9戞ŒªèÓé;âOè÷Š?O9$éÒ—w&­8’|[Ù®ý]Ù–2¦hÑ[åÍ©ã²¦Ô·ÒÆ´wI “ø³ƒšdg’Q@zlZhËR›ájwêËZ¿©OkCf>¨áÿýVE¢Ëu³†v¡$ƒÙ³!}*¯$ò‹\KlWv­¨#£Ir8}¿ü`Z§jêMkê·š½©}šÝé#ꆵ£ªVE}Ƹ|{Æ[)!yD‹mìHî@6oñ<l&}ª–Ø Ô­°ým——Ý£Fæ´ëyNßÔ$,»P¡¤}UžÆì.Íå+*âw˜…‡ÖW‰äî”¶dïS6gÖ4ežÐ6d\KÙ™Ù«­ËN®ÍQ×dªªs¬ŠªœqYUöDH$¿[G² dë”Î¥ Â‘2V»€òr×G6Oš]mï5Ón6†;^ª.í©‘{vWèº6g³?Ûhä·™6
+[
++›Ûey»U; Ÿjks?×Õ¬»¨¯Î}¢ûØ0”RiN®0Œª-†1åVƒU¾Õ0.{$ómJÞ ŸlÞ²ÙTÍ¡ îp¤<ß½€ò°ÕÙæV«ÏÔ«{ØóÎ5Æ8Ÿ¬—xtÕhWf°lYÏßWf6•nIÜY\#¯-lTWìO©0vé·äZn|”Z^0¨3 iÍÆM™qTU–?¦(Ë·Êßyûµ³ÿ¿{)ØL|2‹Òß8›òkËÊ݃‹(7xÙ_h {¦Yðáñ†ÄÕGêÕŒ¶šô°½çF6Z
+…õåæÄš²*yeéNõfSkʦâÎÔ’âž4Sñý´bÓ:SñÖT4¢1ŽªLƦ Ö   ßa&É.&»×òå÷ú”‡{¦Sî·Í |ß>ŸråÇ”³m¬ÙÝ­ü%Í"·ƒJ¿};ô¡µÙuÕÆØêÊb‹¥BfÞ\¯.1ïM),û,Õ¸éLZ^ÙÄ€>ÓÖX:¬)(U˜Æ”LVÅû &ßBÙœ¥3(CUÓÈæ²§Üi™F¹uh:å›'Ê¿Ž¸Ûjú[W[Ä¢öÖx×Ö=rzSS
+«nW&·º./fk­I´iÛViqU­ÊXÙ¬5XÚõ9–S©Y–ïˆ×ºœ-CÚuåÃyTeØ4¦\¿ÑªøÛuÕä™ÅüæMBXEA«„
+RŠEAPAܵEÜ—#n=3Óv
+cÇNUÔž‹,òŸg¦g¾À|øó~zþ÷yî}?ÜÑÃf¡À”Í‚1=ÛŧoéA­ˆî4ÓµV+ºØâÁok2?ÖmW{$QRYkð+;”£.þb‘‚¸Â}ëõë˶¤­ÞSœ±¢´"{qIÝüü’Óyy%÷˜×óówöç,Úñ>{IÑ@ÖÒmƒ˶\6‡«Eô~Ÿ¾ÛÃу*!Ý®3¢®ãFt¹mu´M原T™Õµh'lšéöy}ªOqÝÜàOjó# «—Ǭ;¸&aÕ…)Ë*>K_X¾?+wÿ᜜}móæî»Íôådï}Ÿ=×@V^ñ`æ‚O‡2òw Í YÖóéÙ§D÷ñèN€®6èâ uœ1§Ö³.ÜÑ3þ&Õí“öŸŒsÞÕ’"/jÊT6æ…®;ºT·ªnuÜÒÛfå×î˜=¯¦,=³úËÌôê“sÓªo2/³ Uƒ™åƒYeCs²w§ç”FÃRÖ‡ÍDßï"º_IÔUǧ Mµ·óèø%Sª¿,æÕ\RU\µ,=ã°½CïQØ>ÇwÝ©yÁ'G,iY5-¯yc\vSѬŒc{“SkR“N’oô / ú£Ã†”/?R«>¤*FÒÒËGRGC€žo#zTFt£šèb=ÚØî×t–G_]Ò¡k¶TÞ%”^ 1ßþM´má•D—µ—Òd+/dû/>¿08÷ìÊð¹g6èÒ;¶ÅÌ>½'^º:1¡½Uúº>¾ý…>î’f6#9ñ(’õ‡‘œTƒ¤Ñ°Žè‡Dw]ùЍ½‰èÛ½j/qTyÈÊîM¤’{î\Ñ]¥ÉæÛ‘×ÞŠ³_q3ÅmÑõ,Ùük |³ºV®®S§\Ý1«s·6¾³zÚŒÎÖé1×™Ó£¯ vÚyÄÅ´#>¶3ãšÇúåÏD÷]­!êh`÷?ÁòÏUtr´ë®ˆŠžšÓ–§Ž¼O}„«Ÿ„Ž[þ8Æ:ÿ‘Þ>çá—Œ‡¹©Ý˼’º×ú&<تŒ}°;húƒêèîV¶û†F×ýByÿCxø]DFvAu:íyDöÃgD7Ùœ«#jnfù§‰Ê¯•ÜâÓ–‡"ÚÐkNkzż•½rþâž ãù=Z‹¬žkCOš]JOŽxVÏ×øžÜcz>–F÷”ȵ=U޽͊ðt2ÿT„þ8èòw(CþŠ@u7‚5÷2Úýω.Õµ4>Åò/í¼NôñŽÖü͈V¾´¤Å/y¹/eü¹¯TF†WfɯfŒOx•d×—a3½/ÏN×·Â!²oƒcØëí.ê×ûÜ‚ßÔM |{F¢úõ‘Dùæ7‰ò$þ/!õÿò€Ÿà5Zç!¢“õDul+XvvÞ'úÓ_ˆ=7¢Üß-)«_Ì3ô{pÉý~‚„÷j£Z“èx³¨ÁÙáƒY4Cùƒ‡
+¬UC›lü‡wÚú~¨´óùÐdï5òµƒÏIJáA±lbY?ñhí¬GXœaùD›î|K”÷#Qæ;#JÅÒÃŽ7n:¼øZ(᪡3
+B¼H‰c?dšø`©ã¤Øl.A‰ÅTšàŠVK\ŸèŒ_¬œ0låü_õLj*ÙPü5Ë¿C´ò)Ñ<–?ûg¢D(¤ƒ5E@ÌSà „ç
+ƒÐóŒÄX.š‚"[Ûà ñ$´˜X£ËÄ
+Ϙ÷ LG«b3Pr™hó-¢Oˆrzˆ’žż#ŠQ(L(ˆ½…?&ñ|X2¸ñ$ðä¹BÁ9AÅ9@ÃÙAË·A,ß
+I|KdðÇc¡ÀkfØ.0E…ÐBcœŠÐÍ<£ôëÿM–ÿø|=»¿¶HÍòUŒÉ`JŒ'WöŽ˜Â›'ž «Å
+RÞxó,À‡ÎQœ±[9B6³œÙÌ'”2ÕÌq¦c „°=/ÀŸƒJÉGp £ P'D@¢~™&P,6‡÷:KÈ‹¬áYf󶱃´e
+¤öð¸â
+€û;ÜßA2BY !Þ"¼8è¼ùˆö@"„nšÚd3DæL@ø²IÐl°EÈv{íuDP•3ë\ jv…ªÝ ÊKS¡¼áåæ¹Ê_™! Xæˆ
+’Á@H †ç+5<Ÿ‡B6ÂÜ9„ÉX¾”CŒ'±ÞÄ
+ÅÆ+ab2,1m‘ ´k¦ r‹ỜVîŠÐš©ÐÔK iõ€ú¬êNæ¾'Ô½ž#êמCêÏ~ <WCþVï…Á§7>ßE…pVCÔ¿É.ó ¦Ï4Ž¿áY¨EjE­Ö­( Š"Aäø%¿œœ ®„#@¸#rÈ A(Ð"HêM׫­X­³VG+VW—ÑZë]Ýgßý/ûÇgæýïûÌó|gÞùà]Ðךß$žƒïi ¼VÀcØjDœ0•Ë!D³
+hzG¬[»ÚÖC€q nü@=âüžú­Ë_þ\ßøßr}As}F}íú„
+nsþàþ[
+Ï«Aày9øÿ*Φ9â|ìºüÕfú…9D¸Y@„DÛB(Ï„ÑK€—´âßÕê÷Dñº·Ì*§×ŒFç—!.†ômz<âöŒvlóï´i÷'´k?~å1Kƒ­wƒÀëZl»Dïi&xŸbÁ6S À‘Œ•àaç [nb<ÄÅ$^ó@`1셢—¼ —®x!JsüC¿þ)Wï<GÖnzÌnÝüO¢Çã1èyŸ5¾e–uz믬+[o±zýÌ|á}• >?2aÇ4;N°aÇ1vŒ“àk
+ìZCç‹°o‰—R ï#ÑÉä– ÷³~—H·{/px*Ù½â_Ñ
+LJ‘j§Ù°b×»¢ªÍ·û=oò:½~æöy_#¿ò™!§|ä\Üq™¼çwž|¾ó ÔÓ$|ÀQ>P‡@‚¿)ŒwÀÅÙØuâ°sÉW`ßÀ÷Qn2«ô¶ú#5hÁœ‚óéyÔò»R™ã/’ô×£5n3Qº-߇¼/†5ûžuûM üÏ
+ÆN Îî:!¸x\ð$hBð6&ÚP(õ‡APo8ö„Ã.S€Ž3‹)mOÄEHÁód¬¦ü¥Ú`ö\íiùX½Óf6“iK¶ì'Eœã²dç Rµût¬vÛ阽¾'Äõ;'£Ú¾‰è :>|$l*äë°èÃáèƒáïý@ï‰zg„´‹!¸U 4S€µ ÷
+\Íg ·[ÝÔÛͨË.¦Ç¬=—"w9™”î9)ÓxOHKýŽÄU|%i¤ Åt… D0ûĬ^ñyÂ(žewE¿&:b€hÙ ÌF 0$@¯“@ˆ)ÀÁ=ˆÄ®%ÅΩÄs¨?Aï ÐïÅŸS~+[ov«l‹åLqàÇòÉ¥gr"§2¥.GÓR<G“Õ>ÃIZÿYEà¡„ºc|;³+î»#v”l•œá6Knscÿ$â€S„A
+¬j)0«¤@7¸øbœ-Ç®“çȳC¯ŠíÑc½ºS¹ŠòSåf‹‹zê‚3%쥓aŽã¹±.#ª¤->½J µ;¹,¨3©†Þ*o&e=d}â0¯6áßp]PøŒW%n… 8år ôr`édÀ0ø¸»q¶Âʽ-ü=Ó-@÷«ìÐ}ËÐ÷†Mçªü>š*g./­.Œqî×È<ÙJŸÎ¬jkF­Q¹—Q—ÒÀ®I>À­L”'ê’®ËO¥ÉÀ+I²Xì"¦) Ä]ŒµEú7ô!Ç=*™nïµFÿØgƒfÐßœÍOÖúÚNTÓF*ø«ûËÄÎ=ERÏŽüŸæ\µ>»€fP陕µ}z;¯DÙ/Ô*'DÊË¡ùÊ9a~ð5iÀÕ(ƒakRe
+„-D}/Í
+=Ë·D·u–èzõ|œo.5-Bgš7˜ß¿Ýf´6xñ@ w•qoäÆ]œGSIÒö:m†u~­<¯”YšSÃѪ[xU¯0'k,Tu!T•õH¤R}¨²€§ÊRlu:¦@îB¢‚L tOKA7*,ЕÚy8>šn³Gß¶a6Þ²Íz¨1hQoge§!Ì©©Râ^[.÷®*Kó×—äk‹UìM/+Ï(LÏ Uæ~šš÷P”š÷^ Ì^Z6éj`§«€0¢pöX WÙØù°wÎT™£‹ –hºu:yÀMt­5éÜ2¿¯-à“®&bEKƒh}]mŒ[UMÂ6]eŠ_Q…*P£+¤«K+ˆŒ’njQ·@¡ɵgCeÚû"yÑ;ÁžBüÛä©Ð
+Ș
+àHt@Ä–ËZ¢—Yý‚ïpÙ€p±{  ÑC |i…/CC._úØÖ¦-Ö÷‘+µ½aN¹ÆÝn™Ý‰[S¤úÊ;²©ñmÅ´Ý­ÕŒ¨–V"¬ù0GÔ<E
+›os„M¯8¢ýÀ¯"¢XQ•À4ä=ÈEèj9Bßa÷œÂî7jÄùÿõO3Ô>ö)js23ñ²Ö½Ë^;B|–3$tÌoL>,u—õ'{ÅRùF÷j©+EÆæ^O?ƒì™drŒ7œî— N0¸­Àà7]X!¦@*Bw´]ªAèd BcÝ öãü‘ÿù_ݤªžt¤èŽ{Ì+<¶sAöÃ!ý(ÿsÅX亄#qΒѤÍQÿa»N£š:Ó8€?÷&l
+‘°C ³DÂaI°)Te´Z=z¬Smµ:í±g\jíÇj­=޶.ŶÐZ7w‹‚ÖÁ:V2Ö±. ”©VD¨ úŸ×±ý ·߹÷Óý?ïó>÷ÃóÕkÆÊ/—¦–7¼›áªÿÐZT_ksÔ7å6¸mŸæäïDna-r[;~Óóúc³x|ÑþÍD_|J´íó§ûßÚŽÞ>êKËŽEÑ›ÇÔ‚EG->¯±‹çv…Í8T)ýÃÁ©ŠÌN˜Ð²@]Úò¦n|ó;)ŽææüæÚT{s£%·¹35§q0Ͷ¶ÝÈÌm@¦½îyW–µ³´Ô5°;ØþdÿdûßÚ&¢•GyúS»-<Dó¿çæž6zÌ<m5í”Óò©ŠÊöꈲö™²â¶Wζ7âóÛV%æ¶}0ÖÖö©ÆÚÖ¨ÍjïÔfÔe|CÚqÓÁ”ÑóHß±ÙHôõ'Dµìj¾dùû‰V&ZrR@ìð¢™~4ÃEÓÜj~²Û"¬tçù”»Kü\îÄÎÎéÁù/‡åv.–dw®”fº7D§»·Ç¤ýk¿Ürñ{¹åÂ…ù<”¦sHLùI¦SPÔºŽh»ƒ¿³ÿ`S=Ñ{lV"ZÌvЗ;xš~Á‹&u‰¨ª+’*º¹’.£ ¨;Ë£ »ÐËÞ]>ÊvcŠ_ÖYcÒo,°ô¼dîYbìÙjèm×ÿt"\ßÛžÜý8\Û…HM¤êkˆ©™ÝÁÎíD›Ù ¬ÛÃò²üDs;ˆ¦^ ª¸,¤ÒÛþ4þ¶„·ã¹¼~o»cdÞɦ 8=Æ Tx™îV{îÎöÑ .­\æ;vh½H5´mL¯ûý•÷.ˆ•CwÅñÇ÷C×À‘ê·ma3ð>›Á•ˆ^gùsX~µ›¨ôG¢Â[BÊ‘íQ(e>Ž¡´Ç*Î Ÿ ¯ƒM †C„ra¦xÄa–‡¯yFc…— xGa‡w$xKpÍ'÷ø„ÿÛØ l`3øvËoùg‰&_$*¾B”ÓM”ù# |É„@ÒCBZ(( *.É\,Ì\ ²xòøHóᨄ`† óX&cƒÐŸ Çà ‡W˜!ÏÙÈfpU3ë+Ñl–?颢«DÖ"ó
+C =€qœ?¬œœ/Ê8Tóž˜Ë ±”籎ç°'ìcÎ??g5û—|óÛùÿMä¼ÎÎþ˾Eì¼Är‰b˜(xSü(b
+Dù³ZDÑhÄ’Tä ñ¬/Äj!8˜‰ÌKÌ"f5SÃìdZžm‡$=m2]
+{¦
+”-€²HE•bfy"z¡¤Ë} ];Ò|!ÝêiÒ¯Æ ª‘9ê¨ÓÌbDõ2w˜a1¤xÂÿdì]†
+B,‚Ç#dXù,èT’Õ<ŒI¬•ìiÒ b@o÷€¶Ìêi^;oToø"q• ëý‘P#F¶
+¢n »e@t 1W͈¹<îY0*Y¾œƒ-šCSÀêph(´‘Ÿç {™r¦ú!{®Y‹ƒ±<ôqúšðáôÓ6GÞO«ºg©—YöI-Gd–Èn[®Ëþ“Ö/ëµ<йa¢Ë‚¸«Œ;ñg3&óYHeçÏ–rÈ—pp2.VO)›R“®lO÷s’h¸pVÀ½üWCíKÃrÞ‘ôÛÖGýbû›ìföÖ蟳ëbú¬_Ë{­ä=ÖSŠ®ìW²‰½dVº­H<ŸU‡ª“6¨Žå@ud¤³³ÛÙÞë eÙÌD‡*%ªd*Ó=†'ø •Uˆî”¼x«x^h_ÑIc¹´»pMôµ‚ò+ù›c/çׯ]Êkˆ¿hoRºí'ÎÛ/%väÝR¶?T·Ù¡iµC{$Úæh÷B³gX£8²³$妰^LUp˜6–0Õ,¨¶yßœäò멚xmâì°Ëå¯F^,]}Áµ*ö\ñ{ʳã?J<S´%é´³NÝîØ­9é8¬muœÕsöê:ïé›04:aØ]CÃxvC_7rXŠ l×™@x1”0SÊ=œ¥äîÌJôÍÌð¼>Ã1úҴʀ﫧‡Ÿ4OömåbEÛ„?+[+ÞM:Vö¾æHiîPÉv}‹«>¥ÉÕdÜïj3íq]3írÝ57”`ÜÌg¥0o/ƒiK9ŒŸŒ€<Öÿ’ÿr]žQMgyþ%DŠ
+*2¢Ž2XP¤H€„TA¤D ‰@(¡„J¨¡†Þ”
+
+Àç¶|&€×âüõðt3øI¾ žäl}Ž#ñŽœfr+ãȦéÔ0k4Öf,9ÉnD$sÐ&äº JÜ{ãj<»b›©í1=ôVÞ³™7Åjä=`×ó~f×Åü—YËGŒj>¢Uñ‘wQ1”• Àå°k Œà½Äþ–n‹ÙÆð$o<.°„»öÄÅx:‡»érVÈ–QÏF›"´žvìI–»v$’5Â
+Js‚š¦t0kãµìš¸ NeÜNEüOUü{¶J€X¥Ä( Z±
+çý
+…/ý
+E¿s
+ÃÌ!ºB„hï• `¼žü/ ॠ{o.
+×Àýc¸­2‡ëå{WTî†KØ´EV}Šˆ9±{[äI ©®µ²rUª’ª:]M/‘6³ %}…ø¢_®ø7[¼ÈÍ–üÓ7[‚|äÄ’‹CžŒ_ÛdD[ :·ÈøU°˜…½7Ÿ÷JHðu¹!ܪÚ“5¶„ñ*·5ÃL³þ²
+÷ðXp§;O¦ëˆ kX£MÛa°Ñ‰Ô­¦®ÓÔq-Ô5Ç·UWFíR•ÇïW–%T§ÊVæze–ÐRókYE;')Oë'ÌâžÊ{„ùÅ/!ùs[”˜‰Yˆþ)(šÿJúØÃÃ"ìØûj& p¡Õ´kèÕ0hkñZÛÐÄ1¯i8f­ª ÿ²è ß.¯Zä˜U™â*+—{H˔ԤÒj†°¸•¯<竼ê£|ˆù»/_‰|b +.1ãó]»°$Å[Ä=ÜÅgp³{W#vÏV€ÁôvYB{×>bc'Ù¸¦½Q¥9jUÔº#·‰·'³!á@j½ÄYR›é.:Sà%¨®¤ñ«š™'+|¢*'8•8ï|"*;R…˜Ñ%ˆÁS"¯h5Ë[øK€¾àãL«Æq¾¶ ûf`34ì&œéw3Tõ1L‹z–Ó²-£#Ê6¥-Þ.Y“ì(l‘¹Ä5+ȼ&•WdC#-¬¡¢¾Ì n¸Ç
+V¿a×#Fp¢‡V#ZXþ+*_Í¿E{¸Wpã €®`ç÷ö´`ÿ©Þ
+¶³fjvÒg:¾ôž¹`KýÚ–:³dëuóÃëÈŽüÚç1…ö{\Eö+¹W†w¸ÜA'Þ@?@ίXΟH» ¼m
+þ÷!çäØýð;ïOÿç}î}ï'´~¬$¸~²ÔØ0Ofh¨¼Ú#×ÿv^®õRîÿ+äþu°ó«ƒBW e{;ø ¬ÝF´è;¢mù§ˆ†_ x(ã6QÒ}¢Ø§D›ÈúÖ"Þj)ü]…5GPps¬`|Ÿ(|Hü?ô´-"ŸÖÑ"ïÖ)bÏ߈5X/vÇ~‰nHÜ>6JÔ-«ß³æŽ*ù .ÞC4“ó'œæü‹D¹¼÷´»DÝÙ8?¼‘(
+
+†Š Ð:ÒÁH>0Z¤ArCOÁY‚
+y‚JL”X R`ƒÈEv¸%’£‘áÿ,ÝM4‹óËÚò/qþ-¢TÞ{Âc¢ˆçDAõD òƒ„38Ó…<àAjt"ø“‚Èá¤@4ÉÑ$H%þ“ΊB+g•l»ÀjÙ›æðùû‚û?¢-ÿQ
+ï=–³9ß§•È“kpg.\‡3”œéDö\‹ î$†7çù1# g1,‰e²|Æ/AÌb|ø±ƒ}Ï~è
+,À*Àl!$F„ bf‹¡/ÀoŒÚIl¶ ÚErhW±õvÐnµ‡¶ŠígGОc7Ø3ö›ºÅG­:([tphÑ‘W§·þ!”s-FQ¬^ùw¤MË'b˜3ÄÍ“ x¤Æ 2¦Ëa˜oÃ2î_…†MJv(Xí€À#0œbWaxäØbhpl6¾wh2Âñw#œ P1—×F¸¾ êáœoÓ ˆñç/ –ëˆã^Äòýèœ"†u€‘Ce°Œ“#|Š=Ìs0/r€i•#L•l«S«©Ê¹ÅtÀùƒé˜s³é_ª&Ó}Õó¯Î æwÎ/Íp­3Á­Ö÷ÿ˜àñÌ ‡á!*€s}té$ Käz’Âxåg³[w1ñ¥ˆË—!v”]kt™òCçéï:Ïszk[êü»mêm£Ëkë×WÖj×kºÞzV]gûIýÜVëúÄöÖí‘ š[áõ³Þ÷ØM¼¯tîÑ:Î÷â\BO–¢$ 5J„ä®bôL“¾ïž+oJ,¶ýI©CC×)Î/»ÌVÕvYèú"a…úyÂ:·g [ÜŸÄïvÈãaÂi ?zÜMx¡¹ßä}->—㡽È.ÄCw&º“] mqm{w'ô♯·šåEÈæ^d›„Ö¬QSzÉ«´ly]jâyòhÇ'½¾P=ì9]}¿G¹û½îK4w’V{ÞJÜèu#qg§ë‰û|®&ó½œxÉ÷BÒSí¹ÄF¿Ó‰ÐŸHDÀ1öv( û»Cߺjxÿmù<oe«øÇõäúRk®Qx“)Ôõï*~Ö/]ö { ânÖ0§›¥êk½'{\NûÊëbê‚NçS–ùžK©ÐIÞì:¹J²×á€ã½Îíõsà‘äW†ƒÉÚǪÙöm
+ŒÛS;B¢!•󳔄^ó¸ž|j,ÐQm~¨ðxpŒèî déŸöW^Ê)Tï?ÊýLß ^§²§ùï3Gw4s¡¾&seàáŒJãÁôíAûÓ÷†ìí}"ô»Þ×C«ÒëÂv¦·˜¶¥ó“›°Í݉ÐÊL„´‡œ™Îó^; ®£Ð‘š‡¨¨n¨=*ö§Ûő•¡IÒe)Oçç¹4\S3p\§C¹uûs¦ë«û—öô[¼«ïêÐÙ7mÏ®2oíScÙÜç¢ecö/–õÙÍë²a©è‹ðÕ}a^Õ¦ýÖ’¹ÿYöÜ)},”QC±œžPÐÓ‘Nt»Ä‹.2 gKºJOŒè­¬–ãz`H‘¦ºp´OUþ_üwš¸-oVð–ß„mÊ]aÞSiY—³#²bÀëêg­«<²-Ïi².ËAÔÒ\D.ÎEÄ¢\„/üæöÆýï+åTϳƃ1Ý#£[ctyœ;- Ž‹“›¢Ü;ªŸKÕÈ|ÏÃGøn)§ß4äKãúÂé!k ¾6¯Î_lY9hMÔò¼-¶%yÕÑË;½0ï^Ì‚¼7ÑóÁ6o0¬åƒùõ`DÌ K{ÜþÔ~àyë1ÏwÆ
+tµTJ—ÆÛѹ ®t¼Ì(.‹–ìßS±«´Ë¶±yž›Fõ­,£_3bBЪaSC—Ï_2da䢢•¶…£Ë wÇÎ-87»àfܬÂW±_!zflÌ:£‘Ó‹Ñ2ÛîQýPžuxî¼þ9ÿ×/ÓÙ/etr¢ŠŽL ½“­â]“ì·–e¨6ŒÏÕ¬--ò]õY‰~ù˜Ïƒš¶°d†eþˆyQs‡/ë<kXeÌÌâoãþZ\?­øjÂÔâú¸©ÃË¢§ ƒEM)Fd{è#ç³@ôËp¢Ÿ>#ºTFtf’ˆNL‘PÍÉ®ó¨¨î+à÷ÍÀ( †}߆}fö‘ÊDTDDY‡aVePY”M* X´&F«Œ¶±M0.59îÑ´1iÐh’“ÛKsÚíŸóæøÎïÞûÞ¼[Ä…3r •{°ªõî ×îÊ2hËÝnÚ’½Ûª1Sb_›‘ǯ–ÈÝ*Òª=ËR›Dò”nïÂäa_YÒ9¿¼¤ýs“¿öËMFïÜô""ú,ÈMBÏÅ0Rå߯iç›ËPÒÞ÷QÀt &ËVÀX¹) —»²úÊü—w•¬Ñj+Š\Ù¼w›I½l—å¾¼T»Êœl~Yv‘›<³Òs¯´Q”ŸqØ;GrÜ/+}Ò_š~Ý?#ý¹_†äŸ Š¥IÓQ(MCÏÅ0
+`~'Àý4€Tƒ?PþL)í<å
+2Šd®²<…gnn½(3§Ý[’=蛚uÆ?%kÖ?9ë™_röÏ>)Ù(NÉBQj&
+S¥(Xla¾¦ûá®”ö^ªÁÅÚ;éÕë4í>#û80Pk
+wç `1ÜJ³D}ȸºP:ÿ™}
+Q¸³
+bJÐs±—ñ
+›²»kpò•µÿoÑÖoíü†È±¥>^˜êÁøAêåwP~Ã1Ú?O
+rèoÕ —è}bL¬ Ÿx‘`B/Hb¤
+DÊPAd¨2”¡30€ ½Š€"Š, ÅBKÀ5êZWc‹îšM¢=–hbY-ñæb'œýãsÞûýuß»÷÷Îû}9(zÀy'úóV„œ×&ïq_™ ðÿßûúN”q%]e´v ^Hi¶óØh­d£e-r8hQÂAó:²Š¬'›¹hÞÃE‹Ýä
+¿gÞéÿÛ'\eÐð
+ƒ/“‹ä‚Ëßpú@w Ç$@/#@oZûP/æÐ^õšÍzãáÏ~>+ŒóÄ]Å}ôA&ïÁÌþÝ™‚;ŠÁ°¢Uï'E‡ð†b‹ð‡;ÆÿwÆ>ýë3ë_›qÖàŠâ¦ÁÅÃ)þ0úJƧ(:©@““3Ðä8]Ñz~@ïîI¹wŽ}Ò†€ô<4“
+íÇÃxêAe õµ
+ÆAO¡)tÉX…îÜ6Ÿ eið„Æ<¥q}nœYuNЏ"[c]ª)g–Ûd48楯erҺ䚴OÝ2Ó¾œ–‘öƒ[FÚ yF2Ä)3 3SQFFaí…‘½˜
+p:‡z@õôQþÛVÌÎt”Ú±ÚJœ–ŸqÅó ê
+UébMË–&YçgZëòòmóµ¥öÚœzGMv+“¡Ù,OÕìvKÖ™¦Ö|ç¦Ö<wUkÐ%YƒÎÉY蘒‰²±þ ãÖmšÃ:úÈ£PýþR€å”9HGÅ$h«²…–ªiìÆÊ9üºò¹ú•e¡“JK¢'-K0_Z”n¥-̪ÑÛeÔÈR—¶8«ó7Êòvºªò†Üâò®’grU2ª\tŽÏEÇ-Êrþötd/$QîÍ8ª£Pý>ÊŸŸÔPýjÊ=µ†°ªÞVÔËÙµuž¼ŠZaIõBC]U„(¯Re–Sž*Î,˶I+-”¨Kª–5;Åw01E½ò¨¢Ï]#‹.“§ò¨BdˆS´c
+PFFý ðŸ‘9P¾ £g?Õï­è\Nõ
+×(8ù­ÞüìÕóÆg´„¦¬Š6NZ™`ªjN·ˆmú“ðzвNÃ
+Æ)J¶«Y†ŠY®šµÒØ»µÛΞ³‡ó;0Ïÿ}ßï›ùÞ'úMZðBäø×‹£Ç½¶2î®â-ñc‹÷$Œ)nJójGüó%þÎW¤?Å'±…=´vß¼pU¬ëB`ý›ìÅç¯àîµJÃì56<¹ÖYki3×dé^S`˜¶ºÐgꪉ~E+§øß·bzß + ·|NXá²ùc–.‹µtsôÈ¥Õ±KÄ,i)X,1‹$fÄ=bao§y-~À¼ËÙÿ…ù¥K€UÌ^Èõè¥îÜ)5cFi(Þ” ¦mJצ–æë‹JGîÛxiüÆÉæqoM³nx$p̆Ùö;ÖÏ .X¿$lhIix^IUDnIcDðܵ‘³Z"sVIdîÊÞŽ±5¼þ«^ç½Èüu+7×rÌ~vð'î¢l3`Jy
+•Þ†,ÿö}`k~ ³I_ZŸ×È­Óuò«sû…f털\;')ÍY–”h?ÚP¬ý[x±–ˆJ´$  |„·ÊsœÃ—j€ß¢ÿ7 èA öÞR€7‘©
+Œ›`¸Lƒå¯RúʢܺKeî‡M
+¯¶’ ¿æ"]`½ÑȬ3V„VÖs͆v~YÁQaIþ˜¸H?+)Ô/n(Ѐ|+)Ð1"4è‰
+(¯Ê2UšXEæj¶¡¢™§/ïË-唞×”^’d–Þ—d”þ×ÿŠ3MD¨1A–‰ð³J^ðW%fA‡ç=X¬ÄÔÌ4 ˆÝÐÝà‡›B ­y#45ï¢Zšöѫ㠩ޥõ™~E–¼@C]S_kÍ­iäfרšê!az•S¬®z;\Yuù:\Yù“HUIÂTf"P› _]Ax«<KøÏÁ ôàî® sЊ3hA}ÄÚæÍÖ`¨o‡ÚŽ”Êö(·2k¬{±5ų°-Í'¿U k) Êi.giš,ìôÆžªq@Ú0)Li8/Jn¸-Jª&J²üG˜l!‚ä:ÂO©%¼µ<Å9<À3°‚38߈gÁ
+0ÑÐXã»ßF…š®
+Úé úþé<÷h§É#ÊiñŠtÚ|^wû¾æœõÛ弿á|â1ù£ÿΓdýÎ dœ®å:fÿÎ}õdžð,Œ
+`Cíz'fà,@î€Òw
+;ékW½ âj0ì\ÂŽÅ-ðêÒnؾ$…mˉ°uY [–u”MËe”—Wš¨/­Ø©’•)ZøÊšxåM¼ôƒ›è*¡ ¯uÂËÄ]xéçpþNÔAß{&q38ÔÎ; ¾p;è@Ä5€Í7h~ËD·‚@t[
+"* ˆDˆÁì¢Á (Ä® öŵ×`Y#Q×ã QcCì Ö›¿"9ÄÝsöp~çÍ;̼ï~߽Ú3ïHu1~û}û|8sȼ{í¾gýŸš•O
+øý ‰¿˜Êø™üwÊN ÷ÖŸÍ5ü˜³'sv¾ÀÜ/3÷k€òcߦ§Týöª"-ŸmKŽ@MCàÅÇÜšðn“*¸)\ˆÄDZDØ‚I‰{ôüOYŒŸÊÚÇ2÷pæmflß_
+ÂO‚hÅk+-^·°Ã#o#n4÷Â¥f­ñK³`œþ[7œðŽC¹w²ôƒ÷Pé°O¦â O¶¢ÌgšÅ~Ÿ<‹}>ó-öø,Uîò)QîôÙ¬Úîó½j›ïiÕfßÛêM~/¬÷šµ´æ-ß¿-4ŒË©ÂŸS§ÙÛŸ*p£µ•-lQá犓­|PÞ:
+l0­Ô¬7m°\kÚeYbúÑêþ×­Vø×h—ù ë%µ´‹? Z+jãs¹¹Þ¸À©íl gÚXâØgzjûö~Ší:J[ÛEJ›ã,6PnLW­ ¡. «)˜l¹ÚœkµÒ\ ]n^b½Ô¼Æf‰y«\dþ·\h¾,Ï7WÛÛ9µäü
+eíuØìŽ-?Á?;µ—Öw
+W”tбXÝ1Qµ¢cJƒe†j–tÈ´*
+ÎÖO³Y4KžTh;7hµ.?¨Ô~vP™}^P…}nÐ#ûÁÂ~z°Ð‘]N-Û:â3.mµKîžûŽvʺ
+Sv–fU6ÌzΣåY_L²ý.4W—:Ï~FÈrÇœ ú©!{œ¦„œvšzßiB¨ÐŽdOºú„¸Ï%îãâ¹·Ï´•Ÿ7uÑbM7g¬ŠjŽ¥ÝÛH‹ºwP,ˆêª,ˆê©ÎL´Ìë–¢ÙuˆÍŒ®£lsºdë¦vÉq˜‘¯Ÿ±Ø);|­a\øNãØðÆÑwŒYo _G=9’Ù×7YƒŸ#Y.ù{{3~,°‘ŸK¢`E=÷ôÂÂ^&© W{Åì^•ßõŒVo™Óc€vJ4yRôp» ÝÇØÓ}²ãب<§¬ÈBcfd±óÈÈm.#"Ë]†EÞtɈzëÒ“ã°HáPç)Ç`e8p’¹ˆv$pŸÈøñÀŠÞ(еÇüxÌéÓJÊë¨ø¶O¨2'>R=9®·åÄØ$ëìØAòØÞ_ÙîõµCfÏ ú‘=s Ãcæ;³ÊeH¹~ÙãˆkZL½tN‹r"}zŒp¬ó˜ýp©pŒùïcü-<w­ï¬LŠxœ—h‡ÙI1³Ÿ¦÷k«˜’ÔI91©«:»oO˱‰ Ú¬„d9³Ï—v#úŒtȈÿF?4nº!=®Àypì
+×A±¥nɱ‡ÜÆ^£.c…‘ ɱBOŽuðøµ;ûyïfüÒd 8X2€ñy?«¿ŒÜnÈIöÆää6Òøä`‹qÃÕY¢5£ÄkGôï/gôK³’4Ü!=i¬>µï4CJb¾ó€Äe®ý6¹%%pë›p…j\ú&#’„éëÜéÌù€}ˆõßÎø¸åYI ¹%Åû)Ö˜šêŒ‰ƒ›!;Í_3øï™ƒÃT#R£4ƒb­† J²IOd—úÝeÕô•†á7 1P’°„,,.¸V°:,õˆU±‚HÁA@ÙÙ*›‚`-‹k1X«b]Q´êQD˜Îè´#UëѱêèØrÔŠÖÝ:õÎk+Ú™süãá—@¸Ïw¿ïÞÜûÅ¤ÚÆ,ÈÕF/(ÖGFWÙÏÚä8;j—SxT‹ÓQWÈc‡ð(a>_è#æ ÑöÐÅ}xŠk°…ùßÏ+èö$ö|dmýŒ¥8Á…IZä%BNò(If²—4-y’YrR€"!1Ôbaâ\eLBŒUt|’Mä¢,»9‹–ê"VÂâ6:„Æíp ‰kv
+Žûž<rކX¡‰ºÐ¡íá÷`×Aó¿'‘uHŒ©¼ï§ÐÏ^4?UŽœt[df8#=ÃU’’1Fš˜ác¶(}Z¯Øô`óè´ËÈÔhÕœÔ눔ڰäBmHòÇú™IFûIÛ’š§']p˜žøÀ> Aˆ>0^舶‡K¼úgÝųô×f²ï]̼ Þ¹ù>-Û)9½‘˜;‹r=$±¹ãeѹ~òÈœŠ9Ùaّʰ¬…êÌ4› Ì<Màâº÷Wëý3¶ü2ÛOÉøÎÞ/ý¾Á/M触 ÝÔT¡õOv=œå~leösÞÛ²¸s¹r€’Í×)y@|¾
+qöXP8QE£$󊼤³‹&É §Ëg†(‚ æXåÇ(òSÔÓòrm¦æ•j&/Y§õ]R¯óYrHç½ä´ÎûÃ{:ï\¡õÎv>Ù$Khzè˜Ç:°ö œw=}ÕlIVæ…|¦²ï(fïYb޹¥Ì.sFx™ ÂÊ<%!e¥3˦ÊË‚äÓKÃþ¥Q~Ë-'-ÏVy—[M(Ycó^I­­WÉÍØ’Sš±ËîÚŽ]*l½ŠH!)6=g°;Ø–m^¬!ËI.ÝI¥À¼•ì}*$®´BP•#«† `Õh¼¿ÊKâ¿ÊWê·jºlÒªP3Ÿªyò‰U ïUeXxUYŽ©¬RzVnQyTîW»W¶«Ý+î¨Ý>j·„•Û
+aå^öšcÜ{™s½Æ2 |s@Ò鎭
+èMÝ
+mv0oë EÛ2
+½NŽƒü¤?ÌÚÃ!k‡´½
+n:ÁÅ-ZÉUòð‹€˜C@(ÿ4™nOÎyØiÖþ<s PÒ+ëñ¾pÞ%÷Éòˆ<!¿_OEl s"†6,‚qˆqð U/ã`²Å·¯VìoÕý!,!d| ö™EÆ‘iäCRɇ¤„”“5ÄH6ÙIö‘#ä89CºÈ"ðü·Ÿ¿?ÿaC·5{\UoE_þf
+G¸ô„#½ÖŒCÍåôÆcÅš(‡CXxBôòÅ3Ù <–ÎÆ=i,º¥©¸)ÍA—´?ÊÊp]V‰k²õ¸*ûWÌêpÉl'.š}‰óòVœ“wâL¯.tš?Á?Þh·8©|‰åkD_úûX2„=ŸÆÃÜØÄ/Ö#q_=ݪ©¸¡ ÅÊù¸¦ŒÇeU.ª–à‚ªçT+qVµ§UFtª·à”z;:ÔûqRÝ‚6«Nœ°¹¯lŸ¢E#Ь’£ é%¢¿ÂÙ ‚ÛZ8Ëß;Ùà‘Á ?i‡â;®Øzã‚&
+øÙ*üøU-Ü]ï¯)ƒ[kkàݵMp=Øï{©«ÁýÔåàaj1d‚ºrŒºr–¾r…ž_w~kÝoè7CŸ°fC ë$r"”°ãùCÈkKãä©GmŽoµŸ‡³áWkðqðKp/4 n¯RàZ¸.G˜áR„•ZˆpPçÍÔ9E;}VÑCÏ)öÓg~Ö¬â(ë”b–}Bq‘}Lq›=£øŒ=ùWΑHÂØH¸ãÈë‘„óCHƉ£W‰Ï Ž×‡ÈG1
+}ñ ûT„½ªï„{TDгD
+&tRݱ†õ‘p@Gùô©tŸ>›Õ«/`wg[¸]Ù6^Çg@ÛŽÂÖ¬Î@wV?Ó’å»t3ÒFݼ¬^wKæÔýZV—õw©#‹ˆÆ¡#"$ð9põÁÜ7
+c™Ðãži`à@îJ0†CŸ1ö“)¯QÇê0æ±Û󊸞<+ß[Ð’ëºrÚE 9{ÅNÈÄa˜’ÕÞ’WÞ•Û ŸÉm†'2›H1ÂØõD„>ãìÃO1wŒ
+—A¯) ºÍÑÐiÞNµ›3X­&{§ÉÄm.,ã5VÔ4ë
+ZE5ù=âªü!©Í8)³çäåÆëAeÆ_•¿•—‰´ÌHÄS–GDÏùÖá瘃[xÿ·qõz½ßž~\ñ__‘
+rW”ºâ¨—†6»t¬BW;¿©ˆ›Ûdåšê²ÝB]ã‘¶aXœÞ0#ImX¦4Ü—jêÿ(IqqJaRëˆ(Õñ‚Øw×1ó¨}­€¿ û  iÀo{ ú7,»dPÜú*˜=áPè‰|Ïv*Ï“Açxô,}k!;«µŒ“ÙZÃËØÕºË+Ô¸‡Õîif»û<“è¾Ë$¸1‰-ÿ%6Q¢‹nFÓw1—±s¨?åFß‹V q#µ¸Ž—¶˜:(0v2Ûµr¼¡ ÷n‚ÝñÕ­¡2»ut†×H§yKد£ö6q·wuðU]ûâº&ۺΠc»îc;¿Æîþ·0¶cÛˆ`«ç7±ÿðγ€qÔëGv#Mí
+(¢q]×äèÑÕ Æ‚»¸ADET,ŠNPXªÔh{,G$1®qu]{ÖXëÛâîêÙÃùñ¨ï{·¼ûî‰@óx˜g £y±ägΑڛ‹%_ó>É`þQ2lú]f(2C¾¾ü/•)€™šë©·4‹yÀõ³ÕÀ赌A.çÏ| b3õ‹8~¥Zø–9ÀPæ Ÿrx—ë¼;<+"áQ1îír\é\—Êp®4骎Up,§2!9ýµ’VŠ3y³™‹ÔLYÇ»HÝ?çÜÄR·O VFý* gSÝ€cµíjõ°¯u#`WDÂak‰„Î26–1ÐZ¦CcI…Ú²*K%”–£\ï@]+ ©!ÕÚ=­¬bÐ×ÔC݉ÔEݸr ß6À¸‹úÕÔ¯Ü÷_¶ßÈ ª×AVï
+¼À.ãž2ižà.ó'øW¿’Gÿ¡§¶½ „Æ™ß5ãÑæ‹H?2„|B>¥N"uæâ9R¸kC°’:k©ñ%÷2S£„VoÃÔà>ê©~ŽéòCø„ŸÓG´¬7ßA8RW¯‡°m!ç
+—¶˜¼Gz‘jŽ ÞXjMf|ÎtX@Tjdⲩ±žûçs¿"Ü@#° Ç~¦ðIü ×p‰'ûž4q7Fï \¨ëhK_h®ZGž¥9&ŒÛ‡ÔíGÍxê}L­ñÔ™Š0&ד«HÃæÆeæÆ%ärÿM¼ºÅ8O_œ…ߊSŒÇ žø8£t”»Z%#Rëúá®ã9”ôST¯‚hgGŸ8ã¥ÚåA´¯'5£qUŽË²Ñh’%à‚|:ÎÉçàŒbN+ÒqJ±'”98¦ÌƒUY„ê
+4¨jpHÕ€ƒš&|­½‡½ºW°Ø2cíjÞAxQŸeN¸W^/'žëíñÀÖ7´~¸¬}6}pV7§lGâ¸ÝXµ›ŒÃö3Ñ`?‡ìã`»¥8Ðnöës±W¿uú2Ô:ìAµC=ö84a‡ÓT9¿F…‹@Y¥ÎBzƒð¥¶7ˈ¥†Üðn.yJ\µ·C£½;N;tÆQ§î¨w€®Ã°ÏíÔ¹GûT{$a·ÇìôLÃÏ娿•ƒ*¯|Tx£Ì{J½ Äç¶îHfÃKi“¯
+IA3!å·!üZËÉ=–Øk,÷»ð `™;å­ÕýýPgè†êö}°³Ã l÷ÊŽcPRÿé(ñŸbÿdl H—ŠVJæ€ Òæ
+{KÈí­ÇÚ¾>X„¬¨XÖ?
+ýã¤ôþ#¤´þ£e‹£&Èÿ5E‘•¤\•¬š™¡ž¹F3+²Póyd…vfä>›ÄÈó6Ó#h§E ÍÔ(¡ž)Tm(›¹O|ǧÞJíºx Š òbØcòç«ûÛaÅ@OdFwÆ’˜0¤ÅöÅâØXiQìG²…±£dócÇÉçÆNRÌŽ™¡LŠ™§š“¦NŒÎÖN‹Î³™]ª›mÑMŠþV71úž.!Fh¢…†¨‰*a`+·"Ú}ˆÚÕÃÙc“‚¡œ·âØcòËbuX2Ø©qþHÒ Éñ&Ì(Í—ÍŽ)KŠ#Ÿÿ©"qÈ4å´!³ÕSâR4“âVhâþÍu™GEyaüùfav .D‹&¢è‘¨(Ú((Š"¢¬Ã0Ì ÌŒÀ° (‹Åq¡âh]\Q0.cMðhš›4rZ=&©Ic\kmŽmš¸&úõAÆäœþñ;ßý8Ã}îû¾÷Þï}ÚÔyI¯ir“ŽjIŸjíI·4ö¤Çj{’¨²'ŠJ¢xÊUîƒ?3ÝÔ~ƒíF[Ÿ6¶aëØþ>ð¤(Ñ ˆº´Ô¦‡£Ú…*C¬PnH ’Òt‹´$=WV”^PV©ÈO«Wæ¦5©úMß¡ÍÖÑYôgÈM­YÿHcÖ‹*‹^TZREÅS.qžaŽQû Ø‘läZVóÝÃuÔd¨ÍDµñ9TfŽC¹i*ÊL³—iPlJ“š²¤N“]–—Y(_˜Y®°eº•ÙÆ•j‹q£&˸K›™ñ†Î˜qZ—‘qC›‘ñ³&#CT ¢Ò¢—‹ñ}mðÛf`6½Ž•9àx%×RÏuÔ˜TfõÇbK0ʬcQb‚¢ì™BAö<!?;E’kÍ”:¬92›Õ)϶”˜-5J“e¹Êh^¯1˜ÛµifŸNoþP—j¾¦M5ÿ¤Ñg‰*¢Ô›DešITôržû﵎äð,ÚVÐÌq#×SüTpM¥9:Û† Ð
+§còÓ±Ð1W°;’$9ƒÄê°JÍŽ\y¦Ýa¯V¤ÛUzÛ:uŠíUM’í 6Ñö6ÁvU›óP-ª³Ee¢õ Š^z¸ÿº·o!Ïc.ëÀç*ZƒzRÁõ¸ø,ÈU#/oæ‡Àç4X³³s`r¦IŒÎ,©Áé¥9‹ä©ù•Éù Ê„<¯j~Þvu|ÞM\ÞIrY—û@·PTå<‡¨xÊŸ¸ÿŽ1î.'ï²––`©!®<úNþÍQ€ì¢@XЇ#«$ &×]Ñ0¸æ i®d!Õe”$»r¤‰®Ù‚’ry|I"®dµ2¶x«jvñëê˜âêYÅߨfÝSÍ*•1¢â Î>>dÝßbüûJxŠyhêI%)à{Ûrs™
+Þ Œµ~÷u´†ÆÕ@‚ˆÙÀø7hYGo†· ڥŠŽ$;Fc@G8;#É\<Ó©GÿÎôÛ]
+Ýî² ÚÝû î<Mç hw=†n§];ÙÑG;õ65+¨¹„P×üÖ`0‡ºÓ^ÂÛÐN`Ø`ð~ — jŸŠCp(òC¡ùÂ!ñEAðÍãeÃïãaóqCùè1}ôº¾£ä›úÜC"? ¤«6j®e¬õ¤”º¶6Ö€ñÆí¢v÷1þ.êû€Aoý8•ê]@8¦âÅB{|a#üÞhBû>›Ÿn~p»és»yÀ»™èn±ûuòù󘈿°º+¶2Œ7Ÿº™ÔÏXg&Qs45ƒ©9à=@{Ÿ?0ä# pZM¸Ž¹Ž3ÁlF{›ápà3®ã,?ÆgyÙŸå!ïa±{(pöm>/’»‹¿Ð´÷Á. d/`=Èf½CýãÌ70”šÔ§É'ÔþŒü…ü•œ€óJà‚ø"¸H£ò5›ôKãË\Ç~Œ®ò¿Æ ¿Îb^?EnûD|B]kÀ4åR¨CÝIÔÅXŸ=ÃÚ
+(züšŸ“¯É%ò-¹B®‘Rà&kóOæä»ÁÀm•ïÇ?rwØÜåÞ¸÷p¿…0Á>&üÇ?<aÑÀÎØõÌõì€Æ;’ñQWw}I¿“Ë~½›„%¥…(µ§Ó‹û\ËC ðˆ9Y‘M»ÈFMÔ^Æ"¼ÈÄ‹ XdâçOeÃèD‡úé$ãH$‰'ÄNJH©#ËI3YOZÉ+¤ƒì'‡ÉqÒëpÿ†ÇøèwâÛS’ÉüˆŠ!…Á "A$˜Œ!¿%±$•X‰“,"KIYIÖpþ ømœw`7î£ ÷pwp’:=LÑ ¦êî­û¹íç–QCí€@ŽžñäÏE8‰& ÄÈxŒ¡˜:ÔpS£‘ó¯Âáåü›8÷V–g'£ÞËy},Ùqügpßà*q…3|Ë™z¹üˆ:êªtÌ…–o½ ð×d,ã›J͹Œ"•ZêäQ£”UÔx‰.ãiâük9W ·êvn|…ø’¹¸€S8‡ó<F·˜‘‡8ËYÿÇv™EyŸqüû^ ž] Á[£Q@PNA–c9VØ…v9v…åZŽe9 Š'ƒx•bÔzßV,ÆxÅhÓÄšXÓ4žSgÚ:˜i›Ø$f:}ûEm›dúÇgÞ}ß?öû<Ïïù=¿ïï?¼ÿ5T-u'ú@•D¾ù’ÉOkñ%kñ)"¨©ÇÇÈd>6ê”2«j4âhÇGìÇØLü¿AÜãšÜÅI¼Ã¾x×qï1’Gx“•»É¾ñPý¨ÜR*Çê+°GÇsMµÔΆPSÇÎ20›l<@ÿ±?F-5š©ÑAոͽŽÉÞ¸Á5¹ÆZ¼Á¾xWXwq‰+0"<Á°¨â¢¤â‚üMTŽ8UK¾<á¨yÌX>–Ʊ¦þÌsî ‹qW\†·¥ ¼%åá¦ìÀu¥W7®hZpYÓ‰QŸ\òéňïv ûâ¸C8?î οŒ3n§&<ĉIŸãØdGÈáo¡N¡~
+øü^ø†?Ç` Š½STì!»Ç|†:ãlGæË¹<vîqäßáû›2®kµ ˜…áÃp>0§§¦áä´›V„#Ó84£ߛш3Û±æj웹 C³v`pÖöÌ>†Ý³‡10ûvÍù%¶¿ô™°å%Uè'›ŸÓ÷œ¯8V? â¨_LýHjóȹÂ{ß<µ3&â̬)8>'‡çÆàà¼$¼ör&†æçcp¾{T`÷‚: 5cgP'v­Ã¶ ~akð¡?øÐ|^è ¾.l
+ù©°>ä/ÂÚ…ªÐCÖ„|“/˜÷¨û@ÜŽFcy¤GÑßñØ9¤Á¡ 
+ëôX“n}ºô…Â+úRa¥Þ%´ë=¢Wß.¶ê×HÍ ›¥Æ„½²'á¨ìNQêô?Rjô”jýWrµ^•\ ÿEãkpŸùß4rýiÓŠ 2–)ô˜¬I¯~<Ö'¾€ž¤yèNGWJVRÑn0¡Í/´B³¡Bl4Ô‰C«ä6¬’ê ½ráUÅe8¬T.j* w4e†ß“(Nƒ*;STéë<Œ§ý¡æUÚ³fÖ€ög€±ô§Ó_±&k t§ú¡3m6:2BáÍСŘŒ&ãr4s±Hpb­±F¬66IUÆN¹Â¸Q.3îTJ3jJ2¾¯qoùØ5vãß•â U~Jº*ÙŸóëDÚ2j^¶
+s•XnöHNs‡\bZ§8LÛ•bÓ~M¡éœOé¦Íô[«éKÕ¤*Ö,U&’5S•lä#æú5/åGòè±i763–µŒ¥+“w.Ö§Ù< 9Sá¶ÌG­%Õ¹ñ¨ÊMEe^–Pž—/8óìbIn…èÈuKŹmranbËݪX-û4y–3>ËuŸ˯Èš‹ªää¨ò–lUã'¬ù j^,à^°±hË71žnÆÓFÖȘÜyP“ÿ"\Ö¹¨´-B¹-΂d”,ÇŠ‹`/(Š
+œbAA­dµµHy¶Õ²ÅÖ¯dÛ†4fÛiŸ,Û5ŸLÛÏ}2­ÓdZU%3_•³ÆÈS¥1î1׫Ô=W̽H;¾µXÇx:O#c©ã³ªÀåEþpÏFIñB8ì1°Ûõ(r¤£Ð‘-Ø6!ßQ"æÚ«Å{“d¶wÉYö>eyñ &£ø¤&½ø MšýCMZñMZ‘ª¤ªrúª4Æ;¬ùejŸ\ì¥ï³ó®A¼Œ©–ß+ùÛéáX¡EqÉ –Á挄Õü2r˲S–/d—ÙSY¥˜élŒÎN)ݹINuîVRœÇ5ÉÎ×5IÎ4‰¥Ÿ)I%ªœ´â)R’C•’ÉmÖ|„úGiÁJ%¬i"•¤„ß‹Ê
+UŠ+W¥ø²gÜ`í/”³*¸øì!mÄMœüVXEÏ] ˜kÇ#«ÎËݳáFz}Òêã‘êIEŠÇ„d‰žAï©–yZÅ8ÏZ1¶~‡´¤þ°SIŠªOŠrÿYŠªS¥¨ZUŠ®!ÕϸÂ\ÏR㻤ÏŽ@šH5ß5¼÷¹¬ ­IFJód$µLEbË<è[ð¬u ⽉ˆóf`©×‚X.ào¥ím¢¼ÝB„w«¸Ø{P ó‹¡Þ»bhë'bh‹*†6‘FU {ί'ê¹ÔZÏëa©'å|/¤¶¹™ú^@ß,í%¯ø#¦k&¢» jU8"W-EĪd„¯ÊÄ¢n+ºKÿÍu™G×|¦qüû»‰X²YÄ–•,–$–›&ni‡©žÃTUk_Šp’
+"‚ˆ$ÉDäÔ’PJ‹ ÌÔ2‡ŒUcÌLU«ÆÑÚjj‰:Jû›Ï•˜:óÇGr#y¾Ïò¾Ïû<ê’•¬È¬ …g³*°¬]FhÖI#tÞM#4Ó4B3`Ži M7-a° ­*tŠfpY Sa*ŒágCÑ~mŽÔw®·@Š^dQÔbOEäú*<7Ps#Ô)7Fayñ
+Í민Á
+Ρ üÉ
+ÌŸ¥ù¹jŸ_®vù;\yß)`ñÏj›cmAvÕ3yfó.°f ™øüN5`={e!ñ/–bò¥ˆ¥Rpqµ/ñR»?µ- T@i„Ú”ÆÊ¿4A~¥äkS>ö±jmŸ¦Vöùò¶ÛÕÒ¾U^ö#ò´_‘WÉOjYÌQËx?‹¾„Vv&g¦ó¨ùìžÙRÿ\)¡
+SMÖ4PIŒ…¬Yh¦Àx>]B –I¶öÏ•Rä)ˆ51 JjµIòØb‘Û¶2¶1 oc
+é Äš‰äzørjÀŸôE·§ý èo–ÚTKÞ;$÷]’++«>vöÛ½ Æûðc~ìÇZ†Z}-Í¥–b×b¼–
+)OÃþ<ì/Â~v—ë–VW%qmÒwÚ¡kÚ§ë:Gç6¥|"gÖ¿lä"\€óNš:u›ƒ[ã×–ÏjòDÑì‰æ+D1ˆ²¿ƒÎx4¦bº®j6ö³°Ÿƒýì–plVcwv·R…õ¹ø+—è4ñgý¨SX> ίu/`¶@Ûàhȉ+¸“ËÖÄ„f7rÞ
+¿N¸º§¯!Úë­ÝMúh§Ûoµ½Ùªn6\[›Óæ‰ÚÔbš6ºÏR•û\­wÏ‘Ãc™*=V©ÂcƒÊ=?ÐjÏCZéyF+¼n©Ôû©–{ó>¼@<¡½ÞiGK ¦†ÐþiùOó)r÷ÔN϶ªñ
+×––½´Ñ»ŸªZ½.G«¡ZÛz¤Ö´ž rŸ©Zå3]+}fk…ïÙ}—¨ÄÏ®b?‡Šüj´Ô¿Vþ§•ßæºrž(§­©EÏ hà!úפϣxzº ήA{ÛÖgÕ¯™6ùúhˆ*ºkuÛx•µë/{ûÁ*é0LÅFkYljZÚ1I©Z˜©¼ å+'¨BÙÁ[´0x¯æŸÔ¼kÊ }¬ŒPº¤‡üÊÄ~©+OQé
+B­ûÒÛ_Hê¿gî½3sÞïûÎ9÷|/>ƒ¶£Xî$–æEø êÒ®ºÅcU›8E5IsT¼@UÉ-wdªÒ‘«
+G@åŽ2#èXa”:ÖÅŽ;ÌBG§YàØnæ;ö[yŽc–ßqÎò9.YK¶¹$¹_’môqgœìŽø.jÀõ]ÄÓL,k‰c ­a­c¸ªS®UUê
+¥ÍTEÚ<•§'©,Ý© Ó£g¾Šœ¥F¡s¹Qà¬3òëL¿³Ýô¥w[¹éû,óq+Çù'+ÛyÑÊN·Í+¤ÙfNj?gÑ8žIËK+¼ÝMÍuñÜJ,«‰£†ÚT¥‡©ÒyÊ]̈QifœŠ3©È¦€;Kn¿òÝEFž»Âð¹k\÷m¦Ç½ÑÌvo³²Ü[™î£V†û¬år_°\™¶ÙGF¸ú9ƱÖlƒMÙÔ€xꉧ†º,'žr÷`•fE¨8;J…9SU3GK= ”çqÈïɔϛ«\o@^o¹‘ã­1²¼Mf¦gƒéò<`9={¬4ÏcVªçy+Å󱕒c›}¤fCV?§Ð8š+õÀ/žn¥-¿…xBÄRÆg±ÇT7\ù¹cå_-Ÿo–r}óäõ'Êãw*ÇïQ–?_nÐÈð¯0œþ#ͧ™âßb&ûw[Iþ^+ÑÚZìÿÈ\ìû¯¹x‰m&æàµÍä}$=é—î¥o…†%ô¹PF<…Üçó/o¸¼ù£•³t¢² ¦Ë]§Ì‚eRå
+d)=àWZ X)å†#°ÚH
+´˜‹›Í„À.sAà°x>0ã ¾7ã—Úf|>äÙæxŠü°–²hÃ×A,‡BðóeVVQ˜2Š#å*ŽRzÉT¥•ÌVjé|¥”&)¹Ô¥¤`®ƒ-
+V(!x‹± ØlÌÞcÌ î4o2cƒ§ÌØÒ÷ÍØ’ïÌØbÛŒ-²Í¸"Ûˆ+´cèì/bOÒ~o€&ôj`×ù<÷–H™´ÄiåƒäX®¤Š1J¬˜¤E•Ó•P§…• ZJU|([óCùš
+êæÐJņn3æ„ÚÙ¡Y¡ƒÆÌÐ÷Œ•ß3–ÙÆÌòÊlã1tzJٓЂÞ¨‚Rî}eø¾eèWJ‹«ð>ÕC5oå(Í]¥›k¦(®æFÅÖÌÓœU‰š½Ê¥WåjÖª"ͬ­ÒôÚFÅÔnÔ´ÚnM©= ÉµÏ(zÕÛš\óµ1¹Ú6&¯ègJ•m<ŠÆh‡ÛÊñ|PÁu
+ß‹^TB€ûœ:æ ^Z¸VŠÅ²NoA½µØ¦ÈŽ£ˆÎ‰£ðÎXX¤. ïÌÓ°Î
+ ílPXç&]ݹCƒ;ŽèªŽ3Ü~AWo²5änºøýloàl$ϵ«Y‹PÊõ’FÉu+k YŠk“f`W£ÛÑ¿ýû¥‘XÚ5Lƒ»"5¨k¬Ì®I2º8è»8
+ÿ™¶K2váÃWî¦ì¡ë¡ùÚC3´‡d//ù½¼`ö²¸ö’äÞNžï†'øÝëüç @ÿ¡~:Èsš5PB­½wK)÷°Éwº“DÿçÒ¨iÄÃÒàý€pЂ¡vÄqˆ8'&Ž#ÄÑËaÜËÓËÆïeaõR‹#$päœKüÖþ»Èµ*5o3s@Ù¢;›\'ïEÍQ¿”†?* êEû7p ~Û‡)=I=ŽÓˆ?5Jzz,‡0Í቙ýÙ³€ÏK'™ä“À€'Ÿ‚÷xþ5ØWh¦Î+Ñ-"ÌlòMäg±¿¦òx4#JÞ”̧Ñ<'á9ø=œ†?ÀóWÑŒ £1‰9À üe¢ô2qœ§ù8Ïó
+ýU&ýÕ. ™×¨Åkà[îmÕïäH}ä›ö+iÞ¯¥ò¼žp#Ñ ;9 ÷<üÎÁKð2¼¯ÂßáMøfé]jò>só!&áÍñEš²‹:ŸTK—XÜ—’.SÄË p™8.¦j–káaÖáãRº3нþ”AŽWŸeÜ4ûôÞ€·Rbc¥‡ð/¸L³>e~>g|…Aùƒ`Ïš›ÉfNìû€„mÄìáÜÞh5@ßõ8˜
+q ÙP
+F¬Ó­eü;{ãv0-[˜žnrÛÅtí#¿ÃLÝq½®³úßþ•j¼Ähÿç8§¯ÄЧm>Gçuäælê–€ž“iÏE£Pï0'o©šeX§×ˆã<q¼L/ª“åúýYÛYº=Œ€-sô\—yLÛçÇ¿oΆ$@€„Ä€!Ø\æ0`ƒ ØØcƒm 6lÂaCH „œ%9HšPÒœm%½Ò´9Ö4RÔ4G©›Ö©«¦iªVíȦ-š4¥­¶e›¶ì·/ Òšýñ‘ì÷'½ßïû¼ïû¼ÏÃãü)îÓÍ't—³Ïpg–ÛäÖSxŽÇôôˆûó±x€ Ƭ€ZêÔQÃÎùÝÜåNúïá Â0îÑÇ]ú¸ýœsŠ«?‰›¸€ŒÅuŽü€«¾Æ¹ÊÕ]¦Ê W¾‡4ç™ö“™#={ÌÅÔŒÁOJ½5Ô*á:tÔ0ã#4qþV¦©N|HÐÇ5Æã*}\Æ.¼I\âž\Ğ΋x“N.Ðñy^¦s\áÙ§'÷y¤
+©¥¡Ž+´òä9qJxpBøq|N^3„£sÇðÊÜqLÍ›ÄáyÇphþyLο‚ î`ß _aï¢o±'LÂîŸç1uÿÇ4ÄtrÏßõX>?Lýéåí…á¸0?gÊpj‘ÇÃJðê‹U˜^lÂÔb‡·àPx;†wã@xû#F°7b;&"öawÄQì\rã‘—±#ò¶G~-QÅX´„MÿÇ7ÔÿšÏÌg«Ø÷¦Q;…O-ý¼ÆñSQóp2"G#q$*‡¢óq`i9ö-­ÆÄ2 v/kÂΘVŒÇø°#¶Ûc×akì6ÇíÆXÜŒ.#Ë/a8þ6Äÿë¾Ã`¢„þÇCj~Éòãc>1ï1½ŸÍŽÓÏÇ_¦—Cq‹±y,ö$Ȱ31;VcÛÊJlYiÄæ$+6%90šìÁH²ÃÉA¬OÁPÊKL=ˆPêISßAŸì&ze?ÅZÙ#t§IÌ ›%UÂoWó9R2yÀ¹ÔWP›~Xža½ìJy;R¢±U–„±496®*Àpz9Ö§ë1´ÚŒÁÕv d¸Ìð¡?£}™ëÑ“¹Ý™ûC—üMøä7Ð!ÿíY†'K‚Gþ=2%|Å'å>KŸËäL!×Ïrl/ýŒç°ç¢—Í™s1š‰ ò e¥c [‰àšô¯©DoŽ=9 èV8PxáWt£S9ˆåÚ•{àQN£Uy^¸•ˆ–Ü g#÷?¡”ðÅ3¾¤Þm>óK,;&Y±ÆÍgßɸl`\á*cÑ—›ŠµyÙäŸ_΂jø
+Ìè(´Ã[Ø
+OaZ ƒp«6Â¥Ú‡jJ4«Î »êš°©>ª‡ÂªzB$a-œ¥@ŸSïf÷Ïü4™(c¯CO#ô²Ž± 1.½…aèV-EWQ|Å™h/΃·D¶’*´–á.m„«Ô§ºÍê^4©‡aS¿$¬ê—EƒúŒ°¨¯³ú¾0©ÿ êÔÿu¥Ò3J$a"?¤ÞõJžE>ñ‡ÉN–Å›èiˆ^ú—µ¤«t!:ÔQð”%¢µ,îr\ÅpVhЬ©E“¦v6VíZÔk‡`Ñn&íAaÔ¾& Ú÷EöQ­ý½¨Öü‹H¢ºâ5å’¸OÍ«zà4KáI²½Š='=é§›qñfÜš%pi—ÃQ)CSU6ìU…hԕêӣAgF½Þ³¾&}
+Mé0˜¨5¡Æ¬Þ\ ¥U'*-ÐXúQnE™eB”ZމbË;¢Èò±PY~%Tæ¿I¨L’(ª{ÆÍ:àÛ¢Ã&`aÛŠ^ÒÁq½5ò·…ßë, Q[‰ê†xè¬2TY³QÙX
+[PÐ&C~[6òÚ
+ ô”AáÑ#ÇcÁÙ^äÞdz·aµ÷0VyÏ#Í{2ïÏ óü²6 i­Ä-aU‹„‹œš-Ù8YOÖiæ¸ÉÉžƒÚåmìý¼@®o>r:#݃¬®•w¥#³K Vû5H÷æ·Að 5ЇäÀf$&±"p‰ëˆ|ÿwHìdÅê“°b† çÝ|—¨³• .â&7P[Ó³MÊó³÷`븪oRû#‘Ò‡äþ$3±2˜‹ÁR$uHYjA\¨±¡, íÅÒÐiD‡®"*ôcD¿AtŸ„¥½³ôH8ÅõMžEÒNÄLm]'{Ï
+©S3`ä¼ÌVíjeSËŒ[Û‘CÕh€ê(Âuš:.–:».X¤n0g®<ÊûÌ™+úË[¨F«_sa
+¿SðyH1g`^.õ@×÷UÉsú«$§µ’Ýzß 4Ãfp“'E—†g Ç
+m=E¸ž‹­ž¤®'˜õ•°ÿ½Å{—xÿ€þÆÊK¸›Ñ4ÂbXÆ94sÐ퉮ïj©#š†M’C½dƒ;Œs¢‘ærÃŽ]ر;ö½Ø±;hŠ(|F‰u€l¨…]pšw¾år¦›ß‡ {é)ñ5Ò“‡Õœƒåä º½ÐõÝŒ>š4ÛïA³p…&ŸB‡á‚£4çÇhÎ{H¿c_N`G#I#ů‘KæÉuŠX4²`ã;ð1°'-ÍÌc³Ù®quÜë¸x-t»äÉžû%—CR[«Ö1Y~Ö‚÷à}8€³6Ò9‚ó4ã.ûHbÇUš(€Mö¦ém™&>ºv ~‹f³¿“ت¤×ÙâŽnÀÛäéc8!µ;ݪu.4@¬W¡ ®Á§ð9|ÉÐöé¹ú ù-ì¸M“ôÅçùyw¡t±{ñÞé>SÝý”½ƒûÓ†ã˜#äÀIÉ? {çY÷r«¦Uë ø
+n
+Û§*oC²{Æ´¯SöÎÃ1ÊήÚ<ÛA ØÚÆß<8ù~Zg¢Õ¶ÑZÙf€VØ Õ²¶ÉZÚnœjÚMRuû©ªj?Sfû9ª´/Q…ýb•9Ôj¡Ã&-pÜ-“ãQ;]T‘ó-ºüÔˆçøŽ«¬ÉÒá‹>eo{gÊ)ÏÖQ~ÖÚh£AµN^Z椗0U¹Äi±áEUUn­E&h¡ë•ºerÍU±ë|Íw+W¡Ûr¸mз]Êw?¢<÷‹Êõ¸­Ÿ5«£¥n ký0¼I²!€vß™‹òWÕ_ín/³›»*Ü}µ¨c°J;öU‰ç *ê4Xó;Ð<¯1*ðJÓ¯,å{ÏTž÷\åú,PŽOfú¬ÓŒÎoÈØù·šÖùœ¦v¹©¬®?)³«E™]žñ×ùïC)Ë!´°*mžU2÷aËÂ.mTêÓAE½5¯kæú†)ß7Vy~•ë7L9þÉšå?^3º¥kz7£ŒÝò”P¬¬
+ŠØ¬äˆ%EœÒˆˆ¯à  †C˜E#à\ ç0–6–C9ñ,{rhÉa”ÖV™á®šá­É‘J‹ì­‰}£5¡ï 5D㢒46j¬R¢'kt´QÉÑù½@#b–jxÌF½³OCcNjHÌçð, Q-œ‰§½ƒ× À\ZãZ#mP¶¤GÙjR´‹Rc<5>Ö_ãbƒ5&.R)qñÕo’û%jd¿%ŧix|¶ãó4,Þ¤¡ñ5œðš^JØ£Nh`Â'ðPâ-ø?ªË<*ÊóŠÃ?we1 $€’
+ùœb¬Üoe§Èœ6R¦´pe¦E+#=IÆt£ÒÓs•jœªãLŒÕJ26(Á¸\ñÆMšœ±G±'qAÑÆ;ŠNw(¦‡4‡bS:Fë» Ö@‹QšU<—Cqóäñl6)c¨22ŸPzfÒL¡J5M”Á¯ä¬T%e™•˜U ó Å™«4ÙlWŒy©¢Ì4ɼK‘æš`þnkB–C‘&Èìå0#ÑOa4AÌ‚é`…0eIFs¥f{È㣤œ@%æŽVBn„ârc59/Y±y™ŠÉ›¢è¼M²ÌÒDK½&XZaY§pË…YŽ)ÔÒ _+4Ï¡°\Èqh\¶CÌR,‡¨r(†)ŒJ¦æ¾<zn‹?eˆ&ç{)¦À_ÑÁŠ* Ó¤ÂIšX˜ ÈÂtEXs4Þ:MáÖ
+…YëjmÖë5Úº]#­ÜÀÖó
+¶~©B:ÖÈwhì¡í^­0­J˜VÈE7Q)¹€Ù“ñ1jZM(òTD±¯Â‹5®d´ÂJ"Z£±%)ÍÒèÒ,-SHi‚J›ôTéj–¾¬¥‡PúÜÒð‡FC‘CÓÚžÏÝ
+)‚ilúŒu’Oý
+MÐÕ éõy"»±ˆÅujóúMžï©”5çàZåÁzƒÐõa»Ÿqi¾çÒûÈ¥ÁöÖgð7×`x®Àµža naÖ”»ÔÈw4Æ÷©ÑïÙ'?PŸü_‚×à\cÒ
+žàázö§ â òa:Ì‚¹`‡&h…6¦·5zÀ´õ=Ø}æ»o™­þÔx—$}£‹L™_éŽê6o÷pþ Û9oºÁ@èƒÁ ` DA
+dcaª~P•º§:ì۱߄ýVì¶é_øñ“ß?™ÿÁ4zƒ"ºJ"¯¼Ë<}®ït K=|ÀYxËéC?çÜÛKWL|Y[0ëÏšâÐ2¢cA£û娝Âj¾ÀÏñãïøñ~|Ê´ù SèÙ¼™T?ÒQJæ zÒ9"ñ>–ßuqNÁ §ú?¾%wˆÅ—̺7‰Å5fÝ/”ŒN–þLN>V 1,Çv¶çb{%Ó¨ßáÇ;øñ[­¥|;Ñx…R>Àêâç<Z7tœHCåu8
+‡àÀ#>prWJU}ÑóÄs_ÖD¾ÂщÅ÷T½MNΨ@¿Æ7𣋼œÀãøñ:~ÅÃZ‰íu[±¿›Ê8¢}D|E¼› îBm'ô|î€í.85Égo‰ÿÁ¹ó-/Ö€Î(t"шÇZ:ös°_ˆíl—ñÙØ®Åæ|l6bs‰¶©ll¤2¶9=Ù‚ÇlâÍdsŠ¡ç÷ýÛ®­öëè"GÙ'Ù#ûõ$:¡èD¡‘„Õ ìçb¿]P‚ýrìÏÆþ\l.`w,$
+Ï“‘µTÇV½À·W“UD²Ì® ª—£»âîr¼]æê9Ç÷Æ Ž]®€Ÿ;åA¬é1ôü°B†Ãñ9 ™(䲬h”°êr4*ѨÃf,ÖRþºÏãy ZDd²+ÉG#ºr‹ëæ"Çü›pvrÔ¾ÄÑ¿…+`³Üµ^Þè Gk4:‘èıŽT-“éT—iL\ç…ƒ±YÌn0˰/Ì
+ÖŪ˜×ÿIíW=£› Æf¸8–Ý%ÉøK³(-¤¿¬‚Þ²Z¼å<å.z*<tU ⮥³b—e§e6ËZ,¯Ólù:M–‰…ñ”Ч¼#|Ë
+µÄŸËbFŒkßÕ£(Ç ðY¶ÑgIÄ[™§ÊLwU]ÕVÜÕ tT·á²vÓnõá´iµNÒb[Æa[£Ñ¶Aƒí6vÛר³©ëÛþ ꬟RmðvæB­¾CYÐcbALˆ½Ú7¬c~ÕØ§z<Ö(ºm;pפÑQ“ƒ«¶gm%muvZëZh©ë¤ÙÞG“}„Fû~êí‹Øí'©­¿„­þ&Öú·©ªÿ¡ø=UöS]g<¥Öà¾]¶[œQ49Ü y Æõ; üÚß«ZºUS§=’öú8ÚRhmÈ¢¹±
+©ì°`騥¢³™òÎNÊ:û)éÜC±{šB÷*ùîs˜Ý×Èu™÷÷Éîü 9ÿ §Ã ×e×npS÷?¯ûuBXŒ‰!Ñ'ºDk;4(>Öº•ýº"±tÇQÞ“DY‰Ò3%žRŠ=V
+=xÚÉ÷z1{GÈõN’ã]a—÷92½ŸÁäýéÞï±ÓûkÒ=ŸÑc`ê~Ê5Ùî3ât¦ÅááRLrH»®[ú(ï…¢þm $`H#Ï·‹\_!9¾
+²}uìÒàeù»1ùÉð³Ó¿Hšÿ4)þ«$ûï’è(~I¢ïï$ È=÷Ë5Š«ÒXë‚E±_Z#¢_t ½^ꓬ}ÒWt,RTÉŠ$k$S ‘Œ@:;¹¤ŠI T“h$9è")ØObp/ ÁyvO¼L\ðMb‚ß&&ð¾ø+±#r®ÃròCõ|Ç¥ûÄ ÐpÒ!Ò®‘¶Zjä”öBêØVCq$„’…‰øY”ª!6ÔBL¨‡í¡a¢CSl a‹‚Tdè¡o1þñ'"Çää÷lg¥qHL‰ Ý¢Õ§9(íJi+"åJ_±1uBp:’ع¢Ã D…SÙÎbs¸€ˆ°…Ma}Ôa½Ü°nËBywîšx f•kf?T#”ƒ›‘“œVJÙ­ïaH=AÛaÑ+\úß8¢9°JöI_ñÈ4)³ÒŸ‡ØÅMD”ùZ‘áY‘ÙXI׃(ORS_US[ÕǶª\µª[UÎ\=%®êø=«ð¶¢@vP5,O8"Y=ç¨Ô%]Ú6ë™mzæ²I0ÏH_º)K¯¸¸ý0l>.³wR¹ò¤ ×)ÕñŒŒÆZ†šL®-vëj°ëú¨Ö5ë!¡‹Ö/êøçÅC«pøÌ'ºV5œ4X–Ö„†,(zõÛ©g®›Ö Cþ¢ô¥›|Dú' z "ž•þ9ñü&5x¾óªãÅZpTÇ%Õñ’ü -vz'—5Ñ7ô` BïcㆎSç} óÿ&TÃyéiê0(:5Ö :½ò H×tRúëw¢ÎK÷’¸Âc£©ÿ)¯j<®)ß^×x¼¦÷rSïå–긭…÷ŽšßMìÛz°Û¼[¯ˆ¯è9ø›ªá5ƒYi…Gcݲª9 S OkþI7é‚Þýe½.•¹Ö5âÎÓB ²í2â‹“{2å÷ceˆù×eõyÆñ¯(”K–kÁ]XXXv]`9DPEEE-â}Ö3±ÑªÕ68ƨÑÇš&5UcըʹMššúG϶&iLšLâ9šl?“qòÇg~¿eÙçû}Ÿ÷yG¯ã£ YµÑÆ&×Fa·‘ÔVÒÊ[Oó?Ÿr‰§&û4Ý‘ ©CÊÆ¹šäߢÑíÏûԩÕ~á?­pÞ
+Óx½¿âä6§Oïtò´Âk?ñÀÎÅøüS n)ŒqGŠ>¡ÇüH^òWBéÔŸðqçèsϨ‘سuŠîõÝçÛø8¦Mhl§”÷‘‰6´Îêu¢½F¡ô*üöÁÞ§<°k1N161´‚){:&Ê2lâÖQ|¼‰#ª&öxÆÇ!MgÄM:ˆäc¿Ö ñw’ƒÚÅ/wõEªcj/ÀvØÏwz¸×YΗ:—ÛIrq”\¡.+ vâ»Ù*
+¨¸aÚƒÝøØIîÐ$bNgkbw%[éz\´h3n6i#‘7P›ë©ÍçP]ßIûû4ou.íw:·ö­h?¹ØK.vi
+4jИ€ÆTÍâmnq<ÌMe>¨¢Éì“ô_x ¾ù­ýÇÌËy-°÷5Å+8Þ{wÑÒn}W0z‘h%¨¹KŠšºdhVW—fvËWc·!šÞ½LÓºRƒß8Mñ›¬I~³Tßc¡&ôX«ºž[UÛs¯Æõ<¢1=/ª¦÷gªñ¤jßjàb(GÇË‹lía5ï‹Ã¸öphÆßì
+’B48)J^K‚<–åYìÊMv)'9_®äe§T(3¥VΔiÊH™¯ô”g”fÝ&›õ ¬ÖSJ±^ãù@ÖŸ%w°½Mè.³Ñï¥rdž±PÉç2(áûbü ¯µò¬ÁÊ4@n›Q.[’²l©ÊLÍ”3Õ#Gj±2RË•Nc’–Ö [Ú\YÓÖ(9íyYÒ(1í„ÌiWà¾S}JjÇæÓntÖsõ\œN-Ú©¨†r>—@ßåã)—§;½·²Òƒ”i“Ó£Œ “ìV¥gd(Í‘£TG¡9ÊduÔ(Ù9Yg³«drþF ÎýŠwþAFçÇ2:î)ÞáSBØ}Úγ´$óô:PË´†ó^ ùàæû,žgÙ3•–Õ_©YÙ²â5(Û¢”ìt%ggËâÊW’k˜Ì®*™\õJp5Éèf‡uoQŒûeE¹Ëຬ(×]Egûž“åS ±Wg²Ñ’L†1P%P
+-Z¢þEÿ'»Ìƒ¢>Ï8þdY–]p9d¡j F¼¸äÞEv À‚Ç®" «!ˆfRÏzD§1j5Œ6É$ñHÓš£‰1‰ZsØLµ5‡GÚ´4u¬Svœ´Ù~·íLúLJ=øíó}žç}Þ÷}ž­J,}Z¶Ò—e-=/kÉMÙŠé\!iZP[O»ÑhÍgÀ€J(†<ÈE{,Ú#‹é»K¥!å‘ʨ4+ÝiSš3E©ÎL¥8³”ì#»+W]EJr¹dsÕËêZ W—,®ÍŠwýXf×Q™\gç¼.s%kÅ=~€FG!ç":õP eP“idzÑUFüè3ª8ª¤dwŒ’ ‹$ÙŒ4Y!J0FÈbŒW¼1Uñ5劫©‘©Æ§ØšvÅëe<¡HãyEgé¹
+_+ÊTtuP«ÐX\Â~ ÆZpA ŸóxÍA{ ÚÃ]è36:<RJ 3àÌHYLŠóZeòÚ5ÀëPŒw˜¢½*Ê›«Ho‘úyù· $½ R^ƆƒpŠ`éšêﲃ}t£Ñ
+•œ‡¼V@Lâó8´GTKC‘3ˆ¿N²1¦ÄÏŠPìÜXõ÷1Ãø¸ð}\¸~u?®ŸMìgýñ3gú›!ÖÏ0å£Cò1¬ù†æâÜ`íäv.×ÁtÞ—ÂTÞg»Yb¾3Žôg¡ÏÈï—`6¢•F§¦§fc!ý"æ©
+é2A]ÁØ•KpSåë9 Èõ(bΠljÏP!ÝŸÂ+aÍp:¬ñ+ø5œ ‡ÂÇpH¯>¶?ctÍ$Ý`]¾¤9¼ÉÅs‹Mx›B»à@w~Ç„ ‘q`…
+£a2”B5ÔÁX
+ú?>Ç?±Y>c2ý”ºDÑ|„ÊoIÖ9Ýd¦ ê8Çá8ú>¨//w•@<)Ä3½Ñ̤‰£ûNìL® ú?>Äß0ÕãÐú
+ÿDó:\êÓfk÷m÷tÌä*tìÓ3zßs¨¶<l—`Û‰m¶gb¯IËÇ
+´R K9Òºñ`­6¡²oÖ“‹ul®µúÜÕtÿÃjàÔÖÅðÖ 3œèI{ÈÅnYа£‘É1™…ÆüÎÕfå“íì;±ïÆþLì5bÏÏJ´q´ëaþ®À‹åxÚE¦:Éd'•Óâ²¾uø#zo‡ Ð‘ÄÍF¾BWAâ¨t©h !Ã#Їý‰Z‰ÝøÑ…áG'~,#íšO&ZÌD½ïÛX¡ª¤…Ú\@m. 6›Ñnïòs­¯ÆÜ;êwÁÞ¯]‡øñ{d¹XN}v²&ºQhdk‰&¡QÀî,EÃÉ*xШÇî¼h!#í¬Î#|ÚBåîÑ,Ö£‘¡‘¨½ìr/Ú!á4Gû®™Z¢-°
+ºùÜÁ×Îu¸T&b²¢•ŒV&Ö³ÐÍn˜€Îdt
+Ñ)C§
+Z2шíùìÞ%TìJÍ ›µTO Õe°?<ý®Èq[îÈ Üýƒ}¯Ç¸öŸåX߯‘ºº` ŸÛø¾šã"ÕmÖ¼þ6ÍLÓìþC4+ê5F•7:Gõ1SUS¬™1•š1À£šõ2b}rÇ.Rul·ªLTi—*M½ª0W¹é¢Êân©Ìü/G¹î÷$3ïÑ®¤[ÊkK
+-ßÍÆŸ&X¢ÕdŽWƒy êÌ͈¦ÚøQ2âÇËc™$·¥@Õ–2UYªå²Ô©2a®*ª,a¹J¬4K°ÈÚ«ëÊ·~ª|Ûß!È{N`8ŒîNZŽu\³Ä>Þ7ò]ÿ›/µøRk3ɰZå¶¥jºm°\‰ÃåL£Ê¤ïª"iªÊ’ŠUšäTÉÀZM8K…[T`ïTž}¦Øwj’ý9M´¿®ûEå$ßQnr°‰vÎJôˤþ“®ÙùÐ3i“ ¾¯Æ—éøR•-g²E)v•¥8Tš:LÅ©#5-u¼ŠÒ&©0­PùiåÊäÑ”A^MÔ¬‰éíÊI_­ éÛ•¾OãÒ_ÓØô ë¸ÅkÏ÷x
+­G‡Ò‚ÒŠ.¢ œ õ`ð¹Šï+ñ­œÜ”9"Tâ0kš#Q…i*È¢¼Ìáú7Ûeõy†ñG (—‚A–uY`wÙ]`e…]•]6ˆ¬xâM0žxD£†â ‰Æh´ä›hÔ¦:±­©M“š4¦‰=Ò$ÅΤÓvÚI›ÔÛ´¶;³ýÿ^3æ7{ðß÷y¿÷{¿ã©Íwi¬©J>S¼¦ªMMª,˜)OÁ"•tÊ]Ð-§ù)•šËn~]6óÇp¢²Då€o ³ã}=׿%؃¹Ð
+M|žÌ÷!ò©§.ãÀoNP­9M¾1#U3&_Õ«ª,UVÈSèSEᕆ嶶Êi] ‡u¥ìÖ.•Xw«ÈzH…ÖÈb=·x•µpgÐÙŠælA»S¡‘Ï!®ƒãùŸŸ|Æòœ·(^UEÃUYœ©Šâ\•—ŒQY‰Mî’29m^•ÚÆÉa›,›-¢û<Ù—«Ð¾Yû.™íe²¿ª|û9¸)“-ª0—pv Óæ*½ˆ-˜M0 &ð]Ôðÿ*¨pĪ̑,Wiºœ¥Ù*-5Éî,’ÍéT‰³RÅ®:¹&ªÐ5UW›Ìî¥2¹7j´{§òÜ/h”û¤²Ý*Ûu]9®¨rƒìFc3×Îe.æKFB€ï|P eàä;GY¢låi*.ÏRQyž¬VØe©(—ÙãS§^&O“F{f)×Ó¡Q•”]ù¸FV>¯LÏ+áy®ñ>ª¬Š¨F–GµƒØˆ½¤¯ÍÔ
+}/Þ«P06F£k“”[—¦–²ýyºÏoÑH¿CY2uÊLRz`šÒ •X£ámJ<«¤Àq%Î(ÑYIþ{J®‹*¥6ª-h¬¬–棘
+qê]øüC“˜&é9³©‡õÏx“Ðò¬1¶~Íc†ÎIÃ2è7e§á8c•~³ö!|D¿^ K¬ÙKŒ—Xð}4YÔ×+]$ØÅ>¸£À#œI̯e'{
+në2.ïSÎïY4¿£y­½”åy] ˜çõC4ÏëÝbš¢ú9¼ehŸ€ãÿ'‡{øÌ»Ja,#tZ\S ZŒ£–ø!}‚ŸêÃ5}Lá°Îâº>ÀU¾ÇÆñ3òø õxG;h“^Úåms·{†V½¬ï3šï¡ò]8
+‡áÀåÀJe|âIé·†NSÆ4hTÉO[„ˆ}?±#ÄžIìy´êbZg)±Wã*7êeõÐÆ»ôïj‡¨ÅAªr€™zÁÐ}ž†}FwûÛúŒ–ã„§ ShÇäžGîV4\Ô­šØ~b‡ˆ&öTbÏ n•_Äò}Ø«õ ýÑ«mÄŠ¿ÚM-vé§,õϵÅ'`| …/>g,³“Æ’8B-^T*ñG²™ˆ_L|7¹{Ù¢üÄ»‘m«™˜Ó‰9‡˜‹Ø:—w [i³ò¸ºÉ¨‹ åFµ™™Ü¤¿ò`< WÐ;k,÷—áExö²NžT2yg?‡øfâÛˆ_F¥½tŸŸøAâ7
+ñZ™…9#‹9N–³ZÖ“I:Y'«¨Ö
+ª¹\ïÓ9×yþåÔØv÷Û2»È?œø™ÄÏ#¾…Èâ—ßKÕýIJBÐh&þtª0—Uû
+ þà‚
+!:Ì'©‘øã£×ïë÷ý}Ÿßù<_+ñ–£QËŒ7 ÑŠFQ4âìÔ$íMÁ,«cŽ]³O›‰x½™aö¦YEVWòÏá^e¾Nº{‰Ô{®ˆ´JÚ;ÂõQÒÎ>žï!®ÝÊctMèØè‡:^FÝNÅtúÑf6R´;ÍI¶ha†Ó:fmœÑM²‚ÇØE£¬¼QÝÓÚ£^C÷"Çú)RîQX€Ý¯s<ÛÎÛ
+²µuM!}*F«jTêÑò¡ÓÆ tj"«Wë²5ž=¦dö¤Æ²7kÄð°† 7,jÈpV†Ëê7ÜPÌðE×,+šs_ÑÜ´bð%4—J˜{JÂCvJ.ØÁïYžm\)W‰eʘ£©£&ó¬šÈ+W*ß©d~ƒÆ
+Z4ZÔHAD‰Â˜â…Ã*œÐ@#S4¯hÑ‚úŠŽk­ñŒ"ÆËê6ÞP§ñø“:MAZ]Æ´® wšRðIJãýôfaŠû žó’X’æ<™Ì1Ù”097»4dnÒ€9 ~s§bÅ}ŠÇÕkIi­eFËœº, ê°WÈrFAËeµY¾*¿å]ù­²
+Û7(hß¡öÒ}
+”Ské³j.½¤¦Ò7ä-}G¥Ë\ÓÜ“á<:ŸBó %àC¤ØYXcÜÇ)Oø/Æ;Qbé-ÏSO™Y‘r»ºÊ«ÔYáV¸¢I¡Š6+»Ô^U rD~ǤZ[ÕìØ«&ÇÇÕèxFÇ‹r;^—Ëñ¶êäúîÉŠpãhî§ìÚáÆ’À8$¸ï‡¾:jâ‰0.ÕkÔQmR¨ºDí5j«q*PÓ m«ZjÃj®í•Ï—×9¡FçfyœÈí|BuÎÏÊé|A5ÎWUå| þ
+dŠšeŒÚT­TaÔ¥‚¨OùÑ rc½Ê‰È›VV Ã0ÅÈÆÑË€‰âoúþ
+éUÒþNÚŸê` ÝâÓ+/¿Ýh× ]±V²G%K¶Lƒù*ˆ›”›°Ê(Sv‚Mž`á&Ô$›À´&0Œ ªF)~0CÄ0”^e¯oÁ­C#½üîäY;øø]ß#±íU¶ ›bƒ g)4O†$I-Yüà@OqئØÀ)XŠŽ¥ø`]ðx)Œk
+Ó˜ºÀ»¯æ$yÒ«ì¢o3h$a
+<ß…oÃ7á9ü‡â|N¨ƒFb-C¡OoãcÞÀ¹\#Wp9/ã|^Âu½È¹Š|Ž<~K¿¡hM‘^Ñ#8Ìï0lO1|ÕeÔ.åc¾_ú¿Þƒwà&¼©2âØ‰ãÖ_ð3/ã©^P'ú=‹Cú%îéiœÎ/pZOà¾~†»Œ|œÍâ1òø!Åü}
+ù"“zARBéLÎy½»û,|>ŸÎçðOø°séyxŸù4cñ$cñsyy‡0åÖÊ;¤ÐîG{í ´W¡=‡öíhϳ<¶â*wònûY6e6N“ÅY–ìâ]adÞZˆyNÀÇá8°Kð΋eôT¾¼/·df©¼… }?åedÛÐNëaÆãóA{í)tgÙ"Ö£=¯Säq’y9Á¦ò ¢Ü¯3ú
+Ç¥èOÌTnƒðaدïwð“ü’ûj~ùŸ‘ m+ÕåÐ'ñº§`¹6¡@;vía´³hO²…Íè0y$ÌË>íf{;¢{ùÕn2¿‡ÅµSϰÝÞ€ÜÛa<—_ÖœêúB~Ëc7EÿE]ÁëÆëÂëTúÍÚK÷2/{¨Ó]B?‹æJŽ5h®g¥ÜIÛ8Zö±rŽsÌœáÎy²û)«éuþçþË&x"¿<”ß傽 ǃ‰Ü-hÛQtýú1ôè§Ðï£"‡ˆ‘Eo’•2K•n B>@6»X=Gµ–‘œ£Rf©Öf}†•6CìÙ<ßËo»'óGÛôÂqÈÖ‰JúeäkC¿}7ǶŸaŽð1’¬Œ1úˆ1ŒÞ-ŒÄ4+fÕ:O¥l#«¬¢¨à‡©ž <ñ+xr ŒÃù¶dŽ™û8òö”rD–.¶w-¶
+·2k‹9ês†9YÃÞµšÚXEN³f'Ù;VR+¨ ÖË8WYž\N¦cÔço5ÌÌRÁTù
+>K¼c´@» ZûbK¶†ëIç~–ï—›—hy‰Y£E6Vh¨°Fƒ…~ , )³¤Y}E õ¥ÔS”Q·iT]¦I¥LkÕiºSíÅ;•,>ªDñƒj+>§VÓ%µ˜®ª¥øºâ%9µÀib"î6Ž·NÞ&!Ëç½ßË€µXf‹2e†z˪Ôc®U—9 ´9ªTy‹:Ë;ÔQÞ£eåÃJZV¨Í2§V˼â–j¶Q“åAE-ç±\RÈrU!ëu…­9Eàqös íèmo«hnáÿ0ôÓ*÷ò]7ÏtÙ Ôµ´Ti›U)›C¶jµÛê”´…”XSÛÒ¤ZnÅiþcÆ„šŒYEy…j0Ž*h|JõÆ×TgüX>ãEùìÿà:'?'ÆnâÝ…=XKÛ³’ö|”ÿÐý49u_;Ï-s)é(WÂa¨ÕQ©¸£V±Š€š+ÕTѦhEJg¿Âάœkpn”¿r»ê*Ë[yZžÊsrW^Ruå pSngN5pñwoc-u@[>CÐ )¶V­äÓB.1W©š«ljtU(êr+RíW¨:¬†ê¸‚Õí
+¸ûäÇøÜ«äuo§æn¹kÊUsJ•5_–Óý#9ÜÏ«Â}ƒëœ*«s:„þÝÄ»v|ÚOýA?tñ¹´bq¾o&ŸFò‰xL
+y,j¨µ+P[¥úZ¯üÞ ê¼Mòy“ªõuËã‘Û7)—ï6Uù>(gÝ9|'e÷="Ã÷Ù|¿×RßuÞœìp
+›9?ÑÞ€öڣس^è€V>7C„ï‚Pǵ§Irag+Z
+e´•Ê–°ÊšpÈ’p©œÂ1“xi²E%É”L˜Æ%ÉI$1ILY’Ó(‰ IpB'žÄP½ø¬¶œ¶oÚ+h{Ñï‚$×qhä~ü\×rß…uc ÊHÊÖ^"sÊ¢’”!SÊ©%)
+S jФñVé `Óø«ôVà4NaRRRlœÖäÐIÙ‘Ó]hϠņd “ë6hŽ3
+Š Gñ@e"
+ÅJG‡CÀ)ãØ)R¨ÖZ§ÌСPflÇik©RA Te¬jÙ~ÞìÛNÿøÎ¾×>ßç÷¿ßóe`À.˜Éц³ñ£?:ñ£›œô˜…ÖMðº'
+Õõ>ºî=à»è™wðã$ñø) tŒ¦9Ãa|ƒ$ÆÓ×Ñ™¯Y¼;Á6ÐîÂù¸f¥ð‚•â_)ûÙú…ÜØb¿ Û5خǶ©Ú&b{¶gb»ƒÖxû µOË)¥5dâÚt;m´G/QÀß'*;ˆä÷àì›ÀÓà)p ¾¿Xåô¾Uæf{&‡ÐwѺИ¯¡íöP¯àÇnâñã.ÞN”ßíÔÈVjd ~ôÍêEá=×Kxó:\?דDtœO€Õ`X >¶Êú¤ÕÞû¬-`‡ˆU±2°›K«ú@ ¶+°\£g©§Q”Q´¨‘õÔH/5²?ÖàÇãz ûOR­ýz”æ^Nô–é·l·ß°ÅǨ Ùc~g­¶;hµ$»ñíi=±è• »ÙlYnlá þWÀPƒí:l7`{¶'Ñ!­ØnÇî-àn_Ï%sˆÅƒtAz·›Šî†×D—…w¬mÀÜ_P|û¬ت°”Ž]>ç`ÛtdûØJÔa¿ûã°7‰#u:ÕÑIDæQ±R-4ƒ,M§GZYm‹þ
+ó*ÈÏ—'ß'·;¬<w¹rݵr¹åôLR¶g¶ Ï9<«e÷l–ÍóŠ2<?Qºû×วŒ|Î lÏ…kò¨™1¸ C@Ï"øS ‚|ã^PP˜,w¡My^C¹Þ\¹¼…Êñ†äô•*Û7T†o”¾fÙý3eóÏU†…Òý›”êYɾ7•è;®*É{GÉÞ˜Áæƒðµ WÇ!êA ¨ƒyAÞû¸¹Ï-¤œ@ºœA‡Œ ¸åɊȪ’-T§ŒP“ÒC-J ±Ó†–)1ô”B»&Ãaa0ÈL¸bZŠÝNø¦0‚uÈ£j~Ë@$À_@ï½ÀÃuÏŃd„SåˆØd‹ÊŒä*#âUz$¤´H™R¢Ã”mTbt²¢•(º"ÊI݉ÑÀ)¡„a³„é%ÌÙ‰ÝÙð>ÀÈÙ¦A׃A˜çAà‡·à?L— p”å†ßÍewÉ^òg³¹’;¹ï†ìn² ¹,$„$Ð\ TÄ ‘’
+„‹­ãD¡8Vª^¬µ”BqÅ2¥…Z†N¥ö6¥#Œ2ÚÒ¢±í ØÙ>òÛq˜—Í;ïùÎ9ßùÎËo÷Òyn”'È[é«Ú%gÀ+{ ]Il%
+e VPÈ!ÀüdŽ"Z‚ˆ¤ º"ø8Œá<À<]ÕLpUq-Ãî@™Ô‰h
+²¸öc> )ë $ÈUc—£Ö¥¤ &ÄSÜa
+(ŒÁH w#Éâ)Â<a–s2…9¥C§>ÔÞq-ÁîüJò€íFP ª¸.ã~1¾äñ›Íµ¿~L{‘ ®MŽH²ëiô 4Ú©“ -JG l”¢šl+@È5¡¯¢c`œ÷*oð >40E70¥c»Û­ Ô +¹WŠ/üæpí¯¿ýdK©—,1!ÊÛÌa7“¥•fÞF3ÑÐbl¢‹ál m› ¨‰B&†xjG¸´ä›Ø„øÐ×
+¡É·à¨Åe
+Ä×)VÞ’)V¥óšp—ÈË»ôK4Úw(¼_“à ¼ÍBÎñáÏßSÕûî4n;6YëÝa­s¿µ¶Ã_¬Ëâ¡Õ"™¤³àxPæz^dŒtÐ
+u|L/½ANþAùÞ#ý•Âú$^{Yzÿ<S­þÏÑUŸË)¦®ý ”[x30•IÏPJ_e:F=Žè3ŠæSÔå?)˜(ÑëÈG,â ¸J€.ãÜïyó_Ÿ
+ë¨âù³£Ãt€0ÜPÌÜÏqû5Žþ;‡@ãH×1î·q¶(ûNì{€‡äÀQ@¦ÁS ÏtìÕOxÂËZV1j¨¡ƒTW€ÝS­çUE¯¨Ò_Àmç^\[ÅÆœŒ$- ƒzÁlÐÊuÇÞ ž5àO}‚Ž8¼¬! dÀ• W!\¥ðTÃâÿFl·ãU"[ËyºO·µ½dð¸n‚8÷âÚÏ(GÊFã>ÆŸ9 D¹®!žMǧš)‰ª±;Lr+ªj›OU¶,UÚòà*†«‚.V_½¦ÙZUbëR±mP…¶¥*°­Qží!ªy7^ÿœ'’q~ãú&÷À¹ˆ±§—£>š@¨å^gU ÂcS…;Y))*wzTæ0Tj÷«Äž£b{¡Š¥*tTàˆ(ßѬ<g§rýÊq+˹Z™Î•áxJ~Ç!¥ÛÏÈ—| ÜqmÀþ2øË»‰[Á îU‚2žO%Þ${*v»TäòªÐåS¾+Kyî<Mu—(×]¥wXÙî&ez:”áéWºgX>Ïjž-Jõì”×sH÷OÁUð/×ZlÃÓÇ(ÜÁ¨Ñ " Tr¯ ”àOï¤%*ÏHV^ªSSSÝÊõÊIõ++5W™©ÅÊ0*å7BòMJ3:eòKå1VËml–ËxRSŒ—å4NËa\ŸÉ‘J߯öbøæ1Ç' ªA9÷J@!Ïóñ%7=A9¾$eùìÊô¥(Ãç•ßç“Ï—­´ôP&#}º¼éòøcrûçÉõ?¦Ë8Ê«
+Ão6»Ù$›,›Ífw“ýI6›l~vIHBHè&@˜J$
+†$•AkJJÁJ¥´RZ•j¬Å¢¢ÌXµÕZdZQ¨µ t:0ÕVªv¤Ú–aèV;èúÜìçè0/ûåÛ½ç=÷œsÏ}o
+|ŸÓ·Cy¾d÷}K¹¥Çe+½ ï{²y¹3°½¾ë‘ÀÝŒ‹sÁlP⼫¾¯À—`™Mþ2»|eyýù*ñËðÈ(!ªUh+ت‚`—œÁ>9‚ò7ÊœRN-àF Æ¢®‚Œ6`|K‘ZiÐ
+’ Ä—¢|A|ñlòrå :ä)wÊr©(4K®P©
+ùQ~¸RÎpBŽp³ìáNÙˆö/B×܆hD †Ñ²!|½ÊÆPNc%|½aÎ#hŽpøŒƒJF¥0ß•ãK <G¾r›¼¹ò„ìrGœ*ŠºTPéVÅâ¨ò+·*¬œXç“€-¶ Úc
+±-€[©ê ø1Æñ!Êœåî„§Þ…ÈïöJò
+qÅ´õ¼öyÖ¼# 3Á×6\/5ð†ø»”ð¹áÍï™Qÿã´øŒÐûˆ%úÆ,áiD e§ ÄÆ$~l¡6¶“[)°Íl&`“,Ø4Åoö!š˜$>öë2ꄯ•ýÖóMSÄÙÍ~†w9XeqrÄ‘ìÿÇ&Áf°ÕâÛÁíàNjänüø µq/õ¹‡Xì¦6ï,ºƒéaç“Òôo¤©©“ý¦øºš=puV?ü&¾kÁ¨Åy“ÅG«C&gð–åÈ3Üd…¹L(9!éQjä
+öì!Îù#–¸°„î6ko,c\ÉŠðÿòAü(øª²f¥ï‚Cà~åÌå²y†Cv„Í&¨‡Ú“äã{OKß¹¨Fê*Jˆ¼¤ªÐìy“W³Ï=烀%¢ÅÍ †çqe‡#3 pÜHáÇå†\“΀³äåjô<9y™¢{‰@Ÿ&°/|N:‰gT ·î"bcö|'àkÖìðuÐâ4Ãà•Jg-®_€-¾__ƒßƒ?×Á[àmÎËrr™³z‰‚{Ò¿î”þr€ßGQ
+•-¦@¡t¥÷˜%þ®"}€vĮ̂N£|S tÏLF×8$P¬ï3\¥PßE¿¿M
+3l3ÃÔ‘Á–Ä)e­X›Å%üx?þÁ<ó>jø*Šù
+
+þ-tûëL4¯áÇ&¹ßqXÏsh^¦x_¢xOãÇ‹øqJ»tR{õLÇ(Ž£ú™žÖßPõÊ&˽Üvƒ+p^¯Z!¤‹3ûNe–8ËqŽYæ ÚýÓÄóøñSâqŒ†u”Æñ êsÝašÅ(æCº™2ù”ž ˆ¿Mñ~“]äô
+¥{mfß{ÌL¶ƒmà5øÎ:'“`6Í?š)³=Å|wˆœ|_APÅŽê±ÛL„籟.ìöÎL²ûi$_¦y=Bãz?"÷kŠ£zûÝDZ}‚§“p_!B<DKƒM`˜2:~b•¸9^_±ŽÁÃÊ#nÅÀ‹ÝrŽGL_À½øñYêã^.=4ñÝäæ.r³‹Ù‰;ÈËvÝÂo'3÷é“x¼…llÖy‹†ã`<g•»áþ’uÍ‘Üv‹*Ð49ÙÁŒ9ÍT´ ?n#/[™©neú»… t’¹™ÜLhílˆ
+ÑFþßÀ¯n$'ãDhŒ R-ë©°µðƒU`¶ŽüVÚfµ&Ó&L{ü¸ìØs(!lǰ]‡Ýv[aì`÷ÝØ^L…öS«ù7
+ÇU;MÅܧ°,§Š¨ú~x? ú,<fñﲸÍU`®s%™v½†X SŸ«ÉÉjjcX~l‡ð?†í¶“ØmÁnQèÆöìp¥ßÀÓ¼ÚJÕÜÍU¿Ÿ¬=ŵÿ[À}mtó¹ÏÚ¿¹
+6ZÜæº0×jT´qlÚA!vÝ „g?†#†ýö“ØoÁ^vP­}TÊ æùN²ÔAVçQQíÔf»^Ð\Nû\¸çZ]g¯³ÿuÊJËꘑ*F:tÉœJÓ3Òš¼pàc?†ý¶RØl¥BæãMæ™fbÑÄÎfsR0¥¨º$Í<©7Af¦û™ º5†²e%Ñ"æÙH'#ݸñ°î
+Ô8ïc Ò•£h¡C‘ü|Eò\
+;ж{²ûTn‚¨‚ö¸ö¤üö6•Ù»ä³÷©4wXÞÜ*É’ÇöyyrÃûgÁEðo²šÑØ/óè(Ë3Š?³döoæ›%3“™¬$$`BH@¢¤ˆ ‹€U°d“­ì(Q¡"‹¨EED
+TP‘M(ˆÈ–º€- âZµ"¢‚"K¦¿wæ;=§žþÑþ4œs3_æýîsï»Ýgü}àêÂß–6¥(M@ÏrAˆk&ÉЬõØ%êvJÄé^ ;’î KÈ— gœM@™ø ú³Z|ÎâuÍ9I<Ž9âv,—}“¸l‰+팸¬ O8:ê¬Câ_ ªmkÌ÷<bÔõ™$ìµJº×.!Í)AÍ-A&.~OHtOÈŸ§H¼ZsÑ´ÖâѪŭÝ$N­VÚ±i³%M["VϱxˆÙsRÌî„Ü¢Úx:À{u€>4ù ›gqéºYBzšt»ø}NÑ}.ѽñz½âõDóEÄ£gqé%âÔ+Ä¡·›~ƒ¤éÅ¢“NÔéy}d8/aÇKbñrgÀß¾*bNEˆ}
+Aß3A ƒß"¿Mü~‡èºK¼º[4¿[<~MÜ¿¸éâ ÄÄÈ[°©¤[Š%ØVÌA²[ärÈNjð?‚„ôåý]༆–¤e˜y
+´4J¹C96g! -dSCI23!Ýrmœ3™ò‹AÈæ{ŒZ"ÔŠ˜%
+öESjh’ërÙ“¼³‚\¾gRG¾Ðùì…×Íßì¼ÖB Ë%ÁxPÂEWjI],-™“VÌI^T0‰ ªàG š¿‘FZÑœ^IRkA#VNŠ,c.š'äZ–Ï• À‘Wœ:ÂÀÏg/Ï\ðÚhÕÌL1—$ã’iêRU—kkuÑRG[ê¨bNÚ±ÙÚáE«ÊAΚh˺¼†ýÙ†äTI‚n}RÉ»Ë@S´å3$Î(õøìå¹ Þ4Å[f\âÈ"˜À h /\4 š¹éD×SGwêèΆê†Ýй+yÞC€F¶3‰­gDõv6gTû„T” Ÿa[>uÄá Áë…ׯµ¥¡µÒ4UFÀéh„®Fèºô4¥B@?ÖhöI| búaX_ôá¬êMbêõˆHŒù†Ð”–ð”°tЍ%Þ0>ëøìB¯¥ÒSí Î. ›¶z$Ãf*|Õ¨j@8‚:F³FÇ0'£YP£yùHŒ`ÐpΪ¡¬‰Á4°·ÐM LH9:K˜²Fðf°ý­áççæöÉ ™âT{|´«„j¡…HÀÛÀh#O
+6izEq³Òæe-Z^YqÕÕ­Õæš¶UíÚ_WݱS—ë»v¿á×7öèÙ«wŸ¾ýú8¨vð­C† 1rÔè1¿7~¤ÉSn¿cÚo§ß5ãž{gκoöýs~?ïù.xè‘?.|ôñEO<¹ä©¥Ë–¯xzå3Ï®Zýü k^Z»~ÃÆ—7ÿyËÖm¯nß±s×îú¿¼þÆ[{÷ýõoû¼óî{‡>øð£Oþþégÿøâ𑯎~ýí±ãß}òÔéÏœ=ÿÏŸ.\Jü¯ÿôÿÂõ›ÌJ±]Iö YW¢ÓQ¡dg+Ýù/RÊK”ôr´·Râ+•úk‘ßAéïŒÝ”7) nƃeÂ-Ê…¡Øp›òa¬2b"NLUV܉w+3~§Ü˜‹P~<¬ y G+Kþ¤<©Ã”ç”+/bË:åË&eÌ+8óš²fòæMÌy[¹sPÙó>þ|¬ ú‡¾T}£<:I?(—Î)›.^JŒ>¹\Í»%À~¾þË÷_¸_b2™Í‹lv»ÃáÄ·Ç£a…O×ý¸ áG8‚#1<Á\Á|ÁåLÒš”7?3'é Ötë®¶;©fÀ Ú[‡¿mÔØq&O6}ƽ³fÏ7Áà [´xÉÒe+êØ.j·¬[ȤƟILiLŠT*‘‰N„¢©hýòj~Þo¡ø»'¾Gô©~8î3gÏž;wí?]¸pñâ¥ý‰ºÆ¶ÄÓeÞÄÊÊhbm‡üÄÖ®-;zW_Ú5¨æÔîaãï3ûЖñ‹ö®›¼j×ê©›¶­œ¶sÓ²»ÞZ»dæÁÕOÌý¸î±…G. 54ÔÐPCC 54ÔÐPCC ÿw +ž¡†u%^¡†]½ª/Ô¬9ùúÐ _Ôºÿ½×Æ-~kóÄçw®¼eë ·ïÞðÜûV¯¸ûåOÝ÷Ñ¢'æ~¹HÖPG ÏVFë©áÕ®å‰=½ªÏ¿1 ÿ‰}C&|±wäœwëÇ>ùÆöñ/n{yâ– /MÙµzÕo.¯›¾Ѳ{ß_°dî'—‹×°Š6RÃk]Ë/¾Þ³ú̾þýïÿ{t–tž
+)“U£k„aê"a¸>WÑ~›Õ“Ä¢™¬ ð?J!ƒìÔ& vøeÞàzz¶ÓÃcòÁ-Ì£>ÿ$CkS®F–ŠkCe\ ¦)Æj3Jñ-)|’‘ʉ鱶d(;¼ÔØnZÔÚÿs®ÝõôË>w'C71}f¿Dm<WÒ,ÈQ•÷d¡uY•Õ¯¢ð¦øb …Oi·6 € |È :´ÔÙn\h²ßó[÷ÅSÏÝ<Fǯ£Ûú%ÔáwE:·°!D|·%K—¢‰XuŒˆ # ÉÍÖ?ý×°
+”úÔÛþ}®Ù~Ï´ÉÅv|ôʵ§ž¡Mƒ¾4Yg`&_`3ÕÈ’;
+TEr-ZF•båäJ¼_©±6PüÓç@
+J!CÝÁÕ³Í'×™í¶™ž:Ú´Ï^tSM_CˆÆ¼‰¬€øŒDzŠ1$›ÚÊ$7‡±M¸¢0-¢‰,¶6PÊ÷¬XPüj²ãäw摳ÛZ_8žl|ërE:ëãNxc³‡¢èñ´~T*¥à vâ2pFB6º”ÜÅ´6 Þ² ÈvÿeF`åàÀ±5m“gw¨ß8ØÈ€óÅ’9÷[yÓÞˆ´§XÚ’DBGG<ÂÆáñ3))Ä•‚è‹¥[¨Ø²l^¹ë‹Ñž=ËÛǬSÏžÙ)vÇÀÉñÞ{7wÆoïø8œ2…
+‰˜ÄbpOñ8ô">äßäÄX4 >J´6 ÝüÙ3ÃæO;F·.×Ìþ^²h»ƒÎÊÎÜWã}®FÍ]ÇÏ…Þ
+{óEÍü‘¯"‚f(/cƒ`Ó4„µ½Ñ¬ÿ¤çñ7Ÿh¦^!}wø>8¾5œÞKÇhàÊ™Hàí€AΡ Ô5à.-F\ |é{Gq÷{ëá»ïim=Æ/ÿ¤Zý©táçUpàûpdK*8¹+Øï#€K‡ÑÀëD0€Ÿ
+¨3þ
+ä×çëüý¦j„áò°”^A³µ€\¢ËŽ‘*´†ºÔ$,!­µ2.«KÃêãGv[ÚÃ=ÈÀú×ZP
+$Aê=]æüüž+‚#†¤zwþ^ ‡X¦aFÕ*²b«ÓZ*SR»D ™&^\¾™Ëîµ4Àúh`C¡Í@v~'¨¿x¨<\æÕ¾þÏÕAÄGµ¡i]8¶^@(W±IŠÚ<Ê}IÕXžAï-ed›YIÌÜDV¿¥ýa(„ e¡úü „ —·Zÿg:8éQCHz§,¬¨I^ÕÀPÕQš%ìx£Iï-ÊÊ0ç2˜™¿˜,탡
+8gÜ:
+ElA–,DBHHB@¾„- @„%1, { „E¬¥Š"
+ZDÐÖÿ|§íñ>^õ‹çþwõžó>±ýL³pæP8:HÁå"ÿ•—Oêl@Hü4†@D E> E…ß§Ðñ÷h Ìx 3ø{ =–p—Ã~ß@¾ÃbI»ËbüÆÎ†)Ëõšy›µ`o%†³6…àâ”öÖË5~)ð
+}çOztõGr(î ‹yÊÀ‡ÌĆ¡ŸÄ_ x ÷{œL|ß@¹mÅTÿæåÆÉí+5s7*àÄNØÈ3GRà¼]xž£Ú…øçŠÿ)Üó†|9x‰æ…^d\ \dyû¿bûø.pP¨…dß÷m®ký²Ûk—µÎm_¥
+b} þüà¸\¤÷ÎhX½L3ùÇå5ðéj1|¹‰‡¶%À±/h`·?œbàÒ!4øöƒÐ£( ÷†ˆ^@9é ´S—aã±6îÀ¶½‰ïäïørö¯²Õ%'¶‚ä«ÝPíb 5—íAà¶(Å=‘)|FÂÌøìëIÉS|F¹!†'ÓÑòÝAcI¤n#V¶´ê4j¼Bkn    ¿{C.bÈE ¥ˆ¡ÒaÔž·…‡=Ôú»½’aƒg*"©÷tî­VîP*GhHL­ÒÅf)º¢sUíQ…ÍšH¡¶%¢²£‘PÝYµ¦ÝÜ~5ì[y‡6‚èøV!†zgkhpw€¿K
+Lèy}\DMå3 û3Ù]WÑŸ¦ÖÆdkšijJ©®‘$ÕË#d=U$y—¹AÁŽ•‡
+¬7BÙñ-P¿¶Õ%P¡Ü_6†`×…3Æ$äô‘âè}N¬¬+5A¥ILÖ6±2ºUŒ<C­ÔTE.3•Q*{ÅY¹½3ð­7@9b¨±ß*ÄÐäæ
+Cű- °ÿÔNÖÐâæðVãí>ߊÆ=jÂÇÞVy& ¥¼»˜Þ Ée¶©³¹=™†š”<S%·tX+ÉgJŒy±R½¹½3!鱿Abhv²­«ÃÏíW<æÚñ“Z,ë–2<·_)é*£ª4¥¬6%ŸÓSÍK3J2x&aª`8?A8’Í®èÏbK æöÎ °Ú
+ãz„E\¿ ÝÄËÍNË^禗pÓ$FsƒÂß %ˆA†”ˆAëhùSÏ·ö zOû}Ãô±Gº±Ý-á•êrcµœ®)«ˆí.&xÅ©¦´Âœá¤Bá›W>Àή0šÛ¯†½k@ˆäGÿ
+*ÄÐîh¹Ô÷ý\¿‡Ç̰O؃ì^LA›– m¸Ù(UÐ4Å2fw®„­O'p…¼¡xaé0³°|€YPa47(üt%ðƒ1Ô µÝΟ;Ï\0^°›vwŸºívgÔŸÝg ÉoBþ®¼™Ø l ´æÕÐ;Óe¬^Ž”cdUd˜¢Ë éâò~šPb07(úÍPf¹G?»Ï^÷œ;87ärzú¶›ûĸ~äŽ_|‡)8WÑ…­·„Õå5›Sä¶œÞS«’rûI’ì’TdŒ¬’èÍíC!b¨@ õˆAsú/õç<»qÞvòž«ÛÝ—q†ïýXMCAÙRFÄ×àªÓ¯Tìbkt-¹#²šÖsUÆêÃV%ë±Uù}8Y±ÎÜ@€Šö®©åŸ@ydóëŽÓ;fÎþûјó©û/¸ Oy`ÛðQ\Gg”êCÙ8 §•P£&*)ÊÈ&bµWÇl ©Mì@×fvÖæ˜#b\MG6Íël·OžÝ?þÀñäw3.®}ÏÜCU(šä&:©Àš›Ò‡+au*¨mDYD+¹6¬™ZQÇ(ƒ®±¯ù«ÒÔ~ÊŒ&sƒÄPºçcP|¹öMÛá 3¶[ÆÇ¿Ú3òØñ”þ¹ËEÍ‹KAòIT¤` —9ŠIK0ás¢ á…ä^RéÕŠ×E«î`JÛØ2_mŠ¥M«67n·€ò®ÕÕ/tÿ]wï¦ÍæáI‡}ºYÇSšÅóßÔ½tóN£Âxh*w ǼIHŠº‘N¦d… Òr0Œü #‹ïoH,BéSŠ}úÒÌĈA¾û£7šýarÐúã‘ûÇ>Ñ=sØß¼töDí[gçò×nžyÏQAÉÓAÖ$ŽL½N'3 cÔ8ìFBÈm}‹Íõÿ.)u#=Õg4Ãì b›4~nñ¬o·ÅèøÞUº™c[šíöËáÌQ8ý¯
+šŒ |˜Hñ{…šÈ ùLdšTý}Å«¶íËoÝݸ\7½gMÓ«£[e`³·þÃG›p=ÇÔêÏ¡nÄ×Oü"ɳ@E¿dø¡çYþsì@¿\4êyj÷lf°÷lVˆ¹ê/Ëo˜Ö.Ó=Ù´Býjï:Þ*€»ÿÏn½Æ%}¨¯ã¶êl­ÎNmg[­µ¶ÖÉS›e—é²ÖJKÉÊnÞ/T^ð†¯ ‚ˆP@QQQ@@PA/]ì,«Yiåén­›múìÿéôÙûÿ»ó¢¿÷ßÏóâyžRؽ‘
+û·eƒ[
+„ìÁCä>ÄyE@²÷)H;ˆ…¬Ãaã”#Ás´£sEÇfKûÏ–¡î?æ…ó,£‹æµ?_öV3|ó~lú¶¯ÎEfAx8䌃ÀíáX·HÜ©?BÆî
+P¥+ß²£Š6/…
+äfÕì^ GŸpÖ„1~Ïù)‘Ø$ÂT1|RB¿’Í.»˜Î玦ÔTMª¯Iˆ†â僱š¦è.Yo„Aަ7†7†7†ÿSCÙkCɦ¥ÀC µ»?‰—#4wÆPO¨Çù?­NÆÝãfo0©”qZãbNYÅùLnõ¢ ~(¹VbOKmñ²k¬ZeÄu¨õ8MÀB ̵‹€‰üÕ•Û?„ú?©§#ÈŽº‚,ØkNøD”s§:#ã:'‡>V\À9OeÔ “ØâtžÌšR­2'‰ÚLøæŽž8µN£éhm× é•¡1”!?mb#ù~GPqEfVô¸ 7UO$ó²K.”R+‡é… ¶\f‹9³\cL­Ôu%×uë›MêXy¯ߪoNhkG°WüÏÀF 5ˆA²k(ö9‚ÚÇÔ˜ÙÖÓÁZâð·$)9—«3ËΕçíÅ4©™R¬éÉfôi¼ÞNb]Ÿ6µÙ*MZÄIÊnAÓ‰¦W†2ÄÀùv ·-‡&Ä Ú·Ú»€6
+ ›°cSΙ# ͺØÊvU’DÖœÖZ×­ã )F– ¨¯˜Ïê/à
+(ÜF;¹Lj!—¶Ñô§¡
+14!Û
+èrÿf¦³ýס£^wÎû‡Ž„¦ YÃéÝ]1|•6¡Q¬HUU5gv²Å¹=Åuts¾°ÔšSͳe׈2xRKFE‹MP8ˆ¡1H·|
+¾Æ$^åýÇy9h‚:ÄжrþíÁæL~æ`švú@óÒí ìÞ(
+ü÷CøA"ÄûfÞ?²Oâ /8áעг¿”…ÅÎVž:ósMxô #âôL]d䋯¨ˆç‚Ó¯ÓŒy©ËÈå….ýÿZòíìÚrp_Á‡OþF‡ýËàà6ß™ a»ñ·©^ñå ¹c àP4”Ž‚Š#‘@94ßp`ù…çX(ð^«û<ëØ|ͳ¥”º…<ضœ«JÀsmxoƒŸk¹ÅBŒûiÀ팄ôðëä}…»C¡Ä3ÈžÁPµ'¨{µ÷$°÷½N}ƒs]Ôÿ˜ëÒ
+ËæðàÝ…TØòf¸½ »VãàÀ;±pd}l …ðÍÁûþIÀm €t×ý?än;$Tá‡Ç äC? »ùE}­€Šöeó {,æî· Ñk ˆ|7(Ô„±^ÀÃû›ùœ^–ø”BÍ|XÆÎ»_À+ù'QTy—ÐZs'³1™Þ]û%¾§~"ÕÜ0žâàÞÄ]á_‰¿,p¶ß ¿þ/ ÕÈPá¾Xè¿àþ× ñ
+T7€á¸û¯ÆPŸŸŒ1!O I¸û=ù·;sécâáPc¹ÜƪQ™¨µm%·WY.éë(í°I‹ÔýMÈÁÏ7¹…˜ÚÙ€µú•¡þƒÅ A†dÐ|LÇÜg±Ÿ±èGX"nJŸVx«+›9ÜJÚ%rS}•ZÃd5LV%´¶THûÅd•½¡Hm¯/6˜Ø%˜ÖÙ^ÈÀA†fdPì] =ÈÐ{Ì}Æìó½-*ä[[|òSJј*“9(#6aâ"¹–[¡R°©)ƒ‰©|« ºÅÎ¥t9Xe*³\of’{{œí•ý8 ®o@ËÎeÐ… úÏÖƒÅoÇs{÷“Èé¸ä WtE›ÁìïÌi2´È•Me*icµAÄfa<&×ZO—Øki*¥ÛQCÑõÖPÌ:gƒZd`"Ú<–A72‘Áæ·ãÙ` ÷¿/F„L]8“|ÖT4dHc`JB“FF’É%%*± RÏmd`lÇʨÚkêäŽJF·ƒL×a4³ÎÙþgà!ƒÌãMPïù ˜}ÞµûnÿáÒI¯#§‚ï\ŠI¾êH(t˜SéM–@¡ ÊZ¤EÝ< Y_'¤öÒølkWÐOn”:JTŽâºKI­IïlP‡ ,d¸.92h÷ü0Ÿu/}ݾ øtúZXÐÄèiÜð…ø‚¾¾šÚÁ—ªs¤MJ¶¬LGk­6WJX}¥"ž­PÔbÏ©ìy¼ ©Ñ¤w6¨[õÊ Üº:ÑíÔïy{Öæ³ö‡‹G·=º~bÿÔxHàõkQIƒÃqùFŽÚ‰¥sÅB+GCê¢+K´UÆb9#É­¹2‰ SÚ²›uAdÒ;°‘¡ö½ù ÞººÜ—€Éó­çÞ<rÔuzÂïäíà€‘ñˆDëÕX’úbbu›ßȳd53MÄŽJ}‘ºX[¡ÏSÓLËP‰-iª. ^¡7§ÉÍ:gƒzd¨C†–÷€zÇâ_-»—?òZsÿú‘-wïúyÞ˜
+ ¸0o¼C츚P%¼”R_;˜!ª²çÈŠ­]D Y“ÕKÕáÍlC²©É”h”£Œ†$-¦q6ଜœõó@ºe>è¶/znß½ìÁ¨×ʯ'm¸yÏ×óòtÀ‰¾©SqªÛÑ„–ñøò†ëÉÌšÑt^ñÅìæsç‰ò4{¡g%wÇ÷Q5g,lm´EˆêÒÄÚ¬Jgƒdà­› ›çÙmÁ“‹»–Ü?°âÆÔg‡øzÚúû÷ܑ݋Nç“PÀø*µ’|3“I;ÇÉ! R‡‹Å‰Cä–¸ 5mÑçke‘çyòðÿ°_goMžiÇ#ÖŠNǺ_¶ÖÑŠ bÝZ…ÔŠ"ˆ(² Dö KØ! Ù€˜
+z*£Œ)SüžhSѪesÀ¼ s«ß
+óÓØ‹ö)[+ã,vU%8ZËÀy‡pßsÁû ð¸T zÄÅ;v6Ùø.3 ò-#0l†ò&Ÿ. ž‡¾. x¥ ÷›ÒD  êþ…êž¹|cæì«es`Ó"5ì^Y{×åƒÓ¸íJO‡DÞ'"EÕ5 2p!À8Œö‘ È;B
+.›Ià±-
+v…AÄî“@²†Dl ¤î €¬ü€¹ï8p}!ßÑ„û½¡ø€”:ê.uXbÌ×,15£óç(`Õ¼b°YÈ…­K2àÛUñ°w œ×‡‚»õ ðµõü_ ØyCÜ7^¸ÍR·ƒÌí@ßqX;
+BÞdI¯RËâ&“Ô‰ñº”qõéã±MYÏ¢Ûhc„.Æ“¨ÞœGýì{a·87NsÑô·áoÃÿ5díX¼Ý‹¡`ßJ#†o;àíAã ‚”cÀcžxË⇿¦ÇL¦ŸN˜HV¥<O¨ÊxF®£Å62G·²F‰Ü_½¹#‘ýyCaCüþÐ[yh²³ˆ¾}!ä·D?¬Ù¡u ÷´ƒÒ (Éëƒ ÿ†—Kœd&¼Ì’¦=KUÐ'U²Qjx#$ÿ~ŒYx/º³ðñ²h0²_|%|@t)â¦
+cuvu³&«Ñ¤Hom9vÖ(Ë8_-ͼ¤Bä"ΆyP€äÛÆa)Ô!†f·ÍïZ=¦Úpã­¡ÁÑ”:
+ã²2EØ)Ë–™‹Ø*Ÿ¯«á×kXòF%³ÒTƨo‘ÑÌæâ¬3M"ZWMí¢M‹ì,Î×ó ÐöS(C UöK¡áÀ—Ðâºi¦ÝÃá·sþ¸±ö໦¨øëz2£»’*h“gÊŒ¦ª¦0O§æÖ—çÊeœ
+“„]k±[ÌF[³ §³öTNM¸ˆA„ˆAg¿Œû¿€6WÛ鮣ö//wíÄ µERú $zGu¢ Y™.«-¥«*Å\]Y¡ ¾D i,ÊW˜„y:3?¯¹•Çi5ñ¸z·[‹&ÈûŸ¡1T oe­ýçмÿ‹÷g]l^uÙó¼××m¤;8h =‚ÜÓCo«?ÕP™"Õ–gW”—²ªK$üúBQq#¿Pnâ µf¶ÐØÊä›[˜üŽº~·MxˆAb3Ôˆ¡nÏçÐâ¸úm‡ËÆÉÞ#»Ç®ú¸Üí ¼ÚFî4iÍ
+_§£–(ÕJ©‚ù'ûuÕäÆq<,2ˆ´ZµVP”\Q JUd—}‘=„5@ !a ;D‚HD‚Ȏ¬¢ìЂ VŠX«ˆ EtÄ>óžž¹'—s1ŸûïÅ{Þçÿ«Î.bŠX…Ü›Œ|þmz^y3-¯¾9ÛÜDÍ^é®’d L¤‡4T ÿëä~·îþÔm®¾8d{êå˜³Ùø‡Ç@_h[;žV'Í(«‹Ìç×PJ¸I•¬kiµ)%œFš€'¦\½ÖWTÛÍonŠåuÔÅåwWIØÈæe©É¹U'à–®Òz‡Á÷+ýfjoÆl´§Ç/ŒŽºcº}IâÎÀÄê6K ŽÈËmˆ+Ψ¥U\®f\§V²ëãÊónF••ˆÉe×o‡•·ŠÃ…wE‚î*IüÝÀ> ¤áºæfh:­¸Ö¥¿óݰ™ê˧֚Ï.šþünñ&Öõã(¥ÁéíáÜÌæè¢”[‰¥ÔÆËÕ1õ¬Z²(§>D$h ÕÜ$ˆšƒkîÕ+{*%™ßKGuÕå î¸ü×öS[—ÎoŸ{dª<5mq|ì…½q÷¤««ø‘¾jÄ?Ž?ˆgpzB9ŒÎ(µƒRÓF/oeV[¸×ƒš¯ÖâZªEþ-M¢€Û]5¸†¾rI@Ö.iÈÙ' å7Á­crk]:›_ß×SzöÜp÷Øœ…æÀœ­që´‹sí¤®dÅ ¤§2h¿DäÄ Æ†õÓ‹ƒû˜¥¸>n¹_¯ Ò§·ªÊ«W\åÝÓ[îÓ1(”\Ax*²P£& ­‡eµä¦~ûQaì¥Ñžþ·æšwÞØ5¼rr,›Å` ¦±¡ì)||òdh2e"š1žÈ!>LÎÁ¦øŽpøžÃW1CBzè†Àm¨Mà><\( àî”ò¦¬W•þØyPzæÑ¿dÆfµ·ö-ª´-›ŸhxomPñÎÁŽÿƒá¼ñÃ1^‡Rç#¢£gã¦iI„©””€IVšïD6Óë)/óD˜‰~Rå:.Îrï’äî†òÝÒЬ,õÇÐN©/T¤û´ÛV ÷Ö­]Ð,[µÒãÿyÑâÊ
+Æ)uÅ߃ö仉'/RBHo“ƒþHôŸgÇø¾âÆy½äQ0³Å èÙ
+ªëL=ÍùE‹D€·]
+DßI­ô(¡>Ý‚ê]Ø+ÛòQë[ÑWý½¥_Mެ[èf­Û1ÖÝ-©ëþc×C\ÿDaˆŸ)Þø5:6`5Í»’‰óùäµ\ˆÇ,Ðï+ˆ®ïE$çEqˆ$ ìÔrëVÔýŸ7£zß‚jZÛ'W ÚÛ„pN%Œ4X`®E»3@D‚ß…P Y Ò&(öþäà ©NÞÀvö® æ/ž›ûWÚm½ÜÝåË Œó—FI,ÕË£FîýÕ9"/*J×À~ùÐú– º?¤þ*\8 ¶'CÁå|Ïà X d}ˆ5ôš1RLÜiêœ ®kæ|s'Z8B•¥ˆ¬$1Ú"‹ºÛ³ uó±ªò¯od®‚šü8®”:;ãáœr8˜ €•†8ñÏãÀDCˆ–+Dê8CüN@;å)§/Sײ~²ƒœŸl¡ðŒ ÏZC¥DîÝ‘A5ü"ƒ*{&#UÛd8p@>ý3Nl Ý]80Øã æ{Ñ`·ßÜÔÀç àÔm¤a äCVsØŽX@Òs`5æQd˜3…Üc&À?.‰Æn)TÙC*ï­Œ¶Ë$²\¨)àˆ¢/ò]¸ÃÙN`¼Ë,w[ƒÃ€V6/SðW1 ½F@Úgáû JUâTÏC¢ªÐ÷Ÿƒ4û€$€¢"1‡ä Bg $ê*AŠÁH·P ' `újAjÈ9HŽ7Zª $d;A, Q¥^ëäjì¿Cë>ÅAk„6Â*¾‹¸8òÞ$lû(|ÞgŠ<ã59åñjCÿoø_j@nE¬†DioK§• õüÈ0W, `{k“¨ŒX3 _¶*ÇâyžëÑBìçˆJÜZ˜ˆ°JºEZ n û€ï$/F.ŒD¿Æ>Ž™õy;é5?î5³HøoCìIH>¥,½À1S®½d{jCþ<°¢, 5Éè˜ujöSœ h5ª<äù:y)´!j‘Ø»@¸Ky4@ÇÝ¿4ã÷$iÂgŠ>æ3MöMØ$" qÈû…‚¼ã:Šyî;àš*C¾äa´‹Ó‡,²°¨.1Ò½?'qƒþLà‡.Å\‹|Q÷&¬Žúš$NzE¸sùePê äOøýÊÅN¤úM%÷úO_ÚP÷È@<Ò¨©
+†Ô FI­%ÃÄ>aOЀð~¨¨9x,÷&i<³ž4ÁÜÐÍ›€lÞËG6Ù8<dãí2Kõÿ^ŸQMßk
+ûdº•V¾[È6ç ª³åʺLƒ¦:ã–ÖaÑ)ñVµŒÐ+•fñÄ™ÃL{þjÈGJ6;o‡ TX
+Ú£«¡æôƹŸÝo.yMÕG`ŸâbF)é‚ÌB+‹Do¥S9&j™°®+3%*M®F«Ì¹©—g·êE™ »[ÆÏäWæ>`Û…HÙ7KÝ
+¤Aï±êNmüÝta×Ls ç¤)û¤öJŒMŒï’
+îòòè¦
+
+§¶„.ÒQY2%Y¨®")u"bŸßbää¶Øù]
+q@À$Ù8ö@ášÿ50áöE Ú¿ª¬úØpÒõùü®—­þžæ0Ì¿b¢ûõ‰øv9¾ Y˜C¯ã°uå7DŠ’r™˜V©®,’é8d½I6…÷ªRr¿¨”<ĵÈH i(ß´
+é½’Bú ß  dW'`#g!Ûæ ƽŸÀ­ÃŸ½o=¾îUǹ­½˜C;‚|{Z#"[o]M©¯IÊ×jñÅRE6‹')” oTWV¨(¾ŽÄVòX5ÆÖê¦U“[Ñ+Í+äÛ”Õh(Úà\¤A±Õjöº|l>´ümû±/^ôžýêé ï7¶îK>Ööðˆfs,ÎØ'¯N¥ñµYJdXÊ®KɆ2_ÄÓf dz‚ÀhÀ ÌF<ߪ!ðz¥ÜA=P„4ÐÖ;A%rê¯@ýžEsw¾]úºÓkÍäЙM? _pïï8·#,¼¾-:Ym¾–+jÀQYµåņ<YKçªèò G*—h“ez}¢¼IŸ$·jp’^iŠxo\GJÖ9‚ÐÕt[æƒi—ó»v÷O§ú=Wþ{ìÔúŽyè´ù{7÷†„­Q‰UmqYœ–d
+Ý„/¥4ä°së
+„šb)ÎÈR$Ī8ƒV}ÅpKsÅØ©ŠÓô‰ãUƒ•ö
+v|˜‚yô˜l”¯@ƒj…ÃGórÔè€ j`b™Cû´ÛÓoî+ o=]«ÞœÚÅý÷áÒ×þ§È3a¾93WÓg’C“§3"㧉1±ÓÔ¸Èé²ká¯xI!/¥¸ )uJàTmªÿ söE7óËC»€¿Ôj?EX¢z‡¢Úf–£?l^¤Ÿs_)óØÀž;±­xöÜþÂًٳa'Óf¯œKšÅùÄÍf`cf‰‘³ÔK—çÊ‚Cgy!Á$a—~W‡¼¯Ž¸øÞ‰}g‰ô{×e(]P£æù¨®Ž¨;# Q7ÿølž¶¸ˆaÿÊ
+8´Ž
+ÇÜòáì`öá äÛxˆõˆ…d¯( œ¸ y§Br&ègƒ€í¢ó ¸pô>¨÷õƒ;~¾`ÅØó}ÕÕê„jéuBÕüàŒRÂ*G!lu)ƒÝË)à¾6<×§Ài·xðÝ A;/CÔžPHØ i‚ Ë=Hßøõ‡1À=â bPz\
+‰¿ƒ„Ë!Û:Ýþ Õñ.òÉñ‘ˆ¾È7შ—¡ÃQý¡¢lñ‡ÿuXkØÍ@qŒ³å»
+,Ê.`fû@ºü
+ãý·”|øƒ€þW¶(þm†ÿšj¢¼$2_à+ØÏ’jù=è â‡ñ7ewbîʯÇt‹[bs›â{3ëúiçÒl”E q`®B¤õ”®3@¿{ý–CÞag0†»ÉIðy¯&K¨×<^R¦ŒÜKÓe<"šywqE’Τ*y{b½òVâeÕuômusü]Eú¨.ñIv æ9óTÒ‹t[@â@\m¬5“@¼á;Ð ­gØ5,¾ËÀzÐy´ ÔýC^¬ïp..è•’ó\ÈJy˜%`Ü¡+8í”ÑÔ|ÙU|©ª%ù´¦Û¤»€½¥;‡éÔžÆÞ“U&?â—âzY'qÏ™¶øæ@Zel¤yeHëé·Nóι`õ^:V|`ý§â÷‘Ñ~ƒyIÁÏ´Äøn)ƒÔÁã0o°$ÜËt¸9ͤh$jê •úº”úœÓ)Wr+qí¹e)]ÊbB·ÈšÚ“]Ú˲P2âÀAzSéì
+<æ@ñ¾%£eû×½+r*ôë³¢CRÐ*©Uœ•q‰+äýÊRHÎÒs”§©ùÚ*riN9éŒá$±ÙXD¼e´’:5ò}©‰ò˜k¤ô°m4d¯O[iüìAÜKòFîøJ½®úyíHu ÛËÊ¿žâ¸Ý6ã®ë(¤ßäÌŒ—WË‘J«XUä+N/2X©§L–´Æ<3õfžÚ¡Ï¡Ý•ëhøZÚŽ-¾9ÐÎ"ZÄÁ²y
+”ì˜=VáµèCMÀ𡳇]ûjÂ|»ËbCڬ؄#‘ب¡3kel^¥P$-á*UÖ¬\½9Ãj40ÊóôŒsf-ãšEÍhÏU1»”
+æ¡‚ù˜g HGè+ì@Œ _7 Y¦@éöY£Õ{þUç¿z°áÐÖ§u¡>wª¢ƒ[‹1ñ,b]•Y¥Êä•HùÒ¡Leäéôúl‹QuÒ¬`ÕZdY-IV›Q¾£gu‹DYø¶@Æé< â‹üŸ…›¾ƒ
+÷™_Îx:Ÿ÷[9pá Ë£ó!Þí5QÁ-eè¸ó'ð©Õ&
+㤞É-Pq$F™D¥kô
+É(á›…Üš|·9ŸÇ»eâñnkx¼n1—ûP`  #•±|"ÈœìÀ„ÌâŸà”ÛôçöÌjò]Ñwéà¦{MÁû®×E»P™[[’L(Ï'1¬F:רgK´j¡J®PêERƒ‘+.4³Å§ò3ÅM7óXâN-Kt_Â>Úâ›kéDP"ga^c¥'qö®q÷ÜÁK>Ëž^=ðSgsוúãGÏ×ÄÅœ*OJ)*J¥çåS9ZS¦XžËW
+tr]¶:Ç¡,È£+*,4EC>UuÓœ®ìÐ¥ËïKÓeÝ"[
+·ªâÇI£g·M¹¸kö@«÷â‡76Þº|tïŦðÀÚ³1Ñ¥Õ|~y
+M_LaË­L‘ÀÂQ°M-àͥæXL$}™9UÎBȹn&ê;t$í=)YÓ-²0Ç™ºe pÕD¨Þ`ÿ©ÑÅaèŠÇŒç¿{9výî¿áZk gÃoaGª£Q…uèdC Žª¬ g Kév1[F?!RS
+Ôz‚ÅdH¶œ4%Yêò0ùÿa¾Nÿ©Îû8ŽÿŽý¢˜²u‰©)¦H ‘­dß9„sç8gáœ[ölIƒJÅ´\u]Iãšš¢G£1´h™h’êjs´P²„+]Õ¼¯ß\À¹}ÝxÞþ¾nü~ÇçÝsDrü^½ôûG{¥«TAÙPIþ ä=uzµ:Ú¬5>tÛk¿½ã¢7øØÍ¤ï¾¿Mw/ukÛ zDs7›u´#)yÿÏ)òª6YAÉO9e;Ïí‘·TÖ¦þP{@Üüý!AsS¿¹µ‘wæZCRsÿÁ?U O>©R…¦j¨&;Žš«áŸß¨}ºü­úäM[¡‡öZ
+W£}×^îwÿ±76ôÄMãàU¾°ºK,ÛÕ‘–»³=«8ãRA…ôbyµ¨­¦–ßÚ°ŸsáÔ„Ö õÌÖ«õ ­uìŸTqZžV¨‚"²¡ÎD 'Í(¸`A™í^IyÝ¿’20d«uû•»Qç¯Uë“P—¦è Æßãi5w¸ü²["I^OjfƵÌ<É•üawY9¯«¦*¡³¡šÑyê;zç…Z×Õïè]ÿªŽk^wi°T”ï4¢ Ù„ò¥Ý”xwg 10dB¹õÖV»ó»IÛ°¯Õ™—!NG£|÷=‰‹ªxÈaåò~iªäw¹\p7?—ÛW^Àì­)Žëm,ím*‹îm+ßÞw4XºýÆ‹Âèk/ TA¥!G—Rpn ñôª.qï¾.qstå× [ÝÖqÓ3c>VÇF÷¿¡nÛ=RøŠõRÄL{‘Æ e‹øŠ"I‚bw:C±/ƒ¦8’­hÎŽº˜CºžñB‘ñh$3bà­\ÔPpJŸxÖ®Côõü…èy¨Kü2c¦~þßëžq_vdÚËrßT€]åûp·Â÷tŸìInpú¤˜š<!‹IšÈãN”0Y{ØŒ‰\Úä1~ÌdsRÔd› ò}·0bê(|ü(ltT%4êƒç4ˆÞ_µˆ+·µ‰ögºÄY¥¹Æ)¥ÝâF¥«i­Òó›r¥¿M¾2Ì!sŽæš:Çñω}•²
+ëÂai&Ü–Iàm‘„ UlDZ1ÀXK»uäÈZOŽ;rplŒ@±}v;„ nS0œqÒ)
+Ñ8¨NÔa©ZVhæa® ôEp6äb«)þf1³ˆDÌŠp°V† qURV@fé+_}냊5^¨Y» ‡Özâ¸õ4[{à¼.ªÔÖN!šnÄá‡Q=¥A)ƒ¡Z6Ì4¤X¥=&ôcຄ
+/Ã"ÂԱ˼Áüë6pͶBhæérwr,¹"×ÜÅÎØmá„Ú¯7áð×8±Â§T:ÝM¾— ªQmŠ_©‰a¬Î†¹& –:TØêÃq‘Üõ½àe°_¹!l‰ ¢–8ƒ¶t˜†àÚC`d‡£ ¯G¶±-
+ŒmPjbjRJ¬T×F\GˆõºÅ
+î¨/ìûÑó¬Á˜ÙøáØiÆmœ>MKÿH{·­
+D6XkB´A2]d» ÏÛ¡æ(¤["O@~bN{@¶×i‡ 9‚ä–ˆZ£ ìˆFÒõØ?x¿Ñç9ýŒ Ï™ÓÌaÖc<áMÜ ûãcüóøyš*“·K"Ù²^rò¦ÎÛ¬"/c”›cWŒŠø—ü­È¬òƒ¬>iÇ©œŽFò9úÑÏñŸݬ…ÄÛœÜ{¼é„çü1æHÒHü„@Á˜æ?ŽŸåô³”ñªü¯!i­&R×i#gƒ.ŠœõQêi„ò@sTn·BÇÅäçz!§<èyõ“¬1v>õï eJ û?¢VÞ¬ #éCbhšw/eŒ=(}Íz›ú,~L:Àœõ%|H¼Åžc«‚d²A@6ÈÈ}“GnÞR§Å¨Üj„=˱—j…*–ÊÅn_J2}ò‹Ãærªcf32gÒŽñ¦$MÂIñÙ”qÁ¥´Q~÷Ž7ÜÞÌל¡ìGÌ᜻ ïvÜfOJ¯s¦E]¼Y¾*H±P‡¼éåk´P´N•äæÝëaˆZ¿åØn…ZÆÆÏÕwe…ÌÿCI~äT~%c"«Ž7*kHN¤¿ÉT$žßùŒ÷KácîÍ¢ܧEwØ/ ¯sG²»xïvtð'¥—g„ª@B6$“7ýù®Ï°&Ï5àoÂTÜÚ#¢G­¢Û:«â
+Þk0‘x—Ï ¿ÃæQšbF-•£&çð*ˆE ¥‘jQQDkbaÄ 8?RŸ9ÅQ_Ñ3£æ‰kỵ@Òó`„™‰ôãú ïܦOªË_,•Zí_¬t86]âaªÉ¿e?œõ¼'#¶‹èa <˜RÃN`V0epIt&¿ªæ‘+ÄÙ¤FI©7)ƒüD”F™ä¦P^2’)³$9enUH4 ïÓqé!uL(Ïnø_©ÙŽß+Ü;WcÿõT…û¥•]vˆgGjx@c5¬FÈ¢”óøLGçŦ 1¹¢ôèRI
+­.INë–Ê迈“è< ýE¬˜þš²d  ÿþ^mŒdÈùF1øPaºí]í{fê펎«¯_.õ¶íÎòhÎ"Ô&“ÃÊ% J¡ËÌå‹àL8YÂVˆ¤±E1³F*dÞ‘ Y“bÇñ±S,Aì u-Ÿ3ÐýŒGr¤kƒü¯uAééõKê‹[~²0zÑd{d´îÚ…þ
+ŒÍU€{}.ο2ƒHP%G“s%F†(– ¤1/S”ÀUJøpµŒ ·É`î—«‰‡á) ¿ŠFÐV"ЀŽÌ¥É‘Žì‡ò¨Îÿ+O­û½îÂæ¹–ïvN¶YzØàr¾G}ú¹ÔÏM­Äú•dGàóÒ©äŒäX†\ʇÅ@˜.âÆçKX‚
+CÐ,‹ Êñc LÁ3˜ÉEGD¯æs†‘#9²h¢ÿhÿ©>¡÷®ÉdëócVÆ÷[œL:ê<¯ÔWÞº^^r«  —™M&É31‰©\_.âs¤)"†$WB—I)‰ 2’¸_F‘Œ ©‰“0U8M_ "X»Ð ¹“ä=SvXk¥î˜Î›ö³ëž÷šn¹kyð^»ãÙ–F+u­kQEov1›¢ŒŠJÌ¥Óù
+›‘À£§ÉÈ)
+11¹8)<¹^JH¹'HFÈ'¹‘Ò阵€(ä|x'È‘,ùûРÊý¾é+í×=§t'†Înê·ø²³ÛáL}››eYƒ·K^m€wZ%.DRÉ/¢ÑXJVlt¾€KÊIŠÏÎá*qˆâ¶$XÑ- Íb3'a\ÚtÌZ
+É5@;ã ØçA‚‰_°Ùm@ñ%kPkjÚÍ~Cf–àõeK0»ªÁJjn@A•Ý((ÿg4”:£I€á?|—kXÌyÇI*§xè ‡¢$%ÏçRtšæÔšfš™f¦fjjš¦3Q*jS$¬b…°Ž­ÇéZ\«ua÷bí®­'¤„q˜(Õ÷ùïîëñâóúþ\×}ÿîßý¶Ë Tp0ÊÄzS¼çs¼ˆŽ( 2â­ãA·Ù îòh¤¯ŒB–mT«Â‰c4Û탱Û!û× ÝÑ]Ž~è^ë‹§¯q¹‹D:ÞM"µÞ!‘ê#‘¶M)†ùÔXë‹ag˜
+'ãd¸›&Án,ÂD#fa$È‹ÂÀøO0¸K!´ðG¦¥”–>(¶òB¥µ'j—º£ÙÆ ‡m6 s™+.\ÔIç9¢þM©î‰´¥D*€ñ9æN`± Ë (°7Œ…‹ÑFx‡!À$áfþˆ™ãƒ„¹^ Íó
+Œ'Ñc´þ-uh“&éÝæW䑨¤/1Ï(ˆê£"B`÷pà÷´„È7Y®³ëke¸9òâ¬Ë¶E¶Ô™ù®TxAT€´æðGLroœàœùÂîŽcþ?šüK¢–ÞO~K¢ RÞSŸS?‘{i_â§cÓ#:¢u6á¼J‚UÙO‡ÒÅjoS„š£p³
+’í'r‚"DzR¿qÉÎ1QcÔç´ÖM#¼öø)'ÉØ¨ïXךäækF/û%m˜ÓG{Ï~B¡?dŒ%ÝKžˆ¿ËD¬.À±š¶­±võ¡X5êu3Qâiв`sl‰¶BÕE|çq•Üë³¢0H+«Œz/©‹}+ÜK~Í?Hâc½dŸæ¾`vóŸ1n ûèOÒÿ¤ ¥?dhø=L-ç6k”q“=N¹Æ™LÔRî
+=d,×GžJœfb«‡ *`[”%¶‘íÆ¶¦¸h‹¥>U^ذ¢ló`æÊóô=Ì>þ¾Ô§ÜÃÂßÙ’G¬ó¿0odÞg>õ$Èn±Þ¤_ç¼ç_Iá\âŽ2ÎsÇ)º
+Û$·ŠZ¥½…-™н™¯2šdA£ìCª.þqÈ êþ{‰™¨'Þió:ÃÑ&šC ŽD,{Ú–°îÁ^¦ÿíú´è«;2(+r9gJ‹'
+*¤GUÕY‡s”²[ó÷euî•-þFv½¤Aþ¸dOöse}ö¬Nñ6½Vñž¯ ð Ùâi(&ØAìÌ;}ì_;cä°›ñp‡ßüþï­‰sº»Ÿá£‘}¹V’t¦*›ÓY®´—–IVeï˯W6嵨÷ä~[T—sªd—âJiuÎýÒje¿j§òeÖåkq•ò@H[2YDÖ,%¨!æ¢i¥ÞäAGƒ®Fƒ>sÿ:jùàhìÚ[i~Wš¹¿ß#J:Q#g·Wå Ú*J¤-e•ÙÅ»”uMêjõÁâ*Ugi¥êrY…ª§¬Bý?u…z@Q®––«5B]üã ø;sÔýŸîúŠk:Ëâ
+ˆ *HDPŠ¢]@?«®ŽÀ8*£ ~ÆYѳÿÝ}ÎÃ÷sÏïžsîÁvÞ:Â× {ˆ:,¥—{íþÙç²ý~'ÍøöÅ ×B½zj£Z«øÜƲԨ3ÅY Õù‚ò¼i%9Õ™Ùç²ÅÂö‘p0W(œ‹D/‹DË)¢ì·‰ÙÙïy’@4–!M?a{g6—-—KRï.›‘~°‘4à¤q·‡j4Úh¥™ëÙ^åß\“À­«:Y]ž_^š+(.*NË/¨Êå×g·ædˆûÅéâ)ñ±üÂŒ¼åÔŒ¼·ü ñŸ±’@Œ:ŽmÁC–ã–§yþSçnÂë~câó+™‡×Ôǯxï¹Þ`×Ýìq±é°_ýÙ8ΩZAdùÉô¸¢
+Ñ‘¼òÂTaiEfFñajÑÅœä¾Ü#Eã¹É%/„ÉÅKiG ß$¥¬ÄI±Ø;8®Œ‡Ò-88½ wà>\ÖÂ/é~½e*=3rpëÍAOƒþ^?Û¶¶[cK8£¶™Çþ¹!)¢èLj¬¸F˜”UŸ’^Y–‘\Q›Å/?/Š/¿œ[>–_ñL˜ðóRZbÙ›$þ‰•8I ›Á÷ÞrÔ«â¾¶oýØ{q{nvÒhã䘽Úõa½î†õ…^–k]g¨Oå¥hVñ…Ä0qÓј¬†ÌÄ´³âdÁ™Òô„ÚêL^M“0êTWöᚢõO„Ñ5Ki1Õo’xU+q’@6‡\ET)á I}îQFË£Êè×{*èÞ´!éæ¤Ý–þ17ݶaŸ½ ƒLçê¾jiO$3¯#>$«M•Úr,îÈ…œ#qÍÅ)ÑMUéç2CÎuç6Žd7Ím\N ;û–~æÏ8I «ý“jÐ÷ôû€<z<!‹>TDôˆ×fl”»¦\µšoÓ,jo:” {åFøeõDzS{““zÒbb»²ã#; „¶Wå´×§jëHjNgv̦³Ú_ Ø-ï8çßó$t¬%dõòèi×F4;$îÝ‘FSÉh䑎Tïì^Å–8í¬›ö6©˜òÛ_p›í& £§ŽÆò‡ùÁ¼¡ÔðÃׄÑ!ƒqìŠDæÕú$ÿ«íß!c`Và7ð61 ÷=/°{5J’qP)‹^^ ¢Ù>º;JB·f6¢ágdt噡mÁB¾á±ƒFÕœ§AÑCƾìû,Ç´™POþÝhÞÄÀˆÉ6wBš(ˆ¯ˆò¯¡wðhãñԉ9mb%š~óc„ÏÈçPI _}8-…æÛ¤Ð«Ðè <F=¯Që+©†ß¬äO¾8°µø¹»Žè)Ý4miËÂuŠyåþ(<Ÿâ4' ôŸ+d1æ*9´¹s\Ê\gˆ×ühˆç£'عìýË_lï{³$2ZhBhº›€F®I¡+“DÔ>/οQÆÕý¡·¡òKrák{5áï.š©ËTƒÄå@ó˜%®uøR”=w1щµ˜ê°˜íá»XäE_¬¢Rè^K>KC ÷å˜× ÷Å/>î/×i’¼¬Eèþ%„Fûð¨o”€Zgˆ¨aAÕ¬©áË× HùkäÌU»-É«ÎÛãV½´"WýôCW9FœÕH³ Õ«€Õ”}¾«B[úZÁ~ÊZůµ³/9º}¼êôã§)g×OÿrvùôÁÙeíßN’ü‚õàfB½ƒ8Ô2ŽGu³R¨bE•|Õ ä|Õ#¥5“ã¯ÛlŽYwT [÷Ð`¯Ów®iù} ÓõùÆÓ£}xÏØãù=רý{©ÉPcæMæÎÐeáC–0cy^aÞ[I2Þ†P_B-7:=ƒPÙåÃ&\hŽ‚Î†80’‰€½dØÌWPÔhà¿ÍØØ'Ì bpÁN'ÈÔt„<­ƒP¦m§uì༮-tëÚÀõÝÖ0yª'I7Vÿ:V¡Òy„DË8t p|PÃGÁ."ôIL0“a€<7y€»¢+ДœÀ_ÙØ*ö¶ÅbTm€¯¶Ò·ZAÎV (Ùf§ÔM¡QÝÚ4L _ÃnHt©« ¡’ Ÿ#”Dd\(ãY Nðm" In`!í ¶2ÁQÖÜä¬"o¾d `n2ƒàM&¡`±Š† PÔ‡ÌÍ»!³”+iC­’4+kB»Du£Øý±d- Ä_F(
+†ÎëTðn°àÚRûÁ¸Ì7X‚5Éì7óFp—ÖŠ´.0d´!PF8²;!\vÄÊj€@ndÊ©XNNÈ«ÀI‰€¡C
+*Ȱïwû;ýñüÞï9û]笇ظmÞ@èXø  ªo#Dõû
+âÞÓÂ4ß/¼¼ÀqNé6KLMè(£)|„Þ1LëÜó‘Òƒ"_ǽ'=ŒzC|KxEúŒN™Þ÷ˆ
+÷i~‡¡·ÿ £èh/y¨kJ=@µæ+Hö› šà ‰p Á}FÍñœ@¤ë-ÍæafðG^ÑÎANEä;Vö¿ÑGñ43é%å<õ9éjôSÒ]ÆCâkÆ”¿¨wéS„߀½Î€ÈŸ™°« áÖ
+Ù4ÉÀpµ1:?õ­,Ôy‹Ð7«ÜËásõ†Ùïëœ_ÔïpyP]y«”êÛWÀÙ~1GyF¯ÀŸÔ¦ÐŽ'XGÔùÜE‰ 6¡&¾ZÖ,«”žL(“ô %Ò;HiÂ{I 2Â+F,,ò…jB&)Ö
+nE>rSiT½‘ÕŸâòÕŸÙyê zžú Õóíß¾§C7uÞ27Û‰Z»¡¦5/[|çüÙ²íÛ[‘Wjˆ»Ê™ÛN™ø»[ó¥QMÙ*jm†–U•–É-M-š4åcR<7±U‘¥>«4¨¯©2“_%di>
+35£±™šñèLÍÝÀA»€,²tÔy ]l ÊÕÆrx¹í»OûgæõŽwÛ‚_;ñCO=aCçAÆÖ¶2îîfS<®Ö¨ TæhX%™ÜB}¾0/­D’¥=$×§UèR:U©)?«RµÏîƒH§û‹«ÓŽ1tÚñhk
+œ1PíŒ=ö fàÌ<Ìý^gÌKv½üœ;φº¶žÚ»úp;iSe #ÔÔÌÝ“Û &èëZJMrŒú`:/¡2O_^*”ÕÊy¥ÇNi7Â.»ð*Åü²a^\‰…W<Æ´HÑ=¤;aà€f¤~.æaûl̽ós0ýW0}W–Ûuõú8™/l_Út.Ò³ºƒàWl¦ÿî:jòÊÃ
+µ,ƒ0Et
+Ø÷/uÄÎsYq[[ 7üsbMóFuaÓ¶Œ¼ÆÝ9Y ßækê«
+Uõ EÉõÅÊúÞ┆¡uý9©§&ÓÓNMitÁæùû ÉÀ÷tò°‘InŸ3 ×.3ÉÅ^9w©^SǰöV¸Õ‘|§ýפœŠ®”Э3¢7\X%XÓQ"ù¼½L‘{~«:ãÜ®ôԶʬä¶ã¹ò³õy²³í«¤m½«dmC9ò³Š–éTeóŒZl7$chäI-Üif+í ÒqAÎÞ3$M‘SØÌc÷CVöÅÚïê{n»­ ØÐ­ _}#/¦àzq|εõâô«åIª«JÅ•J•¬«*-±«A#êêHO¸r—ò{š¸kJ•xñ£BrA7좑þ£„Ü­£‘«­tÒ~NZn2Iý##r¢ß’ëw×ÿöY°Éî§QÖÛž7<–{—<L ÌóYÖý¢È´{ëx)÷Êãå}"I_¥DÔW%ö5&Å÷uÊù÷îÉù}ÃÔzR"èXp"]~«$äÁ„üÒ¤GÚÏÑHsœêa’ª§Fäà«%zû_±™ÿ œ_þr¥Õú|»âç2×UÏÕ^™ƒÙ~©ƒ…AɃëB“ÊÃ%»¸¢ÊhÁ`U,°Ç숋}Þ;ø&.v`œûoÄü?OÒ]GHÇO„œéÔ#'oÐÈÑLR9<Ÿ|5bOûr„Íܬ ˜Wª 7/Ðò¬³µb;6ÙI¥ÍtSh <eÚµÞ‰Ú-œíN?ÁÈþ€¸‘£±#?G¿k ‰½9:°"òÝ»®v.„û:õœ äB!Í穽BÈá^=²ï)ƒTLšém´§•Mz0‹'ýŒr'ÃLÓ§¢ÍS¦„‹’¦’–H¦RmEÓ¹ö‚ébGþt™oz»KÌÌ^×È™ïܹ35³Mžá³ì°¹Ê vèì” .5ÒBAíEBÝ"ä«Ç„là ›°Po –ÒòáÊȇ¥B°±+Mg&„Ø<JËh¬¢»˜K=@±aI¶Û¬ 
+Ó¤.B¶Y
+^Œø3C±Â \–?bY>.‡Ôˆd#hŒÝkìŒây˰q¾¾œo‡oLlqÄd)jMlpÆÔ-:¡2Ø{›-O)zIHÆ8!J"†‰Ã½HXÑ>ƒ--Ît?xÒ9ðÑg#ˆá†0† ¸Œeˆe:@À´ƒ„i¥5Ò !—e‰–96±Ì°Ãpõ\€C†¦8ªâÝhˆñÕ‡" `BÊ‚,ÚR±)ÓÌ!*²BÂk÷Ø þ;[ð«íÁ¯s@\‹#âÎ/ï’x7)÷Á¤üîÞ{Ê4æ<ǃ“.Ÿfàqèqô!ócB±‚E”1ä"SÈTæ,Bâ¦%WØ"á€=„UŽœ^Ájo›3âv¡›+øw)ýnà¿¡ü×ü)
+>ùH™áÃmŠW]  fà{Ó!ñև‡‰” T\c¤M¡PšC¾j1de6î°Câß!>êQ­ D®Hhuƒ°Ã«Þñ„à)å5‚Qʤðš£L ÀžÀãƒ
+Ž.Hp¡AêF‡Ú•ŽLwò8(Xa„‚hSä‹-‘§^‚ìdltf‡'Ôß,ÿ˜rØw6¹&`FQ4%o™L::!»öAz;ü½ô׈QÉ›•o%ï¹CÒ¹ˆ7I{-GÈKŸ+௠DÎ4ÈéÐPý*êy…ÞL”a5×%B )mPëø1o­ûLö6ïÉŒ=~ãi‚Þ«…Ž¥Ô†&7rG­Qoå1ÃI×yC²‡üWÒWñ/dcüùlL¿Üg)¢BèCBt˜š!Å‘Ž,ªï:飔Êc}€!Ê"LPÆ·À:™ÍÌš §ñ¢bϱüM¾ïrþü6s_øæ`äkõ÷ÿc»Nƒš¼Ö8€Ÿ„MEqk­K©ëXܪêŠ {HÈ’„ì @ bØ ›ÙB-@"D@ÙD–
+A°^µ£VP¯ÖöÖ¶ö^µuãÜ×ïùð›9ßž3çù¿ïÌßç)­1à ÅŒyDêÆ>$ …< Ýù‘ø‹ô{“ú>pš}n0¡—• 1á[ n32Þ+øÔù»$lw€Ê}  êðb¨:¹ò»î•‚±ùX‘Û¯QòƒÏÄ)Gó²¼rŠüï…U`î0kq·è&üL¨…xƒÖO𤓭Ôû”1ê ò÷Œ·¸Q f#ôêç@O[ a#²ù’O}k½Tmµ‡ê=Ž0ÍÃù}Ú‰ÿÓ­}‘BÝüs"o×£X©Ç}i¢ç¿$ß6h’WŒ»ÆÑÆØõäѰVê0³+ô
+sˆ~™1Íèe<cô°^“{¸w‘;ùÐׇ޶@2òáÈ|)Ò·‘»¨‘ldº9Ìç\ø:÷زßsüW?Ë mšU³wßN’xLÇÅyZe)~W#31Ãâü  ”ÔÏ«¦öp›èÝœvf'§eaO°Î³çØfî_¡fÁ<±U±-"Ø,‚¾¶@*2—ÌAzoò—h˜± µ;ìß|ãôgÑá%Ï‹N®šÍÃo¼ÉÜ5¥zŒ%EŸŽOôˆÑ÷Hs ]:ŠE\jÖ1[çØ&~7§‰7näÝá6~k¼§ÖG@|m$ÄÔDÂ
+ã¾¥^‹t¾ hXøµÝ«’=Žÿ)õp~\êµòîiì†)mè®± žû•ÔHÏ^e¼WB
+¶]žIl. š¤¥ cd5«NÒÄ©[xÑ _/šæWJ~ ¯’¾¥ëeT!ƒ¸r ²2=H¾«@¤­F:Ÿ+êCñôË
+7ûgUüTu|ùL)fýxÕíJ.ǽ']âyAëoV)±¦D ÑŸK«Õ1 Ñ•ìJi=·,ÊÌ?Õ',Ž´
+uÒ§üâXäK–ÏStrˆGÛù‘HßS"2‘÷(Z‡ú§l#ê7Ã6»Çu{ïÔ]v½2ð«‘ÒξÖ·r„ÇÛÒe~Íj¶>9•X”E«T2KãËØÅò^Qì9A~ÌE‘6fL”óH˜ÿŠ•¯ø@ËS@‚V±¶@ö((Cú^ò
+dŸ!o5ê•Þõ¼n3úaÓNû™úÃKǪý]/—wtë˜ÍùüãÍ9Q~uqÁŠXžšN-Qå1‹’J8yŠj^NB³03¾Sœ?*ÖÄ?g$ý—“©zš©„$ÎŽä é{êå
+¦:¶ GÛ.î5y¯ioÀnm>KÙW£g®(žÔKƒò‹âñÙÉ”4m#%'Ÿ”]ÆÏ¬Äf´‰eýiÆ´$:û7ž<ç-3.û#%. lb$ ŸzoöðæÌb0[·Ü=·Üê\&:7¡;ö/êj;ñE« ³¥¾¼W_v¨¤Šï•_U*Ñ”(ÉɧÓèŠB-K^P•åDjωÄÚ^±H;)–äÿÊ‹*|Ë”|¤Jó Á‰d!ÙÉÂ"ð¤ÒÜ5.
+»±0”i¬dÒŒ,б‹M6޳ÉOÃhoht#$1êaˆ-0Ñ ¼Ôp·€ÉF;0rÞô]²Ãö mì3`Ûfouw)ö^£ÄlÑô“w+{™ßÊ/ñŽJ»#¼E]±þÜ J Ë’BïÈ'Q:*(Äv#-¤½3×>ŽxJ÷¿!ÛaÉ ƒm©
+Ï|:’‰Ða9‚‡äz‘…
+'¢\áŠÙyž0—ù¼.©ö+\2lZñbæ†˜ç†ÆØg¹MñOs¾NÐÿ=ɪíHéÓžK{¤¹*¸¯¾+¼£y&¸­O¹™ƒ„î\Äýœ‡ØÄÜ0þTAt¾LÓÞiñ£½/Œ±v¨NuÄ‚ —7óužcżsæšëÂJ>æ?.\ÿ0sÒ}ãöÔ»y»ÒÍmßÎ9,¹©?-íÖwÉnèog\Ó?‘]Í}oDÚå|$u™0ãRâ.`ú» ¡ïÏ÷aÂÌ¥ùS™¨¡÷¨á .Éá͇瑲=ž/4ñúç[‚–/Œº[¶$þö¬•É¿¯Ü(Ü*ºnÚ)ý1wÆ÷ÆòËÆŽ¬K†Nåw†ëÊæ=RvÎÎ<[
+b}Íî_›`ß»FìܽRÍý¡Á|±~vLç‡U §kêÇ,“´W®–ªøTy |«zßœº–Ù_çì);”÷•ùŒq—ùc“ù‘¡É2ªi®@Vs%dM•}Q á»OsçÐüÚ·–ÐYVp¯Öø3? c>ØËéÙ tº¶Få}i¥1è\CiÌÉÅóÚëÞú`±´márùÞkU{*7iwÍû<狹_æí(o3~n9‘ßhé2mµôšþ:oHßXÕ¶ÿf»>£šLÓ0
+«NZ ’¤ ßÚ4D•¤#/ÌC
+ÇÏ¿+2T¾Vk·Âró·ðRº‡¸ÐtLg¦–¼åveàî뢰%1Gz ;ry³r)R~1½)£Ü¿>½ŽY“ÚÆªLéa‹“¯…‰’&ÃK“—ÂE©¿±Ä¼u¿òsèæ->‡¼T¡ÜC"Þ{Ù˜p#¬WÀ¿7ÁÛ Æ°ØnAœ“:hO6zIüw V°­zE'í:JâÛ Ó<ZY”†œº$Kä_É—0Å­!¥¼Nvqú`Xaún~únaÆ?CŠ3ÿç/ÌF4Œ\’¼U¡(|ɺ€rõpz°*у_Zõào2}øNfF˜¹xDk¢ÕÓh¤±S! ÙßUu¸]çÔ"Lv¯/â“« ´rA©ßùÜ*fIvSHA–œ“—y%<‡?ÁÍÊ\àfgýÊÎËý=0_€è#«B'ñ-¤âÍ™¿ÖÄ:xwjÁ‹6XìÔ…G]; “òÚcíî›Zè;z‚-/I"l[+cëʼnnÕçyd±0—VZTìWTXÁä7°²Ùü¼þp^îMnzî<—'ø;›_ðßÀ¬"ä‹Q1Š*‹ï€§ ¨P –«ÔáE³&|I¾ëљ˦„‰nka¹«a_û SykÐ^ic¸MCí©cÕÕ ®âŠ4o¡8ûD¨‘[*Ì,©cñŠÛØ©E½aI…£ÜÄ¢9nbñ2'Eøfz)bðJñ?‘RDU…âp|Mø$T‡W<mՀǚ0×§“ý&0ÚwPýj·‹A·œº½ýbàŸ¥ëšæhqÙã%µ)^ù5™>9Uù¾ç*Τ‰%ÁIeÒÐQ'Nt=ü´hû‰/þÄL¬@Œ¤
+ä“TލªPî![ –Ë
+’M1e?0Y²~¡rDãÈ…#CdUˆðVð—€&z 0Œ·_ÿ@Ç]=h»»‹ÐxÇV³râ¸pÜÇD0`Æ¿ÎÞŸ:esöZœÃé¡dçè¾÷jûJ•¥h *d¾þŠAC1…½bø)VO(¾P˜ýÈ óTµŠ{økÀœànÀÞ~ýx{Éo¡eFêg·CåÌ5á='ݼioãs“~¦)wC,âïDì¹k5‘t$ìÖ9Ç›— ñ27ÿñzOß1™7ml€â36Eñÿ|blÕ‹6Š<è£È•~WõN°P0-¸Ñ  Âù7š§HP5¯ ¢#(^ØCÌ}l¯•ñgIó´­q‚LOÎ…›sž²d?8{ øÏ&`V`ǘ9Ðfê©32gòÌ ‹÷ì´‹×ìï™ÎÞ÷‘#ù:JžþÚR%Àì€ñn€+Wqþ œ÷gåC5(~¢Ù/õÿr!õ…µZ í˜çƒÈçÆœ%¶ ëYôÎÀgñf~ÏÒ÷Ðóöù,ž·"/Öò|&³öx6dã¶tÏÖméG·ÅU·§ëÖnߣCnO¾6ßp»`  sçãýY9P´@þ -HUÂYå.B¬ò )RyL“£ôØÀRÒ7*ƒ71”[hÊÓßP•)ÛÉÊ,SOeÉNweÍn×å6s—e……óÏw,œ~^´p\þ·…ãO¿›;¾CdòÀÐe€.|‡-¸ƒŠ{
+5ìCÝ–è†á^ôûÑpZÅ>}¥w ½Žóñ <H[ˆÅùì_
+Ö°Dáš{PŒ¦9JÖÚr´w¢2í¨IÇué|‹†u¶£© &è9öæ+møªïàü‡8ÿ)@ Îg)èk
+ñZHÕ2E¶¶1Ê´ ±JÛ
+ØÝRÀV$4”³ˆeÓPͦ‘2‡‰4/-¤ñ§!E:‹£ >&¦š¾Ïµ|3Soÿ"j­ÓóˆÎ9O•=®Ãw¹ý60ÿâ÷åG=ïÊ/xß‘ßôþIþÄûÅ;ïÃávM ×+˜3 çï"ᨠ¡¶ D[1`Á@*é»ä&™ä™äËD3>¤+õߤ©L_$«­ŸªŠì~YTã|?®Ñõç˜V÷±è.¯Ÿ¢z}nFþ•s#bˆ{=b˜wUyÚwTyÕï²ò¡ß¥ˆ7Ü Qð>³ áv&®§cᢠa$7ŽôÞdÒ;3,È!7É#÷È÷ÑžÈLÏ Õ{šgú c ûNÚr‡“ËçÞPÕ¹_Khößȹ×Íû&v‡ß……ýüs1ûÎD <}Ip"ú¶àXÌË€‘Øß|ăs8žÃ‰p'Ü4!â÷ι„t¾l2K %Îôd©s¼ÔoÚãbɬ»Ñ&?æ$³¯«3¾K/šw)­Úó|ò*Ι¤µ¼“ªÿã‰=GíŽ :PôuÜiñ¸ïÅû㟈ö&LîUÁw( œ¡dx ¥ÀC¢Iîb’¿Ì€B¾ …R2Ó
+{Æ›*WúI5GûneÐŒ›eJãÑ¢Dö¥åéNg³óÜN¨Ë½Ž¦¯ä^Òäw0µ5ð@r—p_R¯hhñÉ j_Ȁ꘴?ñ²lwâÙî¤wÁ}©èKƒoßpú–Â[bÉ RH~6é[EFV˜Q“µlƯuNŒûõîÌ×èŒV+Œ.”ÇYŸ*Ns:šŸív(·Èû@VoŸºž?˜Ñ,øréÆà=Kº%}©;¥;RC·¥ Ë·¦œWô$ßVô¤‡ô¤CØ“ÿ/ÔàMˆ7&wУ°œ(#³ÔQoÌ©G³©±¿¸0®6úN»X'3<Yc}¤"ÉñëµÛ¾Â|ï¿å•óúskù}Ù™ëE½Ë6…ôdô†~žÞ¯èZz ¼sé)eûÒ”í/B;ÔEYìÌ_g.¸š`HÞ"é{ºddŽ:}êe“!u¯ÅŒº±Î‘ñm3—uºQb0ReõUÊqhEú¼/Ër½ûŠ‹yÛ ª¶æ5uç6‹»rÚ¥Y=ò¶Ì¾°Võ>åZõñˆõÕÈ–¬gaks&%ë–C¸>üõä_GTä·«Iß+&ªgR¯VéRZfQ·6èQ£mvŒó­>¬ã-"ýƒ–{ë9ôצÍÛU™åÕ[^ÈÛRZÁï*^ÔQØ$Þ¿A¶.o³¢9wGxSîß#VçŒD5ä\ŽjÈ}¤\?!m,DPSÖÃO’È;ÈÒ!oQ‡z½R‡[3º¹~:õ¯ö™Ô7³©S½´·éío³hŒ³ßUŸâÚ»RíÕ]Çí\QÊo+¯®/]%n.Y'k*Ú¤XU¸MYW0Y›?U“)ººàaDmñ{Ùg¥Õ•AP_¾&¤ýç²(T°¨ûõLêf ‹ºÖ¦CnšAÛlMÛä¡õU»`Ö`«Ü|wóB»mMIs»2<;?Ëån¨-ö_[])XSY/^]Ñ,­+oWÔ–~¡¬*é¬(9]Vrž¸UQþ.´rÄÕ•ÖT¯ iääiQo«hjl5M]_§M:Ï4êÒç:ÔÉ-–Ôp÷|æ¾®
+½f–ûí?pfÃÞŒ¶ Ý=!)÷B’3&‚wüÝ7- •éÙèºï:«Ã­ÀT~!3ß·…Œ”1áó ײ ¿~\­·btžqÒj©ñZVWdx¢,Ò¬¬d‹°èX‚]~a²ãáüt—¬¼,÷ŒÃù^é¹Ç}w:œ}.()»;x{öÄìB’ržû%FeÊT¤A¹:Œ']Hxœ ðuE:À€áÓL8Ë„ëM|èj2§Û$ì†Z%ÿä©ÀÕåÕá&E•›­óËâŹ¥;¥™ÅiÎûtK+ÌóL)(ñI:Z㟘ןח7¼5ïQp|þo~ ¨Ü^ˆŠ¤B”«ÃD&¼Øð}€ê8™ûjh¸]Ï€Í4\nåAG« ÝÔb¯yºÑWYïoP|&lmþ©M–95q¢ƒU;öUì–í.ÏpÝu<×c{i‘÷GÅÕ~[‹bŠ.n.Š.zSüÔ7¶dAW‚Šm¥(W‡I
+ºÛ
+€Ûµ
+½km•ƒb÷Á>±ûÐßÄî·§Eîó¶îŸ¢ !T§*<M:ÐÐÞP× Pq ` óRGذst9$ŽšQÛFí™1_¹iF}åÏ  Ó Ù´,hd›~€jç*_Õ¾5^ª\#UéZ7ÕWU‡™\Õg&ùÆLþ匩ó_ߘ:¦ÎYl¸ ÷,yí
+pö@U/É¿ý.?y î!6>Ö„ðñ!ô‰>±¦üž8Ð^“®LI Ådð—©lÙT,Çq*‰+™N×¶›>¬³~ºLW4Ó g3s‰'|:DŒñÓÏx‚Éžà ê©»ÔÐDÞa5é`á
+Ó˜+ñ°Æ{X®±YËñ2kY|üu‘S—Žõ‘þ}ö.?æ@È€çO
+©5¸žZ…ŽÔJTP+Л^†4#h=Œ¥upÍÅ,†–08XKt0ØØËÐÄ»‹“75LòU$ÿ;€àxü ™#€€0Ç%°uÀ—ÂjÔ}4„åd/KÑ ôP@é ˜ÒF)ÅAWJ½) ¦(Œ¢
+qŽz—øŽú’øš:O|Iãç~`üÌV=¥Ñ.hÖ`™ t "ð7ÊN ¢qQTMˆ Ö†›—AH†ç¯ü$2øÄª3šl5žcî ~è[ý>`Äô-cŸéÆÑ5¯éSfÐÿeþ‚~Ëü ý¹Å#Æœù}Ƃٽ
+ô±Ô
+Î n½§—ƒOY x–—‚Çb íï- ô;ßÍj„®ÊÕp¾:£ÔDÇUè°ÊŽ0®ô\2ÔÌÔÛÙ¾ªckœY›,ͦ©&k\Âw‘U“ªEbÏÊŠ:Ÿòò¿’²NFQé³ ä ‹_r*(¯äVP~é[† |Á·°(B0ßl5ôg>B¿U t]†«‘€Î*Õщvut´c%Úß±–0ÒNÖêkc,ïl 3R5n^ÓÔb]¿•ãP+Ëu‘H‹<D5ž¥’ZoaU“Ÿ ²ƒÎ0y¢ý,®h*(Kt#ˆ+~ÃàU}óÍ«
+_¤Å@&B/ñncï¼VÐùì}í4ÑI@ºõОn+Â@—‡fw‡¿î¶m!†ÍmѦõ-IVÒ¦ û*EŽS…\à^R_¶¾°®Ú‹/SPyµíþÙÒ¾
+„N«šìÄîÙ‹Ýo—.ê3G½»\5¶÷RuZº‚õå;6­–¶'XTªÒmËÚ²…­ù®Í%¤Ü¦*
+·±Þ‡Ó ¢±å;é©òñ€dùqf’ü3Yþ’žÚðÅ7½(ló‡Ðƒr„fêšnEèv¿Ã8¼¡Á¡¥¨wØu 9©·zkËûWHw†‹{bÍJ»S­‹:·Øó;òœs¶ ݳÚÅä UWj›Ò7IÙKKPŽÒc•“ŒÍÊ«ŒXå Z¼ò/Ÿ%x&µ ãñ=¯ pø¸„ßàÔ6„&z°ûáüáa„ºG5Pû˜j³W“ÿð:júL8þür“
+ÁêV­.`ÕjåÒâ-Ñ>ûd÷¥³³|&3ùeæûþÞ#3ï¾0ÞÊÊx«’=©öKvçH–å»ÏÛUì=kç¿i;m_<eë*Õ¤-Ÿ‡OزE“mØ•e8i0’¾È,ƒ)<Û€ªÔCëðãªÿÎÁ @-Ý?+¨¿mÀ†ZºÿÔI`u'³¢6„³ü@Œpqu²Íûû³æ|“'›QUäþÞ¾YÞ•È'W. È­X©ÌÞ³.$³Ü N+¯ O)?®/o‹Ðïî KÙm
+M-Ãà´2TÅÛn–
+¥®¶>H[Û4î@o¶Æ¤ÐÕ`€®ýGºLspf=À1Zƒýe
+€­Õ
+£å°¢Óò¥¢Oô\ù\ôDù»è‘EÉ
+u6~3p:þ‹À©„7”‰Ë?ûM$!Ÿq6òOFÇÆ’‘§5(n?î` L<»§h¼;Ãß$îðg±/ì䇟å³ßà'9}ÄÍr½Ÿç¾œ)÷XL×xÍs*}æÒêH3©-ä)”ÉäÁÀñä© QöâÉaö#ú {›ÞÏþîd_ÊO”¾4ä×{ù`¼{9è¸5(g†}/Çþ“go
+à[™#|Yx
+ÛCJdCáJé,K!½)—þ.R.û>¼Hþ_†R‚T
+DQ!²5(à{îA‰½W °iÀÎe$ÀJæë÷Á/©§Ûô£wwT_ÜßZÉþ IŸq¤®<÷xu™Ø§B£ð/Wk(¥%•´’â†RÕ"W„K‹¦Y’¢{‘â¢O#ÅÊoà T?1eÅ(¨°Q0dkP6¾ î¡;Î`͈½»ß"vŸ™&'3{Â@x½1ÊþZ}ì¾&ãe·ºjÎá*÷˜¾BtB«/ôS——PTZ}¢¬Ž.+m –húÂDê©¡z™ÅW?ä«¿‰–þ›)*E´ü2D—¡
+ÌP¦¨cÔƒ
+:ÈŸäùß@=eOR
+G<zúØúYÓ([Ê
+ÃÒµ³ÐŸÆƒŽL#ø#ÓZ3Äp'S×2'2õ¼º´há©ÔÑñä,ÉÑÄ,¿›Q ;ÿ¹mu\‘ݾé«U{¦¯w¨ŒÝæ¸+¦ÊigÌaÍö˜Ë£¯¸lnsÙÛ§.›ŽöeqhW¶D>FŽô
+ÕÊÈiÕ…«g–$Y”(2m”y
+qAn‰$/§ÖI.ovÉ’kÜÒ³{=R³‡d)Ùç=“²”¥ÈuMËaND’žËÄýJ³ð×TêCÍŠj@ÙƒÕÀþ`g>:kÍÐQ;‹k«ñÖi¬Z2E]±\PUnZV=CUœ`Q¨J·VåÚåª$YÕNéùM.)ù›g')v¹'(dñŠs²8Å]Ùšü»&ä3§Ä&I*`â‰FÇga¼4g˨”}˜öÏ=õ@W½:êEh[+åšêæè¨ÕïéWÕ•V‡š(«¢Dù•ñæ¹å©VÙerÛŒÒ"‡Ô’JiRqì5ªM®±ª·åhå'*o{D«þåºZÅœb‹™„ˆ'z”
+㬖“ôu1¢ä¦D³„Æ «¸…mÌÚRûUõjÇÈúõNauÛg}PwÀ5¸n˜|ï¬þÅù5s U3b7ÑêÃ%z†©Ctÿ}­ÀŽvÚ=7õ[€*J5Pjfqùoù–Ez›ƒ S:B‰›¢Lã6ÆÏˆiO3Úk¹¾Ø6¬­VÒÖ*YÑÚ%]Öºß)¨õŒs`ë7Î-σZ˜Ã²f&^ÞÌl'ú–~“þLÏÁ êÿ!Êß½ÐPvK'í^Ûe·.=Ó‘Ó#Af'—ÚíÃOìÐÛl³=Rµ-Ö4²+YÖ•mÒYd±bkµuÐÖfÛ
+Ç›žüàCàp+ö·WC|;¶wT°¹ÓÀYßíä,ïâ,îžçÌïüƒ3¿õ‚3ÿñÌnN¶“úßAù ”_z ÓÝ×|Jù”ý>eû|¼ó5àr°¿£ ÛBØ<˜«‡¶°xè óGž˜ùhfüìÑãL{Ó'é0yZLÖA8Ú á³c<»
+ãÑÇ0~2ãŸÙ$Êo¢üòœaÊ?Où”@Ù ([ö@z°ù0ÌÃÌçF=7…é 3˜¼°…𥂗î0zå ƒ×‹ ÿz9øcQÐù- ¼7ÅÀoÍ„²1jîØ5ò„Œ‘ßþ«™ò+)?W $ÐÝïK¯ó(Û²(Ûâ0ý`ò
+2=˜1 ™ ô™|f މæL<ÉB€ß_ý!3zøýè3:£B³‹äÞ$Õ”¯ üDú(â+Àÿ;`.e»þ °»ª3eÓÑ)߀z„Çøô]" &DD,‰=q!^d1 !ô¢Ìè‘5.BCÏNMÂÄ´[:8¿eçÊ1 OŽM[À1“
+€,æIÐøœÙÄKŸ`btVù`¦ŒºóY.uË»„ñ‹w5ó†×2ÖU¯&Öß½6]òÜn|Ñ³ÓøÏ“óžMF<MÏy~ozÆë¶ÙI¯fCÞ¿›ðƦÇÁ€76ù#ì uç™Am„ƒa¡G‹ÑÓùÓÑý€¹Ä-ÿâÆ¼òò\%ãÇ9ZÖy¿%FçüjMÎú®0=í»Öì”ÏfóaŸv‹o}¾°ø‹O¯å O¿Õ€Ï)«c>׬¿ñµ>èû›Õ~?lùµ¶èóÃæûü°ÙÇð\¨È‚º „Ãa‘h<‚ž„ÓèN¨;º: ý-4„ ‰£Î˘'¨Œ‡™Î×› Ö[ 0Xö4[ößÂ>èßa³ß¿›óµÿ~Û}þ'l÷ú_°ëñ¿o×0ÆÙ'([wb+`Ù5ÿ0ñþð¹Š@hl‰FcYèqŒºe®F9¡ Ñ3Йè
+n±ín³ßÔÉí
+Úë°+èogÐY^GÐ-º=øw[¶k ÅÀÖ[?‚0^õcºŸø_’t-‰…®Ä›¡‘:•èçýñaŒqñFûbe¦=‹²-º£µV»£Êؑ՜vk¸íá›¶…No ëæo ;(Ø6ì¸1ì'Ç–ðgüõ¿;¬Äö͑ض9
+Û|ì¿/‘=‚ú7SºœŽÐ…tú>ÕLµCÒOÐ!él¢/5˜Ñ“c´;)Õ´31ÓbG‚Úº=®ˆÓ[n×[ÇÝcàm\ÔL¯_Ô&XÝåØÝ'\=è¼*ú¢óÊè 1ïø†Xì
+úŒÐõ „F ö°êÃÜsžûr-P·Z€v©§Ûsý¨-9!¬–¬E¦k•)«rkCFg™\k¿tq©C¬š®–-s¬L_',—¶¹”Iw‹K¤]uÒ¿ºIo»§¿qÖɰ@·ÓÀp'½ŸØ‹i°àºqꞀٳ¿
+Ì×*08Lz‡Ð¿dÐÈà”¡cE0ïè`Þ
+–;ej׻ȵŸ‹eš=nRÍq÷4íew©ö™8]‹…²,X\€iÀ›ôPŠÐ%ô28Zû 2¨ƒ À˜×ÔX¡µN¨¡vQSãKé«‚X¥•‘&Å æZ½Ô:¯\ÁQ-QÛg•9(J+øò’F§ô’uÎiºí¢ÝW®Iºcn‰º‹à©(Y÷»SŠ
+X+
+8"8Kl‰Uk+“âÕ.DéÕÚ:¬ÖZÀ«uÀ}_»Z¥þ¸?ž•äÏy÷÷í}rÎç?hžô<Oz×wûpf&pœ{°Ÿù»–±|ΠeôI²óSaNJ >L1+¦'Ǫ¦$Å;LZÞ_3~ùPí¸ecŒ^:A?òÓižÃ–Ìñ²8Ágàâ~ý³üû&îhÚ;±ÀØ+ñœ±Wƒ&½^úõI>äÝ7AþR9™ûÀ°ï{™¿u{À±dÕ*`}œªÂ¬4=f¤15½˜”©x/µ‹j\jÇÑ«;ø|¤vØÊñ †¬œ¢øÙG û­XèÕ{E²wÏ”Lßø”m»¥nÜ5ålã®É÷ýº%½ðé–$ Ý“¤W÷åoçÁ×óy?²ÿ»8ŠlJeÒ¤5œý29ûeïg»aÂ:_Œ_„qëÚ‹QÙqÊáÙ=ÔC³8Φ9NÛwíûn½×ÎÒõX³À£Ûšåž]2ÖxuÊØjˆË8ä›Qæ»úgClús¯¸4Ùˆ<ë+al܃½¬?/ ÈböÊ, q=0›³èDŽGcr5™ÛÃó04/ƒó"ÅÀ¼®Š~¹}T½s‡¨{æŽrì¾e‚¦ë–é.ræºÅå,ÕEç¬vÜœ«ï°ù GDΈÍ÷ôáŸëÃ7H}øzéQÏIÞ¸ï;˜¿q-¾Žç€ÙóriÛ9îâüÇѨŸÕ }¬>èmm‰žV â­±¢›5^t±öWt²SÆYÿ¡Ž¶NqˆÌŸí‘¿X–ŸæbÎߢm—¿_Û6ÿ´¶íž*mè®ç®¡;¥k莿+àÈgÿs˜¿–µ'ç
+®dí ;¬À¤ýÀH΢ýl@—ã@‡“¡…„éѺÈÁEA0[T‹V%=вdKÆ Eé4++J“DÓÒu¢I©UøŸ>%üKïˆÆÅ¿+ü
+¥ÒïÔTÙÁü ÌOÍ–°ï³¿&3{ÇÑþ_3ÿ[æ¡§À«à_î ß îð©ðwE .†R¼.uC£KáÉ—†•Óàñý"èÿ“÷ËÛ¡»rœ®AWY ÝE ]…º ombþjæ/eï?fíSX÷hf÷?Åü â rh^øUž?¨¡»åÝmw¸Ýñ¡fp½íݸػÀÙ>
+诮·—j'hjÝàXëU5@‰Â¨#ñ¥¤†ãÞø5¼ékxÐk¿ ^øñ]zB/ÞHfþž¹Žñ,d~Љٖ‹@«Ë€ÿO¬ý&Ðàà|P×Ê:¼t^¸’;5âo
+¤¶@] ðªñaôŠë\‡ä:$¹d‘’–wÞXÈüé<sc‹ç™ÏºÍW¸÷×
+RJ<QýIý– hB~l!ùó»áu?BÙÚö¤×3LHí{BºÌ¤ô)­¤ !×ÓömY‰½p>Ž:çR¼p¾„§ÎU¨u~Œßœ%¹Hüª•x¨•â]²…/d ²…×ÁϦ¯{̽‰
+º@åï!ÌnÓ€\!CuÄ~´a/ZólEâY`<7"Œ¦Š{ÆÙÂn\$n—‰›ÆÏÅ ãq͸AühÌ?÷ˆËÆŠJã ÅÅ€2EyÀuŹf¿)Ï4«S–6—Êb*¢B:UlË|³ ¤Å 2œëçšÚû ÎÜÛšñ0¤#ªZ÷Å­àâ§à âŠi†¨4ÍUT˜å¦$Åw¦Tå9S¦ò¬i“²Ì´MuÚ´WUb*P™ŠÔ…¦JõÉàêcÁÏ޶–­¥ú~‡ Ó2W ©„Œá:b\Qíêq¯}n„EàŠ%‹󖱢Ì2YQjùPYlž¯:e^¬úÆœ¬>iNs8aÎr8Ö.Çñh»Ž¶vûŽ˜O82ŸÓ0ßÕ|aù¯Æj‘š|‹tÚm‘ޝíªGF8BFñ¯"xÞY º³ª;ºÂ£ÇÕ¨&(n‡³1PÓO|3Bq"z¼ò«èÔQÿt8õ/ǃ‘KœöG¦höE¦ÿé2 jë¼Âð‘Ī}Eb±'{¼Åc\jl!ƒB¬!„$K€@$6±Ib·Ø±@Œ… Æ8ÂÄX$vl'®×Y&‰ÓvÆíL“Ž=iÓ&“I:MÓÔmšôö43ûÇ3÷þ{ïyÏ9÷û^ê+b?uU¼L»$¾L¿(¾Î8/~›?b.ü†±”JÐϤ´ÅT‚ºJÄ> ‘FþIÿ‰ l€Ç
+|,‚ßKið #Þ–í‚_ÈÒà¦LAº–YB e"®dÔD­±G_’¶Åž—vQƒéÚËéãŒeÉ sI²ÀZ”¬°OKBœ€äÇ/yÈKÿš3›þkFJ0§¥}únö3‡ÑƒL̽ø{T€¹óçƒ
+¼Ÿ÷røð†r\Ëý9¬çf’W•” 9e‘AEEôr¶5öŒ¼‰º ï ²z˜þ¬!Öœl’=+;ÅÎ\æù2×øS™·ÞÌ‚ Ù‚ñ¬ï¹cr‚0GåãY)kXÿŸPû!æ¾Jî«îEÀÍBl½
+Ó¢J!@xU&‚æKîöþ>zpu¯6¢͘7ð9‹×o=Fl›ÁcÛ ½61Ù]ŸAé¨SFµÖÇ4[Ë© –
+†­ÆÊª­näXª;ùÕæ¸Jóq‘©ÊŸp´ê|’¾êú&]ՇȗIzó"ƒ™ˆCøGÍ/Ì_p…3øúu×8mxßÇ 8Ù„™§‰Çš¡§y¸šRHíéGƒ"²Ñ^m³—QëlFº¥¾†Y]gçTÖµóLµ}q†Úq‘Î:— µ“4Ö×6•ZßG¾HÔXÿ#*³qeµ_k%xa>Å}ü
+¢­-¥±ÕÍZe³™ijªg[¹ºÆnAYè°Ô>_b9Qe5©Èþ^R¡ýs|ÿ^¤²‚b;ÁGxaþ€}xçà&Ö¼Žú+˜?º1s Çð[Ü®hhwñÁá~Ý{I6W*¹Ö•I©væFVvǘ:Ë©†Ž
+zy{-«¬­™£nsóŠ[‡ã
+[gDùŽåø<G(AéxùsB®ãßÂ<!@xù‚æ!îã=ôàú¿Šúçú0÷
+†ÌÔÜA;=g°ƒ%÷ pdž)^†g‘/=vEî¹'û£ }à /}€àJû Âózð:ö` ë?7„Œ¡8^ô` f
+³ÇT,è||Ðú¶€æÄ(ñ‰I*ßr/‡’ç+ŠPNi#³§*¢³&ëb3&[iRoCâõ²yØ©Þ5ÎAï[±÷S¶xâŸlñ8Á:8öÌ0wp7zЬwaç
+IV´ä”•Jòþ;eߊ+bop$rOðTÔî२]ÁÛQ;ƒ¢vžû.zÇ2½ã,³cé)!Œ‚+¨ÀÚ}s
+ ùêØwu콚/…$°'¤€Ý!ì
+éaGÈJÚj#m yH[C³äCçÉ/l¼AÞ²ñ yËú·”篔ç_!"ž[{ÊeÔ_Dý¬}d{€Ú 
+ͦ…”IÙ´†6Ò6*¤½TL'éÝÁ;<¡wxËßÞPß-ÝÙôÖl ùÙ¦-„â/ü¦õ§!ô9M¢J£%´œrhåSíaÖÞ· ¯q–%ºÉRÕ³doð”ß6zò'D+7·l9º²¬‡­/„6BÙBÏÿIhêûZ, ¥´’=ÌeB3·2³ÏX‹'(ar9‡íóç!^ññ¸ßH¸÷„»3DG¶ÃÂÙÌz´‚pèÀštÐs<l9Úaš±4 ïÔ3ðF=‡@:^ª³ð\•'ªµ¨WmBª
+鈛ŠÝ„ìp#×?æû(IÑ‘íðe=>1ãE;w<öêˆ;A¸Þ&WÛ ÆE÷‘8ç>Uî“Qá1§=æH?y,’NxdJÇ<²¥2uÒQÏ-R‰ç.©Øóì°×iÙ¯›²½ÞOeEÞB¾‡v½Wøá§jz•­ä/Ã+«/ü xàã„ëí=ñK{+Î~†rK NX†¢Ì2Z*µLŠ-ÉÒ!KŠì€e®l¿%]¶Ï’%ûÁ’#/²äÉ÷X
+ä»|ö*
+}Ž)vú\Uló­Slñ}§Ì÷ŠM…bc#ß?4esZ à4ÄéþÕtU£¦“çü\qª³/ÊüƒQì]â¥}þŸIEþcd»ý¿”úO‘ï´Î”ï°¦)¶[+¶ZW(7[sUùÖͪÖÝê Ö#êõÖsê5þ÷ÕÿôoPçtêU]„*»‰²™èÊ60ÿ>§´k½€‹t&TŽÊ
+]d“²L—’k»$d‹>=¤H¿(ä˜aAh­a^èS}ZO¡#›¹=…vn˜Ð4Ý›–¹jfWpj/Äs'Ï ?FJØÓ×€‚7äGúbCdr#ûH91²•ýåËûPfö£Ê蛤^Ò'Y›Þg¦ÍÂÞiºù½3lÓz¯2Ì ßh—¾Ë8;¼Ô8«÷û™½ëíRú}J_¡#Ò6{ÆÜŽ
+|—`BV¢– ñÃÂÄ`)-¡·,5!Z>ëÓÁÊ”ÁÃUÓÖL¯MŽŸ¤›7]?1n®Ý—±öbW›þ»Åq\ì>óØØró˜¸{ŽcâìÇÆ ;Ò“m³û|/²ÇÙÿƒ#Y.õÛh=?góo™ÃŒX<¼5æÿ/ÛeÔ䙯ñ®‡Ö ©GAkµÐ¢ˆÊ!¢†#@‰ AåðâÐz@TªBt]O »®°µÖZa»Ý]ÇÚ-3Ýum«µèn­­Õ~û×êL§“ùÍ _¾|ÿ÷ù?ïû~ψµi¯ŠªÓ扫t‹$å©ñ¥))2kŠÁ±89ǹ0¹ÐÕ’´J‘—T«2k7¹gk[=LÚƒžYÚSšLí^í &3iH™$¨²’%QùS™‡èû{Œý Kž·rèÙ»œµ>¯5镨`‰:Ãd¬1ÌUæŠKõ‘kÆR‡âô$iAzºc~šÉ97Í⚣[©X®«Vuî†ÔúÔžé©'4i©ý^ºÔ›š4Ý#ušNpK× J¢æ×â?˜ÿsYÀIjËöç³çËe¿C?Öå¨1ù Ê4«LÓ±Òôº¨Ø¸P\µØ!?k™Ôœ©sÌÎÌr2ò\² V¹A¿Z™¡¯w×émꔌýžÉÇ5Iú‹^Zý ŽÔIzÁ-Y/(‰b˜kÌÃÀÛŒû8µ²ß(b¿Áq£¨1;£Â¬A©y<¬¹/£È,²˜ÃŹ9‹$Ù9 RSvŠ,3Ûà¤_nvI_^,×™*•)¦ nZc³:ѸÏ#ÁøM¼ñ‚&ÎøÇûêx£à–`”D1̧ÌÃÅLžŒ»‹Ú¿·²çbI¾c­¨°HQbQ£¨` ,È-˜-Ê)›,Q’LËR}~’,=?Ã15/Û99¯ÐU›W®HÌ]§ŠËÝæ¾ÄÜîkîòŒÉ=O®yƘT/6 *¢Œ5 Ša®pݧ' ˜‡æ¥ø.–Ÿ›8VrVb±ªk}9%“`* BfI¨H_²Pœf•¤Z—I“W¤É–­09%[\âŠKå±Euʘ¢-nÑE{Ý# ;ÕEç<]UGþàQ(¨" ed æï\ÿg¹ŽSëµÛVÍk€zŽeÔçµìr9²*¼a¨ðGFå4è*CD)•á⤊EâÄŠx‡øòéÒòLÇÅ«r­*q‰*«–/,Û¤œ_¶[^vÌ-¬ì,ùÔ-¬ôž*¬TPÎ[É
+m¥ ¦Ÿt3Œû
+§ðšÕÎsk]C«[å¯WQ„TÿYR3 YsW²Zת眧'J#Ôo§nË ¡XÝ
+·ÞõPõ¶BÙÛEïE(z¡<ó”§(O=§Þ·Ð÷zú]ɸ-ÔÖw "N
+Á“ý­z%qâßx:)$„D-É"d©% d+ÙAöPg?uã1º¨q
+ðâ¯x€«´ékÚõøžÜ%w~…àíAãE?4ô‚t¯N"³È|GÒ‰™XŸyñë8ÿ&>y;uvQ§ ?à
+~•ZðïÂC¾ øjü«Ã]¾ßóóñ¿·ùøFÒŠ›’6\—lÇUÉ~\–Ç_¤çqQö¾á¼BÄY¥ˆOU"Îü1J±¿”üà F* ö!ŸzR}ºÀ/Ãñ£&4ø›º
+OÈþ?Èþ­$Ò[F²M|@¿Æ380Jƒ]£ûb«^‹Íq£±!ÞˆÖø,fM\³2®]WʶÄUqKõ“¹ÅúiüB}£¤Y¿HÒ¤_%§o“ÍÑï–5êO³õW„†¸‡Â̸§²ñ¢”¼ÊO£¨%Rë¥kýLÍù™À!jy{“Io$(ñ'ClHüÖ&ÀÊ$–%™˜¥IfQ¢mNôpM‰åÜ<C€Ÿc¨•Ì6Ì”6$4Éf&,¦'¬—OKØ!¯O8ª¨3\PÔ(¦$>¦$Š2BZó
+⩦P ÌÀ)Òž‡s=´n#ÚŒ2´¦tŪÔh´¤Çâ´1hNMaæ§f3sR­ìì÷NŠ—Ÿa¬ä§C’°1,­36
+µÉKä5Éë¡äe0ùj’ñ¼*`¼§
+¤üG11EÙ«ü`
+rÙ²WZPÄ—X}5 +²Ö .k£Âa]ª²[׫mÖ]šëÉNVëMâ_j«UTXEù „gܤ:œ![GÉæî Ý´-^šõ]4ë:¥¨u¾†³&¹´¨rD…ÓÀøœiŒ×™Í;¬œÛáâ ^‰ÓQ%µÛk›}–Âj_¬ooUçÙwj,öš\ûuâ±Úb•„‚Â3¾²Ð^ ¤Øw–ÑY¨ ÍGãðâr`¦‡ô†›Cµ»3ªÜ}Qáye¼ž1L±ÇȸÝfÖåÎçœnowKlE~©µ¨FÈ/zGn)\¨Ì)\§Ê*Ü®6½¯É,úŠx¤2‰Js¡¨ ä„ðŒKtNRöSìÛ*©4~®$š'õô®ºð—jà+í…’Òx|ÃQ䋃Ë7Žqø2Ø ¥¶ t7¾ÔÍç•–Kr½!Y¶wºÜì] Èð®Q¦{;TiÞ£êTï—ĪÔQ‘V"Ê_ <ã3:ïS öPìí4v¶†¨“¹´N¡±ÜOï½~%ÜþnpUFÁQ9ªF¢ ÊÀX«Ò˜üÊlÖRiår* ù¬JŸ$Ã?IšîËRýóåFÿ*ErÅVe’ÿˆ*ÑI•XñP™T!*’ÊE9!Œ{Á:‡©;)ö¶`õTÒu´i ’/>òÅ”aB0ÁHŒ½¼¹¡1È™ld²B™Lf(Ÿ5…\Z¨„O N”$ë¤IÁ¹2Cp…0¶º]<¤ˆ^PÄUÿ ŸôTˆŸ$
+ñQö’S”ƒýoÙß&Ý7h"i4L#ÝUOsƒÜ: ²ë{Â\?™áa0…G!=œˆÔp:c ç0ãÂ66)ìa õ~nLý>®¾ñ¿\—yT”ׯŸï›°Œ3,²
+ʦ0 ‡'Æ-DkÕ$F­5O465"¸eE© 8€¸ÂHÝpjÜ%б©§‰&*.ÚTÓ¨(1Þ>
+&©üÎÇ÷÷y—{ßû¾ª¾™EêÔÌ
+9³^“œÙ¬Iθ­Ižÿ³&9]hÌéBmž×Á^îÁ]ôu õKY²+Ç¢ùVæÏwÈÛdX¶'†äø!-'¯Yã0ØÚƒ¬ý0Àš†þÖèg#Y¬¤Tëû²Ù:Ga²f)’r
+”/çlV&æ¸TÆœä¦Ò˜õXe\*TÆ%¿RÏ=PC_7f1³&¬àyÈeVr¬â알 ƒódô³ia±¢¯=©ö¤ØÍH¶÷‡É>}òßBRþ8ôΟ,í³$ƒ}‘o·É½ìäX{­c?&ÇØ¾“cò~RĬäv°‹ywÒ÷RŽ@ùÔ´ÚƒÆ`5÷GÕQk׋WÖI%0–ê‘PCi4âKˆ+MA/Ç@ôt C¬c4b嘆HGº;ráX/us씇¥°ÒëRغv)´XH¡k…üœ­ô}/²q ©;“z“8¢Žåxøû@ÿM€i3_ôpz"©G¸3Ý*£ViDhe
+B*"¸r8‚*Ç¢kÕ$T͆UüªŠá[U}•ºÊVè·<„¾B@_.¤ç”S}·YÔžÃqp
+5ÇQod%0¨0o¥þ6 r'ÌùÔ·î%è\:­+’àã2“Aðv€—k<]SááÊ„ÆeƒÊUe]#9Kî@UûêÝâŒ{!ãm¥ßéôyê`<5ßäX:xõkƒ‹þ7
+µfRg>u–  Ëp6êQ§”)ÙÈõ\s;SU[ŒÅwh¦W˜Âp•ÿ}•«´’+\îDøë ôZÆÂBÓ…9 à×îÄH^!¿£æêM¤?Ó¨3›ÑÍÄmæä&–SÇŽXC56síjnŸÝ¸ÈX|£8¯ð%­:ÇU¾àj-äÌ ˆ@oÚáÁX¨ ´^ÞzÚÂXH±Œo2ýLÍ7¨7ŽZ“©3¶ÏåñX€ ȦÂ
+êä󸮥F5*pš±hf,NÂc<ÌGiéaæãõþ& ¸ÿUÓnOâG;ôŒ‡Ö<#pW€
+ .+†â¢âmœW¾ƒsÊ)hQÍÀçªpJµ'T98¦ÊÅQu«KpH½nu5šÔuد9€½§±Çó:½ÚPï#à"uOÑ
+Ôv"©ÚA;öý®*< ·µhõŠÄ?½’pÖ{
+p’-/ XbEwõ(àKËeë‹Á*\Ðyã¬>'üâðY@*šº¾Ž½o¢1ð¨zuAS±;hv§cGðbl ^†š;ªCŠQ² ÎÐT„6`sØQl
+»„²n?JŽp!•’’!­û "–¥¬'õ,ƒ½YÊù<Å÷ã8æ‡}á‘hˆHBm÷þØÑcj"G£:r<œQ“QõÊ£>Ħ¨ lˆZŠ¿D­ÀúèB©4z½T픊£ë¤51n©(æ¼T{G²÷²ä‘UHOicÙ{ø†WÞé¾À‘T^o}Økd4Æh±»g7ÔÄPßåñiØh‰2ÃX8 PbxņéÒÃiuB¦T˜#å'äI¶„b9/a³¼2a‡¼"aŸ¼ÜxFñqâ-ENâcEv¢³:YÚÉ=–ÓkÔ=ןóÖ@Î|¤áU –×ß¶Þ^pöĦ¤X¬ïcBIŸXc†Õ¦QÈ7ƒÍ4QZeú³”kš!­0}$-3-–­¦år¶©P‘e*S,1mU,65*šN*$ßPf˜)ç›…"ýîšêŸJ£ÿC€Æ¡ìqùs5¿•§¨±>ÅÅ}{`µ%v‹«,iȵŒÄ2ËÉj/e[&IK-ÓäÅ–å…– y%[‘a±)Ó-%Êy§j®Å¥úÑeÕy†ñçÞ™{D ŠK] ¢qÁD–pFf`¹âf–Ù•ÀD
+5.ŒKãq-5zŒX›˜TkÔÚÄœ4§mz‚5m<96ij4IÛ“dú
+«õq¥¾Jôé4uúç4µú´Õú]R•þìÕŸ–+ oê–zÈWryjH"Ú> ý|Âzßeí—mìA6³G#¼îËäŒÉulI„–…£ð¼1kLsÐlJF£É„U&›à3-ꌡÖX,V—‹UÆZM¥±I[aÜ(•wÈc»Îm<©+1¾Vlú y¨+6…ä"SH"Úâø?ñ7©w‰Úgø‰íÌ:xÝÃu¼H?6š#°Æ<Mæ‰Xm‰Eƒ%õ–4ÔX2„EðZò„
+‹S,7{Ä2ó
+Û¼Z[b~^*6o“]æýºBó‰°e–Ka–?’º‚ŒL¤KÚ^þÊZ¯eÝÔ}%Ÿs¾ƒ™“4¨Ž…k¬ah´EƒíIÔÙ¦¡Ú‡*›¶t”Û²m©Pj+Kl¥b‘Í«qÙ´…ÖµRu‹l·îÕåÛ:ÃòlÃsmï…«¶/ÂÔ¬œkëCzÌÜ¿åÈužú]˸9оD¶q-ë¹®Æl-ê³#Q£Œ…W™‚
+eÊ”$¸#J”L¡HY"8»X¨‰Êr]©×æ+~)WÙ$/U~®ËQއ)Ê…pEy'<[ùœ÷!‘‰ô˜Û܃—YóÙöÁżÅ1t7Ùìžãº|üÛŠ¥ƒQ¡ŽB™Ru&ŠÕ¸ÔTª¡@Íjž¯:Å\µL£ª5ÚµIRÔ€¼XÝ­ËR…YÕóäVx¦úO^¿×YÕL¤Çܢߗ¨ušúÇJÙû@¼o¤/ÕìK¹} Jí#PdŸ§c:–9âápè‘ïHGž#KPªãX&.q¸5ÙŽš,G£Öjo‘2ì;uûÙq.,Ýq3l‘ã>¯ßÉéöP/’ùÞÎá^ ÖÉà0GOÆ4l¯ä^\NèG×Vâ G¡3
+ÎñÈw>ƒ<×\¨®$丌Xâʲ]9Âb—C´¹JÄL—Wcq­Ò¦»6H&g›ltÒ¥¹^Õ¥º®“Ou©Îoå4gH꣰Ÿ+y|'‹˜{©ßáåûÀX²…¬[Ôrz²¬TB^i$ÔÒ1ÈqO‚➉ŞdyRaõXéÉ,ž|!ÝS$.òTˆFOƒ&ͽNkpo—RÜr²ûŒœä¹FîÉIîÿII¥¡>’KBÚ^Þ gŽSÿ@-ûÀH þz¸Š¹“¾dW BVåHX+' Ã; ï<˜½)XTe‚©Ê†…UªVU(¼ebŠ·NLò®Ñ,ðnÕÎ÷ÔÆ{OKqÞ«ä)®ò¿Ú¸Ê6®â'.ÒïSÔ8\Ç>¬âûÀ8²4’Ê•Ì] Ì\¥n
+ê¨ÓDíÔhÃîÉ9ÆçYþÎÐ’Sœ¡G|ŒÒkwql˜Œ£Cyœe¥ç䑯›ÆãžäŽo$Oü^17Á¸æ Ã‡xô ³âÒ°,\pÌÇ9Çbœu,Ç™áU85|zœ6ã„S Ž9íG÷ˆ#èqGF^ÁáQß Ãù1Ú]Þâ  sÔhž¤!È“¨ÏëíŸÓxå»|¿ã!â+ÆÈ‘ãpÕy~ãâ‡ó.áè­Ç)×DœpMÃ1×lt»-C—[ :ÝÖà·jt¸mD»û6toÃ÷O°Ïã$öxô¡mÌ´Ž}„–qo°c<óÙ6„WL7OfRß›Ú
+]Ó& Ãs6L÷ÃÞ*ìšgš°sf2¶Ï´a«×R4{b“Wš¼*ÑèU†Y[Q?kP7«S¨Ý+¬Ÿ}]¨žs_¨šó\¨ð–…uÿÃS^ë÷¨}‹)æ³Öù¼ZO.º˜†;¼±Žv{{bçûs±í%š}4hò1 Ñ' >VlðÍD­oj|‹Pí[Ž*ßõB¥o“°Î·UX3÷P>·G\=÷ªX6rÞbé|Y,y‡P<È÷\÷¨{-œ©]Í~‹t…±¾S²ï\ `ç<Wl™?Mz£a?êüT¨ñÓ¡Úψ*? *üÒ±Ö/å~Âj¿R¡Ì¯B(õoJüwˆEþÅþÇÅå}bAÀ]iYàb~ <@ÞÏÈâ}®ù6õ/G±çdÊï&‡8ÞÃo;蛦
+1Y·V´èê%³n§” ëPÄGŸQ˜¢o(⢒×D–ú1 r›åç§qÀ)jw²üÛgeÍg#ß«ø½”v,vFîâqX¢ŸŽL½lú
+©G¡5]SDŧˆ2½äX–ˆ{ÇŒƒsI,ý©ßβoWÐLj9.£]ù´ÃžàˆÔ7¤$L†%aç#!1¦ÄpÄ%ê`4kN‚Álôæ<!Ú\*jÍÕb”¹YÒ˜÷KjË E„åsE¸å>y!E˜å_H”¥«Ü÷^ú¿‹šXòµäò<’jŽ‹éú#5Y‚%Ù É`JžcŠ/bS`H …Þ…ÅÖD[ÍÐZÓ…(ëRAc-"¬Ub¸u“¨²î•B­Ç¤Ô+Rpê·R°õ9‘¥”_èã¾÷PëpûÞ|Þ l6’
+Ž iG}’dcÍ>
+1éc ÏxÑÞÐÙ@k[„H›Ûb¨íñˆ°[fÏBí+„û:q‘ý#1ÈÞ&.´•í—¥€Ì{R€ý™h“Å2Þq‘ë<–Í3IÍÝ+Øs±¨#kþËv™Ç×x¦aø>ÇY›=–Dö…lvBP&-~˜¶ª´Ú,È"d“’åˆ%‘ÄžåXƒÄVjI ¥5bÔ´Æc´1ÓZÚi«CƒªvZ3ß\‘Ìoü1\rç{ïçyÞ÷{Þçæólb™>Kz »4&ÞVqñ.ß]#‚4"!RÃhXâ0Å&>§¡‰“øŠ'ÎÔÀÄdӀĦ~‰ËM}’ªÌ½“ö˜£“Þ5G%]3G%>4G%æèVâÛhdö ³e®T‘Á>`
+ 3“³8ßE<ˆgTJÅ&;iHЧbRü48%LSûh@jŒú§ŽT¿´±ê›6Q}Ò¦+:m–¢Ò²‘¶ÄÔ+­ÂÔ3m—),í„)tîUShêShŠñ„°dÃÜÊAr¬Cc#º«±†Ë°
+ÏŽSÏì
+Ë™¢ÐœçÌSPŽE9kåŸS+¿œcò͹,ßìûòÍ2L¾™†É/£·R¹ŸX¿ݲ|Þ‡B¼§…}€©0ß ƒ±ŒÑf…8*¤À]AÞ
+, V@a¤ü ÂùZƨ‡e¢|,3ämIU7Ë"uµ¬”—e›<-ò°\”Ga‹<ó™XóÚð‚Ô|Ó"i z˗З–QƒbjP"M‚1+¤Xè aà·ÂFÝJÔµÔC]J}äU"ϲ(y” ’{ÙH¹•—kù«r)O’syŽž)/‘SÙf9–’CÙy9”~#Ç’Çr*az.nc[5(¢XÒÅhda “Vñ>®‘ÆcËF®g øWàA+%×êŽr²:ÉÑê!«„ÈÞ-;kŒl­qêl}Q­3dcMW‡êÅ2UóP¦¶
+#[yðº†LëÛØ@î«Ñ^¾RÊE7­_Wá?7Hq›ØƒÍRÄ)`+ú5èo—kÍêTg/S·Î |!°Žadg,ŒÉ@£©ãPÕ‘XÖâuw\ êöŸÁxº2îèÕìÚs­ìš/£õü)k¹S
+Ü>i¸ì“Þ–lâoaHa1Œî~R=Ci}_šCP ¸æÛÀo Ðõ,^ÏÇÏÁ×<÷OXI½-äAÎ ÛèGhŽFoz‘<€^—Ã’s£dwT2GÿÝvNÇIâ8EïaÞg0?Ý:Ç™g
+šhrMÙ@-šH¬‰Î| -ðßý·Š/‹:'‘ó´Ç†Ö£¦?¥ó<!9¡×‰­ÄÐsÃÐçá÷ð¡ô‘½ôŒÒE Ó%o†Ãé*q|Â`ø N3 ¨™—®™sѼšà&°'͵€ÜgSë×Èy\ïÀ1)êôOI§Ùû³Ô¾UóC¸l'F h†¿Â§pšÜĨ}Þj”0,·ØïÇ]†´o9-4^ºW B-ìI ¶|¡rŸvˆw€œ‡Ÿ¤ ë‡®;y:\ ö—XÿJ»Þgp þ_ÛªÛp¾…{fé­ô=5ù‘³úãb0´ )ÆT ¥@âÅ63Îá8=À½/ð0qðL‡Y0r¡–C9¬+l…°Žànß׿(ÜÏûO=Òüõ‡§¸wàv«çÅWrnÇ¥=Ž è #`<L7 2a!ë/Öcë'­Dc=PÚ¦ïµ[uPts”ç3ÝåÓ?ˆê›vÍV¾‚ÏáFk œø×ìÁÜÀ"`<ÏÓÉg::Ih¤é;e뾩EEh”Ï*Ö¯`íM¬½ƒÌßbýFݤ×u‰ãr[׈´™Õ>†¿<Å•Ölíd˜løÔlÛëÑÍjÖŸœž%§ñhMFg&³õ…Ò9ÙäG–Eúq\#Ž«ªdÍÍú“êôGÐç(À+tƒŠ|Ç+e¨éÿ`Ø›ØކÔ‡=ßvEÓÍHr‚Þsh½@¯¢ñ:sØét4²ÑÈC£ÕjQµN«Fïi-¤9­c<u”j5’Y#
+ íÔ·cÐbŒŽz‹¼;«#‘w!·`ý™sqQÃÑ‹ÎDâžÊªo 1G'‰ãâ8N¿!Ž#ZÁÚkuX9µz›Zìã{õ§äKíB¡õÔÖAíS´üŸy­[œx݈ç:¯Ö5ud]hA>äÖ‹]DN£Èg:“Й¦CŠGaoA:»ŸƒN:KÑ(gÝJm§5¦½Úb>ªÍÎi£Í-mèøPÕ UÙª|ŠÇ´”û´ÚÊU¸Àçˆël';êà®c¿ðWCÇ(ì<Dûlã´Çv‚vÚ½¢Z»éÚn¯ûdm±Ÿ§Íö ´Ñ¡PV‡bU;¬U¥Ã&­wÜ­uŽZãø;­rº¥rçG*u6´ÂÅPÉS<B÷+®›«´µóÁ´ü
+×éZçš µnÉZí6_+ÝÞT¹[‘JÝËUâ^­bZ-ó8¬¥ž¿U‘×§²ty¨‚®†òÛÉk§ÅŸVÛ“Vɵ׈`ÎæÚÙë×z-wV§›6zù«²k¤Öu¬ÕÝFª¼ûX•vQ%ݧh¹÷ -óNÔïT-öÎ’Å'_>ÅÊï±N‹zÔèMßýÊõ=©~W”íO™þ†2Àÿ|Íu{¹7WØ
+h- ZåE%¼Hy!„@$
+6¤g¡-=­é…X—aÁÚŒ
+¬ÎpŸQ¦ ?|™mX•¹ ™Ï
+oæ^áÉ:"ÜY§Dâ’X©ø\Ôfß!’¨U<ຒ¶ƒš§‹X´`ƒd?¯¹|ÖÍÜlΞˆö¬H´) E±k³Ó°:;Í9:ørLhÌ)GCN¼9uðäúàÎ]‡ºÜQ›Û-\ʰ¨QŠjåIY•ê¢Ì¡úTæP/*ITÇ80Œ´TfÚ?Ú¯>²§ØÎ8žb팣E5ªyhV-†O½ jêÕùð¨ Q§¶`ež®<œy ¨É[ƒêüváÈß&*ów »¦_Ø4#2«ö]Y¹ö&ùŽHâ4c¼O79÷‘rZM;ë ‚—ìƱ™¹ ð½_3š9ðjâáÖ&c¥6.­N­Õ:ªtåpèªQ©÷À®÷æ_/¬ú'EYÁQZÐ'³ Ë̆wd%†ä["ÝG”Œñ+æà õ†¨?XÅu ;i¿ºøÿ& }6ó±Ê0î‚(ÔÓ°Õ9†lT4°
+a+,…µ°寕(5ú`1„¹h‹(.z^˜ŠÉŠLC2£iTVhú#ù†H÷…Ec¼[LÛËy¿Bí~'°×ÅZ$[«ésùÜÏ8ܦIpšf¡Êƒ
+ÓbØLËa5e¡¬XÒâXŠÍ0—ØQRâBqÉ*™[`4…Áüœ(0zËOd:Ëy™¶ôc™Öò/"É´fIèÆ¥ÆÉJžGÔ=L+ÞëžsÓë×Òë3ŒÃY&Pa‰€Õò0ÊJÂRš si:ŠK•0•éPTf‚±ÌŠÂòʽЗ?]y‡ÐXŸùÖ}"Ïz\¨loÉT¶È×Be•Æ(—„š¼me-Ðr¡þA/°{"Áz`-ãòò]c,³M‡Ùb{,ŠìKQhO…Áž‚Š|è+ŒÐU”A[Y…üJò*WC]Ù¥£[ä:öŠlÇ1¡pœYU×D–ã+~–„¢òg9þíæ Û€}M¬Úð.¶G}\ÆáfN*KqõC0TGB_]Í"hj–!¿&y5*¨P9ÍP:+ëZ‰lW3® Ètu‰ W¯H«=*äµgDjíUòO!wIBî§F¯³Žsþ/QwÏZîÉÇ€'Ɇ5ìyøÌÕ0½(ôÈ_µ{”î8云í‘CáÉA–G‹L õV¤×;‘V߈Ôú
+ìbc¸ë'.ýÀ¾.þ¡v7u7RÇj÷ð<xÐï€dú‡¨ßL{‰}h?Ío?pÿl²€ÐÐø Ð ð’àÁ3À‚`!õ3¹ý „½nÕÃ߯pHBÏó̵ƒ½\ƒ}¬C~ÕFÃ
+€Ÿ’¡‰daCŒãã8Á8†ÇMÑ/¢´#<hFXHÃLà0¾ÀïÝàoþMî /ðÕœs+µ½ý<^f ð«9ÔZF­ø!®ÿ0ñ0ùgÔ~œù?lJÎ2çǛфùmÔóŒãÉ(/£Qp£Üä£ÌÅèaÂAF¯ðý„Íù{ØÊ|¯gn©í8˜N°_¥þIêŸfs gœ&¼EMNÍÍç8É/É%6I¿f“ò›‚ËsÙ Ð_e¿¡»ÆKùÜëÜø×Y`×9Áϓɗä[´3çM?j8ßbêªNSÿ,ëšÑÔœöóq-.#›à²bÄñÈGäwä÷2à“)À æä&׿Ó8àoËMÒ-^|·x~ÁÍw›“¾Íº¸ÍIüý·ä3ø™s™©GÝåÔûÅùM}c_×üð¾ùù3¹IþB>#%L-[Jଓ¯¦ßpm¾c£r‡ù¸«î±6îñ0”¸%&^ââJïËìö¢I$‰"³É|’Hÿ£ºLÀš¾Ï8þxË¥^È©œI @’„@
+WÈ›O!ŒÂSÀ\ÇQs2s fN±ÔKÆŸ¸/~læQ@RfV_ÑÇ»ôñ×e€ëò}ôÓÇ›èáN؃>®É%œ¦‹×q®Î±Rg¹r§©rŠœaðgaô°ö¿·ôÈ¿oj²^Ô“2Ÿ4j™˜‹…ŒZŠ×èã}\¤ ôqŽ>ÎÐÇ)tâvpgîÇÏp/ó/^â·_ÄŸYop€jûGØ7‚0Žú¼êîò8ÝâÕû=]ÇæèOÍ™xÑÔ“3’ç‘I<î87+]J…j®übfÜ@Fê´Rcãnã)Ù͇¹CÎòÔ\ÁvîV¸›{³kð4‰žðWÊßy”? äõC~9èç•wyÔDîì)ŒBÍ8ê%3¢Ž§/ DìåÆÞQ¥ØíU^µèõjÀöÑMè½]c:°uÌtŽ=ˆŽ±'±yÜe´ÿ›&ÞÃÆIÑæ-`ÃS< îm^i¿æõ:À«¾?_˜Æ× ¯Ýã|px\LˆÄÞ‰R윤B¯·=ÞVtù8°ÕǃNŸ2tøTã9Ÿ%h÷]g}›±Ñw6øvcß>´úG‹šý¯cíäûhš" ‘¬xÂ^Q¼Žy­ö‘³1À±H௸}^ØåçÿYØ:9?"Çæ
+ž™•Ž5Áf4ç`Up>VÌ.ÆòÙX6»õ!ËQÒŒ%!íXº5¡‡°0ì<„_Euøg¨Šx„ÊáG|L½w’ø
+æ«öd*g>÷° éIàÌÇšl–°)X>«#¢±"B††ÈTÔGP™…ÚÈ\,žSˆEsæaáœjTÏ­CÕÜFTÎmCyTæG@YôÌ‹ ·QûoÇ
+Oˆá}Åö«ŸÚ؂ձÕ!½|Õu² iKdŸç…•Q~X=u1‘¨cQl2ÆjPkDeœ qNÌó ,®¥ñ‹Q¿Å⟠H¼nÉ (”œDô
+ò¥ŸÂ•ðà’>áµ.SÿLç ¶>{H·h§%°’kS'öF$ $¡¨”Æ \*C™4¥ z”$d¢(ÁOB܉¥(L\€|Ù2¸dÍpÊ:àïF®ü8ìŠ7`Oú9IßaÅ0ï²Íx•ú'29g°ÝI¶°ýiãgkè­ž>È' B>eò`”Èç¢H![‘ŒB…ùIF¸’lp&9áH*F^r%ìÉK‘“²Ù)í°)w"KyUŸÈ¢ú(Sõ€ƒ`¥€¾Únæ:ظv`é`볎>VÑG-}”+Ç¢$Ååt(#àRÆÁ¡”#O•
+»J•Ùª\ØRݰ¦–#+­–´F˜ÕϤî…QsD”¡yUdÐÞ _AdÐü
+»:ÙêX5)°h´Ä„Lm6ÌÚ˜ÒKaL¯A†nôº6ètÛ‘®? ­þ’Hc¸NþA‘F?ŒV'ˆ.³§©÷µw°ý/äÌÉçZþ¾”õ¨d=Üô‘§÷FŽ.
+}†ºŒ¤Bk\µq=ÒLÝH5‚Ê|Q¤4@îA¤4 "Õ F¯±'˜÷Aj?ÏxK1Ï#ŸMQóó2úËÏâÌgž€LÓd˜L3aŠ€Á Y†t³
+Z³šL Ô™yH³!ÕRUÖ2¤dµ"9k’¬¡°^€Üú>dÖ{|~y–0„‚¼BcÌ{?µwp$é(ã^ «J¸ô5ÏÅ™ë”i½ÕéÖ h­!PÛ¢f“"Õ– U¶ÊlR²íHÊqC‘SyÎRÈìÍH´oÔ¾’Üsç¾GîBlÿ$v’œaÎ3×#Ì{/u»+9ïTq/𹼜ýþ<ö»ü¿\z1:EPçyC•€”¼YH΋D’#
+‡rGdŽ $:m:ó!q•AìªE¼k-b]ˆÉÑùg••|A#Ú% Ú9ÌiÆ?ÄüwQ{Û"î…î>ë9*VÐK!G½èè%Ù=²B$º§Aê…Ä ±;ñžÄyÒë± Æã@TQ æÕ`NQ#"‹7#¼xŠO!´ømò9B‹!¬H@˜‡+9Κ ~/µ;9­_
+4’ÿs]æQQXWþe‘u
+œdά<Ȇ3D¾Ü$­ºÍ­lÀ×J´—aÛb“²WS×2®—m”zAWÚ„þfô_•œ0'»Ðxn¡é³ÓÚií]¡'Ð Ø)ôvba'©ílî–uPC!d Ý|îRš[Y¿å¯pÐÍÝÀ 1þ5Îâ6©Ïv)–ñ4˜0úì”ÜwIw£ÿ¨v
+´Ôe¾J]¬*éT¡âN›´Ðu·¸þA·2»_T‘Ç=z6kÞ\F·‘«ÿÝXôá·”×Ýaôw”Ú͆§´ÎÍSUnþZå¥=´Ì£Ÿ¬žƒUê9R%žã´Ø3E‹¼¦i—IÙ …*4,Ö\ÃJ½hX¯|ïÊó~C9>Ç5Çç¢fûÞSvçæVfµsÍ÷¸¾¸öpåîN”¶ÆÓ_FÑŠp®ôuQ™·–ø„¨Ø7N :÷’¥s’Šº<§y]Fin—ñ*ðKUžßtåúeiŽ_¾fûÏ×,ÿeÊ
+X­Œ€m2îWzà»J:§Áw4#„Š÷Ÿ ÿ^o®c®–Z ×Ÿ¡­à½­»´‚R¿$´ƒxª(0@sƒ¢”Ü]¹Á}5'd f‡ Ó¬ÐÑÊ MVFèTCÓ•6[3Ã
+5=|‰¦…Wê…»¦FÔjJäQ¥F~¦ÉQ·59º¹¨6>Ä÷£h@ h;ì™3¸bʹvK‰É|ö§ Â]9¾ÊŽ UVd¬2¢ž–1ª¿fF ÖŒèš=ViÑ)š=MSºf)µkRbŠ5)¦\c7+9®ZããŽh|ü‹¿©qÝšyþ…÷ñ»Ý}´;†ƒ¡Ä`síØ¢¾ô—¬#;ÎE¦Xo¥Çjz\”Òâºkj|M‰V“ã‡*%~”&u› änS5¡›Qãr5.aÆt_®ÑÝ7èW=öhTbF&6jdÏѳ¹•‘‰mû/×åu™Æñ¯—T@T@Så¢Èý2à 0 È]`H°ä²¨1¨ `yä ^ÖK$ëZf*h¹YÞ)=æZֶɶ—vÝ:¶µk%Ö´Ü“›®Š¥³Ÿ€ÝãÙ?>ç÷ƒ™yžç}žç}ßçËšá£ÆŽLô¬ã}%q4Çbb\9\ö0Í ó‘-Ü_EáÓU¥‚ˆåG$+7ªœˆ\eGÚ”Y®ÙQ5ÊŒª—5ºEéÑíJ‹éTjl·,±ïÉ÷5ÜãÝù?N“ÿÃøÜ›Er˜±a #ÇŠ æ+âø q”‡-z´
+£Ç)?z²r£ƒ”¦¬˜XÍŽ1*#&UÖØ,¥Ç*-ήԸjYâ—Ê¿R)†ÍJNØ¥¤„£2%öȘx~àÝ)SÂ
+·nW˜õ€fZßVhÆ<û€))}€#ä`/ëߎïö2z¡œ3–•2ëG)ù) NVb5Y]•`õR¼u‚⬊͘®˜Œ(Eg$(*3E™³žY¨°Ùeš9»F¡YË5#kB²žQpöË
+Ê>£Àì‹<o)8ËÙO(ä^`ÝÛð»yçR…Ô̳Ž1¸ÂμËgyĘJ® ¹#ã¡ÈoEäLQxNÂrÃ437N3r“4=Ϫ¼|çÍSP~µóë55¿U¿ÁKò+8­)s>åyS~ùNùC@žSûKèüoÅ÷ÆÞ–t/ÏÚ*zXlȤ,bI&–XÛPÍ,­é…cR4QÁE
+*š¡À¢(M+NÔÔâTçÈÏV¢)¶
+M¶-Õƒ%«4±¤CJöɧ䔼K>áyC¾6§&35‹äÿ¹JzY¸îQÎ…Zz-¦ˆ§€XÒ‰oyaë+Ø>J¥ò+¯)¥“5Ù¨Iö0=hÓD{²&Ø3åSV$ï²ù_V«qåOjly»¼Ê÷ʳü¤<ÊÏ˳ì:8åe  ûÏ,â\¨ã~Zʹ€4\öS©š÷¹H£lâ2óy1†’Ÿ€ª!šPé&ïJO¯ôÕ¸J?­ HyU%ʳ*UcªòäQe—{Õ"®n’[õF¹Ví–KÕ«UõøF.•wåZá”ìÄþVümh È ÆFÎ…Ç©ï…ËéÅzr@LÄ´ýI¬ãê†ËÃ1Zí˜(7ÇT¹:BåâˆÕ(G²FÖeéº ¯«ÒÐ: ;л¥ñXÛ_ÁméQg?Ûñߎ¿µÍÔáI©~%g#Ìã=Ržb«Óù|J“ä cš†Ê¥ÉUÛйMÞÀÒÄÐ\€\<l¨F
+ØHà-4Âöñ—9xp!Ì–ßbhpj+ëÞˆ¯V¾ÖÔJx«¥ùmÒH25”§?ÿ÷F¹ÂÐ5  m [m €m¾ÀàÓÆ%ÛÆEÜÆ%ØÆfn£‘WSÄÕ,b5:³õ.Ã_B±å†´ÊÙO;òo-¶W’®eë9›7°6q&m–ŒOIá0|Á3.Oã ƒoúv‹ 0€naí`ðêð‡ .þ×6tÇ#|NA·¬“žÞ¯‘øs4à5ƒ{àÔ|·âw9>kð1¯ƒ>$mIÈÒÈg¥ià»üSÎQ;ð½vÿâØM»ÉG'ùè$]ÄÑÊ0ÂP´‡‹`ÏC@ÓuQàN wäûôÄî+
+}àÔ|7²¶Å[Ù‹øËÃO
+áFwJ]øß#y‘ûðùPR¸ƒˆƒƒÔåúaô#> eÔ¥›8º º¹tºÙÔÝ4v7‹íÆà1êq”zýîjÅÏiW|?ü<g>-/Òƒø
+ÜÿWðOØÁ×1xNÀÉA^‡S€I½ANÞ$'g¨ÍÛãR‰ã,áY†ƒú³§h¼žmÀþèyÏþ×µ„u/`Í…¬35Æb¥ÿº%÷ãÒ°“ƒ>ÞZ™ÀïÁ Ò…Á\âèOpn„ô!µùp8>cûœá"5éec÷Ò½,¶ý†KªÜK²f+¡Å³Î`ÖéËÚÜ߆¼…ÍßÀoá÷ÀOô!|…¿Áøz-§Kp…ú\uCÀÑ«×é É7Rn±WosøÜ¦©ûHv‰¼ý;”–;¸ âãÀÂÁ™PeP h€fhµ(ÈMº‹²ûEyEÖ‡¾»…¶º‰bü7:󺮢gïéßþ‘Kp>€÷û5ïHCaxÂ$˜q`\,<¤ïÑu}èÜ[ªÃörl¯ÐwÄq8¾%Ž¢þ®¢Ì¾F^F)^Bg~I¿ AùÕgXú>?ÂYøu º!ƒyñaMÓðÉZŒøÊÄÇì—êŠ`{!¶—`{9¶›)E ¶×êïhÌQ»çÙÀh¥;Ê:ÏÐ.çi›o‰fÀï»p^‡ÿ'Ѻ7/}£ÉúJ3ðG©ÍøÈÆ~1í`×9UÐ~ YKmÒ@k6Ó¢?ªÚµzGOѺÛðÑ©ÓläSäâ$ß8Aä¯Q©n<ä¼r_ wà_pYÃX›;þ|ôrñ¾"ð“ˆ4ìçê-ÙØ"el•
+Ö±ûì×c¿ û«Xù:V;•ØŽý½tÆý’ˆ^Àâ*×…·ÎûØ5·ëhíú·š 9óÂß$Ö‚Ÿ— V,æa߆};öplÕõZ|Ô㣠»-Ø]¯çéÑç¨É³|k¹øÓÕÓæyF„;lÌݘ؀±m°°¹Ù@°an_€L áb ’
+Û»þï3>wi‹·J>EOOUE79¼äð“£›}¸Ž!\£Ž+Ôq‰~¹€FÆ6¶øeƒÿõ­³Êè;K ¯0š—Éûÿø"ŽWœ©íÒ½÷€×úMêy…÷åÞ‘ÛÈ`¤ç‘ËHžRžÅÅsxx/YÚ±É8]§Ž§¨c•~9ËøXÆze TtšŠç™ÔOÒ¢³ø„QüB|„éÇøŒ¥æc–š29w¯²xzž£¶°8ž-™çRðLò˜ÉSγ¸hqy¼äi'Ooi=ÄlØfÂf1¾ŒÉðsß÷ Æö½„cï`4ò!F¢?Çјop4VÄðcü:‹öW»y,=Äó|¾¡`oÅ”>1 ëѬD¤`1*§£u˜‹±`6ÆŽ™ØjLÅzŠóa"Îñ¸nÀhüFâ§0œpC L¸ŽÃ’{ì¢_ò‡ÿŒCI"éÿð±šå†%ÿM–—{ÄM–»«l‰¶˜bŸdK° ‹ÄII¦%é˜LTc,QcI%‘:0,­APÚ€!i ŽH;0 ëE¿l‡dcdsèI^EwòUtÊïâ ümä¿DGÊçð§Šð§‰{+ñ3òïšè¶wˆëLíçYfV €3Ô7C;Éã1"—#˜’#) ¤¡/Õ†C©•ÒêГքî´6t¦wâ`z?:ÒGàϘÁ÷2–Ñ–y­YwàËz ^Åω?¡9ûBü’ï¾ x±œ6`¹¿ÌuƒïKlÉfi“qú'˜þL)„¬ ô(ÔèRâ Â‚@v9üÙN´g»Ñ¦ô¢UéG‹²Þœ!4çLâÀþE4î?‡Õ-xÔoÀ­þ êsÿ@ü‡¿Ãyö×Éý<ËüŽ‹m–“6¨à¼Ç6(dᜡgmÏG—J‚€*í*%ÚÔ´ªð©­ðæVà@n šrјۊ†¼.¸ó£> uù§Q«ÙDµö\Ú×à,øø NÝ¿‘Ï{x—mð«ä½Íÿt°M¬°œc[:ÎoƒÔÑCíšx´hdðj2q@£F£VÖ ·¶ õZ'ê
+ܨ-ð¢Z€K×gá1TΡR¿‡avÃ+(7~@üŽøšÁN<à™_&ÿ-7p…%~ƒXâó îRÇ
+c;E즣(3@©y6óuX-/£Äò>Š‹Kü%Å"ßY™‰û´ÿ=rÞ`Ûw©™ó±p€s†¦:üÔÑT:“Õ&9œ&*M¹p˜ a7›Qn.C™Å…RKlÅm°÷ ¤$ˆâ’iX¬ga¶]ƒÉöŒ¥ïŸGQ©ˆ"›È}op¹Kþcmß*qš-ù$µ RG7u´2V<ŒU§5« åÖ ”Y÷ÃfÕÂj-B‰Í†b[,¥n˜K}0•u¢¨lÆò åËÐÛ¯@g¿‡Ç uü†ëWÐ9Dî‰($^#Ï/c¡­¾Ÿ£H0Ïuœï´G'}ãcŒÔ1fìöØìI(±§¢Ø®„Ùž“Ý€"G Œ µÐW4£°2
+ª& ©ZB¾óòœw‘ë|j×'Äßø,r¨ñò<G¾«äÞêäÔÅ^Ÿëq¾÷q¿ƒöi¦j诲šH˜9å08³ w©QèÒAç2£ÀUmu5ò«›WãGnMԵǡª=ƒœÚ PÖ}Ùu ¨ûñW>‹PÖŠü&âEÚý&ù.wëã‘mç ×¾ l…Ûi“&úÇIßX9¾éëã¡­—BSŸ†üúä¹5Èu¡vÛ òT!ÇãÒÓ†ì†^(F‘Ùp
+ÛHo¼´Æ"¥é!R¿ä³ˆô†=Ü%Çù.p$[;ÌÜx„ñÈu¸Ÿw’£I¿¹©Åј©E낪99Ír(›³Ý¬†Â«C–ׂL¯éÞ:¤ù|Hõu!Å7 yËI$·lBÖr Ò–ûHlýI-_ðY„Ì·‡Û´ýÓä?7¸7žÎs,› †‚Œêi¦–jÚ¥”Z ôQn Yþ8¤ù¥Hõ§!ů„ÜŸd¿2¿pAÚÑ„¤Ž
+ Bâ â„
+D íˆú!L`Ÿ°‚0ó•À!Jà Ôû{âëe7ß#b‡ɽ6ɼ0ÅX˜a,LÓ|öq¯†zJù] PSSm$;†„á8D'!b8û‚
+„¿-,F‚I>È d0éÌ ÿ1Èy7ȹfˆÃÜÐ{¼ôœ5¿"ØIq[ä\9 œš§N‡¹úç€î98™ˆ<jËâß%"j:†Ã™nгÔ› ‹}H»WdBLl!pˆâ!Cü¡Émþ8ç݉]^|v­cœwÇÄG¸x‚¹‘¼K”zb‰±øóá%\D ¡å~ö" '$Dß± ,²ñ[L"8[.² [d±_d±]dá] -Ä tàBˆÁ¾FÜdä {Šƒéü—,Jœ&æDla,.sûIJZeNàHڶΜ´Al…\•D
+!á{Ôù·ã¿\— TÔçÅ/Š ²"‹ ;à Ãà û&Š
+
+AWŒ¸¤F&Ö%I£AbŒq\«—¨±±ZkÔ˜ØU“Ø$­K›xHjcb”šX›NÂÔ¦9œ{þÃ,ïÞ·|ï{¯ý¶Á £ +#
+0ÁÒòÿØÍr°‡úØC}ìeHn ìN÷sáîçB:@<@Aï_Æ €á}GÁ{||´>Ð|¸§á_%~ ‡/ .Ë) ._ì»ï‘\Zá!„,8 ]àu'0ÅòÃB
+Û°]‡íx:Û ðk %´ß^ÖÏi opÂtDoq¤>!;ßp¼(cv;Áöïiø¸ ®w”™|¾ú-±8¯8,ØáÈÆ~>^À‡rlWa»†©Ø¯Çþ°¿@­Zª=ZAÛXÇf8Zµ•_m&:-D´¶`ƒkÁwp~ >s–÷…ŽãÖŸzãK?¸"à‰'f)XËÁ~v¡cñØN^¶©F[4E›ÐÑ‚Ž&tlÐ2­£™¯!'«ùæ*,4±2ù"Ñ^ï÷qÎ6çQ{œr¶›×¨‹½ê_\1øa…#b¹€P Gcᨆc2¬õpÌÁîÓœ”çõŸ.EÝÔÿ˜(>£©œ;ÀÒÿáFø¹vÎÑfó|ö¤únüÚ Ÿüá3a#<àH‡#Ž8Šñ¸ ž
+-FÇ"t<C},Ô\=Å;ó:­':»4›†:“ìÎàÕSõõpÿWh«çýàïËÞéϾÃ8°™Ö²m¯Ð;VÊ›jï§gŽ?&xlx™Žþ\ÍGÇ\t<©QðTÀS£YÔÇ ^MçÓˆÅT¢5™ŒMÒ!ªøm*èSÕº|«š.ŽG¸H;=Õ_:Æõò)DZËHÒÈÕóâ²ÄÕÎ>ðÃ)¶Ír±k†K†¦wÉÕ´®ªëZ¬)®ešìZ©‰®TÛmºjºÍÕøîKUÕ}•*{lVEσÓó¬F»]W¹û=àx„óð‹æú2²÷‚u¼ndy>˜ÑƒøÌóêªÙnžšÞÓOuný5¥W´&õ²h‚{’jÜ3Tíž«qCUå1Bc=ÊUá9^£=§¨Üó •y.R©×J•xµh¤×÷~KÅÞm*ò¾*òqt<O3~6“+W<XmaÄ`ZÌ•7/”±‹˜Lõé¡Z/oï *oƒÆzÇjŒw‚ʽ“õ˜w¦Ê|«Ô§P%>¥éS¡â>TÔg††õY¨Bß*ðݨ|¿V ñ{Syþ+¯ï·Àñ¿0w^óÛ¿ÖsÍ6ò|–ÿš;ÇÃ:rTè¢
+?O•ûùj”_°Jü#5Òߤbÿ*ꛪa}sTØwˆ†+? \Cª•×ošõ›§ÜÀç50h²ƒv++ø¤2ƒ¯*3äž²Bð#èÞêëu £èŠ j€ýv®}˜Läê­$7£‚ÝTè­áA* 2¨ (FùÁåÛ588CƒB)7¤P9!¥Êî_©¬Ð)Ê £tÃ2¥1Œ¥†íRJØq%‡Ú•áàéP
+x¿wÁÛœC¸^_
+¥DNRRälÙ£–ȵZ¢wÈ}LÖ˜Kàp(щCpí€Ã`é¥<é9°ñk6£àd>«²FÃB£‹r£<”é£Ì¨~J2(-*F)QñJ޶+):Cö˜Á²Å ×€Ør%ÆÖÊjœ©ã"ÅÇ­’Ù´]&ÓÏgzWqæÛ2™üïà}‡àó6ø×1¯däZžÊ§yo":*Ð1CБç¦T£·’þ²Cd3F*Ñ'k\¢¬¦T%˜Êb.T¼¹Læøj™âëe´<­XK£b¶*:ᨢ¬À—ж::“àP+±ßç«Ã½‹¨Ö£yé´Ô £EÔÊ r–fí.›ÅS‰_%Xe±„Él‰•)!Aq ÉŠ³fËh-Plb‰¢Ç)jÀ㊰@¶…Û¶È`?¢PûïÀ-^;Âmí&ö-ð¿Â¸·¼„õ¤”zäù8ZÆ›2>†ÎÔkJJ%Ø<d¶ù(Î £-T1¶(EÛÍŠ²Û™”¡ˆ¤<…'PXòXR¦*4e¾BRW(8u“‚R)0í×ংҼ׉øÛÿˣ苌à‹XÏæð¬CK:JÑ7”8eS#vbbJwStª—"Sý‘¬ð´…¥ÿÃv™
+Ë¢s»àh‚o]!ûól´0=r-,Â÷‘ƒÜRÉeïõ‚÷"çw ÌÎE9ÍŠt&€d]½á¤p×(…¹2dp¹âž®`w©ÝËåïÞ"?÷AŒÌIp]~®/OþNú)ø7ÃÙ8OZ†5­,E`Öl"–$ƒ\Fyñ^3¨ù$2OÑÓüdÌ5Èà‰Tˆ'FÁžDzz(Àóˆü=, ŽgüƒBæÎå¾à5=Ï:¤©“©to9tq9ôsðo€»®\ªYÈñ{×\þW†ùmùõ!TWjÇû‘äe( U@!—CÞËÁæå€órÀx9ø¼l¼^„ìEÜ^^ž…Ç›µŽMC75ã"ÀkCOMð¬…w%¯U-aªxëÄÅÔ€g#+¤þüÞ¼°H&r þóñQóñ–exº2ù²DÀÁRЧ*¥¥é
+ÁWQÌ%+@3‚ÃT.¤VÞ„ð;@_KüÚ•|VKš«ÙH× Ò˜ƒF©7×D`aÀÐLr¸µ_hvÈ£<È£<iÄ™“F¼F6·w‚¯§Àõ›h Ðes-ô*Í*Ÿêë¨1Ë×2eXÑI˜ä3b3
+x ð©Nwà 8 ^oÜ7hð:9Ïš¹ˆQ¸L3xm\á
+ãû
+ñ¨%8¥b&qOêœÖ×,”¯´ ï¸7·G÷˜¼;¸Õ[çs}„»úF7Û}ø#8Þ
+y 7{•//ñx§ƒû8xéÿäðƒBõ/üÝ?”¨»xÝÛxÌÏd!¾]Ê¥?ÉÃ8
+ôò¸J—q‘q`ïâ2Ï“Ç9­cÊšô¦vé·ˆùuXÎðßi²<©ï˜^Ÿ^GÁó õ¡~_[ÌÍ'22–8}@-®á1/iñ­äï š›zˆ]@ì"bÏ#v9±1®¥:¦:fà)dÜ̲ù%•8 ×)=‡`ÚÙAØ€}`7x¦#vL*%¸Èêºx[f¸áéE„Aä>š¥‘Nìlb»‰=•èÓ‰ïeæç2žùįÔ~òxV«aßÈv²];aÜA-¶±…Š63Ú§am›ÁFÀn‰fÄü±”:–ÖiôyŒZ¥G”ÌúÂ1”øc´W6—ÍÜÄÏ!~>ñg¡Äbâ—»E,C ÚÀÆú™¬%ÛF¢Ö3“«QV¼µXnÁù8×±wl=‡¨Å>tñ ÖÈ3ê¡mÌI³†Á‘Bþ6òwh=úX§)päÁ1Žb8æ{±Vò·œ –òe5µ¨¢Š‹YЕ(¬Þ‡q#ˆ%ÆØYÊ/²Õ>ø`ëÝ.¶²F¶(†ñ$ÁÕ žp<
+G
+ 68¬§jÈ£ZÓ´„<‘Gëå ž”“UJ©\ #›‹J‹õ7Ía¥Í{6h¿^b{?Ëvö[É!®{8v¶‘϶ºõ
+!J$õŠ£ºÉDí Ï@Æ2Ž-DåÊ‚Ç ÏxòP¨žâW²“¬`–Ök•,@9ùÌGó‘§Y?À[ñÔŸöã0GÌ^ŽÝméí8rÖК¬¢.5~aŒÍ_\Ýô¸ú0žAð ‡ÇO:¬YTÀ W*NüÙÄ.gõÔðd ÙmÕ$¿VMô{Mnÿ+rÞ“+È÷ÎÂ}´GXo|/ØÂýž­ìDëA]ýU¡9fyý;kfÀÏTØWùƒ5-h„rƒ,šlSN°C“ƒÝšâ‘;d–\¡%šºXã «•mh–#ì€ìaÇ•~I™wõ˜Ñw™à[ùá~É8êÁŽÛ:ZÚ²…´Eó¨‰7*HùáFyÂb”ž¤Éá=41¢Ÿ\ƒåŒ© ÆT3f(Û8NYÆÉ²GNWfd±2"+”Y«´¨&YMû•j:.‹é²,æ/€ï>RÁ+ð·Ñ†îâhkk‡â÷ÒnÑ‚”Ò–Q“|æk²9TÎ(“Æ›â•mêª,S/ÙMýõ˜y¨2Ì£d3[•n¶ËíÒØè\¥F)%fÆÄ¬Ð¨ØÍ»W#â^Öð¸wõhü ÷ý„8Þ÷Ó‚î%mâh«§õYÊÿ y>—šÌ NSiœ•kTFì¿Ù.¨¨Ï+Š_£‚Šl#Û 0 3À
+(0.£D4DÃwq©;hc¬¨9©&Ù¬i’&VLlkšXS£i›&=iÏñ´Zcܲµ‰UÓtúœÛÓÎ=ÌÿÏðÞ}ï»ß÷½kPqˆQE! µ«0Ô©‚Ð,å…å+7l´F„SNx¥²Ãë”1WÆUJ3nUª©K)¦ƒrFž7äŒr+%ò.^¦önrïÆÓn-dÀò<Ffx<BjàáB'e1R±iˆ
+AÊ7†)×­“MÙ¦$e™Ò”i¡ŒÈQJ,Sj”K)ÑÓåŒ~Tór%›7+)æ)%ÆüDvËi%X®ñû_J´¸ûð"uw‘wãç&|m #Ï2,ÊEÈf¾Jà —JáQ`ñUŽ9@™æaÊ0•f¶(Õœ ”§œ1YrX
+4ÜRªä؉JŠ­–ÝÚ¨ë2ÅÛ6ÊfÛ#kÜ+Š;¥Øø+²Æ˳[6à÷ö’~v#ãçšR´ÀçGy÷«„ã84R‚^sú+Ý6T)Ö`9¬aJ¶F+ÉjS¢-Yv[ºâò_¢¸ødK¨’5¡^±ö%бo9ñIE'¾¤È¤·ÀeE%}£è$7ï¹Ã¨ù‡älÇ
+l¨`ÆÍ|n‚K-<&³Fehek–V‰ƒ•h”Ý¢x»IqöXÙí²&¦(6)G–¤"Å$WÈœü ¢‡ÏT$Éäø¾ŒŽN…;_T˜ó¸Äç;ŠpºeÏÐÿNrn{
+É*Õ°¬I2dÏPpö<å´(0§C9ûå?â(8/ÿœ›<39‚§ÈÓAþMØÓÖª»ÖlîTi&ÏSy?žžŒæ;¹pM¡gqô$*ÂrškPHn„†åÅÈ— à¼åP`~‰òÇË¿ Ì–¦qHA»>+ßÂ#àC *üBƒ ܼgŽ¡ÞíŒþ«9›¦£G0»ÏÅs%\*èË(¾“E¯’Y³XÖ'btŠýT¤€¢P%ÿb›†—_q–†Òà’qò-©’OI£”,Wÿ’­êWÒ¸1 %¿åógàŸº¯Ø­Nò·Õ Ç:iV¨4‡xž Ÿû±iù|‡åU<}‰¢/!㤀òAò+ Р2ƒ|ËŒò)³h`y¢ú—§ë¾r„\áršYþX
+0“嘵rŒCÆ ìSÀWæÖ.jÞBÎõõÜ豉óÔ4°¼/Æ–dÃ%žø„Ó› BûMñÕÀÉþêç
+æã0wEß=Ì\°.ìBD.„å¢×"°c'1!MdbšÈô6ÜÚ1‹;‚œ-s8›æKópG`O+y‹ɇK*üâøžéaÉ@È¡3$Ÿxºé µ\òµ µ\ú5\t5\@5™
+Ÿï¹XÃáÀÂßÃXÒ
+nm ×Ú5ÒÒÊ¡]ÓÖ±@!H]/YùÎû@à &ïÁóÜ
+Vx´ÒVx´Â£ŸÛÊ%ÓÂå×RÓZºöq.|æj&çULÒ+q4+ÜZO¬XÐ9HfúF$‚|‹6K[Ð
+^ú?ni 5è3|Õ5¼îezqA#ˆ_Lì
+ý^.ê¨Ò{ð8 3øºwð—¿‚ÇÛð8‰#=ŽC=†·:ªn$ó2µƒÉyÒ?t,¯àY°ï?8ÜìVrŠšüôÈEžr¤?ŸØ£éì8ýGàñ:ërX³ˆÝDìùÄ^‚³]MÖÇØ2mú›ù~žƒAÂ馲.²= ö€Ý ÃËá&¸æ•‘GZ§ÑÅq’+œžYÈ‘LüLV·€­8†ØDžDì©ÔRKü‡©§‘øóX%Ä_ƒ"6’c§vñv'¬¶³iÚPÄ6Ô´…¬ü
+8K)gYÃBðDài†§žmðô`ÁÓð ³÷$Ÿ«Ÿ]vàÉ>N×K<¶n²¿î'ø2LYÿ%u )tY¶€='±e¿Ì*•³™à³í\v/ä<eð¬'O„Ó7sKZáê„§KØ{ŒlYõÐsêÀ£[Ȩv2»Œo¥V´RíZáß ÞË¥íÐî_¡Õ^˜mOaϲh¿ŒÓ‡Óá4Ãç€Ë ‹žrx‚ÜŽ\¸šáj‡«KmqýjÖ¦øiEãª9ႚ–µ!ñ¾¬†¤Õò
+bª÷áü:mí:rã"8…œ+GS ‘¿ˆðIñÚ–™¬VƒQQƒUͧ6¼Ú`(Vƒ±BãZ­7Ö«Þ¸Qu¦6…Mݪ5íRiŸª3)˜yNUæ›
+˜ßV¥ùUX>T¥å3S
+)1<«øðÙGƒC˜ÎF%„™-Ÿ*.„–ÿE8O#»çwr'EÆXw3ªnçy~iÀ–jlõãñq0:™R”±Þ¨´õ¥F²•ñ()R¢ÄH•â#õŠkˆòÃÕ‚/Ó
+ó¸:;_ ˜ÚÀU®:G
+Ê2¾X&±–8äÒ«¼ó>ï’—_{ ðîÅ´n¸šá¨a¿"örÜ„Ÿ²·*ô_üŒañ î|n]ÅàîãÁäMð>¹gV°c´B~®PøWðÅ
+I·ÑÊŠtÿç¼÷‘†9c/gÛgè–TW6\ÆÛ÷_Ý›²Â¸†
+{mfŸ5¼*c?™ìÅô,küòôs¼5‚§Á™¨1#¥ê‹šÄ àbÔ°þ¼>
+9È1ö’%c›ôÙŠP9œ`-¸ çïÀÑ’ïo±c-™Ä^,p9õ=å±R ëûô˜BÚN<¾£Q¬?–õ[92ÛÙõtÖïdýn=L<ÖéAÖßÈ‘¾]«Xe»¹¿»\Ÿh¼÷E±ô󟧢­x8zõ‹'¨‹môH2ÙG>eè÷k=:BÇêt5ý²J´R“ÈÂ=pt±€n¹ˆ¬Ñ½(\ÈæSݰté*ôO²ù.r”œŠ—ž?àä˜b¿Ì˜×C"œföã€'›½ÀQÆê~ôÙm½«m†§žIšG<º4›µ¡æªv³ƒ·û©âŸPAo“½Û ¢É ÿyš#äy®™ƒ©Ònžs´mE×»o
+œö” ž8ÊáðÃÔ,ÕÁÓ
+á)‡Çcž:ª³Êh†«ž©¬ÝÉ_‹P¶’
+~”êÙÅ/k$?‚f§OþàyFŽŒ>OqÅm›¸r¿ÁѺ¼!.s¹Sc‰£>;\™°ä±§"öã¥üp᪇«®±pMdíptóv¹†‘Zª«f ³^Ðéw@d
+Þ-a4œÇuÓAL&cÔjHÖ¸A©ð9àÊ‚ÍÍžŠàò*P}LHuƒÂª4Z5±Œªê¸9Âàˆ_'üùâŸÑ`ÃKª0\REÂ-U$Fpˆ+mw¹´£‚äª_ÃÕ²¼„±‹q¬ÓÅlIŽî&&cÌqjHNQ8Á¢:Cšj²U“¯`b±ª+4$±JUIµ
+$5È—4V•ÉíªHž%oÊ•¥¬U©q»ŠO«ÈxB…ÆwU`º¥BSDEà
+Í*H½WžÔ5r[¶)Ïò´\–ʱ\’ËzD” öÃÿ$#Fžöá#ϯñwï§£¯8!7#kÈßš¨
+‹Yå›J-*±äªÈR¨BK¹
+¬UòX‡)ß:Jn[«rm3ä²/T¶}•²ìßR†cŸœŽ•îxGéi•3-»ˆöÂ÷Ü[×Õ1n‚ÅŒ€sð¶ÓÐÑJnFS'džR¯ƒñ*¶U`·ÈcO“Ûž¥<{¾r%r9|ÊI );­AYéã•™>MNg·Ò+åÈØ*{Æ^Ù2^5óMð©l™ÙÁöý¼›ñ³kûî‹°HŒ=S°l-~ò@­ £Fª¨‘òÜy2“”ë4+ÇiS–Ó©ÌŒ\P¨ŒÌ
+93‡*=+¬4L¡={ŠlÙ]²æ< KÎ7•êÚ#³ë¸L®_OøÌ”
+ke,¼K)EmJ.š¥¤â¯+¡x£ ÅßU\ñQÅŸáy|Î;îröÜÃÈ» °ª…ž
+Ÿûnâ-ÜÌÏqSýäãMâ8ûz{ކõSâx8Nk{ÜM)»(é3Hé4’z¹~=ÈëÇ1pÄp;ÀÍTÁþ¤_+Œ}XáŠ' Ž,ý?u§ô
+Îæq¼@>zñS'ñuÏã/ŸÃ >£zŽÍ:=E;¦§N*Ò£nž<¨7ÈÌUí#Ëû`Ý :À.ðœ_ORòËìÜ ÔpÙùµ/±ç°öh=‰kë!G¨Ë!Ü^7îï ÊY¿ŠöñJ¨gýõz\›áhçßÚ×ÓDt†*½bn¨ÞÐ 6ëù^ Èþ489x,ÃØ‹®h2š
+‡çn1 öBxæÁSÎÿ¢Ú(fYjç›C(¨—êõÓi>'{·ogžçÊïáºÝG;kç}+ñl$n9¸Cà5Á¥…r²§xx’ÙK<¹ðŒ¢sG“ášÎ‰¹®x*‰jê]r¶ñí~"}ŽnwUL%oß NqÅOà*c}œëe;WÝ&âYC›¯%/‹×P8¼fòfc_1ì)žö” k.\ùp‡«
+ÆiÊædgqeê—dñÏʺèYŒ‡³¥½Ã1@3¯åz©MaL$?óÉÉr2ìC¤&…X5>È¡1A.å%jTPªFgiDð «a!w+'t†²C‹”V¡Œ°:¥…7)5¼C)áO(Ùð²’ å1|!OÄwòDútÎ.®÷ÝX‚m )‘1—‘›PANŠ©O!õºÇç´†*ßdT^„U¹‡r .eGx”‘¦Œˆ¥GŽRZäyS•bœ­dS¹<¦¥J07*Þü¨Üæ¹Ì/)Ö|Q1–ëà{ÅZ|zþýxÈÆÏ- ‘k~ïkËæ’§™Ôf2µKmFØÂ•i1)Ý¥Ts´¼æ8%›“•dÉÇ2\ –±Š·(ÎZ(—µL±Ö%rF5(ÚÖ.‡í¨ì¶—e³]_‚[¼÷©¾}ØÃvÆ­–IŒœ¼Öãm3–ßlò4Lb ÉÇ ‹ RšÝ Í¢„(›â¢bä¶%Èeó*Ö–£[¾œö»mŸ%‡£TvGlŽõ²F·Ã²D¿(Sô™œ’Ùù/ÞûtþN¸Ûð³›±ˆëA¶ ›4Ÿ8î§6SÑÉ42’ÚdS›wˆâœFÅ8¬r:ŠŽvË,»3äÉæœ¨¨˜é²ÆË»Hf×™\ÛéîR„»Wáî~Ü×ÀM>ãþdß{àÝÁ¸×< =‚ZìQ1•ð]!µ™2ŠYäQ›,j“LN\ñr¸,²¹ìŠrÅÊêNi²Ä —9~œLñSeL˜«È„*W*,q‹B=ì9¡ Ï
+N¼¾UH¢O]pí†wûLúÂ,úXÆß•|VLl÷’£)Ôf9É#'äÄCNœÞPEyL²xl2{œ2yâeLJUDÒ0’Ç(<eŠBSf+Ä»PA^ ›Óâåð2ôz6½—Á·$õßÚÏþ;àÝŠ%kšå
+Í¥B+×tʵr‚)—ØD]᥊yÐÙ
+ݸü~0)\ü*°á¢Ö¸É½¯Ã»{‰œXfm…Õ°Èÿ³Ù2É÷V¤á|ÒßµÈ=růËÄ”èT£±MÐTÆÛ©ª±jE¤Z­WíÁj©^v›q–¹q˜*ËBÕ˄ΆKZã[µo¹5Öc[­¥_ƒU°`¥8,õŠïGñÑ ¶ôbK‚^2Ù“:£±hhÓ5“i|1•/jöZ­V<ji¢V
+ôሕ¦æ¦Èå¸Kk4‘ëU»ø@ú®ò{.ÔJ£1Pâ•¢4à™ öÅ—qEÛÚ`
+1³T÷ƒ<ȸѹm¸ µüa‘éÀ-ÿí߉MKvÇ€z«˜´(j-|ÑBL‡9÷ðÏ~×àô¢3ÿp.ÇQ3 xÔ­ê€GònÃß¶dÜÇàN¸;¹|Ü›ü}áï6Ü÷'Ѷ$œ3'ÙqRý<%&§4ãS|qJ~œDzRÍ>ù”çÞ‹•x/Ã9×ÅÒf ž|®Ê¸'y®Òj= kÍâٱ ]øý0¸’Õ!,£‹Ú£p~ †œ¥Ó³´qÎ0rNÝ<ÇçˆïüQððÙ—cž;ÖÞ¥¸Ë`|øº<˜\ø¾“äø^ò\e>´›ö¥Pë³ÆF<?WŠÃO£cq|”€x™¯ª£¯iøoÐç›räM‰ö_¼a«{ý “u| ©= Ø&RÿŠKâïæå¿š—Û6¤¿˜ÛßW$þdvÿƒ}ê÷öºßÙ*ß1?ÿšXß¶¥½i¿{…#_â¬ú\ü–YŸÅ3Nmc}¾ §àË6ü>±Ó|d†ÿÀüþ¾)ü]“ú;¦ø_šì_³Ãü‚/±ã…ê9ûÔ³
+Å3¶±§%êOl»OIœ' ø<!=Žå19ãéGãc“½&É}/Üù>•3d,‹ÓmB¹ñ²=âE;ÕÏbW8{¼;Ô
+Ã,g_æì…Î^êìUÎþ*IltþB¸›t®‡l®î ¾xÀÛ÷Åý×Çá(´À¡¤ Ÿ$¹/¶‡³“gáÊÃÓÇ ç—:{¬³'Äivœ—6½l`÷ÛÄîµégÇÝvÌ;Åå˜Ýêö8hûCºžŒ›ãûÒûQú§Oõ)ØÍ s‰'éÀÓI©=Ò.ù \¹xºãèëü¡ÎÅþ±Îïì)îñç×9žó)#+œ¿ÖùyáJ×ø¹9š¼½Û‰»xs'5íÀ󠒡 Tªx>)í3ÉtSI½•‚/ WBé)t~ÿø¦-ívì·Ñí£kè´É~¹ÇÆ»›FvÑÈNZÝÁÛí™ÛâªØâM¬ÞÀsëEp]ü†z>óDk;áùdе¥ßédI8š,yùb¿iŽ|ENàü­b”¨WÄVûåf{Šc–ÍrŽVÖ€co¬“5Û(¥É‡¨÷žX‚e1/âýÜ sÐSG j+ƒG’¥Yùv¿œ™øºáꎣ7Žb%8ÊpTÈŠ*J¨Á3ÏL<sð,röêXÀ²y<3‡/.¥:jšM›³¨o&î 8c$|(KÙUV‰¦u´#í³½%nˆ4œÙøxzÈ‚Þ8â(‘eYާOL™õfûd–'gðöt¾˜*jmê™L›Åc¢*R£êÔàoÃà å]‹;
+7i¹û”´]ìÙÆ–¶q¡QŽ,‹.n–ƒ/WO<}ð ÄS‚§”2Ëc;¦:qŠ¿&yb" 'ˆD5}V‰d%ÕŽ•=c(¾BöU¨åøÛð–rÜøÕ¢¤€½þßΞ F³5ü²Të™ù2g®{åËÌžîÔÏ@¬%²¤ W9®*\“pÍÀ5×'Ëâ"Ñ-¢e]wÅHÍe„ì†Ï¡5NØk¡Zì>¸JYß–ËÙ²œ_h9õæ
+ukŠ<©¡q쨔+cåìv”Ç0•´ ×jk*®zç/jWÎPY2„ÂÑÄ@º8~ïRø¿¡5î3~Þ¦µnÌ0^iÛè³5Æ%Fõ9F€|2™fÆ¥eàÍÆ™ÀW€µ®~î5ˆÃñÆUƒùbµSå
+ 7gaæV^hÊa,¹ÝÓ½)C"ƒ¡Gú`C³îÁ‹äÁÌëA—àN îyb*ùcß4Zï̲IhW¾GóóV ‰LêgMb©g½XÞ…8|ÍDIQÂËNh<)OŠÇòz”DòDïF(½´73–Ï@tßi–½iN½i6½ˆÁ3OLfÿ1ì;¼ïCuIËÙ…ïQáÌ|ÄÒ{b _âð€?Zøj`&¬Þ±.v?._ŠÆ ×û¤Žþ€éÏ‚þñ(3–ÿ8”—ÑWÒ—Û>Tz.²Ø?ƒ½‡µ¥.­7ãi<Ÿ¿%ÿoD,!`R•8¼—\à‰)ÚÖÐó`.Ð .¯jÄÈ¥HB–@H‘1°%ÚíŽá@TèªXb¨’/&f:û¦.c¨K4®#1ðwsb©‹=˜8|ÁÄL´¤fÇÒ–œ§µxxC¹ÐCxìtœ‡ŽóÐÁ $2Nºæ(‹ê˜mt †5&j0T?ŒCpmG2Žaôè×EˆhË9K(ñU/0q&{–´ž@âAk
+D&Žxâˆ#Ž8j$,byüb!p,dêLðY¼õÃ]Ù‘!1š‰¢C¾H£l‡p…õÊÏøŒÂñ¢Á„\q .»÷…°àçâU%NšLH„ö-Rø‘dch€’Ê
+Ñ^ô¡û@äDˆ•À½3Hvägxíq™|òDêp–„*Ýжhc´æHê
+?R‰#•3Iõ3<†Ã ôP€ŠSÊ,§>Ÿ†ˆÅ Øž„Eé†q…„އ™\·¨=ß-øR'6û†¦;Ûø9IA³Š”zɆÙœI6—|XdAî,@œÄW°æÖ¾+Y?†µ[ð»Ú“©C ÷iB”F-¦›ý™(¡ÓŠ †-A[(Äã÷¹¯üožq@
+á¥
+™$Y’&y’(™’ê•«$ûëuÒ½y‹„ïܽ{’Üðà!y?züø÷ߟû³¼¼çÏóÕüUQEUTQEUTQEUTQåÏ!ß`¿yò ¯çÿ·bQEUþ§äò±o‹µÿ°nÎÖb¯}S“V\+Îáè¢ìeÅ:<=¾cÿoÅ¡Š*ª¼¹°oW±µ¹gÖˆ ûбŸŸW¯÷ÊG&íù;‡êDÔ›&N8:< Í‚[¦ìß9¼qtïäåOŠ âá¾ÏÖ*Æ®Š*ªüGrïì~“¥‰\Ý13¥çšÇ¦Ìö.æ§K?jÊaó˜è`[á–°Á„=7%¤¤¶ý
+st¢P"M8<ê[^›R–6í×+;©k¯ ‰ˆkÑõke‡U ¡ —lY»jÛ9e‡Ks§í¾b"<ƒÜ.îòRE•?­Ü<wxçÓæÜc“c›eÝ0eß;ª¾ŸƒQŸ›°_¯/¬îj“n+;¬íàPèà’|VÑþlLuÃý`Ó]ù:kfp]”V†í"þ´¢Cº]‘C”b wÒÊšžQrÈ[Ý+ˆ ,|j…w:®¸ÅõeýUnÐ3cöŠíß+:|³a톣¿<5aUE•·"WvÌMíqФùüâ>¡¶Âlª)ûÁŒ%dátÍU¶Ÿ[ÛPYúÕÊ‹#Œ¥Wq†¢ý€rF‡tÅGz]‹¢âî(9dj_8ôºª`¿Õî…]Äü¨à°­ìK‡J7HŠÓK‡wsÿÙþ䓨—1TTÖ..Œ(r°Û®”ÅÝõéѵ}­4fŽ>¦z±‹{–ÏŸ·à£µ»LÝAª¨òïÊÏ»sÆŒzfÊzkfL°½(¿Ö„ýÔäCõ ~ h?›j¤}«CJö§3ëÕEÐ%‡/c_TV¹ÙJÓ*½p°ÎT°_ëú²xEêì×y\Íùð_’^HÙÚ¨;WiˆÙÉÅ Y¡A›"ÝÒ­[öˆl£!¤I²vå–^ÙŠRª9­$ÊV¦d2öåþnqÕy>ÏÏ=/÷™—ë<öy÷;ßsÎóýžç+N˜Èw $— ÓŸ°ümWy0¦’ù\°*c »†<0gלˆuNä@§Bö„‡É!“ˆÉ ‰·ñ¯+‘‹G[Yš›uï?Rê|÷^î—Èrr¯ÝýH®¬/¯呞íëªüŸÝí,´~$¾åÒKý]×Kn±`ÅÀÿôlëï¥ò‡Á=ê»Zò5ö[7ì‹q|g‰Unr§¯Ä"~²F§8ð—ß›-cYþ¸ÙÞ Ä‘\à³–-K1¿EsÁÁ÷
+®²+
+sÐ=ét¢®##URuMÝTÄü„¤ó—³ò‹Ëî?|òšÇÊúôzq+[:¨º³ÖsâÀê‚åMžnþÖ´áÛÚËòú›ä‚¹3´‹å3šoƒ\P…¡7.‚B ¬<¤L 0
+
+òȆ×èˆ ÀDG­r€ <ƒ6§
+üˆž83ð|6 Ô~!Æ]{=i¶»ï’5á»$€²>¡*²OE®t³OˆªŽ¯œ1À°Ñûoâï,Nõ4•ÿªðGO\Ùž~ÙêpcÝŒ)ì àGàRÂÀ0X»èÏQAÁl²(fà4ˆ¦ ƒM<ÒAoc0
+ÍÓÔ ݆/¢UÄ)ã öÐ|›íáJËæn¡×P‡¡W›‰
+Øx ì£À—•°k’àFÁEà‚Q®…`0|”=t€[“ 1x}Ô
+RÔ¡àWvHý
+p`+gQ€£(^±£‚1£ö¦¤¡×±ÇûÙ>¡±—9¨½Ú–ã騬ÿ¡*ó®^¾œxú§ø#qb ,Y2aHwcýVMëOüb’/f=-ÄÈ/} Öô‚-ö ´£`=·œç€¾}’ék஄)˜ÏÀ
+Âð§à4ÎÜì€`"þ¶ C
+ü€È^ÆSpÙ|Òv:Q ú h Z€¢1
+€¸VKȘ 
+=V…yí!´pÁ’°Ýñ‹ñLYÊú´zY*K;—x2þð˜}{"wîü7ûeÔrÇñ„"Iwž:G”Sy¼\šV[´<¬6'RÜÜÕ¹n%Ü:溬'5Ém¥¤ÖDtbè¸Z·¦òºL\·&$
+³–ܾŸï~vç¿×Ÿ{¿¶}ß»Ïç÷ùœ<%E…„&Ýg™§‡›ËÔIN£†¡DHÁJÓê"ÔNÆ„dDè]ƒ Qè!öcØ@
+°d *¨°>7 º8P°W% \ò² h¢`3‚^l:®9º3â Ðc<…ÍxWo67µü!ÌõúÊʺî¡Ìó·[ñЈZÙÒI‘ü4Ÿz{z?cŸvTäg‹„‚C |Þ®ø¸˜Xî.þqDh\â1ÓyâXǶC>¨M|;
+Ý¡P†
+ê`(d¡B÷G $‹8½‰‡º¥¡&Ìú{j?›ŸS„®£Æpä´¥ÁñW5zŒáN®K6Å•V·áiù|QþAh üÁ4×ݪª¬(——•Êd*®Ýi„³sZdD(k}P`
+âÈ®|BpWãù©×$ˆ ¨ï¿%IHþOÞ¿y­Óé:ß~0?_(g¦‰D¢ô,ñ™¢rE}K¾cШ+)ÊJ_*=ˆÍI,€?!ñ4Ðl=Ã…—[ P½Õ®¿=¸Äž¼ [˜Í÷þö ©}gö˜… OZO$oê!xLõá=é¥5ØdbBCö) ÉO¡Õ´¶>ñRÛIðrþ\šš’r"#;¯@V©2[ƒÙk©þ«‚˜›cç›Ìlz½Œi5a#*¹XÝmþ?”;¼6ñ2.Þ#œ:ĉÖ™Li­Ã’¿«ßÌðü_
+%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 14.0 %%AI8_CreatorVersion: 14.0.0 %%For: (Thomas Werner) () %%Title: (Untitled-4) %%CreationDate: 1/26/09 2:06 PM %%Canvassize: 16383 %%BoundingBox: 17 13 198 195 %%HiResBoundingBox: 17.8257 13.333 197.1748 194.9985 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 10.0 %AI12_BuildNumber: 367 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%CMYKProcessColor: 1 1 1 1 ([Registration]) %AI3_Cropmarks: 0 0 216 216 %AI3_TemplateBox: 108.5 107.5 108.5 107.5 %AI3_TileBox: -180 -248 396 486 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 0 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -104 222 3.19 1355 717 18 0 0 43 169 0 0 0 1 1 0 1 1 0 %AI5_OpenViewLayers: 7 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 20 0 obj <</Length 13581>>stream
+%%BoundingBox: 17 13 198 195 %%HiResBoundingBox: 17.8257 13.333 197.1748 194.9985 %AI7_Thumbnail: 128 128 8 %%BeginData: 13434 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD1FFF5227F82720527DFD30FF7D2727F8F8F852FD42FF27FD07F8 %4B7DFD2AFFA82727FD07F8FD41FFA8F8F8F827FD06F827A8FD26FF7D27F8 %F8F805F8F8F827F8F87DFD40FF27FD0CF8277DFD22FFA827FD0CF852FD40 %FF52F800F827F800F827F800F827F82752FD07FFA8A87D765252FD072752 %2752527DA8FD06FF52F8F8F827F800F827F800F827F8F827FD40FFFD11F8 %7DFF7D524BFD15F827277D5227FD0FF827FD3FFFA827F827F8F8F827F8F8 %F827F8F8F827F8F8F827FD07F805F8F8F827F8F8F827F8F8F827F8F8F800 %FD07F827F8F8F827F8F8F827F8F8F800F8FD3FFFA8FD40F87DFD3EFF7D00 %F827F800F827F800F827F800F827F800F827F800F827F800F827F800F827 %F800F827F800F827F800F827F800F827F800F827F800F827F800F827F800 %F827F8A8FD3EFFA8FD40F87DFD3EFF7D27F8F8F827F8F8F827F8F8F827F8 %F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F8 %27F8F8F827F8F8F827F8F8F827F8F8F827F8F8F8A8FD3EFFA8FD40F8A8FD %3FFF27F800F827F800F827F800F827F800F827F800F827F800F827F800F8 %27F800F827F800F827F800F827F800F827F800F827F800F827F800F827F8 %00F827F8F8F8FD40FF27FD3EF827FD40FF52F805F8F8F827F8F8F827F8F8 %F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 %F8F8F827F8F8F827F8F8F827F8F8F827FD04F852FD3EFFA8FF7DFD3EF827 %7DFFA8FD3DFF52F827F800F827F800F827F800F827F800F827F800F827F8 %00F827F800F827F800F827F800F827F800F827F800F827F800F827F800F8 %27F800F827F800F827F827FD3EFF52FD41F827FD3CFFA8FD05F827F8F8F8 %27F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8 %F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F805F852FD %3BFFFD45F8A8FD39FF52F8F827F800F827F800F827F800F827F800F827F8 %00F827F800F827F800F827F800F827F800F827F800F827F800F827F800F8 %27F800F827F800F827F800F827F800F827F8F827FD39FF27FD46F87DFD37 %FF7DF805F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8 %F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827 %F8F8F827F8F8F827F8F8F827F852FD37FF27FD48F8FD36FFA827F827F800 %F827F800F827F800F827F800F827F800F827F800F827F800F827F800F827 %F800F827F800F827F800F827F800F827F800F827F800F827F800F827F800 %F827F800F827F8F87DFD35FF52FD49F852FD35FF27FD04F827F8F8F827F8 %F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F8 %27F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827F8F8F827FD %04F827FD35FF27FD49F827A8FD34FFF827F800F827F800F827F800F827F8 %00F805F827264B274B2751FD0427F827F827F8F8F827F8F8F827F827F8FD %042751274B27512727F827F8F8F827F800F827F800F827F800F827F8A8FD %33FFA8FD11F8277CA1C9C3CAFD06C9A1C9A1A176A176765276527C76A1A1 %C9A1FD04C9CAFD04C9C3A15227FD10F87DFD33FFA8F8F8F827F8F8F827F8 %F8F827F8F8F851A1CAC9CAC9C9C9CAC9C9C9CAC9C9C9CAC9CAC9CAC9CAC9 %CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAC9CAC9A127F8F827F8F8F827F8 %F8F827F8F8F87DFD33FF7DFD0EF852C9C9C3C9A1C9C3C9A1C9C3C9A1C9C3 %C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9C3 %C927FD0DF852FD33FFA8F8F8F827F800F827F800F827F876C9CAC9C9C9CA %C9C9C3FD04C9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9 %CAC9C9C3CAC9C9C9CAC9CA2705F800F827F800F827F8F8F87DFD33FF7DFD %0CF852C9C9A1C9C3C9A1C9C9CAC9C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1 %C9C3C9A1C9C3C9A1C9C3C9A1CAC9CAA7C9C3C9A1C9C9C920FD0BF852FD33 %FFA8F805F8F8F827F8F8F827F827C9CAC9C9C9CAC9CACFFD04FFCFC9C9C9 %CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAFD05C9CFFD04FFCACAC9C9C9CA %C9C9F800F8F8F827F8F8F805F87DFD33FFA8FD0BF8A0C9A1C9C3C9A1CAFD %07FFCAA1C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1CFFD06FF %CAC9C3C9A1C9C952FD0AF87DFD34FFF827F800F827F800F80527CAC9CAC9 %C9C9CAFFFFFFCAA0C3FFFFFFCAC9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9 %CAC9C9C9CFFFFFCAC3A0CAFFFFCAC9C9CAC9CAC927F800F827F800F827F8 %A8FD34FF27FD09F8A0C9C9A1C9C3C9C9FFFFA06F6F6F9AA8FFCAC9A1C9C3 %C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9CAFFA8996F6F6FA1FFFFC9C9A1 %C9C3C927FD09F87DFD34FF27F8F827F8F8F827F827C9CAFD05C9FFFFC96F %936F9A6F9AFFFFC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAFD04C9CAFFFF9A %6F996F996FCAFFCFC9C9C9CAC9A0F805F8F8F827F8F8F8FD35FF4BFD08F8 %27C9A1C9C3C9A1C9FFFFFD076FCAFFCAC3C9A1C9C3C9A1C9C3C9A1C9C3C9 %A1C9C3C9A1CFFFA16EFD056F75FFCAC9C3C9A1C9A1FD08F827A8FD34FF52 %F8F827F800F827F851C9CAFD04C9CAFFA89A6F996F9A6F93A0FFCAC9C9CA %C9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CACFFF9A936F996F9A6F9ACFFFC9C9 %C9CAC9C9F827F800F827F8F827FD35FF7DFD08F827C9A1C9C3C9A1CAFFA1 %6F936F6F6F996F9AFFCFC3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1FF %FF9A6F996F6F6F936FCAFFC9C3C9A1C9A127FD07F827FD35FFA827F8F8F8 %27F8F8F851C9C9C9CAC9C9CAFFA1936F9A6F996F939AFFCAFD04C9CAC9C9 %C9CAC9C9C9CAC9C9C9CAC9C9CFFF9A936F9A6F996F9ACAFFFD06C920F8F8 %27FD04F852FD36FF27FD07F826C9C3C9A1C9C3CAFFCAFD066F6EA0FFCAA1 %C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3FFFF9AFD076FFFFFC9A1C9C3 %C9A127FD07F87DFD27FFA8A8FD047D527D527D767D527D7D52F800F827F8 %00F827C9C9C9CAC9C9C9FFFF9A6F9A6F996F99A1FFFD05C9CAC9C9C9CAC9 %C9C9CAC9C9C9CAC9C9CAFFA1936F9A6F996F9AFFFFC9CAFD04C9F8F8F827 %F800F827527D527D767D527D527D527D7DA1A8A8A8FD0EFFA8A87D525252 %274B2752275227FD06527DFD0552FD08F8C9C9C9A1C9C3C9CAFF9A6F6F99 %6F6F6FFFFFC9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C3CAFFFF6F6F %6F996F6FA0FFCAC9A1C9C3C976FD08F8765252527D527DFD04524B52274B %2752274B2752527D7DFD05FF5227522752527D7DA8A8FD11FF27FD04F827 %F8F876CAC9C9C9CAC9CFFFFF6F6F6F936FCAFFFFC9C9C9CAC9C9C9CAC9C9 %C9CAC9C9C9CAFD05C9FFFFC36F936F939AFFFFCAC9C9C9CAC976F805F8F8 %F805F87DFD12FFA8A1527D5252275227A8FF52A8A8FD0FFFA8A87D7D527D %52525227FD07F851C9C9C3C9A1C9C3FFFFCF76996FCAFFFFA1C9C3C9A1C9 %C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9CAFFFFA16F9976FFFFCFA1C9C3C9 %A1C927FD07F82752527D527D76A8A8FFA8FD0FFFA87D7DFD0CFFA8A87D76 %5252275227FD04527D527D7D52F800F827F8F820CAC9C9C9CAC9C9CAFD07 %FFCAFD04C9CAC9C9C9CAA19A75FD04C9CAC9C9C9CAC9C9CFFD07FFC9CAC9 %C9C9CAC927F827F800F80027A8527DFD0452275227522752527D7DA8A8FD %12FF7D7D525227272752527D7DA8A8FD09FFA8FD07F852CAC3C9A1C9C3C9 %C9FD05FFCAC9A1C9C3C9A1C9C3C9C99A6F6F99CAC3C9A1C9C3C9A1C9C3C9 %CAFD05FFC9C9A1C9C3C9C97CFD07F8A8FD0AFFA8A87D7D52522727275252 %7DA8FD08FFA8A852522752527D7DFD13FF52FD04F827F827C9CAC9C9C9CA %C9C9C9CFCACFFD05C9CAC9C9C9CAC9CAA0996FC3C9CAC9C9C9CAC9C9C9CA %C9C9C9FFCACAFD05C9CAC9CA27FD04F827F852FD12FFA8A8525227522752 %7DA8FFFFFF522727527DA8FD18FF27FD06F84BC9C9C3C9A1C9C3C9A1C9A1 %C9A1C9C3C9A1C9C3C9A1C9C3C9A0C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1 %C9A1C9A1C9C3C9A1CA52FD06F827FD18FFA8A852522727A8FFA8A8FD1DFF %27F8F827F8F8F852C9CAC9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAC3C9 %C9CAC9CAA1FD04C9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAA127F827F8 %00F827A8FD1DFF7DFD22FF27FD06F852C9C9A1C9C3C9A1C9C3C9A1C9C3C9 %A1C9C3C9C9A06FC9C9CAC36F9ACAC3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9 %C3C9A127FD06F87DFD42FF27F8F827F8F8F827A1CAC9CAC9C9C9CAC9C9C9 %CAC9C9C9CAC9C9C39A6FC39A9A99C9C9CAC9C9C9CAC9C9C9CAC9C9C9CAC9 %C9C9CAA027F827F8F8F8277DFD44FF52FD07F827A1C3C9C3C9A1C9C3C9A1 %C9C3C9A1C9C3C9A19A6F9999C9C3C9A1C9C3C9A1C9C3C9A1C9C3C9A1C9C9 %C927FD06F8277DFD46FFA852F8F8F827F8F8F84B76C9C9CAC9CAC9CAC9C9 %C9CAC9C9C9CAC9CAC9CAC9C9C9CAC9C9C9CAC9C9C9CAC9CAC9CAA052F805 %F800F8F8F852FD4AFFA127FD08F8272776A0C9C3FD04C9CAC9C9C9CAFD07 %C9CAC9C9C9CAC9C9A1A1524BFD08F827A8FD3DFF7D7D7DFD0DFFA152FD05 %F800F8F8F827F82727514B5251765176767C767C767C525251764B51FD04 %27F800FD07F852A8FD3EFF27F8F85852277DFD0DFF5252FD27F8527DFD41 %FF52F8275958F82752FD0FFFA87D4B27F827F827F827F800F827F8F8F827 %F8F8F827F8F8F827F800F827F827F84B527DA8FD45FF52F8F8F827845227 %FD13FFA8A827FD15F852A8A8FD4BFFA8F8F8F82E27F827FD13FF2700F8F8 %F827F8F8F827F8F8F827F8F8F827F8F8F827F827FD4BFFA985FFFFF8F8F8 %2EA8274BFD11FF52FD18F827FD4AFFAF61FFFFFFF8002752F827A8FD0FFF %7DF8F827F800F827F800F827F800F827F800F827F800F827F800F852FD49 %FF6161A9FFFF7DF8F8F8522E52FD0FFF27FD1BF8A8FD48FF8B61FD04FF52 %F8275259F852FD0DFFA8F800F8F8F827F8F8F827F8F8F827F8F8F827F8F8 %F827F8F8F827F8F84BFD48FFA8FD05FFA8FD04F82DF852FD0CFF52FD1CF8 %27FD4FFF2721F827A87DF8277DFD09FF7D27F827F800F827F8F8F827F800 %F827F800F827F800F827F800F827F8F8F8FD4FFFA8FD05F85227F820527D %A87D7D5227FD10F8277DFD0EF87DFD4FFF27F827F8F85284F8F82727F82D %F8F85283F800F8F8F827F8F85227F8F8F827F852FF27F8F8F827F827FD05 %F805F87DFD4FFF7DFD07F827AF27278458F8522EFD07F827A8FD06F852A8 %FD06F8527DFD06F852FD50FF5200F800F827F827F827F827F827F8F8F827 %F800F827F87DA827F800F827F87DFF27F800F827F852FF27F800F827F876 %FD51FF27FD15F87DA8FD06F852FFFD06F827FFFD06F852FD52FF27FD04F8 %27F8F8F827F8F8F827F8F8F827F8F8F8A8A8F8F827F8F8F87DFF27F827F8 %F8F852FF27F827F8F8F87DFD53FF5227FD12F87DA8FD06F852FFFD06F827 %FF26FD05F852FD55FF7D2727F8F8F827F8F8F827F827F827F8F8F8A8A800 %F827F8F8F87DFF27F827F800F852FF27F827F8F8F87DFD57FFA8A1FD0552 %7DA8FFFD06F87DA8FD06F852FFFD06F827FF27FD05F852FD60FFA827F8F8 %F805F8A8A827F8F8F805F87DFF27F8F8F827F852FF27F8F8F805F852FD61 %FFFD06F8A8A8FD06F852FFFD06F827FFFD06F852FD60FFA827F800F827F8 %A8A827F800F827F87DFF27F800F827F852FF27F800F827F876FD61FFFD06 %F8A8A8FD06F852FFFD06F827FF20FD05F852FD60FFA827F827F8F8F8A8A8 %F8F827F8F8F87DFF27F827F8F8F852FF27F827F8F8F87DFD61FFFD06F87D %A8FD06F852FFFD06F827FF26FD05F852FD60FFA827F827F8F8F8A8A800F8 %27F8F8F87DFF27F827F800F852FF27F827F8F8F87DFD61FFFD06F8A7A8FD %06F852FFFD06F827FF27FD05F852FD60FFCA27F8F8F805F8847E27F8F8F8 %05F8538527F8F8F827F852A927F8F8F805F876FD5DFFAFFFA984FD06F859 %5AFD06F82E61FD06F82761FD06F82EAF84FD59FFAF858B618B2F27F800F8 %27F8855A27F800F827F8596127F800F827F8528527F800F827F827618B61 %8B85AFAFFD50FFAFAF6085616160856127FD05F8276159FD06F82F85FD06 %F8278527FD06F860618561616085618BA9FD4BFFAF858B6185618B618561 %59F8F8F827F8F8278B2EF8F827F8F8F8848527F827F8F8F8278559F805FD %04F8278B618B6185618B618561AFAFFD46FF848560616085606160856152 %FD07F8606028FD05F827608527FD06F8616127FD06F82761608560616085 %6061608585AFFD42FFAF618B618B618B618B618B5A27F827FD05F859858B %F8F8F827F8F82E8B6160F827F800F827538B6027F8F8F827F8F8F859618B %618B618B618B618561AFFD40FF8B606160856161608561616127FD06F805 %6061852EFD06F8616085612EFD05F82761856052FD07F885616160856161 %608561616085AFFD3DFF8B618B6185618B6185618B618553FD042753608B %618B5905F827F8F8F85A8585618B6127F805F8F8F8526185618B532E2727 %052E618B6185618B6185618B6185618BAFFD3BFF8B606160856061608560 %616085606136613661366160856027FD05F8596085606160856027FD05F8 %5961615A613661366136616085606160856061608560616085AFFD39FFAF %618B618B618B618B618B618B618B61615A615A615A616185F8F8F8272885 %618B618B618B618B61592700F8F82E855A615A615A615A61618B618B618B %618B618B618B618B61AFFD39FF8461616160856161608561616085618560 %61365B3661365B3659285336616185608561616085608561612F2E286136 %5B3661365B36616161608561616085616160856161608561FD39FF8B618B %6185618B6185618B6185618B618561615A615A615A615A615B6136616185 %618B6185618B60615A61616136615A615A615A85618B6185618B6185618B %6185618B6185618BAFFD38FF846160856061608560616085606160856061 %6061365B366136613661365B36616085608560613661365B36615A5B3661 %365B366160616085606160856061608560616085606160FD39FFAF618B61 %8B618B618B618B618B618B618B618B61615A615A615A855A615A615A6161 %8B618B60615A615A6161615A615A615A61618B618B618B618B618B618B61 %8B618B618B618BFD3AFF8560856161608561616085616160856161608536 %5B366136616061365B366136616085605B3661365B36615A5B3661365B5A %85616160856161608561616085616160856161A9FD3AFFAF85618B618561 %8B6185618B6185618B618561615A615A615A8560615A615A615A8561615A %615A615A6161615A615A615A61618B6185618B6185618B6185618B618561 %8BA9FD3CFFA985608560616085606160856061608560613661365B366160 %613661365B366160853661365B36615A853661365B366160856085606160 %856061608560616085606184FD3FFFAF6185618B618B618B618B618B618B %61615A615A615A8561615A615A615A8561615A615A615A6161615A615A61 %5A85618B618B618B618B618B618B618B618BAFFD41FFAF85616085616160 %856161608561613661365B366160613661365B366160853661365B366160 %855A61365B3661608560856161608561616085616160AFFD45FFAF8B618B %6185618B6185618B616136615A615A85616136615A613685616136615A61 %3661616136615A6136856185618B6185618B6161618BAFFD4AFF848B6061 %606160616085365B3661365B6061365B3661365B5A85365B3661365A5A85 %5A5B3661365B5A85606160856061608585AFFD4FFFA9AF858B618B61615A %615A615A8B61615A615A615A8561615A615A615A8561615A615A615A8561 %85618B618B85FD56FFAFAF848B5A5B3661365B6061365B3661365B5A8536 %5B3661365B5A85365B3661365B608B85AFA9FD5EFFAFAF85AF858B85615A %615A615A6161615A615A615AFD0685AFA9FD6AFFA9FFAFFFA8AFA9FFA8AF %A9FFA9FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFD5AFF %FF %%EndData endstream endobj 21 0 obj <</Length 35936>>stream
+l-ø¼¯ç
+¹Mßßä[%r<l1±8•×îØ­èZ_Í\¨´í<[ô,ÜýÑj÷~VŸá†þ¾¾y‘6˜–b«»r»¹‡¤øžF§¢MD·"´¼ ÓƒœYµ‡9®0¡òËÆ„ËÓ×¹œKæ—FŽ{¼fùJö4vÛY›-2±Gß­Ç&f¬nµâ[–ØÜ[–„
+Òon°‡;¬îÖŽ¿r;väPþb½…½aÉ£±ÞUΫwJ¾Õº@zúnJ²’rZí˜sx—áÅL
+ôL°ë#ÇÏÛ´Ç“/±Æ­µ.ÇÒ£»½±V0js$Xƒ‘ývðƒÐVÒÍ=ƒÐéíÐÍç¢I_ôc«=_Únt©”²aÞ¨¨ß–hÞ}c€âÔLíóÕf<øÚkÍñâÕéæ„WŽ—’Ýè)"úã*êûd·\6ŸŠÒ¶·Æ
+dÌ;õrs•Tæ<é¦R-~r^
+ =]H
+—
+¯Ó©>PRP>ÊøÍfÕrK lÞ†qïDžÐZ¯´ƒ/ûÔº/;Èd¦ÇÔëv¹§úJ¥ÞFËŠP¦c”Å?ý’Ðt»P-(ª`€ê±P#_¨È&ë¤\²¹Œy
+úN½zJö˜ÝëÞÒ¹ié[öq‘fKcÌKÈ»A¿ë“±djiôø°)ø¾mBPjý||U~|Ø
++'¨fœË
+›]„m›°úiQX¥çbûW>9.HÜxȆÉÝr~bž‚Ñ£ð2ˆzšD)'$è%ç\éé
+âŠ93P8š•ÓD[»œC¼gÚ T@‹®ò„Œûí¨€:ƒp7›`–ÔrïjE×Ôz€^V€•’rnœb?Îáqïã¶Û€ªqg@ß< 2HPŠúøšË–âí<§­‹Á[šØC±S7ù“W‡Z_«„°Öq{&™N~жSL ¶™
+°ã5ØöÐl-N6v“=ËfF®‡£«ÓbôEfíÚºfeÙ›#÷pºåÃrêÁ´Óî
+xÿÜ*(æ~Ÿ¡Õ±~éHq½C‰OEZ™Okk½7\ú§?±µYÿ»}ÛWºýÿIoнËKî_Awü†xó¸Gˆ÷ä¢/¡Ò H
+å]v+W·ù#ÛØ¯©æšî®ãU
+5ÄÚx¦Í‘ÞÝ!7=Éš÷•ífÓÓŽã}·«Œ“·O·,}öê@½Iªí¤&h<ËOȘ˜Í¹ÖÒ½©ÍMù™ir{Þ‹BÉm6¢twR¿vé[]OqVØÍß;ßFYß„¿ÖÕ?²ŒÑl;f­Ö'A÷È¡q!K=m¿“;ïf?Ý:ýLtœÖ²PóZô¢Qmr¯…¼—*õb§ɵaýÚªLêÚÛ‡ncZ u¬ rý{ š.¡¤Š.Otà²tº²¬yÅ
+}:Æeî^>úûîÐðà æùx–þe]ýø“W=WùB :[ñ+‡vêèϲʣ¹qO¯(d‡ ¡œêÈ'ÝôV%kï~Ž­ÙPá¾ZHWÑyÁ \ª`W–•œSñ¹R©¼ å2w.Ôü0]jz§Nµç)ïö´Û«û˜ÂÏâ{™&‹n¡©òüƬ~ž¬oqÊ/ˆûCúŽñk¯rî>B:Õο´ë׺Z¿6ʓڻ',ªr\IJ¿.oª•]™;Öξxõî^¤ tíHIOù¸ÛÍ»ÔGëCC¾8p©­‚ta\NŠt7pW\–óËèåé;7ÊqïâÞí dKHQÍF®Ñʪ”óúV3×uý⛯; Ô×µH4£ýñ“±=/Â®Ž ’á™­
+½™E¾x±›¥ØMׂ·í¢Oz‡îœYЛ9Ä¡·Ê/ëó}ž¾LN¹MkuËqÕË­u2pÁ³QîÅf®3\É褜TâV·\öÍ÷nÕ6m‰_™ÐyŒq+2 G_O ôe\¿Ðg}á2&G¡Õ3–K…Ü8¯ºç…Y@¼¡Èß׿›…|ÑÆîŽ3«t
+y_Ê?¹
+iͳC“«dO.̪0×ÌÄ®“Ñqe`u=qlY´¹0¹1¡CöhŒë•»^bHwµ¥-»oUcRf!½­[iK¯Ôýü…¨¡ŸL$æ:ûAT‰…–_ ä&ftÙ…oŠÐ û¦sJaî×ãGì>ks¯0.yZÞ7²¾šU!^µ³[Ú|¯¶† 톖áˆÃœžV®îªs_'n‡Ú¢ùÆ!Òè׳“²ÏAš‡™¹ÔrZ87E¥Ûˆ|ÝÂ’¬ ~Azœ ¶Ôú{ÉJOeñýh×<û_ôLã`Äts!4´g«¡ƒà^ÞsrRï—:õSrò3M?Ïrž•=:guÝ žŒ,Eè³Æ”Жq‹Joz“æSž îG U•К®œf¬¢’­¼|]M=YgUé±»4%KJõÅASXˆvºzþA„qg‡H+ül¸*ñnÞð$¦½x÷rÜrq©Ñ{[«N§R”ÆdàßÂ×o§1›†]hvÃ*f­—" Ƥ) šËï4Ïj=­<åë2û–uÆ‚¤nÕÂ$Kp(qÐp8ÑVYÇ&8z7ËÏzã"ïf®Un9&Úœïgìf>=³§Ì~³0™°š Id6ôé, ôœ
+I%~NA^›tzV”&¦Jm²Ó•j»—ò ©tü—½V;73ïð’NupL­÷R¹-jÉ
+fGÑθÁÉë'~6S¯¼ëÚÉΪ0ç7«øÂ1¹ ÇÓ¹Ìò!°˜ƒ¤ºŒ4 êô9·Òjrï§ná¡ô‹–&Ÿ£{@öœá‚Ìàs†Ìˆ®MfÌ™3Ïa¶a éê"?cË a—2çúåk®MÎéõüÇ^ËÛéôž`IEÉ%-e£G~¾¶¶Üªv[³[¸fùmoÇÚÛ S7nOúÜ¿§è¦Ïtš18ªÝ.§)ØäÈ‘éßêÃÛ,™[¹Ž•Â'EÇóÂ[ð™à™Áñª^l õI®že¦Z•ÖN‚Å·˜;Z ïNÅ©¬íæ›ïZ¹ôÂ×ýghª§eš‘Íò¦ñ~ÇUP2ù$3r53ý|ÆgÔ¦T{#oÉ/ÉÞ³p'3½FŠ¥:8‘[_ X/rE¯Ï#̃øFm6Ot]u…dîD”Pö´£ì!L2hE~:?HSI óáè¶6‚
+x‹hm~OÒÎx¢!=â(Ò•…11Mx"ëÑVJwˆy=ÚÞÓãóþAK.À" ̲
+…,öbÕLÍ £¡Ñoê<UOZ«Õ¢>lÓYš4Ç®UY
+š›Ú“sƒ“ËÊEÛšÂë
+6[/Ñ»tOf§E‹™«HvK̃[ ˜Ì|{ï8§4ÚÄ£u:²ÎȰb2yøj|eX>_p‡rÕMå&Êû÷X<‰ìâ+F³Õ— ízãQVŽÞÚ¶hÑM×ËG¼Óø‘ösõ`[lpœÓ°ÑÑ&¼IŒ¬£ezX¡g¹Á1=+ Ü«Y­ß²l:½®c9ìa dÕ%;ä±CœD;­mÙ[Ë”/×2­bá^Ž£á'ǧóó¡«·þ䃯—>ðߢZÎåþâ=vgSº7I5ý±©³Ÿ “Æ¢Éæ·½®-|è\ÞKgìshÞ?™e¥Z˘‰i™¦zc³¨§- 6§‡4ÖplõÞzÝîˆÔ.µH¦æ»ÔjÕ®=¬ "Ô®BŽ›t9ÁL
+¼›¶”6ûg|¡Á'Ê…®~ú“ùÉñîб©½ˆê:Ä× îêAÚ{<¡v5Ç[¦1žn°éS¡^·¬ªµKy uÅú¤[ N*d¿7/#“u™OØRZ79•ôóRÌÏdaS¤Œy¶jqåçd4çAÚÅl+¬©Ð,2d&Ä`iÁQ5!¾î*ººëKáCY©“=É;ò:cwÏ(D‡sA²†T/ÚÊÀ7¦ËãÐÀXJSu[1§¯¸ EsΟgK¹0ç=ä⹺;ʹ/Å\¶å/–²H/WÍtÃõVÕ{é(ݤÆéÙ:9ËoNI=kÐ&rδ=^ôŽ£1¶ïÄœDŽÇ?ßÜ„®¶´õÚÕŸÌCh-ÃÅ»C…º«imhQ(šðuÞÁÎ7ÙK£ÁfºÁä1=ˆÆÕir’ú(½šÒ'õ›”9‘³ÅmñM5âŒ])oŒmB+؉¤¢*R3¹ ›¥ˆŸš¶ÂÝ41 c«ð)µLuÁqÅÒçÄ$0k…(¡à*×ø—=3ÍÏP¯åK! ÕÔ¡œÀêK>t5{©/¬‡zr*¡£[‰e~ÜŠ•n¬âs=¶sÓ¨-®"­HnAƹC8¤KŸÃؼ„„¢Æ"×m:˜°¶ Y¥g èOs?ç=E|›®9ï³á¹Ž·cמã,JpÞÑŒ;̯òq«Ÿ _a·Y¤šxµë
+îKú‰y
+qgï…t+ß”¶ñ豕OFZaW*¢Ñtˆ\.²Á„yQ ÌJ‹j@XÀµøsîE×oºì†¾¢=ñÙ4祷>l<ÇÑùà©'hµû2·’n¿1htu e· cw gÔåm8ÆM¨†%üyÊ¡C—%ûlPX|6­•¿g©´A¸–Ò¼ë8åçÓ,ƒÛ õdÚàOÅØZ&i…FîPTorfEÖíϹênŸ y¼NÜæóÔcΰû2µÅÜ­”?íF–žœ«› •\Ø6TsJéö{&qªBôe°±§ý›³mÙ×’63µY‹‰YÔjc" K%{ÞX*y—Çâ´êj° uù¦á*̪F…²öxÌ¢éÞ,¹ ³­Ø~çÏEüUµ?8ÎÎLþeÕ¬ã*{:G›dÜÈÂȸ° jp’ìÎäH8w6}:»ìiïÁk7 hÈ– ©£63N¥¬Å“·°SKÕRI…;æã2?1{ÌéU&0“ßQ²» MÜò¶ÚÐÑŠÚˆkQào)ñM-èdÌ¥ì˜ÔæZèæœŽ¯æ«„Õ–êDÕu].kªCþ¼lõ:ƒyÜÌûÀµFÔpJ´Ú¶l/ÕÖM¿…Xmd³Tuî SN]—1×3]“ÙcÛL­ÂØeò[Ù€±[¹ÄŒ!—)g4}5ƒ) ôãîa«×A‚™.&(å ‚çŸé–ËœE·\Y°q$+c/j)°{ŒÎ†²'µ
+M®Ô“M¬^-³³X°hÔ{È ìî@®J9µÍØ N/²¶#úÅIo6¦Ëª¸6ù-¹±[NŒ!GñbÔKCÔÓ ôãv[§OæffÖß:™tT e²n9±%uf&Q¦7‹Í.悊ÝnªRN)§ÓÒ œ®$¦=^´.Øxåtµå.ŒÉP@ƒ&¶-m»:ãÐD›«óᵞ‹‹u»ˆmUgÏÌÂjµÛãk3妯ni?4 jÑ~Ì}XE§ÉL™t8¹` D~£ËÅó{™n\èbºÑ6ÃNG±kÊJ9­.ŸöÈV“Zs_#/çèŒôûö©ÑíD·oͨP"õn‰PÞ¨'Bª«”wŽ\{MÒÉ­ˆBàE]»yp>©·|ût8ëѤßI·'F[!EÌçlpcìÒ}ÒÝ0†*ÓÑ™cÓ],šÔ­{*”ª :#Êεõíz£õ”ÑÙÆL(é_fõ¤&7ua5%ðN¦„Ç `ŒkWU€M
+6+–´²)L·?,1]@óLpKÔ™C›z“ðå6·S1‰¸©uÐ8Yö|®tµèÆÇ¦#ÝVkÅÙÁ͈)ÅFùÄ„1“Ú[7µ§e°¦…vR•lÈ©YТ—µ‰°;¾ ðKy‡Ç5N1ˆ›"6Š%k£ÆxŒ43iÑÌ4P#Ô4kç,­ÉŸG MþR£4kÅ%ÔÀˆÄ­–äÆî¤ù(àð˜4n59ò6CÊkDfãöUÇì´œœ;Êr ž¬ŸYŠs]!‘lS®]§F¢‰K ž‹<Ö,–pÊW¯aSdÐÆ’³ã’½DFßÍœ»8jl^Œšu"î×të¬ÆrHö‘²ût@ jP:ΨºÒT¨º¦1õ¡7µç/€ðdÆV·-Y $ô×8`wì°î¬S¯¯‡7]Ô¥™²[:Q¯¥QµLxÔèÓãVgݬ†ÚænT#"þKKÑž,š9žÒh7C[ 5®BuØÔ{šu~6Õ|4«±"!)§[ â0".õá\H©Ý¾C\4¥-ðe4@ÈI
+Ê´j~ š—H]HîÒHÁ`“µäºIÆŸNÏâ®DÕÞµÕ€3î‘Ú_õö·žž®8t¦s–²­b¹Ä,ˆï6ƃS½ÞØkT(]]vwd/຀£Šš‘J aó†‚úXEµûpª€–4_k
+ßÂ;£c›œYR¸ßÛí=aÿlê4 ’u[eÉX4À4&h—)ô.åÅëz‡˜ 64%zSW7Ô¶2ð×VE qo¹¯–k<:8‹hb ›ñhò66uh
+´6Í4д= ikO@Sš›¦\OM¥<šLS4Ê›1HÊ’núãM⢀׻Öwn2_<D [:ÖØ¤csûEsÏÓŠ NÆF›3B‹Š{\{ǨeQè ®
+Û”³
+PÌPš£­ÿ*`³\
+ÉlÒ¯^9½®,^¦mÞv4Î÷¡î´9–µ1}#†mã>§¦0DbеÐÇýËÕƒüÏWs"O‘°±ÐÜZb13l&^@ÆÍIØtë€LXgïÁ; < æ ƒ›x.œZ&šÑD[ˆN±™JÐѧ<ñ~Ïi?êõZs<•cõ+1„ÚEÄØ*ãÆ5•YÜ«&"6#ïž”o¥”uBŠîQïaÁ\Õ‰Yƒ‹ ž¶»µ
+•ÃÛOcM…Êáí§±¦Båðö]¬©ÐW©í;XS¡¯RÛw°¦B_¥¶
+^¯ql7dÍØ éZüÍõÖÚô ‹–q4á̦ ×ÌH-<äæŒ@TÎ;¶«öŸA¶¬Bƒön`*Ḑ0´¥˜(=c}±hv”‹÷,Õ‡Ö]ÊU÷é²OïÌlPu2§¬š³ÝeaÈ5%Í*b){‹öH¥æÐVª»ÓqR7µ¦çF:ØÔ7'DƯB9,ç;´©ßmšP¶wØî´ke肨®¤Fsu¼5Ñö\«i8Ê ³ ¬óó|]:rêªhÍO×ËCè¡ܰ‹ùÉ
+z³¦¤%›Kðn~¼f·ŒÇD)à¬9ÊK2Q9t<š5¬_ÔsZi̦;k+ºßÇÛ½ú¸ÞUûjsHÐ)µ
+í—‡^Ó`“0„F=R'VëÑ¿À,ЙçÍp=¸8jSÅ•uær¤}ØÌ-zG!¿Ï‹ZÔ9‡Jb;gýÈv~H¨PˆÑX5Ö·-Œ™EÇØtÄn9
+DdÂR$6ŒX´{ösNR ½¶ÑÔê 8¶—@ŸÕ—Ž =Æøü”4 r³¦Ecm.­žCãÚ©B!–[9Wjx˜y¢Qåk‡gÉ }–YFô¨3Ž—“ÃTMßµe—¹m¯ˆ·Y¦Ü$«¹š5ãØ@zì!ŠNÉj {‰CßᥘA9ù†Ëþ,7Öy mÊ‘âR@ê(éæê‚Ùéq^A0·,w H”™ñÚLœ6ƒ= Æ€v; -»˜õâe S*Æá.¿ïX’ƒß¡€­ŽµÍìÈcQ%‹ÇŸÆ¢
+•ÃãOcÒ˜ ‹\Mý;<~]ھм¤!üBù…ò åÊ/”_(¿P~¡üBù…ò åÊ/”_(¿P~¡üBù…òu(µÁúµ~QË«PèÝÆ”é÷ÿ­û…ò åÊ/”kcX˯ô³œ¸Û•î#ϺW±S¨N ÏovépÙÃÉ—Ör¨Œ{[.¡jó¿±_(¿P~¡üByo,ë
+-'˜¤PŒÃQ$}É“ÏD˜eP÷Yú®òøøt«ÄUÀªf¢›¥]=zгZ´$Kb\š„‰Gò¾:ÔŒtű& æ9ÆjcSg='ÿ‰ûò åÊ/”ÿ(vuµÅÕ£-QO$ÞšñÏA„ úÑÛá<tê=fÿl-moÄnÒ˜Ç|+‡ŸÅªèg3nE€ÿ(w/Ї½it¾T‹ª#Ó¹íƒW™$ÌcSïw&YÏĵ©:4›éî|>ûäÜ¿¤Ç.Å=¡.~6IV¼Æ%Ò ïÙžâ°KC%îæJ vžï|-­<Î
+}ýBù…ò/†²™b;î»x¾G/Ò±Rƒõ´ìÍ}Àð°Ÿ;±×¯ç*
+”Áo¾ùqPTŠJ¢]yZ´QZà4i·ÐN‰)Ë]8šòΨ×Í8u‡q¢òCÖ|­pXgÃ8ØEۦ᣶PÑZ+Q‹Pÿ&jý…ò åÊj¡YãA§¢Äí|C—Ê»Ða€ƒn=†Bû“[ éâD‘v¼ô¢1¥ ©œ¹k|³¤ƒñ®"èò:M 9½Q3©j{Í—jÒÄI¿R“«~%¯†ÔÇÝÛ[ä´mi•°SŒßÞ
+]ÊõÃä„c¯½Ka4º”ØYmg^x̸ïÐÉêˆü÷‹8C3LXÖ<ìǨ Áqr°<“¥mìR·BDŠëÅé‡Â ¢|RAQ®-9A¡M»4Òáµsnnœþ•Ê,qð&ÇÚÆTDø/›½ñz—Þ‘’H`cÐkæ^GˆÔ䯅FÂÐ…„ü~_S\f‰«$'œTÖÄ*{ézK7'x%2^™5ÕËî~djs¤:<v®÷±YŽ‘7ý<d&C ÑÍ-ëËÑÝšþyÔÿ å? Š-Å$VŽ®^ì^»iÌ“-¢BƒùRÕRI¡'½hU›…ã๒ÅHg’ÆLñ øô ‰e^+VÇ{ïô½aUn¨¼§1Ðò:G[–a¦yÜãz×§–ûàÈ ¹ý¥5Ná1`þ’T)) M^`ä5üV¦VA(èRàÔ‚+#TóíËbC”«0ïî¹W “„$¢Ý2)—%®.#¿µàí]‡ëg|ƒk1µåMAÒø;¼úFùV´ å”B1AóÊxé„°VÕ 0¥('ãÞBä‚•ºªæPÉ—ð;¢ñŠ î
+ÛK"xØ=ŒøÏä£_(ÿ?B¡›ƒ‚ÅžþHÁœ‚ÍÂj mõŒ;Ó„Ä;T
+ò£ëõ¦¹â(˜+·/öµp=’QdÊ6%ìóñ|i_æ¼^¤>»åç\´Î6¤ ¬Ü÷wíâa4ºÂã3Sº,qê7>ôª¡©8,7WW•ñÆÓœâùÓ¿¬Ä÷–œ«È½Jz2’èpû¥{Ô1J–¯N«¿êEi¹«cÈ®Àä©‘\Zã¾¶öªµ—Cézè¶0´UFI¥ãÜzFÖ~6(Å4ØáîU[Ü,uá•*ü3@XB2Ãîœ*q_å:a!NȵúàŽ*„ ¹ÍÁYLƵœéÐCŸì?ˆo~¡üz1w¥ZÅgc§("]°¢Ûtç@éTm+hÝDë~BÎHÈ%v…$ç[ÂC6™“Ÿ®uK«óªëÄrs‰9Ý™0$~îÛÏbï÷À‡Šï®)tõåTm©­duW!P<Ö.ìY4q^f½‘ž>&¶Èø/ñð¯VWRž+êßuDf=Ú7,÷©±dqûuíï#;YWRŒk-eÁQ…aRm·¼;\¥¨Ì…4ç啬wì„àË:ÀþØ)yµ”Ô'ÍÐ*·ûtN{áË»½HûZ‘o´-$£i*Ô¨1oÏ—H„
+ý†)ÙÜQÊ‘“ÿü…ÂCÙËÎ긌WøÝÙ¥Úº·…Íoî–®$§@ïÊE[IÇ›<é¼,¥vÂý È ”*{…˜Óãús.\3ÁrIÐa°aW’ñÀ¤#‚8
+I ¢Ù#—A†§
+½žv“+¥ÒCôaÒn›¾S÷âÎPù~¦P¸B„¼1O0¦ÙÀò’u HMÌÆ›¸[<L? ‚—VÓ“˜òœepõá ã®N8¶~CòÙ´˜O„¸n
+ñ³$H ÔUçPK+|”̼锎±Šëûú‘X1è· -—`Åì%Žõ´Ý
+òþ“)ʤ奡^‡4Ån¯óòÌŒ4GN)•P“Öa–J‘ŠᆕÙÚ*Å.*a³®½a)î‚ÓyB°\öb3JÎú*€Ã²· }Ǹ¼àºtèVF,lOL˜æ-Lã=†c\ò(®UëC‹ü J•à©—Øt©\¦õTèDG$~2ÆóŸÀ»ÿPlµØFø ’¤
+­æ5œ1Ïf)4C¨ú(2ú®–Qต¹Kò”¨^W¥s¾ú-ß$×ôUo„gb Ù”æV¼_Cê“fÝ¡ƒê»R#u–ºyAÇ0g¶û 1šäjHT쪞‡˜hUR‹ÜÎãúw¾æ^å>bOëÚ=?¤s›1ÇŸêD^¶¨[^æWö•\§9ªjNÙyk°©¸FìÉš¿ßNqù’˜„Û#øiбÔr)/Øw).{ÛÖ²-Û&Nãñm+É-7=IINhšÒò µðF L6­f¤ŽôìR)ø±ºª#Âù¢t­Ÿm
+÷”* >T€5âk N…Ž|5)‰*1×èa@É`»¶o¾)°3w»dTbf:‰·Þõ t_*}.”åmGçb÷ê ½pak;+Huã“Ü­Óº•àÕŧ2ynãuâÛ´ëªòµ¿—‰œE#q‡Øv/|†•še %õcnd4¶QE…F§U°œLÍMùL;GµggÔéµZÂÿ±Üõ E®9œÓ½Âvbpj· A‹¹qv²k4 ÄNœ=2Ó²žª§ÏÔVbÛBA½ >XLrCQÚðN
+@]œ¢ëäÜÕ·
+£äNC=¬W®¬. ó(n÷Œþ¹Ê«£E†Í‡Õ “¹žÙ’@";zL:ÈâÜû.(ÞU l£¨øÎ+5Ž…‹m<ËH1­÷›®7Ij©Ã³ZàÜ3²AB*Ôo:ø b.±Uüª´f‚ã+ -–Põ™®Þ+ß|&<Œq‡€ÖcÃ%P–p½!EßçÏÆK<.1­7·N‰Œ#rQpðáúP© ªÖ®%ùúgñÃ/””íè2‰ çÑî^N¶ÚFã[4­ÓÂÝ•Ù,¢Kw€©_,áZÄIìÔNG…ÖMÊ*‚BçÔ•rS:ïÕ«.ÿlICɺê-ö {kC "à>±¿ô—ÃøUŒdVZعW¥×âTT^jê
+×PÐd°ɦòÿSéôÊ/”‚rtÛü3iÎ(×Ô²çº1ds¨ÐlÖß·$Úx¯·‚©äåÔ»wÑ#W"lhPõ£Óäm}뛬páC”yÙ#V.;¿Ùcд„Š
+ÝÙÉØEQ¬”Žy““ÆÝR¦*èY2fç´XW—¯øKãk¼"=»ËÇà„#á诠õ<mdó´VC½&ý¦Ù*LdÙW(Æé¨t­ŒÍpÆUhxͬkcè¨TKTZ}u!< RJÅ©þ54û åÊÿ)(¤9¹,{h÷4LöVmÏ{X:«Œ$ØÛŽP.ÆChÜ´;¦iow:¤½½xKšÏ^Y̵kx½Y‘p“k~¯“‘6I07e ”®¦«4z¹Bóë­e˜ÐŠ0@'=Ç… ØÆ"Ö”öy-¼)„¥:ʳÂÕ÷žßÊÔã¤Cƒ.ªôD½µšíNR'»ØmɲƆö 5oÎ8¥!)Ñü>×pI#¦ð·ú.í€Ý†•÷¯Îis×ÏŸ+lØùOm¿P~¡üK Ð•q|R4uÚ©M¾¥Í´ÃNÑç–+h§B‹ÍžÉëí¦<{g–YÆíe!íí{¥e5ôŒ±„Øb­`K3ê«ÁÀ›R„ü:¾Vx §©…%9ÉŒ½ÖSËée0PE8R›e¥¶w$—ÓÆŠÏ‘É9mš¨[®æI¯ÎùDJă!6[lfåk·9ª…•¶]_yýþ¬×0ÊûÒÊz^ëZl} . ³{5 ôõ1%þBù…òïRYùê‹KaT©¡«lµ<Ò©{'cO<¨çVó!eóÒaI
+­J¡§_
+®æÕldº’ɉþÑÐ/”_(¿P„ÆÞÒwÚÙÀÁF0™ÚPï݇͞ZÅžùˆ—P¼idü`·¬Ú†q()Mű6y<¶ý)iµïZÝ~¨lvËŒM­›ùqÌIN[/pÙYdŠœêÀ`ȦêPþª½‰Í°Nä?Id¡K8P«g;G±ÑÚx”-9xâê'¸Æ®}9Ñs£ËCKS9<ÀÔ¤µ¸[:SäAùs¡©VØmjkyå aÿû»þ åÊ/”z²ÚøÂ½µÙdÁ…ÞÒçîŠÚ‹øÎä©çNÆHôÜAa´DÓQÈa}ÿå`ÑÏHkIáë• “Þ:bj–bû.á”D†h®¢BKáðLr­ußlÛ¬N߯óOÈk:r³´«Å)žÿ„}ù…ò†r<z4|.çû?!Dô¿jøÍZq%ÿ¯O…ö݈`ÒhúaÇ—ítwþS8²³åfú'2<É?4ìbµÚ6Ç}>Uè\^¾M…ÎÆ~ÎÏnåéù²ÿ³î¦¡—>²Ÿ—ìnxüŸ?®?ú{.MÇìq2ð ×AôÃóù¸]ÎÓ“~ÅŽ˜1»Ù ÏWHôeøp7—vÛçÓãp7¿v$È?úên9f'S¥7†Çóˆ'7oæ°‡#œ‡p×WNäÃ)§ÿ5=ž¦ÜÚŽ>­—ûÑf¸[¿_è%lŸnüõOŠ%w3ö‡]ñvÀm &ÿäáløNÏC,»¹ép9o–;¸£ç#»žž$£ÿD eÉ0¥a*Óÿ>K»\‘ô¹™óÏŽýþƒù³Üý³Û={Z¾o?ÑÎN'ËËöOizb7Žšºä'M¼O$ æ<ÝMEÓÓôü'7ÜNÖÞ,÷ÂÌfúßpØ9òº>»U„ÇÒðtžõµéøÌÿ„†p߯ïÃZáRKÓáÍÀñãp²äa¸›üÉNO‹›I /™ ’—2n‚Gá%G’—ž£@@ô‹8:+#AÚW–Xnq%t{YB÷¯bKxë9ºž¢™Î†—͹ûX^pBhÊt‡F ³žÎf"ŒÈÓð;ÿ?GÜk×þ»ñå¤Ë~dÆ—Ó™Ý*ÏësƒÐÉiëŠ9q¿ÿg99/®[èT쵘.ç‹ë`DÄúäÏèþDŽËÿâVøhiÜûwk’‚‹f¢Üév"rèá;¡@?½|
+<Æ÷xsd¶ðíG;Nö8gNþÚç¿®»fPìu7Ó÷±žöû¯3ç;í7ãÿQ¦¡Ïxwz„RØç¼„?@•óíš9ÿgóˆr¹>£Ít:yÒg|d÷ÊSžíÎÌds·%²}N—ÑûªÌrNÌÒ›\tbFKNl<˜î‰ÙMçÃ3d‚‡ÆìîÌî÷Ù¹£nx–È:Q¿Ób•Šékô u›3ÜíØûûß{†ÝŸLŽï0¹(wà¡Ì.»ñuNU¦Ìü©OGð@zÛäO‡(×ó…ùç¿LO„ì
+A^·|@²Û~”×ùùÜ
+ÉÃI´¬ÑÝ¡¤€ð<GwÓÓá&ù)q’þïá ý`áp.Å¿cV-°Ãžá¯6û ‡ÆÍîåÓl?yyp÷oJçÊr •vȈ³ók=Ïï’Áê´<îyü<'ìVÅsMè;bϤòà|bg3¨F==¡DÝdÕùÉô´œï†ŸZŠY}èý#ö²{fqðoXça¿ái´<o‡ûÇ]…>G‘>¥tHBUóiÏžL•ëù¡ßC»g¼~|ð°GN'>;Î ñ/ØãÛ•<zñ¦ÄíX
+'5œáf¸~¢_û=8…ùmºƒï5þŽ@ž>wó•wà–—çýgLVYªçºB–æ4ø{K~êœU½„Öå“nGh2œ Þõx£„µ±ö8Oû©+@nº‘/tþ/ô=Þ)“Ïzߊ ›b÷íð¸>ÝÏú…γ~¡ïͬ_è};kY5çn—Ó4ÂŽCÜ>àbAÛgìî³óšÏ–“ §Çdµšéùþœå ¹Sî(H¼1)×sþŠèâÎMAroúx³÷‰6tÇñ²š… 4]môÓI2¯Aþø_ÓñöÉËÏŽìyñnxÞûŠ‚ïÝo¼EïcñÝDÞ»Ìp7¿@ð§Àî/û÷%V´[…“Î~t7yŸs$r?ý” ;Î6¦Òs¿ßþÂYæÁr8™tX#SNÊq£R>ëÀNùk#½AOe)Ê¿8›¹¿LOÑeþxPüø‹à6û+çPd振HÉ;ŒÌ MßÇSå-Ù€4'¼@§ÇK*P¾µ¹–Ћ%|4˜1€º¸Ê€Î6"@gÈš zo“àÁ[A˜,ZN¦SN.²¶øóŸ9ä0;l-Û¶åÑ b¬½n6L>ŸýiX…>ï(Hïs©@Úyò9ž:c[–ZôØi"­X³ó½c£6hß]¡˜5E*¬Ó– ¼ŒMi¹Â‚sÆ©“tÊœŽG÷©ÐIƒÞR–ñ9³“#v®™zì3L0#9ÿ9ì±k8yàØŽ 勌_8¿/‘5£?9âÇÁ½‚ÿßÀ·›‘{ c7Û)ÊÛ{VW²ÇÈíir*û Ô¶LÇQy @OFô(´`ìXÔ&—ö¨
+ý{Âé¬N¨uA ñfT¨…ìPÓ\~¥êXÏ¢B‘ê~“‘[«!J€ÚÍîHz€6{†Ø,Wþ
+×r6<˜1Õ¯È'LEôšZ¾Ê€
+iq½ßÓ:¤äÎÅ—áÀ’Ò]%šæöÆHB VBJCè
+´€ã" Vë¶¿ÿúIÉØþ±»ØU€†6{Lc”ÚóJJ@*Tc&Üùµz@÷„,¦%y Å0é;h¶9 ”{ãò}…ûrOJZ+—ji6 1§!' T›Û5¥­)/T…bÝnLa­6]ï²a% CCœÔäÆ A|JØ›<P*Fð¡ýW M!B°?gñ^Ñí¬cw@[CÆ®3r@µ" *ô¤NLY[i¨3A°ö½˜~3íÖP¨ ±¯'±žЈ!«í:y ‰Öš<¸«c®$ ´ìÆÍŠ@ÓÓ²Ù ”“ü–¶ÎPî“j¹µžÔéÄ4×n‘„,о›+-/ŠƒT…J×ÚŽjé½GhÆŒVcWè>¥–
+e2¶Ö«Ï
+nç u:"4ë‹ö{ÅnWh7¿Nò@¹óEºÖUÓ:‰)
+@›UCt¹MÉuätjèê ƒ`]1Ó\úöþèÈLŠ˜†le†Z¨ ÷‰û•f ­+È¥?B(X8ìêð.ô½G1Ћy¯@Ï~J´RжIh¨iLÞ Búxªú
+§y ”+XÍñ8í8 Ðã18eßé7D@OÖ•ëzÒø]EF„^5;TTWýa:ìÛî'•¨b=Ö¥ø´Y4nkJO†Ä
+íEûŽAo¿Šã›°{~xÜà„ )+Šò Gõ{+InRkgR4©Û)õ/8$Ý,mÂý†?šÈÈ£JËî%”~†t®¹N^0æeÖ‡Åõ©ÐÏòâ¾ï_Yý|ÿõi¯4&·BNy­>@ÖëûÇòô)X/"ëÁ`FmhSJõ
+¹¿#Ë­ûe}ÒÕ݉,PV¼sø:æeñ•›Ö•’¿yS¨åL=Æ»JY÷¢‡”Šž^ô^ô˜EêéF;Éã-?¦|‡@L=ò¸ëE9æŠ`…ùÐQZh®¸3îäq×ÔE±­Ä•¼?NviMõ×—v{Šñ‹3ûk•ì3TÇÓØõ6Av"J´ªÛSìfUÓƒ]w_"F£JýÒ‚ä$ ¤hÎ5npûÁ†·t> 18¹FDa›è½q¨Áü?ÿ_"Æ |J/XÊûN97”Ì;uä3¾:˜ÜP<Þony´R¾Æ^å»Ç\ÏÊ¡ºqy¸—ˆ‘®i®iS7—²€’‘ˆ¦å¶bìvC¹nè§#ZJp}RPJùâÇ”næÅ¯å—™/>:)-2`˜ž«z‰&Ý’O‹/KI·d¦…½*zÖŽ“
+U<˜^Ð?;Cm¦}V<–TèWö7Îá$ñuíIŽ÷'ñ{9-ƒ¬,ûyð>˜ÒÙŸR<+ó¥ë|^Qéà””º÷­S¡¯í ´ò*Ïtþ»Ü¡‹/ì_¼°ýJ äo VÞ½0/È•/ v |u^ï7¼×Áš˜¯,òV«û6ÆúÇÄØ½Dûâ¼ö÷~˜ý(1\ϾÓ_jÇw÷/p> 9cPI¡•= M¡Æ<‘c7
+¶<WžýêãÊPã ùš¡,ÜÇJv2Á]™d>qÃYâ_ÇNS½j||è–bìøNÇòS7‚¼b˜ Y~y¾M¾ÓcO4‘+%K§òD
+¼4‘nùw8yÂö7ldJሚ'8×uû 6¹p)ydôÛcêžFÌCþ•äh>ú
+N ¨ ‚:Œ¬øP`äcµC…~eRÈ_
+€{iɱ\÷Œ>>–_
+¦äo•ó¶
+–…’¿•ì Ì'o/A¦Q¡ß¾¤àwíñ‘§zyœ{Còk³¹Þ‰ ã|—…Q>¬ÉÏQÞeòWÆùú¹÷aïË vwCøµCôþ~çµ š¸×8Ã|˜êÜ0„ùàSúœ _8n¸ÁLÌ—ïø”.–Sû*oÔO×áîK´Óš”H´ÓZ|÷‚D“õ„!b‡ïJ´ê§D{·^¿¢Á‹{.ÑT/Þ qƒý½DûàÊxý‰wMN¢}™÷á8_—hß…0Î÷%7Ê÷ï^ùqÜÑ!âãG¯Bå´»C¨Æ>² o¯åu ]º·„6Ä7ÎýöÝ Ø÷óe†”ÅЋšYòyþ,ÇÌ‚þkbV_²*TQÌþeôÃ=3×x[úÎÿîš(¨IIÂ<ç5Õ^qÞÇù~?Š‚A-ºy>Îý¥û‹îe™Û~°ŸðU g!\Åi÷§!üU÷ýþ†H‡êÒéÛ_£þª7ëÆw¡xÖ¿¤ß?öÁrƒ}_¿ïäίžbÜ®}Ý¥%=Ÿq¾£ßߌòqþý)Æ#£ßËòa½*Œóêiøø,ä%LçðíÓðî,ì‹ï‰y(ð× GâälÖõ¬¼ žú ÃPÔúÇ[r‹Jú…Û·Fã;¾j‘Ÿö]î¾™— o?޵~€±—ÔÝ÷ä (YDp/™—øüÁíi⳦à¹z!œN4%EŠàc{ž²—ø`
+3:ɱfô/Ù•/L¦Pc¢ž§¹¦`ïÙ™^Cå Ì5:ý\´-LñîæÃÛój@=̬q§3?á!³MÅPˆAäv²Ï¼äƒS`öæÃà["Tïþû—&õ"EH˜ëz" ¬ûyžù¬Ù¬eqÝ>[î4º]±èZ/
+tAsË›‹ÊeÐ •[¾ŸCw{ŠI3èDCC÷8ƒŽ—É?C§”Ï SÌübÝã ºÛlÁïäÐ=Π»Óú¾‘C÷8ƒî.[ð9t3èd ~)‡îqŸ-ø9tû]s«¿C'aÜ» ºËâ›9t3èx}ìyÝ]@òƒ ³“Ó¶•cà•3DÁ./LIÖÓy¸­}-_*&²‘ÿ:6"Ö~eœ¯zzÛ‘ûÓþk[wsÇlü5žÜÚ;r½Ïý8ã‰W’Á¦«ií™åþŽïÑ`ã°^[ïë{–9÷òúDž+)¿¼Žtý×§¤ùøÄsõhJr)sÜZ¾”4÷’¬‰ŠBA>c{¾–@uþR(ˆ?& òu·±’Ó˜¯ ömUS‡<^šêÅd·ûHȯ:yJ†{þ]±ì¦tmso%=KvS¶O_8ÜHËèÃ`;Óô™e3Üe
+¿6Ø}–Qzc32že>¾&³†±/½<W*™½#³rå—RLùÜ·ì‹Y‡Ïm{ˆ,¹»ì}Lê:{æ8\;ö"7 |·*4û7VqEµK1 ìFSç=U»^Îã»<‹ÿBßèYþ rç´RNMSN‚y¬JKcàqïQšT@.ESyÿžÆÀ¿E0‰ß{0e÷ïÕ<>ÇñYNÌ#b¸Nçû± Ñãç͇RfÊW{Nñ¯cìÞÁúMŒ=M}}‘Ÿí¯0vïø5…MÝ} SBX/“Éõš.+ñùJͬV/Éû-çˉ†P`­¾?‘e†˜zF®<äîÄ ÆÞ‡Å÷ÄØ›'^6ö”Ç(þÅÏJ£À}þ¨ªñZªÜ_íPßðü|yWiäÙ‚Kq{îdP=tIsYi
+Fœ„ìåÈ©ž¤9¿påÉ/èqÉ•ļz GºN’P~"r~å77#J”¼J~ͽ#c* šÒ“9‰ÔQΈ:Ëžæ¡%_åøO~WÔÇVɯ¹w”ÓìÄD§ÅOƒJdJ(“À=2wSºËG†“2}Åýñ`J÷Ìüü{€'Ë«Szþ<)îєĵ;‘ÿWHIú LŠ÷È|3J jš<2×;¾'é9ƒ}Ç#sc茶;¾†ö7I¶à÷=2\šÈ#£”‘ú,?Ïò%Œ¬Ÿ?õBzÎkÉ9\Šd¸ç”üZø÷©ý²œþT–•ãÆ_Q–Íþ*…¼@ s÷韉dàöÅUÄÆ©—æ"žÄô¾’†ö—ì·ç —@¦ì]úBú˜$tA6[ðY^Ý—ƒf¥ª|^Ý‹ñzóênRGd
+?w‚¦©ºáÞÝòa¨^AZ‹”ûíŃî=ç])0égòá8(?S÷8îëõÇþ&N.
+º°ýé|¸oäY|!îQ„êÏåÃq^ëorà ùp2A“¸þ>î.êýü§óáÄuG…Œ¸ŸÎ‡{Z%àGòáîn¬^µü»|8±-¦t­Ãe²}?³ž;à~,&u©¤[¾Yc_Љ|Æû§µù»j@˜ø~6:?ýl÷_ç•<ÅQ}Œç2¹$‘ bàŸK4.¹NÙ!&w¾ù–BÄ3~ÐJ"žñÓ1¡¢·§Qû~&Ýx¾Ë†p°‡eeää˜"6÷¯²ámœÃ÷×ìEYBû™jÀü8_c…|d8Î$BFô¯bOÇQ. ¬PMK)ÌZZøi5´{¯´¨Z#IK}X\òÙãÂNO êŒT£úoDnÖ¬TXø¯2R‡êþ I¤/f¤Õ£W\ORý~$#µsø™ŒTnœŸÈHåòžŸ‘Êò©Ü8¯•™Ärq}<ƒ(Mýr©»îþ=6¾Í†¢T8y9öÓ©p|<ÿÈžŸH…“Ý—O…û{¿å=ÆÛî_°+¿‘
+w[ƒˆO†û?’
+'ãUø?
+'ï{Ñ>k>R oEÁmýäG‰O ÅXÕW*Âßëcaæ‰Yôj ×ÿ×Þ“®5Žìúy‡¤!p¼/4û’@C³oBºÙ·9§ÿœg¿R-vy·cÏ¿ûÍ73¸b«J*•¤’J%edò(fp #°ø›írÚ0¡K†ó„lô^ÆNÒí¼ Ê£Tž×Kzrs0¯î<ö:œ ràÞÑõà­_¬7Õ›Rârnû®ûptu¹2ró¹ÚqF.:s;« íqüƒy«Gʆ¯œ®¾-,®™ß–—¤þòòR{‹ì¿puTð™yœü¹XQYg¸Kb9JgñygöîôŽÈ^þd·¯ýíï¢óYèÔ¸›¨ÏÔžã2ìôÞIR²ÛM;¶S¹s°””aWßþýý*®ÓŸ v§¡Ó`.Ötë]ð“Ýô“»M·2a ìK#)ÙM‘úëñ}}ŒË°3GF^ÍÏó¸d·³¤¬³Çä »çƒƒØNÇ6w7q’ëñížÄwººuÖ%ïè“öõ2®Ó]†™UX° kòãóéŒï}zz|oêç›™ êÍúS›f„!ºÅwIsχ BMòg8rë·`[O_Œhg친ԞôªA¯I|«× Câb ƒJ;œšv΃î^ˬ$Uv$âN• Þ¥¤Jr¹¼pg+™H¦dC’;!sœëK+"—x®/;?¥‘‹Å/ªî[Z±‘¬ø¥×ÈLô´:#»Ô
+Ô˾^ºƒøBD±Ç]³ Köô“M7œ&o6]Ô>€×â,/›.ãý0³é¢|‚ÁõR<›.*—nØÌÇølº(_|ôIû"Ùt>²°5yÿX¡lº(P)7„ ‘M7¤FΙM§queiÙtQ¹tâ~¿œlº¨\ºŒg{rdÓEùÚyöò²é¢f—xzKͦ‹2nij£ådÓEåÒÅÜ6_ ›.<¤»‘xKiØlº(ã´R/;›.jþ"NC̦ ‚J­)<T6]œmYn6]vŠɦ €
+ÆÄKʦŠb¹³és¬J˦‹Î­.;›.
+
+›'{æÜjYI…î
+—¹î¹Êh’Sæ.¶ºÜF¦\¥Œeîâ2¹²%Òe: ‘|>Ù+tWl®º½—,Uf½T™ÊÜ¥z‘0…ËÜ1&¹Ð]á2w4÷-¥Ð]¶àÑËF ÷\ió‡Jò$fϳØ(íˆÒˆgr˨Í6³ògl” Ô¥ž=LãOL£ÃÍs¢>S"]–Í•ô ÃøC™ó ¡—„c‡9‘!ub]~NÎVa zæᥩ±8GÝ ïéî<™Øå; L•r<ói(
+ÁŠ‚ ׳ÙÏ'Sm×((ÂÉ®ý2V ÊdÌNÏe'À2%ÖÆÕ€&Öî?…k÷ŸÊ»•RȘðŒ6‹0uW,)¢H™Zï5é õD8¤‹™xáÔ‰;ñ¸3Ôƒ÷ûR2SÜ›ó[aP›ŸeU0„vâCÄ™×þa®BÕqÑ7,¼VØœ P’Nuæ€SÂ-NÑjÕ
+_„Q7æ;«z˜!‘!W„·ÙŠX†G2yà W¸ãù•q5î†X†‡+†­[¯Â]RæceÈU¸+¥R^j>Q¶JyEó‰ÜJyÅ—aB…;^R–üa*Ü%Õ{Åwù+Üe½Õ³ŸŠ'Öž¿zfNÜî5sb-
+U<ĵ+øŽÙ_¾$¼­ù=±S_>\¥þ>rÔÛ]Uþ:lÓËg½˜|¸f|ÞÛçÏiÅ/“ixòD\îŸ92jí5Ä%áÇv
+¸|é¼h±¸ÊÝ‘ÓÃØNÇׯ¥Û¸:lM¯S7“Ë#ðŽ¡
+úSÓÞÇ­1¯Ž Èrl6æÏ¢:­Ô‘ÀÁšs¾4¼«`îŸHÞ™˜NÍ‘º¾µòÓóÁ3â.:펚ñvwGN£:%ußì5_je°Ó½¤:‚Gñ®®v|'» ÛQü}Òý‹%a~N̶ý³óž¶,Ǿ'æWÊ?'¦3@lM?¬zjpîéA£Ó Ö0o_y.'ø‰]E—ñØ$Õh’h¯ŠYúÁÉ º`Ï›R=$ñ)Iá£U±CBNŽÔr–Ô¦ôĦ÷²*³‡nJe–¬ž¤•ä£U±SªÌìç9‚–”u¸y²@VZÊ MJ¥J6~
+½òâçz­1ï.w•Ř!…Ni×Kf¢ç:¥U©' *ÞOœoH -ãÏi±ñ„ÎÆÄÕêùÜ„?x½®™2ÜV`¯g¸ú:Å{±ZÖ彩Z¶3d#%º—³Fy.V‹ÜDëå$†®Œ*Ù-:dë^ë‹Õ!=Ëþ[41 °è%´QwÅÅæ‹¥íZÂwÅ%l€¢ýcþC#!Ö«N®zñ¦SÚùª”0ÃF½iÂ]Ü:&«ôòu¾‘ýüé.;R/Ö«¢¿†ÜÛoù푘ÚOã³'ñÁÁ|‰d¡zʲ%g2åʱrrÝs )ÿrÓMò§Zã¾! GõY´ZXµ9SïG3¤rŠ÷\%¤¦•4N™?/ò~ÓMOÔÉÌ ºÆ‘Ufr
+G‡ËßÊ`ÎóHbÚ2L«ë—oöŸ"¾®ŸŸÚÁÝâ°õøòÒˆ¯ÇWÂòq«ú¯Ç—%‰)íö†ðY|¥ø‚©ÅêúùÛ¡ª~4c¨x]?7U*©®háº~ÉfN å®ë—\Õ¯ð­M¬®_A__ƺ~ò^K¨ë—\Õ/w=¾¡²pÒ2\×oødx¡ªŸÐK¡º~aW²XÕ/î–³¼uý’w´ØÐuý’:|JC×õ‹˜¡ª_ò½pÙëú ï·ôS,ÎS̾²@]?OpEUõúô` ®_"G@ŠÕõK6 éîµx]¿ä„X¦‘ ×õó#¬ê²Ç2ßdå¯ëoQ&å&«Œuý2Ù0…ëú‰3à$dׯ¬Ïèº~ÉÊ!1‹³´„Xª÷Ûêá4ÉFÛ>Œ>ñ¼WêŽÞ¨ЕÐïÚM>xH[ôqç1/z37û"æÁSßëªáÓÒ³G¹½=0]znùv¢›ï˜û¦¾ßÕ@­|«µÎ¶FåöɽE_Â|ªÚè÷¹uIn<ÕÚý³]{»ûÖ™{øáìN4¦›ãK¯KòÚíÖâÈëç’Y;¿ºÑ௟ uÃþº6ö}ûuÏx½ïUê–!é}û{çDÚÞjÆfWÿ¹´w8³ÿÝ<¾;l¯GÁ¯ÑãŦ¾òçËÁÞÆýôÃÌÉó`¾ñôzi½Öߞ̭ÚñÖüø¨Öèšõ_gëëÍÏ»‘3ýù|0ÍÖ>Éíœ;<Ø?ªI­‹…šzûzÔšÑÆäÎÒÆ’ÜùÕÿ&wͱ½··ŸNãíÝ>^¹0¯ßë³7Óò 5Ûž:–´Mç O{ûÓ~{?ÆbsÎÍ­Ë_~é×ÓÍKg±¿û‚j·$Ö“üü©Ô'6¿E‹ÐýÃyÑFk±¸’ŒÆÆÕø`gäèô`nôé­ÝSõÛÏ‘‡›Ú f’~ãÉ¡µÏÉ} Ï«}"9‰µÅÎÁA]Üýþγßd ,@mgYHšóü­j Ö¢è0gÖ—?+õ•ÓÎà„Ð\þÑÝ;1·–o¾,´?f¾-´ßo¾vœ‘ûÕãíÈö4×}8:9_\3k×
+?aß NV=Pb0=o!ÎMõÇû€Ý=…<jó‡5xÜYð`_ ´u&ñ• Ø}–˜§¥ rÎbnív¶Û˜9 ¼‚ÔÙ9$`'´QçñÏ´ö÷ý{ ¾=hµæFfFð×t =ÊíÞ¦G–Ÿ^/?üæéÔJûjflúöiü°3³ªÿd$eìîÉ®hÚFíˆÔM†ù´
+÷ô¶¯/Ô9g]…y»¾Òø_7:Á&ù~@œ"†(<ž´Ù·÷g2ÿëÂã»kÐv›s.ÝßhÂO5í²sÙ<ÛP~­\üzqY´eEUvõjG©2âçß|Uö1VkÎl鵩öø*µ•ÚèŠ=ÃÔÛÜý<þð­6ùú²?Àã¢vS›ü~²V›xy”ßÜì3ªØö"Þpãb mÂÛƒ ´”0÷x‚‰™Îçž•šBÆ– mþxõŠ ‹î”:k L×{D
+ÝIk´+tþ¨+Sg-Ä+M¦ˆAd š*'L˜u7š,¢'z»f¬Ëœ+‘ 9/‚ŒÐ¤fJ–x£1Í%Ç4A£¡¬^LÏð¶²’qK²÷J¤Žl\o €ßD)÷²,¯|›y–ò‡Ÿ§“bH¥2_oµÈ¼áÔ.)Ý wô[S„J÷eº°Âüñô11äÇÖKÈÇÎü Á­¤¤ólû«D×äÁ£í™U¤ ³öwå›úÓ
+¼üÖòï+ ˆ™ù}Äúù¤äq7wx96áÞX‡¾‹§¾@›ýI­¬±Sõ›ï5<f¶=»aŸ>kckâRÑ÷é
+
+gÝcâ¹zÆ¿4ò™ýc‚XýúhÂã)ƒxÙ',pꮜSÙýKà;ÌFÿÍGsª‰?¬Ü’]<ñÂý1(.Ô=й4b]Cº¹*õ\Ž®!Ý\•z.G×n.è%£kH7—]üß\Å–m­j«ŽSmï}> Þ¶ß~ßþ~ªNV¾VÚ‹ëŠrøtóÜy ÿýXyî>ž>ªÓÕöâþòúºm¬ úÏ7ƒê$õ„[®$o±a1—˜ôùÊèYùåtï×Föf¯V~ɽ9Á“FÕíËÁkmòîÏZ¥^kõ¥zmòòæ¬6©vökµ÷I|ìQuEƒ•‘12âúžY¼-}®¶¾o9ñ+8?º³•úêñÒ`o¡ý~;Ó^[<ÝX9íí/´?ägîÛÂД/,øp#âQ.bÀetÅn×&› Ô?ÜÚý²…ømE;‰]/4õ.Ž©-*³…˜Nçò’/ÉgÂä
+T&ooçH`‹9åï¿HäkøR›<â*j°¿þ ˆzl²4þŠÕ@ÖZìqº1Å<â9áMøu8ýÁ‚°D¼x‘,È•òbR•ú¿•òbR4¢÷ïD¥¼˜9aõ/E¥¼ðQ¥•O¥¤ðh¾CfˆbQQ‘(ó%Ç¢¢"Qxî±ÜXTT$Šé¬cQQb¥e¹±¨(MîË-5¥¢I|ªÔXT”Цqö2cQQ*šÚÞeÆ¢¢"Q®×…Lc±¨¨H‹ƒ”‹ŠŠDÑóBeÆ¢¢"Q4>Uf,**åz]J‹EEE¢Ø›cQQ‘¨J½ìXT”·ýºåÆ¢¢"QÞ}ìeÅ¢¢øÀ»§¥¬XT_hŽXT
+¬+ ]®>݈a£J½-ûƒÏ|Á¸\Üþ~Ú¼ú;x«(Uú ÿà-§ª¨vU5 x0°uóºÒ ïV•fuó©R¿l/¾}¬üîü~~ºzû[Ʀ“ë+Õé*}÷ÞýZmÀhäKx~jb¬êFxY‘«‹ðïÉ*Ÿ•‰Oøk»"K𡦆ÃdUÓ5þ¼¯Èldðö_xøü¦ÿTõê÷êÙ¹zƒ€ö*ŠaK†®V ]rS©>º-º!©¦aVÍ”dYsªš%i– Xʶ¤Êšâ6ô+6 Ãq¼W K²M ‡ÁŸy/ý
+o1uÉRM³Ê!Xªd*†êuÂúw î;l¤F—~»‡Ê¯Œ;•‰ÃÊ+ДRUÒ-[‡žeIQ Ý!D¼ÿBqBVœj£Y=9þª_áê—á/¼¶<Tª_æ§SýrJÁðòÓ*Œç´áò–aÝ>=?Á7#KE¶"ßÊ*F ©R]©Ø&€0õªªIº©8
+¾ Ó/ë Q]•\`^ÐQqˆ qÛ ¤-LÌ,(•tjË(€»*+Ð`i(shg
+S"ÀXºì
+ò’©3éCƒîlÄÑ„@VZd
+œ³åûoh­‚6Sl –*,fXe0ÕH¦d848¦pAu²Žd
+Ì‹bš {`Yi¸h(•A¸(^ƒ7^L—¥‘›Ì:Ì(ˆ)\œŽ‚\ Ó®©&*NÝD†0gÀ
+ÐTI†EO e&L£JÄ+ã0 •…ü`xŒøPÏT©§!Ȱc¢Ä˜… `c ™Á°t@Πê1`M£<¡`]£rÚp•Aši6ðyײ!£z‚&ÙQl1t€ô4ªMF&QD
+±œ$\f†&Œ·ôɪ²eÛÞBEh#É0û´%.‚Š»“ÅüO÷(Nq‘`÷Ž4yôšÐ:€Ù±Ñ R‘g549« ø5GΞ º–zе€à—Q¿pî3ï¤_q›Ð
+¹0`rUhçvà>Þä•C ¡ÓcˆÄ‰@ÛcdZ0•X³‚ÑbCÉvÊBV)6D:?z-Lú½ ¶¹M5Jm@vº×@Ì Ô¡×d9 ƒ‘G8¯uds›Ð¼ÂM‡¢‡)ÀnO¼zâ£áMîp BýŠ„f!´Ãì¤h¨Â4]$¬×ÆØU%{ „ƒ,7Ž"’•È+þ
+QœrˆÛàMµ×ÆùÁƒÃYÆëÌã+w@ÞK|È. býdɺ‹ AvÞÂ,ê‹sÉ,´1ÎPÐÒµ Ÿð'U6‘ÃÈŸT?ò·Tê$¯Å|¡1ˆ‰³×ŸÇgÞ¨¼·Ü‘sH!üú8S:†)án°Ñ`Òd:|7²b›Æ&
+·Q&Zë0c†n£ÔöšLæïÀŠQ7÷d¿fb\‘?Óˆhï wçÍA¸ ¬›¾°;·AÎid×Ç€€âF~ôúa ð‘;Öä –A !Ôãˆ4‰@Ü¥‰Š¾`›ø ˆ×ñÑ×$Ã.·wnÝd²§¦®0K³¼êÆÀo±›*úü ñ™tCÐcMèZ6ÈV€ žnB7¬ºq‡ÂßqÇÊñé‡Q$$ ãí’DG¥¤½§ŠþG¡I¦a)÷ÂD–ÊT¢‹ W7¦ÝÊd+Ê_Ák×â6°Ž(‚¼ÉÖâÏ`PTâ?pû¡ýŠ7ö‚0z
+!ˆN?Œ!¡Hm—" ²pj›:`½‹j%ØB u.ny1fÅŸIŸŽ…~þ†ˆXŽ?³>È¼Å’É €ƒkIõº`ÏdGÁßð†M
+%AI12_CompressedDataxœì½ks$¹u(ø ð¸!mlÓ™H<µŠ «H¯×-[!ɾv8n(Ú=m©¯û1ÑÓ#¯ö×ïyÈÊ*V‘œ±,‘iš‡H$88ïsðWÿÛ/ýêæ›ÏÿöîÕr=]¹¿ú«Ý—wo¾~þò³+‚^ýí‡ß÷õ ‚~ò«Ÿ^Íáz‚N7[~+ÿéÝ—ïÞþô3úýñŸþÉo~ÿùã›ï®þÇ»/ŸÞ}ùéÕO~
+ùÍû¯ÞÁßþñÓWü×7¯ÂOõ0ÄþÍWøãü×>ýõT¯üϦtõË_àßß|úÛï¾{ÿÿá_ÓR€Ý~þþÓ7ï?ýîöóÿ À|5/Ws-ð¿ü¿ßÿêÝw«×ÅGìv½,Ø5_Ï9`ÿp]kÁ‡öŸß~ÿñݧ¯¿üòùí»ï¾Û}þðùËw?»Úýñͧ«_¼ùüåÍÕ¿¼ûðáó^Ý~xóö?,Büíýûïà{?¾ùz5Ó×ßüíì{ûýûßüý÷ÿí¬Ä’2‚—ßÒÿøŒÃ⿜û·òëw_¿Âdá…¸‚»_üËßõó€ö“ýջ߽§ ûŸ?•‘¿|þöã›/ÿCósÂÿñß~óîã·`ey¦ráÿ3ý¿ý[z·P¯Ws™®^yXŸ¥¦«Pd¤¶BïþðþÝþìêï?zÇËpóåë¯yB˜&þþ˯¾ÿðîË?~zÿ•?íæo+¯Ã/>óîÃÏ®¼=ÿáÍïdþ0@ûîð›7_~÷î+lëçß%t+úXç×oþø7kæü÷ï>ýæó?Ñ_ÍS¸òÞ_-×s…ýñ*#¾zC
+ÿÍÿºtÿ«ú?ž(|óׯï>ÉÄqv¿èaºþůáwŸ¾Ù}þˆ ÿ">lÿ'ÀŸÇ³Ó_àñï¿åÙÓï¿…=úå—÷ŸpL÷÷ô—òÛ_~øþô7_>ÿíß~ú÷Ïî'|Ôùæëï·ß}úæ;8¯ ã_¯ø €¾~ÿ‡w »~óþÛŸžï×oñë¾\Ý~ùþ»ß_ýæóç6ìø']ÀÅþïø%=ðé>ñ¾I:¬ßHý'÷è}ü ðÇ?åÑwo>|xÿ»/o¾ýýû·[/Øø»½‰ÿvÁËàh~yמ§_õ¿g å?þÛçï¿ûذ±ƒüòÍ—¯ïß~x÷ë?~÷õÝLJGÛ¿ûw`1ݲôîÓÞ}øüm7Iƒ¼ùôÍÕÿxóåÛ‡‡þÍ—7o4\ýÿý¯wo¿Âhÿúõ÷￞übÜïÿé8jÔ·íÇçß"¾úõïß|û޾ûëïï©ç¯Ï8Þ|zóåŠà6$’8@ÏF’Ä04þcOú^½:A—tuû©ûûß|yóÍ{ · ¨ì>û-|ׯ
++vC+¶£»£›N­˜;½dÛ+3™¯þê··_Ö§4Nj˜Öj+šMÍÙÜ*Mm˜Mo^M¯M÷fÜÔ;G›4î+oononÚŽ6 ¶M6Ž·7·6ÐñB»‘mä„­”ÍÄíä å-ÅMåm­´‰öîzYºÂЋFŒépÆ­ÐfD@'¦”r*ÐjºI·i—öéÚ=ìÓ Ë·dxL3國Ë5ß@»Í»¼Ïwù6wÅÃr…a” ­”ZnÊmÙ•}¹ƒv_'Ø5*~kª¹–ZëM½uum_ïê=`à ›ß Ëo¼§ÜÔü¹½ÙÝìoînî[fÜDØ“pi͆ÐOàè?A>€æ³ïçßϾ¸aòãÔyâ2õƒy³¶9;™4Mù6ÝæÛr[oonoow·ûÛ»Û{@ÿyçÅìP‚©X“›Ýín·Ûïîv÷@åfÀÙeöqŸöÙí˾îoö·ûÝ~¿¿ÛßÃÉ™¡@µ[—aÍëÝÍÝíÝînwww§
+i¢!“->ë¶?hw«v¿nt‚&Çÿ±ãÔ7ÐÖ?aÕ
+6<¯¨ï)Âu„l5ªål5”‘ Ô`Pͧë9ˆÖ®â| ŒKeöØTàßžÕ÷ö;ÓÏÂÎyÕu%èd?ê½qÜë%B—ŒkÂÇ‚vž®r¹ž€øÚ¨çô¥1Â]<šÐ6Së7OSï~0ÖíÞ¤^^·åÙý»ïÞöëï?}÷î+ȳãï°¶‰åY@(– •Ýßz)‚)Š)’õ²ÅýÀô Ýœ`œB¨vBšî÷š
+³ÑÆ G
+Ò„Ë0ã6&<pÛž¯5¶Ù³Ha‡Ê]Çú”ÝÝ©œ+²î$È8‹êEòeÔdôäûâ„—eiEZµvc;°YÛ~hw.Ü í~l°*ScΦlk)7¢ í­Î‹š¦Š,­µ|¸Na’o:-ãËIäƒÈèìátºƒnáÑ
+ƒd.ÂÀ ¼b¬÷¬ÿvä6©ÀLŒ`væ9ÁŒïÆïÝTòRŒ#©y£j,Ê”cmJeÄ]S– åf[±ÅM‘¬éL½Ö$$Í1:ÑØp**Í G4–,°Asž€ÄÜÁAÞÁ¾ÃVà€'8è†÷pô' ]w@
+vtn€8  98˜ìôjqæ›?ޜ鲧Z8ÙbßœüGVd\“Ó«rl]lU ZE»ýð`çYZ#yŠ>ÒÞn©!‰ã¤¥ÈXZE„ŸIÚ
+P…l-÷Ô@"sDsIìDQé%ÑcþAž‚Œ i5ž=¦dÈ)…ñ‘›kA6ìÆç¹aÃîʉm=» Џ3°h«ýDl'qÕ¶±éA]ÎÜxƒçÃM‚v¸K¼OÕö©íTÊO{ål»Ú†ÝÛ†íEFh{JÄ `W¿%[jÇ´» É-ðÐ
+É ¢D Á ªy7ÝÞƒì¿ çøl…Ifcâmp@æÛ ¸íðÝðà@×g.—@¯<ÐÁ ø8kN€¥8ãLzZ$>²OAB:‘6r‡zÐL¤7´\øu™4¼H”v!ª‹?°Å¤¸YÞ‰FR]‰lg"áQ7tܯã¬î27y|%µžêDâ¯G‰v!©¾X‡¥\˜êU!>° wVowÆžeðÅ÷/ŽË5HgœþPGmò¬€ÞÞ‹žÑrS7¿úñXðƒ‹A]®>1ÍMëH³G%Ì+/ÝÀ<Ôfq&Õ¹S¦Õ|õºD¥¸óÍ»«¯ŸÙ)*Åø;z£ÕD>uªŸ¹[gëo¬Ú üà[ÿrÉü_¿ÿôŽü /XCÚ7üÜÝßƒŽ¿¿ßÝß‚Œ\eÐ@"™Ð$0‘`O
+ÿ i÷™tù@š;(ëûûýÝ~¿ß)-qÏEL€–x¤&îrB,ò–Xc!–‰"œˆ®î‰š"-ŽÈg$¢‰Ôr"¹'Êø(ÉmIO Ü–dÀ‚ˆÖUHfrɤòNÈä ‘È,äq!ÒH„ñNˆ"Ä,Äp!v…ìh™7¨àÖß ™§£È<=™Ù1Õ#óã¾ ó 2ŸDæóó1È62ÿúó¿e6rõOï÷éÝׯï
+>Ûcõ¥Ïò;ÏŸçã¾îÉ(R·Q¤"ŠDF‘Y™×á?`Ûïž·½ øä¶wû§ýìÖÍ‚¨Ýžl7G[uûzÐÊFË«–†[c*ÖZïÅïfk“6 ¹©'™>ÍÑô‡¼çä¿!1›ÛÄØˆÅžx`‡{W8ÂdG‡Ò8Éã÷$“ß‘\¾§‘ùç†$ôJ^ûB>´D²z$_ÉB;Êì3Ù‘îUrßÓ¤nE~¯dË'EŽgIžeù{±àîÌr[T¦wXßûN´;ß–l(݃lï6…ûÞ€Ýäz•êU¦g‰žåy–æA–w$ʳ Æj”á9“üŽÒ{%Éåö™döÉë™duŒ
+;q#Qèìba³«œÆæ`Ò,¾ub!Eœ®ÜGažÈf>úh|
+ÇŒúg>ÀH˜ Ç*ÁBgîœ?-§Ìø<¤Î©§tá<Íã6šÇÎb/I˜œ%P¸Ê†í‡,PN´ÔÀà:$¶ÔÏÅYLuÚÈ•½“Ä×u®ì‰œJ7¤UjÖCŸZ-ž¼Å‘ßXÚ£FŒ[p¸£0ìIøŠ—„ËE’.9(;J¢U–Æ‹UÚ*¹ËÉî:Ìïš:ç»Ö›A&ü¬ó½Æ¬¯1÷kV²Áªkÿì’ÃÖíödÛõÍ¿vmÿ¨vçÒÝó¶Ë¼¸¹s:]Òþ Ô¼rOÔ#å¸EªéE$JqC4➨$
+7D 8¹
+p”äÛÜÓ‡ø³°§ d@¦˜$S;-v @Q½™rx£((ÜÑ@‰ …‚v'dýH1Ã#½@ÎÔ‚i2]¥L'”HôBˆƒ³ŒÚÍ|ÚU€00ê
+ÃzLôÏ€ên+‡ˊl–”:YçdM)LÔ?PGΘ¨îRlbçRÂpÌŸsî\ý EZâ¤Õ‡€IÓµ
+Yˆ­Ž™#…‹<¨–‚Î
+UQÊPÖ,ˆb2‹:r'*ý­(ñ º;)ýÐ8,ÿpA™3'éc}WWü¡+ýÀ…´ìÃDD–+>p½$p/ù?€/ù/ù/ù?t>Ƴ”NÜ(Eø2àË€/>Ï€g– }låÒÿºŸüó—”1O@ÓÑ.™xί˜€šût,!ãì'žhÌÜ®?äÇúC«¤f øj‹±Œ]+¡å1¤iÆÂÅèÒÝ_$wñ{1\«]âr{düÑbŽªÚ“jÑàêÁ²Fç>ÀEHg,ø»`üšô—p*â‚gÆrª'&táüŸŒTÛ‹üÙ‹NâÕ^üKð¢ÔЇš{ÆÁègÛ\ü¸¢€î1¬|ÃmÍa¦ÞjûcÍmš²O¶·MÜÒܵ{Ãê}-ºm§öPE÷ÓÜ7D¢óÒ=à‹yéżôb^ú¡ÍK?Þyþ ð…„½°öƒ[ȺêÂö2àË€/>~Àÿ6üÁäþb!?°O9ø(75L³¯Z„èÐ2þ`ϧ/—MÛåÒ]ï`Uò)‚¯vXWÎïûììq»Ùc°O×ê§eÛ\{pÁÃ%6äZbÉ›ýîw¸à™õ;j˜·û¯ïw¸à™õ;ÒTÂÙ¶ð“ÝŸˆNÓ6>M£ƒåðäw‰$þ C£ýMót0Ëæ~õÜl# ËÐ )ró3»1òI×Ç9½öØ$.œóS7z»æS|¹¤á¿hÀg¸_áØ
+çÝ®°q«‚ëÊÙºWáÌöÁ¼Máà&…í{ìÉïPXß °¾?ÁnOX] '¸ƒ{Æ[Ú S§²îW×%d»è&8‰öíïJØIˆ®*°Û·Ÿj,†ÐRŸ;¸ý¬Ý}Ö.G¯FàðW }åëÎôZŒuÝ;¹C\1Àu–+0°U/D˜(ž•¯Cˆ
+-_…
+­ë4Ú®Lг´¿œ-”ÛuE—žÚ(TÛY5§Ó-?Ьâ›[•€ÛJé m}[@k!·’hë]l½ÏìbF²Zú¤.¤%ÔU~@–cÚó÷û}d~‹Ä—Ø{·yau‹®__Wí»;Pî‡åŽj7\‡-X~–KQÖ×¢¨ «¿øp¸úÐq÷ùxùáxõa»øP¢Þ½sÈ{.KQs—Ò½.…£ÜÕèu/T‚.LééÄ)½ò1?/¾ ø2àŸÕ€/tâeÀ—_|hÀ¿$3tÉs(X?ƒxý潞G
+è_öàÓŒMÛ%(¨E²{ÍŸõVó}vÏ{«9ÞŽø¬·š—ìž÷Vóxïž÷Vó%»ç½Õã5ºÕü™âxÎŽAyö –G„¸¸ #a5੸™Ûé°›Kš–:«£‰º2ªûçhî蟎––>ÝÜÙ]oÏkîÜŽ«¶Qž’›;þ§ ZmÍõ¿<GsÃ=Šaë;ûQÊÁEŒí2ÆñJFºHÅÙÍŒáèMUGîdÚ¾HɽéØHǪÕ-%³}ÃQŸPp¾¿y4TþX—íèö:U·Çþpñ–x,XÝjXIu?w]]÷±æzîJVq–­–ÂÕ­nµ³ÂÕÓFáê±lõX²úÈ%8ÎÊS÷Ũ…5¦Ç"Ñ\©˜CùôÊ
+òsñÇñjõärúüüý!Þ#ûÌȪAˆ”(QŠvï5p”
+¾Z'—oè*(Úg+ÄãyaÑã¾äñfÍ9÷ø¢sT¿ø°êÜAåõ±îz«ºN5×­à:—[¿•Rë÷Rf}¹ ®KÜãÊêXTýžÊ©{ÂÍHØXÿn ãîÉfB«@ˆ” unèÎ$B’{B Oˆië íö-íïíèL{hÓ2mÔ mºÎa;Nß¿{ä&Þƒf—ñº3îç=u[ïºEwXýtû2߃väG/ý=u áé\¬UÜ—‚ÀîO¶µßÕbÕH™’·6ºRÂFÕTJÚè«ÞF‹•:7š­´¬Qw¥m&úÓ¥µåPîÒ8Žr¡VíLVccÊÚºÛÿ” 6Þ¨,Ó˜hGô•{µ»…²ñkeâµ+»oB€ŠMX`骿Äl¼¬í˜gûðê¶Ã;‰ˆ/¸Ñѽj‡·i¯EЃ;ÝarÚ0}wÐ}þöãš×ü‚¤õ •ƒD× »m¾ö±Þ”²¾qÅ.iaºuæ%ç¶Ë|èöó=_0ö¬íOyÀ1¤ë™¬2ŽÐ3Ze\Ëx«Œ[M~ªUÆu·^l•¡-9ªEÖ½\¨Ü¬>éθ7u­]¬5Œ¡„¤ÛÈÙ=~{:¿VnV=V;rG{:–/³|dîì:Öz¥•ص‡íÒC½òðäU˜ ^næå~C¾Ûnq_'ò€díUÎù¿­\Ýü…½ì5Ê]½Ä5ÊZµ³:Ë­¤«^®2‰ª¦zAª¡L|rìÔ M£ÀÔ‹J½¤âm'¹A,E¢NÔ
+ ,±²†È+Ä౞¡— 8ã]Ä™DâW½ Ê¥œ7r1ç­0xe+,ªêø³iD=šþ/B¤[Ýp½-©/›-l57$öÅ•ž|ØÖI‚½&MÍu¿^FÓíuÛ65Èn›ƒ,j—ö9xÖ×Ù# \“È‘€öW¯”™^¡9TlÚ‘km$û·nÅF¦±–G†³Æ›U5ŠõÏÈêÖ¬pd”C½æ^W:Ô™úƒ¨?õ:VäÞˆ“?²CÛ;ub×Ü‘í;²•C;(HÁE)¶¶÷ØFßrkk¦¨ï¶cõ:†Â§°äƪЇµLŽý¯²r´ÀÌFµoÈ_œ¤/NÒM'éc8øNyé¶Y–´u^ýé,ûS÷ÒÜfþñtüÃvÀäû_·¥ƒu
+ÿV›‰!C¦ÿV;é:†wDH’2/q/òŸ¦ÈÍc|æˆ:Ý%™rg<GñGµyälʉq¤ ¡RbjK÷Ž3ûÓØ¨6uð ð
+~&éyáÙMó¬kèc§Îê.iÂu™S]UP§ÓÆšú!gø’ÇV)†5dMœý<ax¢÷Ób¾¶+u–ŸÒøÛy¤/rí‹\û"׾ȵ/rí‹\û,rS/#½ øßnÀïÊ‹wåÅ»òâ]yñ®¼xW~@ïÊK,ØK,ØK,Ø©¡^bÁ^bÁ^bÁ.ˆ{ÉPyÉPyÉPyÉPyÉPyÉPyd†Ê‹KäÅ%òRáyÚK=„—z/õ^ê!4ýôϨ¿þ`Ñ??füÒƒõãÅ‘ýÑpm%訾ÇÇ|ù­/ß]EñsGDFµ%þ‰Ô5îˆÂ‰U¤ÙDVöý$·l¹vGm¿—©©ð0ù¥Ê¼ ó’û²¼ŸçÂ?<G˜/ÍèÁjä½=ݬç2Ë )ó¨ŒÙDÌ~æâýk¾¿æ÷C»Ì=}ïDß<wk¬ëk«+_\™>ÐøÁ²Å`ô]럵Waô:È"9sšéN=ÖQßùéÛkÆ›‘z8J1¾ÀJ,|¼à…ÀNÜArÉCt}, ]xžQ³a":ƒ’Qã‰KH.yˆßRaµ"Mk©‰X¦©Ò ëÇ®!¹ä!~‹ŸJ¤b€u|¯ÈìC.e“{œÕûitdóOwø.vt;E ›Û!*ØÈpG6",@Ò‡ôA$“ëгGÐE$íÑ´GT#=NN Ú{kcooì-ŽÍæØ[‡“ÞòØÙëcoì-½ r)Á™Ò ‘d.n–ÈÞÙ[#{{dg‘tƒQ²7KšaZ³Lö¶É»Ûûfb TkSËÒ˜‘Øp4î­†éîjˆÐÞéËþ*ÒV"´ÞãFŠV»l‘CÆc”.­wZ-˺×j]^í¶3#ójÃÑÔLû}¸ãjoÖ=W›3íºë¶]L϶ñj~^ï<ïýí½Ú¡i÷]·ýjŽ>D
+7Ü(p£¿ð¹G'Ý:î|¿Îynw¾_ç<·Ž;߯sž[Çï×ÙrëŒòTŽIF)×Túdž*Å4§pDï¸è©QÃ9ú“ºì9O=M*™7¥’¹»2ëçîÒ“‡²KÜ¥ &‘]Äy—j²¶‘€r<ñäÁmîÀôp;ùã.Î_y ¹Mð<ŽÍ<é¢ÙôМôѸM7ÍÊQsÓûjzoÍÞk³³ûlÖ—S©Û¦9nÔuS6®­™Øƒã̉Óß[îvæôw×°K‡:·ÄѱSˆI&~ƒ'N:É}6w$=íÈÙsKwùTqù Ó'[äúYÈýãICëÛ½#?Ðù‚ö³qkv‚s­‹˜OIu¨Ø‹=¯ÅÂ>`vÊÕs™?æbGÊ£ü'Ýç'׸ÓÙ6Ûw¶Ÿj§ósÎÏÖÙ .~lÛLø¹¨½äļ xê¡1Sf«fO'¹ÝéŸ|ù$ƒßˆ<xÿÜ7w2hpÝN‰G–¥»-YK÷Ý
+6š;*ý­½A‡-¼¨YŽjÖC¹:GóuNå츖¶³ÑŽÇˆÍuÛ)<Clæù™<é÷;Î>sðÄqä¸|sBh:±Ç“}žéçeÀÿâöº>Vwg·Ã̲Owg·ÛÃæ¶€«p‹SmH I”[mnu¬­²ÜA:±Ąã­ÿè¶ÚºØ„„Žw«¶ÛU›žÚnXmš*…Žºª“DTV«)«‹ÈŠ¿­«6Mµ˜¦Jzª“EQR‰ÚÞI¬âNtSÕK³i¤ÁôÐYTOU5wÎb­«G'‹f^,üx²xá½…iÝXDQ¶È¡à,>h¶( ;CÝ[ãb{Mõ*5Š
+®½Øz‘qέ½Øº_¦ÂëV QÆtzŽFéãQ4"¥Å¤hÌQ$×c¶Ð =Òè3tG©¼; sò­ÂñH(ÿrÔ
+Ç%er~rü
+G(ÝH¤ÊÆ;Gᯜ&­)ÌjMjdU¨[¾IËg ÖHºqürɶ–‡V†VÚMßœüãØÏFEkû­æ6€wOiõ‡çlîy‡û3Píè@¿
+§ÿx®ö2àÉ®gÐ’D76¶±©m1éÍlÍÈ&&6g"ÊÑLŽ#ÔäPV!zâV1ÒÇâhGzÒGI[œ4S·e8JQ¶iÊÑ *®‘•N¼Qª²EWFÊ’Ê´Å­ˆË­UãÚЗ-
+s@eÜϺ›®Å¶¦9øHz"B9Û6q:Lmþ¸³{>@å©Ý3µ?«- DIHÃ-Þ“~ƒòH#$‹°$RI³aºÈFÉâlj¬ÐPÊÒ ¤H#> u`IÉ“&ª¾¨Å]ííµÝå»GŒÖöÞÞ¾¸­0yaiwC*LK†‰¢±Âߘ$Oòù ÔÙ2“YÞ[u)­+ÕªJµŠR}ò^—;é8)¯«$uª–T´Ê†c=©Òë8HS"ú Áñçnh£üAnˆÚr¤¯g³YÎÏ­ªW×âñæNýñdKÛÍûÃcÛŸÕ€Íû>»éØ?ÎËTÀüêG%+ŒÞ~3B;Qºš—«8_O@t¸¿hÿëái¾FèÕ®ÓÃðÜvñù £FŸ®üt½øš7ž_÷°çs^ÄÖà§¾jÚ´|–Яì¬'þ±e=ú™V»d´M£§ê²ÜÕ³gg¢L×°/'ÒíÏ~ôI+¶Ö; ÷«Lñ;)¯×²ÅÙhMÉô¦]Éô- þ ~p˜5gLÅ^ö`¦M‡èßWÌnõ²ËàÞì+eùò²³¹:-aWÜ6Ó'å¬o„¸QÙYe¯ƒHØYé9VÂòVoÎñ†–¤=ç9OáÐl߯=§7›çPªô\æ’N§¦ŸÓ›ÇÅpW/=ç%Íy»–ÉýžtºâÖéŠÃéêDo׆JA“\,ºé„DN¦¡®]×¢©ö¹!ë,u>†¥IÐC"ìž@;”Åöƒ`8Їa¨qÄÄ<Ζɴøª›.ÆJó’|rX+¶š‡ˆ+lðŽžÝYü¬ÆÄj˜«Ÿ×hT 0ÕŠ¡$RKÄ禼´bÝAI@2,exÛO›ØÜDéíJ䲸î8‚mÔ—}dÔ›,ÙK¹Ë>È¢³¨¡†¶×>».àb ¹ƒ.*m¹˜Y>Ø<âò˜î3&ÒŒ×âÈ­ã¥6Ã=5òžášT‡h& P7Mru¸ÜcÂÓG.P€åDôÈÃT9Å£æ«nÀVÕ|Š'<âY®Àó
+ôœO)pÙAa?UÑä’‡ø-Þ£‹zœÎÎG˜Š®s-Té÷xijüÎ ;ƒ!K&Íþñv{Ùf)çugÙ”“ÓÈYôಎ.5ÿ a·Cßú0›Ã
+ˆhÆB †X 5 ƒ 2©úÅÊ
+ÊÚ+\Ý”}h*bO¤ž =’ùÞPÈ^É»å"qw":¤ý±á¸MÒ‘;ä…Î$¨ÁxæÕè%<7tÀâÄø×M»3{Ÿ|ãTpSaM+Ä7óÝÞõ•á;ƒ\ol[׃'ÿ‰ƒ?(ïºzð;ŠVéN‡W ¹KÃíBn3éôýA'Ó‹ÜÅ÷©(ù@Êôe%kŸ©Üî‰L¥.géê ÿŸáïØKDW¤x.%bè1Õh®±æó¸!Ó‡qG”¬ˆw´÷z$ñ‰îŒ˜y“Z×~$h±†b×Çù锇„­O©ìOò}H¨“ç‡Æ!ãù8ô{t^×JFž¼=ãðîŒÑÓa.·òpú5VþŒ•ãÀ]á6§ýù •Ö\÷K=ÒnN¶UP—;íµqíÔ9Í­À“›{¾¡þr<R¥M“Íûú{{.ãc•ÚZ ¾V…¯pî &gíìv…{«Ç7Û
+Áê.jý6­Í×ߨHEËí2Åî*E¿ºEñØŠ7(öòÁDnFòÁH|P¾¶•°•2«ÎòdrWqµHÖLµÜ™Ë ÑÖ_ˆÖYrœ$×´¶NÄ^× 8L‚
+~;ùÇö 'áh;úãä¿é¬–nî
+«Â7üÐXv±¿8j¼6Tì«sø´»ÕûHkª?rñ-ˆÇ.Aäo»9(¿¸¾Qk}“ÖúÊfY—ávÍc— Ÿº^¸ùyF'ÚÿOºØ¶lû›Úˆu9ÔÙ§~´
+uç_ùzìºW«8¾¦9#NøR.Pò0¢zšc*験ÿ²‡„*”R‰³ü!ä¸öó2ºõ/{HßR§iZ‘n¹øøã*—=Ä7‡Åì,ؼ4u—sñ?Ò¶ÿ¢§žFï6Kd?ÜÈspSÔ3àúrª¡¹Ó¾¼=mÀ Ïy«ø„Öi¼Êèô…7ÝÅ(faWë:®Üñ‹Q®¼Q™£X‰(“ŒD.’héã—ÞŒW£´dN¹%nÜŒâìj”õµ7êÑÜiüa—ÒÙ.;ËQpvW¾몟¾ŒvófÖU\­U-?UJ|q0ZŒÆZ©a c«
+¬cÖ±ôäªøkÿi®‹vìc[ï´Æª*ãŸh%âUþ°{Þ:ĽÛüYêßLŽ’pLjÏ)Áh‘©),™„Ø%fJF‚…˜NF|^þ,×oÂgæM –®’ÚVsÝä6gõ~—©›\¦væ¬(Uɼ,â4S[µÚ{s—Yº0  æ6®<<¼ôðlsÀ8Ìò†2¹ …Bó‘<‹Ý¬1“„{/÷kÜQ¨ù^æ­åa4_º:©>Ñ~r×ÒA{ÐAæV¿â¤>T†\Õ†ÔêÅ‚/tãμ;nÑg Ô¡­XàV¿’ ¾}Ã}»?EoPá;Tø¦û{"{{*?¹£ŒºÃ”âhå³Ü«’Ô+bNÛVûg} õ‘2¼nõk§”ÐÂívz¨Ü!JÑ™¿!Òwù–EŽ V%ûâKÝ]`U_ŠUk/Yå%^ÙS·Ôœ¸£f(ëIE=»N–Z¿¤隢̓Ýý”<ÒÀÓâ¥pY\ºk*‡Ì½ Ÿ“xK¢"v hbA›ç™Käa*îZAõ,ûž?ÏÇ}݉xÙÌI,aìû ±–"z9¢»¶“e‰–£7^ëx(厾Ùf Ë¡îÝžÙÃKu[XG åØ[äÝ2t¿òËnyfã†gvðÍjéTwÒ;{×ÃzhG/­WmËÚöÕŽÞÚCŸíÊsëNºp;gîÍ|ºnÃÍû°Ó÷DsøˆÏjîi¿ ¸=à%ÕD¢U¹“j"qÑÈZ.(åùiNàÚˆW˪Âý*I ªŠÐ)Cõnw‘rp"ªVƒje hÞ@±ê:®v–J„š@ÐW‘4×
+‹lF×ú•0ÝÄ貊²‰Ù "ñp¸ÝÕ3Vñɤ]*©ëv=Ë&=)®ŠºU1’uØîáÏq’î²@þ!
+x³¹‡:\ÚÜ‘²)nîy‡ûq|("_¨UX]Hth‰E\½,v©Ewz§w8ˆÉ׺ª©«EÔ¢ò‘~ŒF†ÁÄàN^°¦#=%Ù¼¨¹f'¥¿Ì·'*Z"µ/Žš­—Ö¿Ò"|@_\Wþt¶ðý¥ áoÄ&wULo¬”ÑíX€Ô‰•I]*“‘ F†T¯oä¨i÷±ãÖ¤¹5¿WôeS×?ÐöÝQ%ÿtXûÑ$ ×]1ôÀÅf4!¦nó*™'´— ´YqO²F«ÍÌÕT÷s_™k˜µúÌVÿCLšýòøõ"}õÃãæK2`ºîŽ„“÷é}Ø›ÊEp9§òáºîa«¦ºª©Þ¯«©«zx²æáXòÐT<<]ïp»¦jWêÐQéðP9QÜÐW×ðüB†Ç
+þhå
+2óø Ð-0y9UìÒ_‚7· S›å|J¼ ݵ\7bs)v=W3H´ë¤Ç+¥5ÔqÖË¥Ñ5Iäã½Å?jʵ É?íêu»qkªuF«Ç»G3¥_¿x:%zuKôC©Ïgf.»ãÉËNÖµ<ó1¿Ü2Ë7"„Ö1BÃâ¸ë²yÏäCKq˜çýô>£Q––3Ò±Úµí|/–wÃÍízwûn¸¿½vw¸g³òQR+c—(ÒÛ%×·þ¼Ew« õêÖy3šdw<qƘg›qVRÊz»,d·ŠuÁÛAÛwœ—¬ )=TF©N£ºÐ·Š5Ø-õ¸IG\çnÛ<¶*‰ƒˆƒkò€`žøpìI”%V¾å¨¦€77aZ°T*̘AÈ'¼6x–ËÙšÙ¹_pšà·ÿùÓ/¿¼ÿôõý§ß½zÕñÅþîï¿Å¿,ü—_¾ùúõÝ—OÀ0ïß÷û+ù8åðëUÎ×ÑÏñj™¯GšcĘÝ|UÓuÆÌ»¿úí÷øW,_LWÿüGúõÿþ/
+Wðþ÷×7_¾îß¿ýúþó§7_þxõ3
+xwY°Y—xgXæ~Ú
+ëæÝžã‰ÛØ?ÈÌ1R“mhÜ×6ù*ÃŒÂìùK°~ë4Á‚çkÇðjNË5üq¾*0¥)'ú.øßeæ±GËu­+£/¾
+–깿f…ƒ¸zÀXÃU†ýO Ô¯Ä̬ŒÑ€Ñ6Uâ0|%_Ž…`îø•9WúÊçG™yZ®—Zˆ4¥‚F“QY^c•ökRºžR]F´©óuÑð¾–žãi]ç V'…e\ØyÆûÒàd+;ÃQð„Óº´v%¥Ö¥]ãt
+0—)SxV]Ž)!üü†æ2L.œ@ñ›'$žó5HI¹AÞÒ'¢b7÷ý€9ôWu¹®%ÚÑá¤*`ÏÕzÍU…É
+S,yֆ
+R@gk¦³‚P`‡‰¡ˆ5Á{4’dÏ£lJhZ `+Ó´ôýra¦@ô±d¢üœ
+SÎŽãÌзg쀵.Þ`0?”aã„—®ž}ñ7²nÔ”§Dd KÇ9DÞ‰£ãû2ð(eèð;3ÝŽŸ¬.ÀQ•ãïY~Gn DØ9`vªð­ÊÍaÍf^Œ™ëï=/7˜°òõÖžÉÉáóC)ËÀÈ FIßGQ´é»ωÜú€ØaR2‚þÖ1Uƒ 組5Û[:þm3Ñ>:Ocõ-?ó)&ØÖžwLXw‚ùÅ\ïœÃÓ‘ugNóÒ87
+?I@– ù16¾
+ø8%@æ ×¢Ÿ°ªˆÛ SJHæž_vXñ\T%*¯4A,3OÄ?-ƒÅ™"ïùT@œñÈŠ»ßY¹€õd¢K°YDT™0Á÷€å¨Ì,ÂP’¼JX¾¢-~¹¸%[|
+岈ꄚã¬ËD6P?=šÊüõŒb!¬GõÀ2õwz*S2®iÚª
+1X÷´A%’€"qI[T@õwÀ¼œþ]8ÒHþ×»w®Ü'Ó—*z¾€ôŠÂ'<•Þ“I¯
+Œ OYXWGõAÂ#ƒ¥Â€µÇ¦T$?Åy û¸ Ò,¡üú{Oü &ô¿Ì°ˆ¨˜z±…‘׳
+ð;^Of¿"Ý(a`
+h © JpžÈBe\A﩯Á„+Ø BôõE[йh™ªòýœž/Ø7+chK%œá`ížÛj×\.Ë5¦ÓŒ—H”wp·¼Â‰ À¢ÞR"ZžÝ‘EÓZ`ôgv˜šØ& S£‡­\×Y<4±¾(øòã¸T •=¨&ïÑØ¡^ 
+|@ÖK]@ h. ô. «î»ýY†î]@ð†D§º€`A‰. š žF¹Á<@òûà
+ìOé?  ²ëY^zMç ê§øC8~z¢öüÆ@Ô˜¯üCmù?ûÐ)õÜC§Ô3óE ïÐIý¬C­Ð=çPƒ2õ/(KP÷BÏ7Ô» 0q.(PçBÏ3Ôq <Ã~ïx†Á„g¨s¡gê\P˜:„¨o¡ãêZz”[¨g¡gêYP^¡žeêYè9…z„Q¨cù„ºzF¡nåê2P6a¿w\Â`Â$Öz¡[¡gÊ®æ|%ýjòïùƒº¬¸ l ý½#ך®c(ѷ༎3è\¬ÎUÇX}ÏéZ蹂”)¨…]¹‚á{¦ †zå jÈr¯¶þŽ#¨;@@ê-P~ ¿÷´VaÊ Ì-!dÞ|/0GëÃáæ&éùR„è‚(#X/šnÌ?º;Ž.½ûôD>*<µð_n¿|ÿÝïuËÞðÓOlüïŸÿÓ}mˆ;ÝŽ:¥˜ÓWx"pš<!'ÊWK­éSÏ:ðë §+i!ØFØ‚µÇ?9Œ9å9Íp@»>¨EG 
+´D`ŠAËé"}g4$2:I}EB FE-3#É«<
+~:ÅÃßè ûÄ=/BÅ@)Aa™ß0EB[½¢­À'¦ € ™&ƒT*™aFí@|3Ñ}T<ðô<ö­yéÀ<(œ¦ À¤@XL .:4<{Y¤MB|^tV…4Ð> ¾úH˜x
+r&ŠÆì\ps9@ÁC\=ÀèäGAÅðh͘fYèËAlw…}‚¹và×+p
+WÁÄ Æ…eÀîèÈ*Y$,
+¯ V– “%áµ€a¶y\ öÛëFDŒyf ¨ÛD‹
+“¿™Lî³âëÄ32ŽÉw«Í FݰEv±]´æ^YO™AÆP€¢"y
+úŠ—MDà¬'¡Ì*¿/’¤ÌŸ‹@ú$£Â ȲÎ`ÉÙ¨KT<yt™M¨†¾°`<èÊšÿ ä¾¢–^€‹ž$ŽZõ„`%5 ø$¨‹uñ±&½…M䡜vu3«pÞ ^o` V 0à’½½®ÀÒ1Ì‚ôûµ€É%Óšu„T ©L–ôyª<ÀÃS‹L^
+ÖàBstNU(›¸๨@¸ÀÀX«ÌžbÜèõg£›8'£±6y˜Ñœt
+^øp` }KT·›®'91¥I¢'¹œ#Æ…Ð¥¹€1òTé10¡Ù&à1_˜K"8³ Àlâ¸mCì¨tA¢¡ãÒ:Ë©—µAj¨-&æ`_›K ’—¯
+Á}EŽprr°Ä’ìÄ &ñ
+'Ä6ÚÍ@©VAŒÊZªrÅʾ^²/ŸH¤àlÅ|)%%æ¨,ÉØ*óuŒ%XÔbêÅ'&©`\‚ƒ ðªF}^Z@cnd‹¨¾ |ÓlÏÏu6‹”׸…àXŸ fSª¡€hC.¤2¯\bUîQ5Æ‹²Ðw-lJ šQȼº†u1FNŒ‡0Ù†¤QFQvaXX“ð@ÑB'°` ³ ·CUXL]ï%‚«}nÍ:@6&‰²(}^qkV6T/:¯™ùŽš”š`íT›Ê§e¶8œÁlHφuÊ*mˆŽÀYçŠåöm€<)ΪÑ#`vD¡;ø Y¥Ö˜Tâ
+˜wªÂe0pž&åª Î@µd`2Þâm€4-vì¢ÀÉÂ|<ø|4eAƒE×
+H@-^
+¹° ŽÀ%k“='Ó¥
+>Q¹ •TÑ»1¹2í+® ªÐò2búRéÏ©ŒZÕ\E@ ID@m€yª+ ÅŠq€jÓòJ¬›].dÎb•£ãu²"#;dCI@ñÒd?ÔjôùdL¤æ¢HÆ…‰7Ê›ÈMôÍ\—¯Í߃PóˆÎ¸ñ\›l19¿[Âf6 4Ô—ÑG
+Ûø0©v TË`aSP¤â-Eo`3Ô$Á2 EÖ))r«LŠàÔÐ[ê¡Ù;Lå׉'&Ç£J•V8Õ°D`e¯„2:¬:Ù2¥
+0¬“ÑD/ )ʵ± ,‰â'ý0!µÀó£2"ŽÜÑ{Ó6LŒ%­t‡íO
+f³D m˜? ã¸L&Õaƒ8¼(ŸR-v¸j<Ýë®kád_NMÉ*ŒFÌk.‚ï°Øü­ÑÄéØÔå&3˜tgYXÄ¾Þ Ð˾Fö|ñ¾š »¯¬¢00æÙGÕAs5SŒ‘èYº·w½h‡ÈíœPA1Ù€©T­9ð)ˆèŽÏ:jÒÓà¢]•pbëdÔtf^%t q€ò—x\á©1))æ:޹[Xr ó
+%/­ø•
+S6*
+ª§ûͳÍE9±Çñ>y•xØÓbþ§Òï
+\°¥Í•”I@2ò
+¿6š‚iOzxÂÄ&$+šOß›lbqÑ}(& $1Að—IÂXBAcxQ`j(·"eË|CZÓ}XÓLÔ&OïZVR^"½Ë€UçU:1McRáhP™—¿ÒŽ‹ldêŸ÷I¿+‹I>!PÖ¢šj&Þ L*Ò©s­*$j6–³2¢Ð!xáÚ°rnØ> À&>‰J•}ñ"»¦6ƒNgGÖË+[¹ôw1{Ækím!v&ºÁȰÕÂót¾•#´
+[½@߬–r/F!
+
+;œtgj4½ÐB¤ ¯P¨Ùs`käˆÈR¦›Y”q³Ø±iÒ€ØÙì³ð’Øqf°z" Ÿ1ÐÏÆâʬ4“X4“PÕ%…}%F«é«¬fÊA’¤4on” œm©kR;+8m›ATÛÕœôyµ6¶
+H¸ˆÕ~bþŽØjÎA‘]#Kê¼²“EœDv¡¾b©„ EFàWäw­â@ˆ×‡uØEr‰lÜP/¯ª¢Ñb,Ñ!\‚VqR'-Ê\³k¶³HÙ4²Žäz}EÞç†4*顟zÒ#Ì1“E.aA)fÄdTW$zåS’õjd‚¼€—Êá5n0ë–å[)Aã4¯&²ßòµ€éÓå{ŇÀ"ãª^€ãVAó9aÌ„u‘¥ W´ÑÎMR§øbIj±98
+†]Jç(ÆH±Þ×R®ŽŠ=ÝEŠzãŠ%¥‡V­tŒE±#H–¡?ÍuM>E YCèò|×Ãô0‡–$96ãµ€ƒd^
+׎a1²âs
+Ò5æh1„Á<xsKeœäÁ€~,5¢¥T"pÒ5ô>hUMîœa­øÉHë%kh(¬Ô1)…6ɇ£qÐ8T,öPµT%@ D”….ñëXdföÁ|³x·1ƒ…ìOÙ²¨B(>e“pƒ%L(«ÌO„¨In˜]ñ.¬"ª|`cÄkVû?ì'Ý«Ñ&do– Ç)›÷ t5^V²€UŒ™f.ôÊ›CiP\Gb·‚¥Y
+÷—ú:hî šNôGz.Z¢Ê¹ùˆÀ1Y¥$º— °Å'úp;žZFÎ"£‘ˆ~Tp´Œ‰P'û¯'ËÔ50fjNV!É V³t³ß¶@®l7“{$7«²æ«3De
+GѨñªZòê ôÓ‚©…¯*ø¨à¬î›Dèâ#¼˜£ÀkÌÁ2)%=÷µ¼ÐwKA–ÉÎΛ£ºdrÔÐ!Ê`z%Ž"Ÿ¨œÁª9XäÓQ÷•ƹN`g3S¥LâRtf*ÅX$Ú,œwÑJ¤5\ “°\<vL©›A˜Þj¼ÊzÛ|¿L ƒ Šôf¾cPÀ³N,OcàƒzLÕ¹‡z¼D1’ÇT5þæÉ4ŶrªGáØž !
+«×ë¼úp)1‡0ŸÕ <™Æ_'ŠÐŒA4±E$« Ö V‰¡$I<YUù[–?Ôô½š• åª…™hÜGÇj©(phš`+­@‚×ÛƒÈè¾Xa¯Ì2ÔG+ioyœ¾t1hZ2¡˜Ù²O…Åa“:–')}Ÿ´ªî¼Ã Èa¹TË«#Áü£‚«ÕŽÀ\…×
+.j¹#‹ÏëíA¸èßKúÅ?Ñê’xb%¾¡¥×(V5’6Èt×0*7;«SVP©4|Fóba¼mÕ…×uVçÃ:•Ý€ë&䢂–? 1^Õø‚¦’ëø ÒÂ`¥ò59 ¼Õb—ÝBÔÎךú2Ëc ×ù°4f7Ç#ຠ9¯†&@”Ç ²—Ã
+(¤_›Ô·°pÑ©V_sXdÚô°cuX[VSFXÃ4ÃuêkŒëÇL¦"@•Kú¯~½ùQ*~Ƨч˜,zF¶®A(²fι8„Ô–ÏÓÆáòž4‘_9Ä78ÙXÈ.¡ýµÄM
+Õó$W1P"-$@‡\²˜'ó¼qþ~®Oê9Óû5"Gwó
+çªQvŠÊÁjÏa<^©ò]Õk†:r~w˜Ižåe”pË@±Gâ.àù}+"](‹&4y%ùfî!|¥Ç6¤šbéͱ»¬ÄbÐPªbœá‘Xî,ÖvTï'=ù”ÅÀ’kUŒ[Ú
+•-j -±KÒÍEñôµUY4ãgÐÂr¤¶AÏø[ a‘ÅÆvõð´B1`PFAÅt
+H–XIÂTÄ®ê‚ÕË«FψC¹$NÑÇU*H€}c•èJ^Ý–*=)Ð[˜B‘q)ކñd7½_+Ïr÷ úU¢Õdð$1ÏIÍf%µ¸óÔ}.é
+T‹ Ep{¬PÖÖ MÄÕE“\¤:;®´äØcG¹'Kâiõù†TZž…R.Ÿ@ËeÙ@Ë©¬®€Ø+}µ\f y½*gˇ’JÓtœÍ`êÎpß ÊÕÕ.ÕZ,©L×0qµ=“ì9r«´zé,JG5ýfÒÀrœº1m
+"e‡0ž Ex¥zx‘2ȦœOz^»¤Î1P®ÌD;@ÍYé…mŸg—ƒ}Õ* Rã§´+”c76Jà9*kˆä§t(qýW[Æ‘·Ïb_OÏ`@[ïÇ¢©i+š?n@3›ñ`@»Ì8•¢\iëÒMK>£…ЦF>Vó™)d0’]dÈKÑnÜ›9vÚ‡`™j^ëÔÞÌPº5/^`üÃuˆšÚhvÐÄ  kÕèñEÐî6U¹¤•t½?Ð'T%ÕÛ
+Ô—¡+C_ÉÝÀ›‹¹žë¹
+´o"  `ŸÕõ¥ä@[k‰Þׯ¿•õëzΑ¼Ë·oÀ¥{0eð«`"ª ëè€Å´”¼dË$×ù'»S‚ÒØk\A××îGAIBdÔ8«V:@4·ë+ñ½%YÁ•X¦­Èöúœ´ðCSZ8wvÚ°^e쮯$Ó“ìkå$ü”z ™ä'µ_t}PÊó°jׯvàÌq œæ¤’ŸdT¢–Ãñƒ
+4å‡smǾM"/ê}´Œ»u€%ùå ï2W5`ÈãèSGîÍÙ“Fžt}SåÈI–h›·Eª¤*ð­J*j }›L­Ñ÷²pm
+Tµ¬rOßw^Ûj¦Ì
+ŒŸµwZ%MÇè†*6âõíâÛ€d>\ Pg –€^ÒZÉW%ʃ˜Œ©V¬±T¬ñ÷ÿ³÷fËq$W¶èàòEfÒ±>„»G×’2ÝSL%U«íÚ5
+DUá†: H‰ýõ××Úî™ÈH’
+¨Ì—ÌQ]4ËçPø ‚¤}¯E~OýÒ'ž7}>§[Þc|Ès£ì¨¾†©)÷^Âc—ú€ +v© t¹còÜ ûi8’v¶ÉNªœacZˆRXÄ›J±3½—3.†~Ô¥Qªpy­É|ôaþÂhh3ØY¬K-&WÓÑÙsB‚1¢E–1éÕ`3µéÊìa@Ú¨î »&W©çñ£²8û›ÞÞÓƒŸ·‘˜ä ìÏ™D1º6 U™;‰_~€ê«ZàPKˆvÄÁ½[ú¬$ìé„ä.®íÃ%ØxMÎÍÎ
++»®Üß“a3$w}Ì á䮋$a~€aéZd§ÐT*€6–š`!ˆI,!Ànpà¡/„9^•ØýÇ×f¥/±¤…AÊ4ä ây@BÎΈœsvÉÝ A¹Ri‘z_iÑá^(°—…¤#ÈĽw½O†‡¦ûa̯JÂÜ„”2Y^;ò
+0€³‹Îη'¢4À¨6vºÖglèC|êlÆRû—”@Wp¦´Ëû‘!‘2t
+¡<x9e±Éhnï„SDö'Õçt‰U> û¸kJ±-®÷¤ZÞÎQSV™â íçqJÑ}æ/i@òÇf“”âƒÉB™Ç`ÕèôÒµJ’(Ì6áHèUV ±é¹–Â@fdÑÈ´½þBßSnBæ/2¼aÞ£{e¥ÙRo/Å™A<}ç=]`Myxæ¤Àgy-z£?SÓÔ,„ZªÉq#Ñ”q4½']NŽ$Ì›‰Ò#š¯Å—<°Í
+±êY»}Šƒs¬š¬amÓN„k–7ø•SÍ9ÏltôØæB§î+¯‚|ÎuBДf¶’XkOƒZÈJ¿9~Lì‰pÒ¼ f ‚í©ÒÑ©z4Y×{¶ŒD[ÒyØIà%%
+fÆ|„Œ¯¦Îd³°ígráM…¶Í[IÓ'ÿ¤jô!Õ=Xšþ С:­fÛ´@Zù°rm-žËí< “+R—{¼ÍÇ×f(Çè 9 ³0®Z\;âô
+7ÒñÀøß,ëƒ&)?uR-ÏÄVõ—úœþ<È’)šdy‚µ e1\éÄc"[ï•;ÉCÒýüJEcý
+7#„’˜…ùIa_ÛIgqš’ B¦p(@ºÈ·‘Šš˜¾ç§hó¹™S×gA"¥tH¡é!iôŽæKSM.\§W„$Ë÷â|h €¬bsi£³@hÊk•ÀbÊ㥵‚œ”ûKqÞ±X¶KíË£T•v…°ŸÅb!¯í„N„Ep¼_¥Bœø<ÓÜlóùÐ¯î¡ Fû€ÉiLãkn5{ÑÇÂD†™0ˆûü‘Rè“~±¼¼`®s'¦ÚŒX†ÌñbµJ‹&cÍGÂ0xe1B“6w¢üc-ý4pR8 ó<Ns¶¼¶Í›1B%®7ýu«’Ÿnº¼›%$hr>—k/tM2©ŒOigÙÓ’ü‹X5mïJÎ"ô—î–…NÜxKŽ%¬°\’°÷ A‘Éü/!ç-fa>Rõ˜òÚÞýá‡*PƒŸ…½£@Æãk£¶oD˜C,#á ›b7"†IŸÕŠûq|¤%¡œ‰=.»¸¾CZùL-eCˆ§”Ï.Â`²œ™zþÒÜ…Ìd•vù4 l.§Æú-ª]¶óÈópû”€úÀºÍt¾Éˆë§æµè{ržþÚ~÷'µAçV„˜þLéÅ}¡n˜—™3ÂdÉ•³3‰N~@ÛªÄ+Ó—Ñ4=ņíÓå ¡)ìÑ8G÷‡½ ñ,Óo'é`ÎÂ~;q9¢Õ_Ûo…ÿ~$äšYÚN
+ÄR0Ÿ
+¡#Y[>ViH°$–Vά"mÛ¹áàQ‹sã>†R$ a¤2Y©ˆD®u&_ÛkîAèÁa~À@âR\;ûL‚RX˜J¥8WY@Ç$º•v°tJáà‰M‚C8¤âuý äv•¹ŒøåW¯ÍÁ¼¶çLJ°Ó¥0c—¯í„[„ 28–N)¶‰ru¤CNeòêǃe±s}ßæ¯µ}ÕÛ¦.mqmW¶9#ÝJ²|»ÑaåÊ´EÍ¬Èøùýí½Ø e¨sfu„]/Gll"¡}ª5+½ª ¥°ÿ‚¶Y½vð÷÷9Mˆîv¥0? 󈌮ÍÌW©þβôü€BœŽ‹.¦|í.e ÐÿXœ°R臲áù[GJHŸhS\ Ê ë˜j,ÂŒ…å÷I9ãksÞ`‘·P
+o†$ÅAÜ'|7=CHŸq_ŠËv,Öý›T/˨áCGÙñMO£7º6û‚
+ C“ËšdaVç3Ƥ¸¶fBÉ‘ÙIýTªl­êÓW qöÐâŽû˜¯mGâÃÒL?@éŒØê†€\ïÑ+ ªgk3}íÎ")v”t
+i>ȆGæòàJby!ãoóÍùtí31
+ÚÁi<,Ò½i ½Ò·}ÆCVa„½:)¥^˰Ð` ]ZS"I~À Î¥y
+EHúž\;Ì"Ë·g†þJßW`,Û2ªöÊ`}Œ®ÍÜë‹0¥Vea~@â_KöE¶*«¸©yædÊѵQ£Hm—ùHÈoÿ€^lS©ÙBGÖ}]ÞBu×RŸ"±éDÎÉHúžŸ<Ñ=…­ä1öîå^Ü¥ˆ5„™+hbFBß'ôR/Y5²3d*Ô^7ÐøN’!"Òõ¤ãQAyÀ­†8%ÿ×?¾
+ƒD•kb/EÁaQe»¨2cTY4ª|Ü5*ãÇ;H•I¤Ê9²–Ÿ¤à2©²žL0¤ÔØTª¼+ë9ZJ:—UÞ— Ž˜:ŸÌóÌ–š£M…ûf‚'§Æ©Seß™bꩲúTù&¸‚ª¼B5¢µ\E¯Q•i‚-©Ê¬Tå`š`kªò:U &Ø¢&˜¥ªTkùª
+n«
+ ÖcÖ»V‡ë~ή¿W• ¬ÊVc«r‘­ç-8ΪlhÓÌiU–µ*[•»­FóVåƒ›àŽ›à™«2Ò­e¯«0ÝÍáÏ«ríUYùª ~U¶¿-`@°F5Xå$\Ë_XpVY'+l‹U^ÆŽ=ßc•r=‰dÉ7¹ÊL9ÁbYe¼¬rcÖy4«œ›uvÎ &Ï*ëg•t’K´Ê;Ze(`3­2ŸV9RëtªæÕ*GëŸk•ûµÊ»–QvÄ>[á©à´à¿­åÖ)uWÉw«,½Œ¾Uöß*Oð§ðÿð½LŬÆ5ä1Wò¯r…ƒ¹ÊÖ<Áì<Á]å‹žà–®òP׫ÇìÖ'+`ÐïrE\òf×¶'ظW™»«ßÓ|àUîð*Ëø#y•½¼Ês^åD¯ò§O0­¯ae¯¸¯g…¯2ÈW¹æ§xé«ö÷“ÝÃz’ï³è_®Š Æý7ÿ•ó¿R`¢’ÀDÕj}‚µµ úºÕ
+ 3ª)”•VJ4Ô‹9ÔË>TëCLÕ’˜_Œb]5‹¢òEµFFYNcЂª•7j%:&ŠyTÊ~TëƒLÔÙ Éšb&Ãû«õQÖÖR)ë®T+´Ô«¹T*¿TkÄLד©Öž¹¿JÍ€ª­W¿Y©”óñ`E<TÕ©Öß©Öê©Öõ™¨
+ù}[ð¬ðœ]%q•­Êž6Á´Vee«°·­åy+(á*Ôq5’¹ݘ¶Î/É&î&ØðÖsìUùøêÌ}5–¿*#`•;°Â3—z ÆI·J^Wá¹Ë<uKô7WÙ¾J•S%Õ™$à©’õÔX}Ö
+¢<8Uº¢%j£+×6våÒô{æTš˜™—z¢˜Uz¦I*§
+íS• ªN&U%žš¢¨ªÒYU©¯jYSlZUâ­5]t^ꯂ$l‚Pl‚|¬ÆRv?ŸÙõÙ:Bµ*ùÚMÛ:J·ý­J7A)W¡Ÿ«ÕÍ µë ðªTy´zU
+¾û¹ú&hýF€U²À%bÁü€* áaáÇ•k rÃ* âeb^±JÄX'm¬<V¨ ë´‘UŠÉ:åqe•䲯†¹–8³
+ÖT¶V-³«š¶”/¶ì?`œ‡öýÁø)kírE\Íp[—77‘cWÍÆ›ÈÜ«fùÕÒ×fö9†Õ\ĵy‹eŽc-r"sr5˲š9»YÏó¼?#t"{´È4­æ¤®Í_-r]«Y±´ÕlÛj^îDo5ß·ž¼.‹¸È8®æ&¯Íc.Rž+©Ñõ$êZºu%/{"‡{"ß»š¾”Ež? ’q¾’þaåÚ2“}5ç}"?~"—¾šu?‘¡_Íæ¯çý¯ã(øªÌkY
+ªŒëyªœ
+Uö…)¦†«C•ÿa-WDÁ+Qe ¸Ÿ¬¢àµ¨2`L°eT™5&88&ø:ªÜU*cH•]¤ÊC2ÉYRå7©2¡L°¦TVê\,÷ó¶T9^Ö3ÇTYf&øhÖp׌xn*Œ8ì9L;UNžµü=×O•¨Ê Tešà%ZÃaTðU™‘Ö²(—jÔLu§Uº§*/Ô$‡T•oªÊLµ–Ū`¼ªpcMðhU9·&عÖ0yX¿ªü`\bUÞ±*CÙZ6³‚ù¬Â‘6Á§6Á½Vei[ÃèV°¿Õxâ&8妸çªLuëHíú»Už¼ N½:ÿ^•©o-«ß˜°Æ8Á+Xá ¬²N0N° Vù×p+Žx«ŒUvÇ*d•3r‚_²ÊEYe­œ`¸¬²aVy3×rlŽø8+Ì,ŸUFеܡUžÑ'i•½t‚é´ÂŠZåO]˵:âe­2¸VÙ^«Ì°²køf¡TåcµÂa;Áw[åÆ`Ñ]˸[°óÖh|«Œ¿Uvà:p•q¸ÊN<Ác<Áy\çG®2)¯c].š«\ÎkxŸGÑ6é æé –ê*ŸõZîëž%»Â¦=Á¼]eé®òyÏáþxÂk„âk¸Ç žò
+Ÿyù¼Ê’^çSŸä^¯Ñ´×èÜkÄï5Šø*—üZÞùu~…Í~’ù¾Ê’_åÓŸàÞ¯òôWý'Øÿ«•ª5ÖÖÕ*¨V5¨V@¨VK¨•U˜*ÀP+ÕP©é0Qÿa¢VDµªÄDŠjµŠj]‹550Fõ2ÊÂË…9–kpTëuT+{¬«RV ©Õ©Ö!©Ö,7¹¯J½fÊDu•‰J,Õª-Õú.kjÁ ucjf¦jÑLÔ­©W¸©WéTΗØÉK«,Æ3`Úʲ=ùʲ¼Ïªl¢PQ5¨Z_h¢QµnQµÂQµRµjRµ¾RµSµnÓD…§‰jPÕÊQÕSSõ¨jµ«ªU®–HèúŒƒ‘%zm™*íjió.XÕ&ø×–¸ÚúXò*¯Û
+ÜǃeqÉWc–«²ÐUëªÜvkyð
+c½Ê®7ÉÄWaí«òûU¸åVÄ]…°n™Ú.SÓ-Ñù\eË|•ú§J´ŽOhL=T%)ºŸÐ¨ ?ªÒ$Õ)•ªôKSDMkIJ¨UÔ­T•‚ªJV5AlU%ÁªÒeMPk­Òpøº².<ðz ‰eÿW¾ªJ¶†RlD?¶JT6AjV'@«1¥­#U+{©ÆÔ6ætÒ"ªìo’¸ulrñ\¢n‚ήB}W%É[G¨7&ß«ÐôMPúÕèÿªDëIG„UªÂ ZÃ:âýd‰ÄŠ% c®q‚Ú±BY%Œœ —¬QV)+×±[D˜5ÆÌ)nÍUÎ*]ç jÏ‚´Jº†\tDDZ¥, 7­R¡VHS+œr"®òÏU™êVXí¾ )]•“îå&¼t’´›æÓ\ү嗔Pü’èçÞgº¿ÈÕâ—ᚦ‡˜Ó‡NàPøvIÄL(^¼"A>¥[§ÄVü>ß–‡ºç¨‹ÿËÛa§1p,çÏÎQ'SäÛ%q':AñÙ+„'¡t.Ý:%FàÕ¥þ> ˆg03}›ö޾=Õg3c1 ~ÉŽ§´avË»Jâ6Ä’X„9K²“Ôåôb0b#¼~R6™’Q“Òä[ÇbUl =gPÿAÝÀÛ4||ö
+kÛ¥k‘d2 ½Y‘ HÎíIEÓãÆ}±ÔoX¸ÿ8è¿ýÝâŸÿuÏúm!|óç›ë¿Þ^\ß]\ÿtx(b.ëòþQüé»ïÿðúâ2>æà÷ý‹ÿ<øý?ÿôíŸoÞžãÇ—gw7×§·ïûÃ7‹ßþûêò:þé0~ÖíÅïïÎßýnñ¿q{{ºtÅÙÏ—ooϯñw½øý¯ï†¿áÿî>þzŽ¿ýöæ‡ÿs~vw|óþúmüöã›ÿnñû\_œÅk¾‹/¹þi|߇ÓË÷rãlI¼ôîÝý·`‹ÃñëF÷üÇc7U5Íoæ¶î_oï~žÝ®tõ£·h~{>ÎnËÇ­nÇšéY´ãß[>Ã~>¿øéç»ÙÍÉ—?z›^üñÍ‹Ë_>}£æ¶ìâm¼rMspÍÌøâmÙ&¯o¾»»¸;[³% }ðŽWÿýâò|þî8ºçÑÇYÏmÚõû«¿œÝ~Ø eå-¿¹DSwnÛ~8}wþúöüÿ¾?¿>›¿e.Ýõè-¼{ûÃÜÞž¿{9ÛÉ—?I›Þ_Æ=ŸÛ2¹uf»ò{>ùãÕ=_ßý
+ÕéüïeëîÿÔ{zþ‘‡äâzîPÜüz~{zws;{8†}¢}wóþöìü·§¿þ|q6ûˆ[ÓåIw½å;ÂÅõš­Ôý„«æäæê×›wwsÍC|
+Ï2Â8û,ÿeW¥h.Ý↬1ረ§0¾g7dþVö‹Þꆘù 1ObKßýõôbsc÷Îϵœ¯êìÜ[ys7ñƧÒ äŸÎo:GOî´énñÇâá>`úÔMâ+ÊQϵgË}¡íì†ìÁQO
+6
+ǹ±ƒðpðD/ß}ÒÆ|ó’œ=o6óýÌÒvçˆz¾¼Eóa;ÂZô8$ùOµ›n“³ßÑîßÑÜ~G{Ž;ÚìaÝïhûí™ìh¯n£àëVÑÎÑÏq?ÛkhûýìëÜϾjíÙîg{ýl¿Ÿ}mûYÔy³¢â™mk³ÿE"Å»ÃÛ/›9ËÆÍËfvã÷Ëæ+_6_Q™­¶Y´³¡mò©Û
+CgvCæïd¿è­nÈdZÙóÄëþá wÁêùªÎÐÝ q|ÒDÝ
+`Ÿ‘¿u6ôsBcÎ.úvÍÑPbþ½Õ ù8¿!Ÿ !7?þøîüKâöüíF[Ù®-ÿ¿°¥_‡ðé£ú-m•ç]O|ošíM³0Í”m~3·uÿºx»
+³¡×ï¯þrvwúaƒ¦•·<>fóh6mЧïÎ_ßžÿß÷ç×gó5Ñ¥»½g‡Æzì–læýÙâdàù-Ùç·§q _þùæb~„Ü<³eùM»s*l°]칎îSoö\G1b{®£ÿØs-µmÏuô
+bBc™ÿ¿=½~÷ãŒ*Û3ߟ§?äSTµ]P?%\ò(
+èndšíý!7$Dν¸¼ÜÛ:Ÿ¹÷XÍM>à‰^¾Qù§ß¼ø£jÞ¼º~Û—‚¨…äÍŸo®ÿA6™CŸÿtq]þáàÏ¿òVþôÝÇ«n.~kÞ.þúñöôêâíâo§×?Üüëwïã›Å_ìÉ??Æ_þŸøÃÿ‰¢-ìâO‹ÿ÷ÿko£ôŸ;8ôGÊ™Ö/U{dµ ‹«ƒæ¨i´µ*Êô‘iu»øöàBe]»8ÔöÈ \¹ùÛƒãbÇþö×è„·‹ŸnOß^DUg¡Ìïø5¶_¡
+pŒZÿtŽThÛv1üÛyÕ˜ÅáðCÓé#×(µF¹£à´Yüóê@™£N[½ÈÿVnT;Šm°ñÆFu&´‹ã³ƒö¨iCüàüï¼7³s:Õy³è¨ÝüQk㟌VñÚwê£ÿnù߉ûlÛxܨ¥=nœÑÈ&Ø£¶5¼ÑuªÑhäqŒãÕµÅtx¼1odÐ}SôØë\X¨æ(XcJ5Þt‹C{Ô˜8*iTìe Aü‚®Ótw¤µ~¡|¯ë7†#«âDÒÇ :tG
+oâ‹ðÝÊÏ|úÆ}Í‘V! û›#¥ã¤9ž÷!iÛ:ÜØÆ)³<lþ FMwÛ6jºë|ìý¸~ãÞ0{Øš#×6¸áÈo¦ÇÍi-ãÖ4é¾Ö™›Ç£k\}غ#§TîC§äÆÐhc}\ằqùOÜÙXOÕæÈß}™ò›7ïãÿšÅßðÿ±McÔâžþ7¯.vÜÖ:åZœjñ§(8ŒßÔ…8À˜1_|i¼Îºà1¹LlˆÁŒKbÓÅÍý0nR*ÄìÛê3 }ÿ÷ø¿nñÛß-þù_± /~ͧ֌ÿeþÙV¾¾ ÎÆ&Äckð¡q‹nUÜ<õ‘ã—¦€rnQ62þ1ĉur@a:PW@Çëâf§
+D¡/ Hkë†þ‘[[ì凪;òm›ïÔ]|{<Z⻌¼´‹'ÍÐ…é¥>´šBã¼—ëÚЊâa©ãÞ÷6Îçx+šE”Å£ÁYN&#{@lp¼qè øSÓñc]Ü)°‚ S±É¸ÊÚ†’öHYtËJÇžO·qMá‡á L|‚²£}ˆ¿I½ÒèOM#ÝR_Xå’¨õvèž3ÞjºØ·KÝØ´Ëw•Æc"·BÅ­1nEÇFIk³Ïv×´¹_eº¨øiZS*9/=¶Ô¯gèW÷5Å>‹kœ½¯t<ãe<bWWúõ$.ñä•òÛoo~8_œœ^^^ü$¶ïâøöý»Ÿ¿¹‰†–EÜðþù׃ß4±‡šøÅyåí‰yå¬9n_›—ê…~ݾ4'Íè?vô[¼{é?ÇÖêüs<fâ£ã,{79çNœò÷¼‰ÖÆÝȨúÍ›A?æ—¿áÇž¿}ó×Sa6ûÍ›¨¿áI'h±¦ã?o\j¶Z¼<ÀæVì]°ñ݋ў6ÞŽºxšÄ^–ý싞ïò¦[~“·ñ¢å}6îà‡Á¿°!V>Cž±ü!ÜSW?¥öˆ{÷Ô—ËûêêWn°¡–ï6&j9ÜP½Çî—½i<ö¸ ,c¡êxZ-FíÓmT“NRGw­¡‹ëcUn°N/IÐ1gÅ}Ø—œ‘†t^6ð–» ¦tCÇ, m'×)…’¨‡Å¸Ë½öiÁ›œ,x×â=h2w¸¸àÛµQÇ´*n\gì¬Î¦ÅlÓE<ðÙ{[ÁJÆ×ÅŸºx6ð€Â–swíº–ZðÜ'ãa,OÕj6OÅÔ°-—/Šþ[WçÑûÃ8  ¢&2ìzQëõBÜì7©8ÌñÐui샽­Äf´qOnÄÆ™ßYÈâ~lÚtE¥/1m¥COØ¡¶e Ï(áYe*zö¹è õÒÅCëõ±Ö¯šWæµn}óªÜ_<õÚÀ “è?ê÷ÉòÝ÷xóÝϧ¿žÿ=y?ú§ªÉGþæ våÜÑ®7þæß/]ºüûºÁﻺ“ç;ަ>˃ôGÒWŸ¿yyzwúæ/ßüéô×ú'Fë#öÈâ÷;?½\üöåE´ÄîÎoÿvýV.Ç×Púó‹ëŸ.ÏË¿E=0ÿío¨â}}þî]ÿ÷oúv¬¾fÒ=/ªªziT8í½/]îŒìÞ¼ú÷ùÙ{|¸kŠ©¦ð&àÜâÔS
+]ĉ¢Z¯Ë‰‚Ë,U¦â¼^¹uÒØµbêÞcër汋Â0ÃIAìƒ]æ=Ì>Ÿí2º¥ ½uJ6¸¨…˜êŽ ƒÎt⡊]ÜpMÕÓùx:Í|¥ø¥¢–a¹ó+­p<ÔîÄÉc`·Æ-;àh¥Ãt®ÃAâñÔNÞ¨:#7zÕÑ15«™^+mÅ6¯œöL=Ê ‡vÛF]<Lñït1Ñî™7êónlhyŒœSsÇ.ÐåлU&½S3r[·^ÅçCc7žæk§º\ádb£a÷«©£¾Ðf_Qè·–£è£2?1rQ96®;¨âl1prÒ—6Ñ=m¼\ÜL½GìsçJöO½üâ^*ØQ—,{hQ™Œ†}7ö:áÊhStcÕÊýê ÞžÜLd¯DrFE«*N=ø«ÊÖ%A)².G)±UÃ⦅‡ÏÅA“Ax3‡2û‹F=–¼J¹6èÁùIhM;x¨â e# wdQF-;j Iv\òÓ@’Ì“ÞMCY'Ɖ‹³¯ƒjBÒß­!¯G·!ZA®ü€øü¢ÞŸ4Áë_¯ãé]SE§ô¬¡ç’ŸkèÛä
+[³3ŒYðN žF«x‰ilòºE›kuÈΆ©üLÃdI®¨r:‹»*î¾Í=h<šd”Ó.ãz±2?ÛbŠÖ¿‡Åô›Fc^¿~Ý5ǯÆÓ”‹Ã”‚–qìÚ® ë|M_ö(<MqN©8Úñu5Q£âx{ôHÅÓTùˆh=­~†\ºü!µÌò3Ýóø™ÊwÓa„Ñq~$¯4®uƒã©l\²ý£Hë&»,.ѶUɃàÂèžä·Çœ;xsláÿ);,{‰ÐûâŸ.½IÉ£_ºœ
+ÑàšÊ»\ïÀJÍO..J$¬0xÂÆBm%r‘U·Æm»׉’XF“±ÝqîÊfF-ÇÅ£'Ý( ¨C›.K¢¸Å(<”"ãĵÖÂÃsr0jlÊêHäY!¾\BnF§ÍÈáñ‘q*)~„j¢°ñã8†þ(î̈˜uN%“V<}Ûtâ„
+q÷ÀlŒ§AšOºS9ÃØÅÒ6ߪN|UQ{âUº‹mKNÊ «Só³]ô¯^tq*¼>qöø¥7¯ló¾|=vÑͦ‡ÙɾÿBé÷¸z QS®·–Oó/!<ySKÇu;ñÒ8‰ïwj}–iaBSú˜pthXÍQÇRPæ$*$Ú+¶Æ%¿]
+Å)G…ÚÖ¤)ØàºÕNš²÷â¡¢ýªaÖ÷ÿZ¨šgüâ®wä ®ÆËé<ToˆŠ`ÿïê*îïÜ1ãf¸¬J¨£ñ´Ö°äßyo¤9ªT<JâÖ¨Þš4ãh"÷Öx+qÃNÿLÜf´óbT6AÀ3Ú””ÜO Õt÷9žpÄ'ß¶‰ êˆx‰¶@“-5‰–¸hò„4&m#à¶hZøø®BÛDúE<2ãÑék7ªÆ‹Ÿ7Î{¤¢á&ÞÇñýh6ëÖV﫼жõ÷ÙFÇcƒ¡ám3¾3¶?ÎÑF Ï`¼§}PO:~˲ÑhdG[wö@Ò\\ ¾hœª#ÔIÈ6¨t[p›‹jV¨ßæâQä¹ ¡[Ň `*´ |Ñž¾3îHyïÙçNœ‡sHÑà ±±™Ñ¿ T—°=Ñ8ˆöxš²­8ââÖq+ÛxvÚ‰xåÉ ¦ê_•joZ‚©âg[˜<H´‡[¡Ëqh¨CÜmc¨8ŽP˜¸´ÙÕ$H˜Î&¯R 7´ElÞ'Ü“ÕÔÏãzsÇ7ÐÏã47ÚË:n\›F¹KñMEÅ2§löˆ-E¡Qa„¿Jƒ‘î;)éNu#èVüøNpC ÞÕW¾SG;ÉŽ^zU ›DòepÜYZ’6®>Ã6¶¼qŠ
+]× WT«Œ8¨ò\èàÃ}è)CpVÇS§I¼«K]²<„±Sûg[èe?¤÷ëhj§儌F¸mr§&C9Jã˜i•ÀWa/Å61]3F«ïžŒÜ"´ èÅd@—]m|KK. Iº3 9üò} žFQkŒÍ㦋£w˜QÛÓhÞÙØW­‘éšg§í‘[yÇ#/´£i-8HÙµy-ÄžÌÁY/L¢Ê@žq é¶ÆkÚN‡äóý-ni­­ åg›¡'ÆYýœœøãñÌxí_§ŽÍëÝtÛõ‹³Gïl¤Øc(ôXüNx³ïEU>-=cùã–!òy k±ôµo†)[ýöMΕáÝÑ4Q80¹<4ŽË&u¹j‰Þ:’fõ͆=×°Ê`&n6m
+üQü 6‹‹¤ÃJX™Mhv`Ç, ݺe*ðit.!Ôøâ&´ÊoÇè·¸:¬ÀµŒïRˆ¤Á©ÍÅHÔþ~ Bã–bœGœJ¶”Ð4^^§t‚UÅvñÐñÃeÊU;Çí2žAñáÎ)¹Jó €òuèë•8áßY„6: ­ºNv.nºôáSÊ)Åí•Ø·¼F”ÎDÁvØ{wSœ*ÒQ
+­t‹–8£>‚â7FÙÆ„±DuìÍB¦âgW@£¢#$]úܳbY»Ø(B Ž„®Í`¹ÅÂ`+»„xKGI•[ó˜S댕ŸâYÅCˆ«)ÝŠ(YF27ÞItApÑñ1Nï¨íðÃyÈÈáäú†—T€Æ8ˆM+‡§Ñ®2è'"è:gF0BƒÕ•Aÿìð=ŽŸäÌëο~ýº1¯â"z¡ÃøùšÝ™»‰%nN¦=Žšæ”›³sŸèæôØÏîyiܤ›!ׇtsª%(Ýò‰wuP™.˜WDu`^)¿¢äˆÑY{ܤ_Dß Ø[Ä$ÃL¢U´äJ–|'ȬÕ`“%‹äŸW¸5b4|DíNàœ“­<À Z|#-¶CªÜm£¦n]yiÙµØrñø5S_ìÒJé)íu'øÆZbÅ¡i¸z'îTôvÄ;qpÏlªspØ‹7G»àÖ ìžj>Ä^ÛÆÏ[*å):oB̺3ŽiË´Fñ}jµÉ nRúèîà=Ý Þ¨|’A¥sQyêT± ¨þÌ_çmˆ†:U´ød×N O‰kÒ .Tëé¤Ê¦zÔC½DD`(h—¦SCµ9MGLîÄ|²8´Ä›<·Ÿ=Ÿ¤×JTÖD×èÒëºV‰êm'AÙª‹·ÛNü<X
+ç%¿“7&ß%p1L`¢3T ÜÆÄÅI¤à0mi¿)¢.S·
+ŒÒÈý
+XSÅG‹ ""!PbØuÑX4«coÊ“¢K˜š~Òhó
+K8ó܃[S„˜g§=ŠÝé‹lå
+ H’ºd ö“;gœn+J…ó0£s(µ“y—8æåÊó¦½÷".ã«ã)¿èÿíÈá´8ì ‘\§; zª ÙuÿŒV
+xW41¸‹!f€N‚xÌ5L*ã¿7j«¼‘ÅãY­ŒÆŸ`ëÅ-Ñ„{}Ÿnhu?POG£@ñ
+­1‹ÅYÐÅ.Vñ:ä~„Ø×µû PqD´‡µ£;q=ÆŠŠSt‘Ò5ç…ô͸‰˜žS Zâ!p$ç|i 5Xgú2Ž;ív|ºT÷òÔ=þ@"yD/äz7{ žÀ
+@à?4‡qNÀ •Á‹oÏ'”ZcÉéÆMè@Á›é8spÀ‹O;ZcoHK(à@ªí‚´‹¬iXy'@#ËtÝ–¸\ô8ÐP÷óZ àq*Þ‡î
+ºu!1ê-‹¦w²%¿ \ù[{!D3"ãC¢¬m\!1Pf»L"Gø&ì]-&erN‡36”^S%n%4“Fxà¢Ø½6~¯
+¬I÷Gc\’J™Sm%$¸z#RE?N3EÊ#vNŒyoL°IÏx\–ª!ÿIíf°©Ñ±ÒÅ[™DZÖ†Bè H„‰û|k¬Üç¡@Ïkf5™V‰?†ˆê9 ÉGž¾Ûº‰
+ŠaÕû-å֙é öþµõhÉGM³ýØ£Io 3{èl§î¬¯k¸ ©:aFªÞªá­ËžÄÐ;=[õá+™l'0
+W„ÂeD2®uêx>êÄS; 2íÅï9xL?s
+=<@2{GRVÑCÑŠŸ„
+  ÝØÙãÌJÇåòŸ WWÔ4-/`ͤ™Ö‘‹î‚ð£5Àp½5’çè<;P4_h¢
+/7êºÀ,r Ï`‘ÖeaIÄ ÑêíÒh@ŃF‡N¡&tñQ°=0Ï€pTâŒÃ'ifJl.EV45€áWk d|fçBHFú)Î ÜCǤDñô8¸Ôd#Yd¦
+š1»üÈmé\É^A±¯·anõà"¥Qt¢€X‘¿îÚ$r-º)9ÏàpW uŠÇޤ…†&¼£Ã ØcŸÀ‘HÃå¢N<0Ú¸¤nÓ’ŽÛ±_”}<øHbï=ƒ™zã’k¨Ä±³îËìÏ<bƒÓs$3Êw…“³*)œ¥u,œ¥þ¨wĉ;ÕÆns ì.Ùì
+
+ ëÅOëÒ€P=ˆÂ*
+ÁAñûÎøÒ&ÞF
+w–%W—n$õ Dd\WpŹ1cQ4ÜyE7qÆ› 40¯˜Á™µ3Š˜øÆ„`ÆŠ<œ²ÜšeÌ—'Þ„êdØkP9ÁÐÙ/T,ì\ XZ­Ï¸Ãeì£Õ¢dh•Ÿô‰j¤º95–`á¥q&Ýel*Ó£¯hœ²$Díé*ó*.),IW9b°ìA¸åê;–"0íê]™$³³Š….ÆÊÁ®z#t™
+ÌmV‰oÂÀÜØSi)õk³À ãXZ®ÇS+‘^,ç/”àíÒhq´:îˆY¹†“v±V•Ñú|˜dcÕ±2¯Pš3¼2¯^7eAwÕÕ 8›æ­©ž.÷ƒ%V¿)’:
+‰¢Ûž|'ñƒ…‡jš5`ªQ_1ž7Á%,kׂDÆ‚è\¯ŽÓ‰ŒSÔ¤dZù4–€ìÓ—ÚÚÕqúüâÍ 6Ãuúò•׿uœš Jìì]© LùXÛòWâHeÕúiŸf<&«íhÐe|’#•D®Ó/µ÷À7;–†²­")óĺ:X+«Sj<õÌ8çc˜£Sð¹5x+ÓéÄÖÜ 'òž£“§ PX“_x
+ÈLí¯Ìó «°v'‰!Sµ1›>¯,Ó),Ïü™ïL@Ê.Y1ñ´u
+1À‰[…\·Šotf[[xi€º0 Kù˜3Á©mœ
+¶$]ÍTÏÖæÂÌ[‘ÌiÇžÕ¹£ FvºY`ÊGÎfû†“Kz è±dZÎüµ•Ö¶e ®Ö©}¡•*»#w§8g¦Ùv‰S“Á7ÂÉÚ4l”]‡$£éy„¢ #ÏîgO¤‡‡TÒëb|Ž/;YkŽÓ·VxX—÷$xJpi™þU8‚¸pš¬A˜j,t9PKyzá‰S!“M!-9€Xw ¼…/vÉO»äÆ]õàqAýɪ:«n=˜pµ’nJ<¢´D
+#X¨ÀW2:fô€®„s>4¸2/ŒÏö(6Ç:6.Ú‰ÝqTÍÌk…zj'»Zºº‹‡*'­÷¡uõtÄòAÎõìQÄ÷Ä™¨ùaÚ¥E£‚wÞ¬¾)n㓳f›V¾lÊà]ãH¼ï£?N™} Y*Èì/¤CÑÅU' $.gaÓ4­ùãªÏLÇ5<œ~S¡‹áâ$}6ê1[:óœuĈcÛ¶É»†@xͼ~¬u^ÜÝNî?¬ÚN‰‡°S'埱àu—ˆ7c£FèKÊ
+è%}•ÙØ£´¸§4âìô®€]ÒªÊDäÁi:’¹&sf×ꪤt¶R¤HŠR| JŒ.z„%6ŸëÒHkÆ„“6}M“ø4Z¸ =úR‘í¢#fU”á2ÏNcuqОø&‹r<ŸÎ=˜Ž^PA"¤¹ìY´ÌÄÅH’ä‘'|‹,ä¾À'‡A “ÎR™<-X)Àz‚s!ÜeI4 ‰ÓåÉM(&ܼä!‚Êáa±En¸ÂÄ‹wÂ+
+t'SàG ItoÓ×Ö¨%dHZÏPKÞSsœæ~ÿŽgm^9¤‡óÊ+óÒ½Ø×ªYZ>ðfû•¸[`×§=uÚNW© è²Ot¶÷U©>aÊ% 6¿‡ôºû&0¯Íkû7/þ¨š7¯®ß~÷ñꇛKþnߟÿtq$¿}}—Íùíïâ!ý~ñãVbZM?Í? =lqzXÄì©«ŸÎÊCÛ
+ý.8·,Æßãë·F,^p06î ¿õ
+e˜…— ^WAú@Ä*£öº²!œ`Žw fÅZ˜]ñÁÞ‘FÇ ïk ¡‘ê@\xl¹Ò².ÀHXЖ1ü,DZò"8ͪåoQg#®’VÇÔññ‹³³÷W»¹;ŵåÁ.UkùG3(b¾G*iÂ: ³L †Ã¸•×ÝBåµè²CT7‚zfáˆËíò@a&Ä6ø5¢f¼òàÕù'žhl y¹ú!ck¸,±ƒaL±+£h´EÑ hYIcÈ«hµ#…Dcc8.+Ïýr1¸’$v†'(¼°jÞ ‚> ¤Ö$€DÂíX–`æŒLŠyÆ#UW@eu¥zÁµ$“°
+(!‘A}ŸÁ˜bè ½GF3('C¿¿ð‡d
+b«º?|80&„ˆ³Á‘dâõ*îÆkZñDc“阜`IO'zgg€‘ÙB ¥yìì –˜2ö¹ø`FºŠ`Á ¡–ª©½-®ÿx¤(TXÌ€x› ~â]¡i`+ÆèB+v,úÄ¡Ê ç8†Q9Vh³ˆÚ}Ä%‚NÖÈ{5
+× ZC};Q&(¼Œ
+bÞÄ%`•p$Ae C꿬–CŠ8XЙ€³ÆAÄ:Œ+Ý'êªÂÈ[8il2å©"„ õˆ¼®ñ#Y;úJ±ðá¡[¡ÇY䀧¾¾<Ò_Nw¡¶Âð y*,ö§ø]½}yœÓœXÁ¥X¶‘Ã%»ÊXžtmê‰D lSkÁeã¡oEM9nÔ©b³Õ4ª œÂ¥Ž¥Ë@M|L<¹'Ä-¦N¼‹hÌÐè¢êìº
+6±îžÉÈ`Å·\’KÒžN+Ÿ~gá¬ÞL]^š]E^€3oÞ¡-pç¦sG õœì•8“>І"vÜ6)÷Šx”„t˜Á n#8mÂTÃèÖTéæÑZ`:Ð'½V⣋ïé‚7cÏÍÉÉãñ-Ê*êUËä.ÿ½Ý;ÄúÏ[æˆX6¼1>ç–ëç–¬5Å8+í,z¿p¶Ó«‚674â"–À,9›ª@Ï¢U÷%œ°PøH÷be'„±Œ¥µž«ŽŠ4yhà3‹†j
+T-hàL…Ó$§×>øãƒ{OVœ:WÜ8UIMBhTPi76¤q’xÁÒ]­¢é%SÚ’m™}{y ½‹0s,.½îK™¡+îIoé”YRʱUâlú
+Œaöã ¨ìïôÛ¤bž´®XàEBô#%ûãjÓ.Ôðw<w7ÍX=wœQ¬NŠÁ’8?P±‚_ÅТ‡†Ï  õXŸ˜¢½˜$âó¤NšFvÂØœ{–JXãPÌ¥ú#ëÎðíP;$SKªÎÐÁà`ö)î¥434 >•H‘]KVI¼ÛIÝGwÊ6[ÅŠ2H¿$ ¸HÐÕ‘LÜKÚ95AºKa/±—Tg#š½K‰Vt]±\®Sƒþp´xàã`jÔ‘&©ó¸‰†!鳬8NÏVàKwK{ˆ`–Å;X˜˜ìÖ¤ÛZÒØ¿ÇfUÌÛ&¡&µG¶xº’ee£¥*9‹Ðt@Ö4a¦°cjHÉôñ‡<xÄ
+áWœ#ÁÒî'U÷:b\SÁ…\„O°R®]K|4w'ù‹4„á¨"5\#4"±“hKz $ŸÀ“ÕÆU¡ƒy
+~(ß
+õªTÛhÓã@Ч!ðáDˆ >ˆs&aå cØ*ÕÛ‚ƒ¶IÕœÄô?ï"Uà ‘d¤3€v ùµ&Û«f¬ˆÝH)+l:ñ»0'hiŒâÌxýe†©Gn|5­„+œíÂÍ܈KŸÆ9Y' jÙ÷ðJ6)o›ÇV\­™ì'Dâ cC±ñ-k1´|<ô¼:œHr~ð$¤ãâþ^ºÂ-‘ÑÞ
+`<*L•7N\º¥˜Î£­hŒ0Ó0¾‡£Ï`{b4D|Çô{ÒÛƒèÝÒ3¿x#VN3L )
+reŸD}"¡[€î˜ÇÞˆîÈÓxšJ t¥ÜÞÛfIh
+··J
+ºËŠ©C žÇ
+ÒNt+ˆ_8Û7Ödº&‹71µÈ U[Í(Ì »œ#öº¤"AxGÅ£MŒ¹ð\Xºþ ã-–áAÕÊ
+uÕ—ŽÀ¨lïŽ8(¡V0OõE°þ› Ä÷ -ÈTm“« œÍ5‚Z`”FrË‚M!þXH+—
+ãƒe€QTŒçLxV Ã'Ôhƒ¸CxÐ& …Ís6°8Nr-Eƒ‰x9ã½oT²ºH h‘!x“¦?"Ùuxb»¥ò%â³Øc\4 ;Þþ3ô•€#-ŽFÏi$PRë„Wj¥ƒÔÛÕÒƒ‹Ï Ç’-óêà0~…XÎê„6y9e4OóCoèÐÆä‚·á2>ÃýƒGÐw·üÌ/çæªm¢˜_Ù‡È%}ýG¢kJV%”È åhökƒäl@Ò%uα9¨YA£Ä£×$XDrnÉA뀃ˆÛ/ÚÍF½aµ ä ðHÇÇÁ˜r¨\BØ!)wåóO¾”®Æ¬Bú|ø¹W8ŒCˆB倻ÀA/vl,ŠƒÄÕ
+,Ae>yp€dÑkT5Çn²ü܇7ûPåƒ= SHµ‰ãw‚K·å¡Jû·N˜)„ÜÐ9Þ2ŠÍ.+œeÚ$<©ªMÆÓØñ3¶´C¨º’µ­é‰¹ÛwfÆ!0­ññðdÉÙ–‚ q·¢æ/ìtZúijIf@lšéƒH²×1–FdÝûP˜‰)&Ô•°°­
+«DD¬"˜Ó®ã
+ !£c<“pÈâÉDÅ£)Ü™ðì0ÓË'!l\Xr¨ë ‡iœw=èlIÎ+î}¬Âä! p‚x6À;`bNâãqŸí€pˆû r P`î°‹5‹ÎÉ×Ë4"èF ¨(`»~à&¤~„¦$ÒÔ.ÑßP"UÖ1ìˆF®É£Næˆð™ÝvâihYÚ®>æÑÆi 
+´°AÐ@¡þboä)+®¥3îbÜb¡þ †0LË׋« nZb6¸QÂ+ër4ž°¦ç‰ª©8êÈ΃B¶VÇÒ~óœ ^%¹‰Ž™¤ˆ‡a}"¢ nàŸÀ\[B~‚fˆN§ªš 9˜K(ÊëiP¥
+26ÑâdgaUâw÷Áˆ‰ ˆ¨Âáèæ+x¨ôÕ`ñ»…?CçŠuÈç
+2Æ&Û÷Ð%ºŸ¬áàMˆñ&± 3–ƒ{'žmø^:3°ÖFÑ
+Î(ú2ˆÝîèÏ–¨)ʬÁx‡¥®¡$Ò„üMP~™ÀÒ™R‹AÊâ…vÙQÄ´éX ,q…JB t<l{:¹Ïp*Ã…C§©ˆO3 EåúJAp<µ´üRÆ<;‘„L¬¤l²2³Â—Ð2¥[@4Q IV„dBâ
+0©¡-+ÝdÛ‰š %$1€)ãžÓ¡c…>* ähhÓ.BzK|';•ëG#ù¨Ìp÷Ìù%ÊÉ¡ñ@¡‰¯±¼yË+ˆÑƒ+l«&Qoža³¥«˜ãÿîÖÑó”ÚG?‚Þ;'.
+
+ØÐYàœ1é»-Qëì:Öµ>c 
+'2¶
+¹Br±2‘i S¾‚  }x’Ëséÿgï½ÚH–îá÷ ð„1˜$˜HFa’Á€±±±d„$KbïãûÇﳿ}NõŒFà]Ö^ïÕ »¨§§§sWUŸ:EÎ|“˜vzžºz¿§K¨GN Ëwéwª÷ØžISˆ|íÚÙÁ¥P‚ ÈI&\€ŠÓÚ‰ˆvt˜L\Ú‡BÚ¥ÎÒ¿C9òiáÀIçñö.¨)Ž(s³Û–Ÿž¦­ 8ÇŸ
+…‚Èt¬KJ]—ƒHmÃÕ2D^€ÃaÝ Ò5ÄKÏ}&ƒpez|ƒR/Zóóı-ð]~Â÷!0`§u4„§Íø¶v\ôxcëÅÇ$—£W‚Ј!¯š‰Ý•7hÕ´Ä`P¸€xRzz¦Ð¿šQ{·á „PåÅ1¡©u02˜Í<b”’°è<G{AÊ#gÀüh3ÛAÉfª]¦"¡}\ÍNYã€é¸„bj׿.a–Ôöù’¦7„|Ì0›É<Q•G4 <6ùÓ˜ ”]^‡¦°q
+ÚŽM†•…)ƒœÙ¦(é€zôòN ¾†œòÅ$R²Å‹Fá åé°¼½"óŠöÖÓ°aYzïvœ@˜FÄ2ê
+-k‡k(ñ
+C¤Z1wÑD#F!A–Ä5„Ë_ï3€…࿬}2‹xeŠMÇÙ5§´bGb¸ˆ”ñ„i§Š J³-íõi[ÂA&%bÖèJàðnÖ®šJ4‘¤ŸR‹í%ž.ÞØß…‹Å‹ºÑ’(¼¼jÁ•6$LË 5 Æå;ãBÑÓX¼ à«d
+ÕHÐû\vÑæ~‡[u G‡v9¬M˜_ ÿ CLÈð@´°A/oÊÝ¥lø8„]ÍñASž-R¦tìëC, å"$‡/‘ãBüÛTîÐÔh/
+n>#c‘“ˆ&u$ ûˆ¦¢¤êiäl‰@¸jÇa5‘Å48,ž²¶v*NËÕ©°nà¤K\@;£c‹Ø©E`aš_²¸••I37ÆŽÚ„#ÆÌ+ÒÔ9Úd‡+]bû]½ýaùèõŽ$„DG’€ÌPV˜!iÝ~nbù” }™™°“Ò4ásUŠ_TG˜…B"bÀ1뻂isquР4|/ŠCÀÿ1f±?…¤ÀbuaÀὋÍß
+4òšOè\Ž#s™6  @Þ‡ã~Æœ0b0r"¿K u [vF:lšÚdÊ;;ê¾Ñ„r æÛš¢ÏÓSd!ÂÁ¡2má;^(S ËB|§Ï¥iÊ#lOã†5C¿§ºÒ mQ¼‡"tC8f8!}rˆ”V8A~>ÐÑ(V¸¼’G8¤à‘‡½™60ÞnÐlÉKJ="Ñe4qš½ B
+áFŒšGõÐökÀ¸iÃ=ðDžÖK¯#¨8ž¦J“™K
+,-Xøöœ{ÔE€D+ܱœ°Õ@¿ø–^~Ì*æ›Ú#÷MXµpb€l›˜¾}èÏ»¾¸‹QG¶ñ8¤r*Ï›gÑ‹xÍá“"i„y2•˜€Ã‘°
+lpL¡¡ÑÔ†œ¼fÛ¡~7«¦,¦ ŒÀŸàÓѨ¿w»\þPó`d =’À˜uÃ'¤YŠ
+~¦Ü¼@,†ÁÆB=š-è¸@“Þœ…FŸyG6e°
+‹@«&UïWŸh¬BCnþÒ¦©îI3u› Ë¥Þ€o¡#M}Kã{W˜†cq›iô»eš„®)ÛõWá‰ÚFWÊ€VkGHbotZ¥¹BMȹ')¦§S€-%ÛT@#éEeábs[Ò|3zÓ¯Eº8JŠ­ËêªÃS5ŽA#
+Ã+/ýˆ ÂÖoÆ‚U§ Þ‘-QíRfd4V=ÂÚ
+&:Øò¢Ë_IH,tn/žˆ-í³|<»"ÞSeðL ‡DÿÑ Àj©zã=,BâQ ¿%Ey™¢Æ·œÐV™'ç
+w”ÅŠneäc¦ ôlP˜Â«Ä´¢‹zî°¶p@Êe> É˜¸1è?tžê4s,Y×),ø7L2‰:„ÔH\
+D¤F߇ӡV—@âå08–¶ùüfl$Íë@½i¼0åYè¼ÆØ
+ˆâ˜aKÆkÑìj°«kÒ²=»–ÂH c0iZäÎT
+ô/¦„!‘å¤kÒ¨îpŠšt2áGh嚎*åÑVøéíäá’Iß¶‡:A{<Ð_KIIûHZÆßB±&܈Hð¨‘B6‡ï/ê‚M͔ӗ Øˆ¨G
+éGwE!uXGGåJAɜжæ&2…ûMvÛ04åþ”½šnh¡¾ÂñuÌO
+£D™" —@Dã 4À¦,ìën$:r¯fâ hÚ0®ûÊû¬gFg¶l©8b%0’#ž²+–舸8ˆ˜á]96yïºB‰%Áˆ<M‘
+Îu\1ÀÇÌeásIH©¢" [w¨yÔ¨‹8Ô½åK„•jSGY
+¹k´yç*@-n!õüp%—m „wê˜i»ÚwΠ@' ú'"Šƒ{Eàé;8a4õ H÷Ù\E5™ª¾G¡å%ç¦è’Þ6tÈY‰Í„Ð½Ü \™^¸É–…b!.Ô°¹ÛãvŽËy¹!ôEÆATžÀ’ÀdT)ÕàÎéø.
+ÁîQ¢Ò­öÍ〃}0$ÛN(P%llŽäǶí
+§'ˆˆgK
+IZ*,¨A‡ $é:mÂ,õ2)÷¦ê×ìƒ*‰&O$CT
+œ.Ü×mŠ,B1ƒ‚@Þ°=AÍ{šÜü¼Ü”Z iâßôE1X@ˆŸ-ÞnHx¹sDPš$ˆAF8ˆËöîQOCêœ$ õ°\BúÄ":¯˜lqÙ 
+¦\(¨
+âvâ¸(ˆ1Ï9õ±Iƒáó*¸AH3@a°ÄóHWÿp`3*oúJ÷-ñ)NÕ (b<UÛúc“³”ÜñHä÷O׹⎓¾ƒŒgÌ"]#Šjn‰Oa ¬nQ4!NMh[®·äêÃõõÂXÉ>-!Ahˆ«C)‘$Dˆ€ýú£ã†—º*€ñ¡#‚ƒD@5ÈEÄ(̘|ŒÌÏ G\Ðë´á[Å3€ëHh0$!nEŽ1«LxR‡bḄCbƒ 0(™!à­Ú„ªóŒ¦Õ–°m a3®°Hß=•qf?v…ÄÁ9!ŠðŠH“ŒGK'¯2ê |3¸ö°ÒH¸H½‰ KàòŽvU dSpt¨XÀjm K’œŽN@Õˆ²©cŠ”ŒéO•#ô$· GËÑt¨±(O>…oPeðÄ'\€¹¸Þ+$ÀÉ z»ðæw&pàÆÆ¿!ú 3Ü$>
+(cd[Lñ£
+¼*ÕD+òj…)ŽS6Ц0!W&m[~ѧʉ³@%[S‚šŒßK§
+p:«` ÆUœÒйŒ¹+QCÔ¼á°r ´]b›y †}Û•SóHÚÚ?ú9ˆ—v#ºí7èI<>S/Pгˆ *„±Ê Ó–ÄA‘ø2p…ptÔWËvã‰ÅPR®£ù¿ þ1Ýÿ0–\ â¯Ä [úŸ ù¿ö@4†  m¿±õZ~ì«ñæä÷ü‡¨UÒ
+aî…9.º¶'†f'ŽªçÊщMÓ°P(&©þSªåuMÐtjÀͶÏk?ÇÖ˜·ˆ³Ü¿`8&o YŠéiHwaC˜`iÀñ}Lþ/°ëÀmBLª<Ùé
+fp‰#…R¸Î [¨¯é¡ïxQè
+Äj¨/ ãHï4v‹×EB±L‰[¾ï8Z—¥£££mÉœýQˆqA^Q¥tRŽfË.1j«©e8K(VPA—é%g@—R!wwꣾDÿöÅaÖ\qBŒnÕ`BA(
+'ÂÎ:`7gª˜Y=ú¡=¦-VXº„Î_¦¦Ãœô~d`ÆñxE‡k!2Òb‡ôIÅÀûÖ€W
+ï`³ {b¤âÁÕÁ±®4M‚çÎ1È
+"®á QT ÀDqÑQneÐáL§,lôŒ/FyáxA
+t'¦€g¾’†P€à" HÀØ=G®á Ó b”Äm K/ 4"Ʋ‡ˆO6nz G(=p% Ù商mû¼žQz‰\­øœ¬`E€Å£ú
+—v¥î;ôÑ Ä …Ô‚UÓÕàQbÚ”`«c­‚ßÑ
+ …÷7ÙOïP.y#xóMƒ‹Ðšñò—<¤Ô€ˆ–ñ‚3×eä L¥z¸
+Á#ä  QŒpà‹ã£­Ö|¸ýÃ¥VšÍ2p› R`i¡ˆêeß×Îl–ÈŸè œµ¤ú €|Zvb°<Ü¢`|eÐW|»i47I8˾ÂÒ°U2ÐWTPC•ð4û‘!{YhËnC¹<ßH¨½ªÀ-b\Áë6\ΰ];z36™"Ö"”çj>  lKJ $Ä@22@9•/czÂ}‚i8a¼l‡Ô¾Ô@uSj9˰ endstream endobj 23 0 obj <</Length 65536>>stream
+„
+æÑP_ æàÑK«b¨n ¹Â:„‘›ƒ%ª"*eJÜCz³‡¶ì©ô…¤—ÕftË¿ÖÐŽ!Ë.Ð0«?Ëûù0æv…AP€Ž¦¬×@ãI’‡í ÎöXù!®*ÅëÉmÓÁ’ÁŠ:Ú¾mÖÚŸ·ûJ‹ƒfêË2mzô\ Ì_ær@1<k;€jÉÆÑ‘¯
+ÇcÈÉ.yÖ? öJ‰È Í[â:Í)"ˆ•íéûeú ¡, ÓÇW½°ÔXB~g2BÁå
+ ÄBê{‚"bpnWø‰dÀ2 j|ÜA|é¦s9QÝN(ëÆ³ôKt„׋ëŠ; ¾'ÓCjìz^(“žç@ ‹pé±52š($¢]_‹êBáG£>%wˆÉ`> }‰î 4ÈäÒÈi Aw¦Ž~q(Іœæ\rÇÍÍžÚx]­Ö×Ô±h‡öÑ\áXh:\Äz#"0“é°Jj
+`¤ø[:!ñ°·k2?Eñ!òŽ€$÷cŒH£g$‘oÓÑáÃekÅÓæ*
+¯8KV¬ÆÀkC"I-Ñ<ÄÐçH²€ +uÈW0à‹Ÿ)éK
+.¡BïˆØDKÖ¢’@ÑÓ©œ¹ Sð\¹eǘ©C˜MG|[Iý‹Ó‡t˜–Y[‡\©c—ÁÚ€÷¨ C¼ `h:ÔúˆGG + ¦ƒ¡b‰Á¦µCÛ“ú O\ MOÌæ¥áWèùBOÌ«
+‡z€ÂÅ9tq…òêX
+w͈”Ùôï`@—0*QØ®Æt!øâëÄ
+‰ '®Êa(t°9…½Á!òšqøˆé…šŠ­ß×6Ì4.Ì“AЦ¡Žøt‚†eP"÷1ÁààEs$œ¶M‰‰­}óÈ–«Ä Pâ–y®ÜOÙ2Ý >âYg\‡Â¢­™ ÓŽÆƒ?Æ–· !õÇm¢M$„†7ã‚Qˆ° 8CËIÃ}ÞØêDNÎ0\D&ü@kÇŒJø•¯#øÁl ¦ØÁµ¢
+: j'nÚSGLØtàŠ§ÿ1Á–tEÇD€s '_‚àà†1°D÷MCk´Ä);ð¢88pþ#9Š`ø¯Ä\Óž­IJ`°Ò8ì4'rÈY¨PJì@ íÎYës‚ì%ú<!¤A„i5\™Û4&ø¬‘‘L`mI´wÃg5ðnp3ì Ų±:Ó
+Ñœún¸n˜Dß z¢˜˜<q‰Ì£Ä‰,„Ì8šÇÖ®§H7}7gOR{\Z¸cü…¤×½±4u¿AÜ™E´oz@3Mh†ÄìÑ fé›qÃlVK ÃÞK\|¶gc–
+»²ÒL˜bsVÝb ¦EÜ[b° Þ[)-K9
+Seœb lô]c‰"J°(®ÐÁF ¸š„)Nð5Gò%Ðí
+˜Ø4ù[éÜôÏu9\ºúóÜ‚}"ñŠž]Ýù,Îtt²´/ A~¡ïu/_– q„†Ú¿ž|¦fu…:³çSé¾ê¤{+œîoSÚÑqt o¤c‚DJ§÷:iQ'ÞÓ£§Ä§DCÙɢǺ“O‡d’ÄÅŒbD<™QÉ#SϹ4‰Óƒ ž–¨íëѼ0µ‰ç¦¡Uˆ#Ò%•ˆ,+è"¡ýNfÎëgåT®X­V”„Ò¸ª”RÙæmë*u¨ù)žx®›z¿72®
+öá£f
+m›qŠaí. Ž[ ¹±uÈ*ˆc>ï,ZÇ…¤ë;ÍYor’wIÒ©†Öt%E0ežƒÛH:N–†åo:bæJDEmóå.bŠK(ëו`ú:œ$1X´ÕÉbÊ¢K$˜
+J¼ »­÷W&ìm›‰·ô¨tý–ðÝHÆGkçø‹g~<‡uÎ3žÖ
+锄c$Ê!¿õ9Ð3wKUËšÙ‚³ ¾™Éå²öš“/ØÖZ·<–ùÕò˜ÑcÄë‘Çî²°ÿìÃ`ö(®cóÎüŽÂNí/vÊ|Ï‹½¿škqé¡”÷èüŽ”hNlÔZíb­T>ÍÛÅÓüéN±1¸ŠŒ`ÊEFj2_)Þ”Ûåæ~í\ß/À.D3µËj9ñÌÀy©Ÿí㊥Vnµâç‹q;ú?ʫڻ?êÇÇGmà^îûhogd6ÂÓµÿ+—nQ…î®IÞð©ìÍ’ZH)Þ~Ü·H}cýª UÕ=GåR»ÞLíKשl±V+7§´èn¦.#š›Íæ”Q%,Ü|òÝg
+ñ Žþþ¶ÀgŒÞ@èEÅ…€\x49š†î(À%À
+Žÿ¢
+C”9_6šÎb2éMWÉ%/𛍷D²{Ö2è£ eßqI'"°9B"=MNŽ×\í¥-ì¨7
+ 8Î,Gœû SÜò°-Ñ£ÃÖ}À 6€(Ê®kRéE±ØõvC"Ë×!âmnn„[›B»Ãí' }G`»4‹W ¦¦ßA…éÂDö O`º$ÀÌðO7Ê
+47Œ&¶÷PÄ ä™ Ù0;s0Ð\Ú‰“EŽÝx&k_Õ\çÄ
+5íA4ÜHàP4%&cdãNR ”DÉž£_(ˆ’/Ea'¢pJ˜‰w|Ž•›|Ç›K<Çš áDïdÍõÏ_lhÓo 1‚XêzÒÁéF®¶p5J°>Ø æ'²Vy<X!sÑÓÏÓdQŒˆ,I8«¼ ÎVZ>;Q‚3²êBq«ÆËÍø«6Ö$ê®ÙìjñÇÞöѬh£H «+‘ewœ7¡ z]½&ø2I…qޤýãàõãS&²;’·5EŒ%oµ¦</–¼MO1=#ö¼ÜH,ŒFLZéa¡-?‘
+öà ¼®,¾tkœâÐKÒÔïu(¾‰’ˆ‡ØŽ:"ÍPc C÷Â-Ø4âÞ¥nâÇY,>ñ¨ >Tñ¸áEê‘å©JŸNý;?éLýô…ò¦“Ý„ïxœ,÷ðãÖ?#úÀ8;Ž[Ù»eZù¤•î™e¹Þi·ýÇ5ŠdÀ™Ý$’L‰y'@¼"ãK”àS}wz&n:ngâÂU‡Á"ôÄõe×–‰ËCtýY%¸V<À¦¯5=]f0ü·ÀÒÛœèÌAD ÷ІÎ*ªƒšVbäCÙ¸:¥…R|¢pT`°ÉhÒ™šá:®·ƒh†¾Ïg‹dÜ× ®H€äº:ä}d‘BZÜÜH2ÍöHЧ0ä”gw¥Ø¶l"qš-ÔšLîøÉÙq“/Á0t½h¦­Dµ]iÛAí3ôSqK![‰Þ õ2 IñİÄÿ8îÑP;?Ľîj®þ\g/•˜T*;¸+Á֯ˠ7\QR´Û'hä‰ËÖáðÆI´kº‰<Ð:Uê$øÑ±ÒI
+„A'™@÷–¾Ù—ëŸúXyÛ¢Ñ8üG)Ò®vÎ䀘DźžP0µ7{:¢ÆÜD§lÁ¼åa˜£QÁòäöÐ%?X4Aj6oáK6…³
+0™2N£ÜñØš$œ12\b—äæàÉíé¹»]lÜpÆD÷Êjé«Ü Ý$Œ‡&ïÁ†uK:8m’ðU·yû€r
+¦€
+AA É·½IAú’¶‘$¡în½”r¬¯/¼ñŽ%ÎWm®¿wîvœÿÁc¹8^Ø…²‘à☌yH!N8ÒvÑïÌ”ýxE?²Cà"âÌ% ì„ÀœŸgëÀ24~~IëÀSjÅÉlu„l¾–”Äø=ÿãÉš¬ú·ÓDPNrK„]; EQví3 *UÒ×Ѩk&Ÿk& Œ²ISq@féªÄ 4äºÀ¡q°Úÿ'[G¶ðƒ˜:LÇÍá¦àG£Èoqþ
+~ª
+w¶ê[í³¡ô6ôÖz·¸ü[ªÕC1ø~1¸õ$ól(à>€ûD#ò?!Ð>M_uvɈôú4ÍúõÒëÓ´ƒÒëPZýAiõiúžÒê?K&}š†ý
+!ô‰–ÃÏBŸj{5$˜Üz¯DJÒßoÕþ›ýš;fW¼+ºŒÕ},1$p$>,ƒ«\‘ëa9\ªñƒ’¸zé²øøéEÔ!I+µêû~]ÜÃ"ú€.#CN¿˜®ûuÖºWTWµéÖÇO#®«\?.°«Ïý‘]ºò¯¯KqsØz­ÚÖ'Þ«´?#à³Å?.â«×þŒÏJþ¨˜¯^ú‚¾zëOˆúO7’½Æ=UŸ~`üôO©
+é³psçèÕd7êÑد)`+îÓTâ
+WZŸ²‚Ê÷©+ÜYzŽcŸÊ2àSOºû•Ö§º`ÏëU^8Y»Õö*0ýŸxÊêÿ{´”ù÷;Ûo7ò©…”†ÕœÚ§S©E!Ø=UMT{|©kõZÊs͵ºÖBrñóôu½¶§¦G[Õ7–dB“’F^7ð$'{Õ[õÏݳ¯åR;¢¤Oÿb­xYn¦v›çðß¾÷Yê^¾€ù”•j´çR»ÕßýY§RiUD2¿{oþYYD¡Zl?˜ßf~úïwçe^ðïz¡?¯4ÄtûßPsp·¢Œ®¢sWÅf©Þi¤ÊÅ"d=,6ÊÍòyJ-ÿúuùÞb¥…j½Y¬¦‰ìzv (~_MÚeIe¿µ^o«Pœ“¥¦`×|zâyyÅÈd²Ëñc€ÿ†üŸ¿ôŠ¿¯¶êãÉÍO[ø/âðÄŸ5ÿ–ÏÚ=MVÊFÜàÄŸü»ÒÕXkü¬›’ž ù¿¿÷£qã£&?˜†Ö+lþ ßõ{:Yë?ÚÉO¹­wU¯hádœOE<Ô’GeO9ó©è¿è©ù»KI öé?ÙuwÔ(¹ûÜY+Ó\­¸RæÓVª{í©–e<P™Ÿ6Èwí·ªÂ²áÞÙ¡®;0ÕïMåöÎGž4Ø¥5͇<
+Û”§Ä8õ`tdOí‰?×°Þóç®ÚØŸÈ|ÒNõu-éhЖé¢XDâ·aøƒ T?Ö?<3By¢æðAû{µÜ™ßªÕÿSã%½RÀUzÀkÕ
+•ªêkäW›C¥–’ ’*Ñæu–Y%5ÏUZ%q¢ÀþÚ òy| Ùb«RJT ^;¿­´óêâHj~¯Øl®Ç¶¤§à[»£¤Iy‰¹‘y®Xi¨!¡V¯”õ`Lö®²VÚ,ÅbñÕݦì?¨aR/QCZoï—Ku%SŸ«‡n/M£€
+©É395ÿ¶V)ÕÏË‘¢˜¯´ÕâwùÙU ®ŽŠÍJ¬AÆáKð$_n”kç­Ýþ'ûeõ%5ëëÉGq•z_ìnÆÀF¿—Üõî¼ô£žú)Å\þaàÓû2ð` ß…znû»DÖߤû…)wóùò…R9GR“‰¢awO²Nëjåï«üJaMʃ„jÉmú­ž<jI¤8KRÉ¥Ñ3±³UÕg?°.î›uó,¬Ó§½\~wúªtz©SÂõ¿§)zi<º-?¸¦š2Žãyš2¡"”ñO™A£ù7mt»­r;%ìŒ÷mn’ùîÜ׺ʊ7´{»:m¿Š^!õ‹V³ëÝT+7S2‚ܾÖÚGÎ÷ßl#·†ù_ÙÈï `sÇât=ÓsÍá~Þ¿Ÿ÷så?õ~þä›VÚõEli}Û*¯ýQ®íž“»“‹¹3ùl ùÔ|Êž©%ÞüÉ´B©ªo
+ëŘMNŽëð˜#þpŠü”)b¿íq×O#þÆ#|_h¿à_¤ü =2ó"óßdèH™nøÏ6v<ùnö³U ÇNóWl~òµ~ÒfôO±gýŠM!Á
+á¦åjIòÂ%Ê^ùO<ÒþÞ)ôïiÒãwn;0þÙ;÷?tYÿfÖ¡™úÏX7lË0 ÀuÃÐ_vTÇG–¡™zh¦8eL/ð=­FΆöé¡}zhŸÚ§ÿ5ùÐ>ýóLS#5Mÿ†ÆÇ¡}úçL÷÷"CûôcFøI÷€ÿEû´˜C+ÇÐÊ1”‘ï•‘½0`¤à!ohåx$OMÆù‚ñ†ÆŽ¡±ãŸ²‘CcÇï¨ç˜–ÿÛ"­†ÆŽŸ4E‚ßvŠ á!ï¯Ú:¬¡­chëŠÈN`9ˆýжä’ÞqlÛ:†¦Ž;
+G«“ËŒZ`ÄßD;wЀãe7‘ÏÔn
+øG"ŸL7 9-·Ó\3Q¹Î¬ì³Dטǖ)n»ñ°;¥Ùnr<¤†‰VÄùÜî©ÉæZ]Kmµº&µÖ‰§>l!§“ÌéèñšÓȼJuÜèÛngšòÿ^¢Ýx yF²Ý(£'µÌ¨ƒÇÌh»ÓU]GgNT׉³Æ=ª«à'»4®‚ßÕ£vT… Q_;®BÐUßèë².’ýà¹Ñ’¶“™}=^Œ~g•¹‰•ÍÙðßóãø’U®5`Ñ*6Õ¦+·]iôåï”T(ž—SízJƒ‡ÊéέK龑¥M«úøªõæXèAý¢-MU.k8,xÐ;vçË-%¶¥nk­òÕìÎ=°Š¹z£¡D#i΃vçFJn]¥´­mªëmùïÇDQ'©FÏ ²yMªnüNãÐÞeò¨—õm;jî%„¹ Tÿqúó…ÝÙ|•+ú³ÝÙ<”æ÷gó»³¹È6 r^O¶þnwûŽÚ;ÝÙ¬;jowg3悔Õ͸£öfO6gðGÝh¼²ÍÊåU»3b9l¹³±÷
+~dâ»2E'^²°'! ggç Óçfïæé=±~Ìw¼¯ìec8u;ÂoR*yÝù™O" z[I»I©>¶© ¶Ööݲõ]£öøŒõ‰ìüàƒRû€Š›†<:ø~sV¯¢˜ÿu,]c+™,”‹í+¥`©¤ò¥ÚÊ0Ó«u­ö­l±V㣂ªiå¼ë™”¦žÙ穽ïÍâz¾¯´Ž3vÛÊ4›ø%uPµNÖàî–èŽÎ×K·7jGÎÛÅ‘ñÓùè·Ò”ð+¡òá÷ûí׸(ôp15ù7ÕšzœV»U³rvÛ–+
+•UU±øsŠx‚ò¹JW•êy³\“<‘wFôÿho”åéäD­uúG±ÙZTºgä¦ÌúGªäEzëŽ|À2H6]“VׯߴwÎ*µsÕTó£:ñ Ü~Ínx¸‡’¹gmkÔx¨}Uì
+çi[”ó‰†ÿ/¶Þ¼·õ×Jñ¬Z~ÌÄp$§…¾ðÇ£—:²þâYŒæ•n[íúͯÝÉþ¾y¸Ð*Â$ˆ3OmŽûº8€Žò©Ê¿a•¶.þó>ñ2hU+¥ß}/ŽopîjäY½­6²íòE{W)a„Z>8ÊýïüÚÙôÃ9Û c¨Ï]mýOåœþÈ6PgüÅ­²çlÛ~¨MßӞ-þ\`¹þCù¿Gí1¿¼19çy®ûPk®Ê°j<¦IQÎÀÙÏMï ~Û,•³0 üòÃ_Q¿º
+7åvQÀÙ­á_¬Çع6><fN%2#%¾¸ÏVËåó}Ì·£NÑÝOë;Ÿe¹÷'÷u]“ô \}Ulçë%Õðrs#ßâÓAÓ9ι]/«PW“y“!€á«ò(^‡ëÕúY±º_nÜV[š,@ušéÌsF¢Ç®3…Ûj52䕛ȭžÆÍ•æ¢ÔE–åZ¹™ªß¶«•Z9Õ.ÿ_»çlíÉYªV©Rôÿ¥šåKUº^À@+ŸB÷ÍbKõNú1l©Fk%ݽNüFHFõZñæ¶X­´¿§¶Ë”«÷W§Ñ„ÙþrªþG¹Ù€«uÿ QK[ÄéÌ“wÊç•Û›Ô~¹U¯Þê[÷^Ö U’™ŠMdJH¾mDµVï¡&íTÂÑ3w⾿)¶®%‹ã9Åó󞊷u=¶qJœ7*sñÄ$J½Ú;¶ªó:bjf#•¹m×Sû |îšaªQÄEE«rs[™(: r›í³z±y®F¾ZoZ:˜º×_Î5ëL³\ì[ßÒ˜ìÜ}˜´&†6+«?,Óðç_Ó¿:3_77þ^gu™žØq[¢ñËk[q‡$
+šš\­äÈ È¶—/ §ªäYËGÏ˱‚;G2e¥Î’c/3í°©ŠZÅvYe,Óˆ×Ý(”Iy£¨Ž‚Ò÷Ô%¬Ø­¸_íN¿"ëîm»qÛŽg#§[bÕéö¹]ïì”[W¼¡Û½»•¦Ú©Î™óRèä•·Ówvs¢Ø󯕽cD®kõÒµÚÈT¡õÛFÿ~gÊf„mñPm詵óJ»˜œFg§îÛѶ‹µËÛâe9µWoD…/b8b¸WÏÖÖ‹{ðqôõùŽ8_©]W[í¹’’ÊÚ‰½KUo£vÂuFªs(&ºå øGyç¶Ú®¨ó(£û1(ïqts^îN|œYz·üb±ø~vÓžØMgW›ë7W —µÑÍÂèìä‹\¥8×zî½}µæ-¬¾]_Ùq^.lz±³Ú¼-ù…5k'7gÌ0Zù¯ùËYãùêâç¹éÕ¥ÙFkµµeÍŒ¯.n6£L›íìå«7Û«KNù WY^)åçæ^\ö}jûüX}ÏÏÆüëíüד¬ó!=›¹©o·TsÛW3+ÞØm!ï<—ýZ}ñnd<alž ,ì¹^øGo>~ÊææŽîþh2ßÂÉêÒuádu¡5w3“Ÿ¿-L®Ÿ_ŒŒ³³
+_Nwoó'ïüluµú~á"{ÕÎ]ùÌ®îø2‘/™ÛßV—^¾x'å¨*·rŸ/?×Õ_ßòç£Ùtðõyæ =V“:¼/žßŽŒ‡_'gJk%÷ÍdîÊ9]\ÊŒÛ3Ù׳_fVs/ÞråÛ镣ͱ«ÅR©x¿*3kÛWòeÓ˜/úÍÊó/ •Ï›çÙêøËéæÌ§ÛÌöÁÄ7ÔjuqóÊ÷NV3µÒ‹›™åÅyÿæÓrÅ÷ç[v¦YÚ0g®̸ÄR~³u¤ºÍQößÙÆùB%7_Tãkî,O¦gËÙª¿w#-8Þ_Ím,½[› Ý–—ÞØŠŸ«žY::ÿ¸`°Ø•Ú¸jЊ7=†!ùè½óÞÔÐO+Ùë)/­§æÑù¶ažŒíäç‹K…Ñ™M|ÅÃÏ,…YFƳgÿžY),é¿–Þ­mIöÜìÚ)Ì:¶6ÔÔ}o̬¬¬ÍZù——˺œwËK‹ç__æHÆVåíf]ý•)»Wà¤Ssry™ÊÓÜÑlþ”]/·^:Þïk)s˜ÿ:“¿˜ßú¶V,¾xžõÎÞ¾Yz•?]É^•Ú™½ç¥Ì¡e«ÑÏø'Çcêók¼»Hfm×4ý|Ý),¨Î5_Ev\Ï_¬³?U±Å©™•±Åw2B(yd|íÔœ9Ê:ï7 «ÍæÕ[gaçÝKŽPàVšž¼éÙ™l=üÜÛ•Ý Oö{ÔO2°(jd|qfùv²¯šYcÓ]PÿX7*RΊwV_]<l?Ën¶oû»²g$ý üûæ(ÒÕ>öm®’éí§Ûý°\˜œhL宼ýkgÆÒt¾ÜlÎå½åŸ"Òqgl¯gN7Mζ¥ô—VêÖ\~ã«_”µ/|»ÙÊì~Îîr›anrç7ÇÜ<ŒÁz¶ê-¿ë”íµ·—?d_Ž-÷Ôad\Õ¢¼›_¿/«O½YÂc ïêýµíÍWRͶ×ÏÎÂpfÑyÝÓ#‹­úE®ÒªxØ-Ó÷¬©±WV-–&nÔJÞŸÂôÚ™YÞü°}ôò“ÚÇlõt|{z½}z~“9ø²™Ÿ¯,O¤€‹¯¼ÌáNýrõèp£XX Þ¼_š1Ö>ÅÝÑX›­¿¶
+ÞÂ{5æ·áZéúÛDô.jQ6æ‰/¶Õö¸ü"³_mL'ó½)¾RÛß Çß¿šžËlÔFÆ[xôütõПØÍm;4»ŽŒé‰üEæÅç®3`¿P8½b3Ôž[+«%åžõ<Õ£ç×]'Mâíiuˆ®××fK˪ß¼S ûýó™•eÏæÓÌÁìj-W©¼9GÓF‘å0¿µõ*]XZs§øm¹œ®IöÃÕñluÓ˜ÃX}ì¬O?xY™vVÎæý½W;ûæÙç·{bôùš5õüsÎ\Ïm¯â¯5ÅÍe£|¶ðҜ̽ŒÓVFƻߑœLÃÏ,6Å_äOï`Ûzƒ§Ë|;þ@iY)*³”.øn:¿o~h¬!Ë’Ú“‘ ùøSYdzÝ)G¾‚ïu±W~%~c‘YP›=V‰uPmQåd¤*h¦4•Bï"m‘…u¾Â"z»UôQþÔ=†º;“ï,â)›ñ²Ó•’•“^¬].¾‰»à€ýe)Ñ–Îà­Ü;´ˆžaïyÕ)›Í`?uwÇ2[ÕùÉ¢â/¯wê02Þ=–+ƒ¦Ü½C"Uæ_q«ä£Ÿ2ÇNŒGL dÞŽ[Õ™*½sVÚrgg-KK¹r¢îXî´™wujÏJ»p¹§™qŸ,tñ²»6¹Î»|ÚóQ=.u—˜1]£ºÒ™µÌŒžÈ X*‹q ÷âÍC&©´¥À~d±íêDî/ìr5úèǸì•A ›/Í›†é±¥ø¬ã¾˜S»üKº?n©ïÍ]¨ ü«­Ï½0SËì_\n)±r«Ñs`d¶j…UÓ¼__?U}âNz ÙäöÍøRá´}8½ºhìά½eéç q"!ôjIÉ|j&C1ñEÁ3 ÕÌ •²•õ^(ÑoêãÚ”¿øa»±¯ŸoRènÐ „‘ñ ¾iøkéÙ '¿õò›“üJö´œÙ¯¼Í´7nÖf¶ç'»ŸV «áx#R*ö§:Ê—|`ããá·Ìn.»FKW7]£4PHaÝ-Mþbéí~Æÿ´¹ŸßlNT@Æ}u²‘Ùz÷FÉɺfoǯ×[»å¦ý¶½¬:¶?ÍźÃÜýºÃýš¤‹Hw@a¾ÿö¤¼•¿xÓ:˜¯ì}TbóÚ·œÈÛ¶õìãýÊЪÐÈxRêÌP4M4ì÷™ÝBþë»Ó\å«7¿xq3~©äÛiSõÉÊßß?¯£réŽ|¤Å؃êI§(5“uaÿ&Í›Ží+ùËôÔKùÀ{å‹Ôv_ä®fŽsó••å=VÝýÔ¥‹\«i?Wè(ÂjKŽÁÞ+v¿ú¨=ãÁ¶Ìy=9¹K©˜X‘yÞY
+™jíÍ¡=óE>®ò^çæ?å·¶–æET³Þ¤ kæÅda!³ÿ,Ðw“jç?œ÷– ™•¬7ÞpóÅêxßþSÝ›R}s²½t.¿µñ,§vËî´4{»w\X5Ú§jÌkg…ÑéYS©°oñNh©•cQËðe(ÖžžœåÒ?ô?GwW«ãÝ·oº “7™ðêÓë±B~oç 0ÞpÔæò®|¹jg?8µ¨Å õWGÀâ^Æ?ËeÔÿ_­.¥ç¦~™&Öa,þ¤2­Ùýklì:ãLfß®M¶ÎN°‘˜½e´f?pÔÂMßù•ƒí¬5uq8°óÅæ9Tæ­Lýjw:©9Ý·R9í#ïÞ‰ß?ò
+2wõqé[!³·ÚXØiÝÖ»Mƒž÷Í ÷ó¥\ñ:?]™/ùKöZyíÃÅÆZlžd–µâ
+8›Š™ =÷ ¶un[ì±½þá—Wó…üm†×’o”Äñ"/Ô2;êü<œÊ^/.¥µ€ÒSâ¥ÚànÏ;À Ù u¡ožeݩ¼ÚÇÎò—‰ÝR??Ó½¬ûîmf¿¾ýÅ[ù6uT™ÑÇÙKÛk³'Y¥ï{ÖMÇ8ËÎRg流걕oÓ›ù³÷å}µÚV^­Ÿ6¾pŒ—Ï=޶.« <]óšo^½T¢ÏKµEo}˲J‘uÔz3ÙÆ|ZòÞîÏ«•úv¶0nN*½ùÍþl~sôÂH¶@27LïèÛE†GBa|Û_è);άæXaâÃe-Q?™¡éh ýZoõÙ
+í|Æ
+ÆNÆ[Áé͘®›¯æÆ/>.T²viq÷òãõ’̵—¾¼›Z~?7mŒ¯¬|z¿øòùÖ'÷åçëWÓ«yÏneþŽÎ+AY%
+Â+˜E(‚(¨`É9A@Ì9ïÿòßÇ™ùè>§N¶ZWî{j³qZxaâ¢u]õåsadÄ™³í¬}dh0ä$öò-sð;i1RĆÕÉÅui”Kô˜Je@£– j¸©£7«<~N_5LÔ‡³Óv¾KéÌsr3*¥e5ùdö_/Â<ï©Ä—‡–]›Àšפ—C¦[Ò¹ù¶âÊ“~äÕáïÅ‹öä.Õÿýë¨ÂR=÷ÃêM>†Ë¯,‡Ne°¼Ð¹‰5Ö‹¡Mm‡Bö] ÚL{u:U§ ïï´œI4ÑG†ïÏõìèÚäšõÅøÝÝÜ[þ¦{Ž?p³=,¼-e°ööCðúNßM„}³×šíWʵw  8œíÒþÈ ‚}*c§ÉɺuÀs{: Ï«… \ýxrÙ-Uã:Ý4ãë•’#̓ÛûÞî*
+࣊××°_Ïö´T~F;´üê-vù+Q¹ö»KÈ¿w0L±O¡m™•;›‘ÔKø8wµoÅ8·Î`oö¯ŸùÇ’ŒÓ±
+ã²_͆ɨ63¹ÍXû°W¹îý¸kcøz¾÷×vëêÜmvòtÖÜ5;ͧï6$ñûv,²ÎdG­:»n¥°gP|àŒÐË\ëw•írØkëÀ¼·«â3Œ£GS¼Ø‹B’_MïSƒm6
+S¹nA³9a‡+k÷ѶO½îÎá±ä‚F‡vÃ:{ƒSÞõ®&ñe®Gp»÷ ²ÚÜ ç°=P_zƒôûFƒ}nQúŒu½SY¡NÚ]êŸÈw?OÛÏS+=Ð'“¼ågÝÒ÷£õnÛNrMs× 7;óŽlE~™ìZžØí5ö¶—Ž’~èôÝîᦗ?ð¨"Oã„v6ÀéT»OÏòöæ^ÚDv¼ìL¸ŠÓöêÖ–7ÊíˆÃØ] ðóÉÚÜã”ü~O‘æ¯Î æ‹’ñïEÅ¿¿—tøOke¼´9HåaþÀ»{ÚTB"Ûã7Õ?»¹Ór¯Ú/‡¥ê9 ­š¸BÊõÚLŒëë&7è¯Ã
+µ4‡kE¦…nµŸß]¤`JÒóU|€ š3v9!
+.o&h~¸þš†W*3¤ayÜ$‡AýÔ!ˆúªF<F+ˆ4ÒSuÔ•ºÍÑ©ÌtÆRcऩÀãÉFpù)«!ö¬ï×…nòùü€TyË·©0{ éÑŠ£è·qΨ§3}Ðü÷^þÉcµÒÃæ:“Êí+†goìP€¬j]ˆ[þM+Õà_kŽ’Ìmɨ.å=ì ÖN* jÈ‚om¸?)Ú­@º’(¾ÑÄ(ÌÈŽ£ƒ97Æ‚ù“Ķ妇»Ý-RûÚE}GU¾Û¸©»çú?Ð[}ÜoRöÒßµÉ^ šÌ=l+·Uxü¥þRoüVž;×èõ¶84ÖŠJ¸ZÒ@ûar¬ÍF©YÓJI“5àaâ:#,—)è½Á¤‘¿ySÌàÜ.OØvGoâî¾A\äý4‚ƒ2¼.èçl¯
+µU¸Ñî4âxÔY„€ç‹‡Ü m‡Ê³a¹[Tûݘ*«=Ò½{í`Š8Up¸l^ñ$‘ý‰6JþÀþK> CœøÃwj 7*kôªV'K›Ð&Ýϸ6Â+7ó_]–Æ|¾a&[ÝM–U
+™]GŸ^Áõu&IÞ¼v(©óèpYSDºüRWAhiÒ¢þÀE­¼ÓK‡Ž™aõ¾gÎüìÁòNøæ*úá]ئӃï•.W~›ÞŽÕ߬…w.EM ¶ ^ŠoÀH^Œº|Z—Î
+«ÒžZ¢O Õêàm­]‰ž –<Z‰NÞ–ª~ù §†P½ f…r!ÓÝ2o«ËÌïÖ¶E_m
+õžöG(W³µÄÜN[—Ý\òŽÕ/ ¼ßØÚøÎS]Ø'ƒƒ3‡?B¹qÝ,›½}ú.ó5°‰˜½ýŠ«#¾§\â®%Nªu4M²DSvS&3Xø ²½„ŒÖjs+æX/ZçÏW18ƒ°˜3óøîêÃu¸;ÈÕÅÞijáa¤æƒ#4mM µÜ‹©r2ŸÚæL•×µK¯öÚ^ÁXv¯·—hÝÖÏWz÷ÇSô¡6Ëá“&Ͼ¦6o(ØUßCß﹩HÍûj,UY‘Îýƒc‰$ªRiºx[åª[¸>Ñž¢ªµlfnáêB\n-‰dA®9PêÜåè»òýLÎVsµŸ“¡h‡y?ü4<J!K$ † b!Và._Ëín­¯1(÷m¶+±Ýù¬è£#ä¦^·)±{—àõJûŒôõóÄÁ£ˆí5Ö& ¹3=­á\íNtJL“õ¥QZlFJJ°áZØÑS{6¹9À£
+w4ÂÒ-¥ÍÔÙ³R„NJGÀøYO¿îCqìf¾½Âsm¾©¶Ä¬ÝÙ>é J†UÈuW?-³euˆºobž
+ 9!߀íþ¹ŸÑOA½›m|ײ%—ø¶Þ`§E–О¿ä%°°‘[
+UaØÔ¾ËÃxõ¸©]ÞúSð=¾.Û/ñ%D£pT}9ÙM;6íº
+æ“Ãî™1ŽH´®Bû–gÚÅñÙ ”ïú…ãt<™ˆÓ㌓݈±°MØÝ‹K
+êïñëˆæÚ’¬“Ò‘-KUy‘ªc
+¹©·µŠÓ´õ¬Y¥ð]è˜zŒ”Ì4™ò×À»¨–÷€Öª+Y»Pí«Â4µ«ÄH2šVÖ1Œï«î®˜]¯§ÖR«L0ÎbÓ~$(§¶Oó¸Ùð”yèô<EH¾‰ .Ã?Ð@¦«Rßã{¨FuXUM{¤íävRÁðA<T6z%ÆV|]á3Ù+¶kˆÔõ³+j"qX€ZWŽ[¤yøš™ÒÞN³ÝùÕhL4Ô-‚EFvó{æ7ï§ZOWD¾YæE­KCiÛ#±¾¦©3¦3yðDåVƒJQ–Ö§ñUBdi9ÙÕ”¹ÚÚæ-£7ðnËô”±y¬Q<šaþæÕº]¹.Sd«r‚³×¬µœÝÐû6Öê[gÒZªæep÷~LßýWlU_ )pE/äÇxàÜryA™ÿÊÚ·;­øÞe¦8õiÃäÇìäŸZš{$09u(iJ¹-X“²i”>)=÷”çP8[»ÛWÁ\ ,NFš¡‡ß}>ú5t9ëPWó6.Ý
+$¹¾ðëÆ´€¼{¿3RŠ-B½Ñè?pèn–wÉ}ð%%ãK¥]*rcåH€ÍH«y‘†3¤j¬ÅyU®^5µ;ãÊú¢”Ñór w¥¡M´¹¹%fl•ä·µ…½Ú®¥\>Z@löÿ@“(Œ½ãÝK;c$õ=L{–v‘öÖDB³ØÝzX±Ë¹úkVõÙ†kÀ–@;
+@<æûn—›á0sêÍl —•ª¶ëS
+‡Oí.¯N3š¬Eq_W‹Ã§Ó£ò‘~T뙺ºíÈl=­¤ámã²øöýcMÜ]_úèó+Úàˆ6Øò‘S5TXäkô:šG ÀUíÌ5l"ò X¦E E[6ßx¼ìtÁ¶þ”²ò0µ­‚››æŸÏôžu4'§¯eÕþjíëÛ
+ZPm-hçé5æ5¿GcÑÑ|zónº]]=Â2¦?é£ÝæÎoÝxõɉõÀG›·Ž£RøÖWÉÞ׉ªñ[ÏãÙÖ½ZÐI>êVgôk^?uÇÄJ»ŸfÎ’òeùœ_ò%H³³VrÓÓƒäûf•ê˜)Ù…è§QVÜ÷ñŒø80zöuö#õvz]—[’¹’
+¬9Y×Å\¿sö¢gNÏ-3z¯DÌM”àô•¢â^2f]äJñ¢pèäj6ã1p¶Àr·ªu2öŽ—ÅºÜûI¶ò
+úì1.¯zûwb*êD¤íô6^bIƒÖ¯=Çv û걺Ö^ÔÊ)×¶l¯˜È|÷±m£¨ú`Ȧ¼×/©óL†bÒ ZŽK‚ LHÅjí˜úžø¹ üœæÖl×ú@»—¾ÒK6`ÿag%íN]‰É=Ée Îþ¤
+­Ùi1+rò€‘š×°–Àèe…÷£v·Óº7þT}îûH8…2„¹S–w
+l1z¢º·v7áX}WÙ% hÍ–JB²§R5×§æ­±mì2}1„?úæ‚—}yÒÅ<‚EÞ¯7d”¶Ô:~ì;R†·æÐ~ Y„Ï­Y#V»ùÞãM™èæ¸ýR^•±ÔÞf:e3U°¾mÊ®Öë+\ŤJ®ØaÇvת„‚W·ö)¾ÔÌyqë 7GF
+ t»qÓ9ÿòKÐö;)­ý‹ýQO ‡}Õ#dúY¨6òhìíiªŒ `[€T»—nY5N¨bTý \«ÑI›ùàq†ºÈïû§±E¹w¤Ø£Ft©1>ïéí¬hf…‘¬|Ú¨&•EaWÖ¢%`bIY®uC.ê÷TŸ%ñì†ÐÄédg!Ôüß¾ˆ‹¨dWî´Q ”'Ró’U3ŽCPiÌ]~ÿ…ñÓÎñ9µÀYþ<„¹0ÿåö* ¸Ðy²º/¿ÎøJ/œ Ó{K[tzo}‰b¤Ä2ì%™›gådýPï\‹Zþj„N&¯,fðBªÉtÔ&¡ëZ¼õƒo¬ûv©þü$vÓÓþhKú–nP“â‰Í"ó& r
+•ðV([ ÉLæ7½b"{y×AõÙ’é0Óp`u.'$¡èj ,KgVC¬ívIÁÃ5)ð^¦ÒßÅÆç„ÕîóA=Z€·LAò% e’ŽçƒDG«ÂÁš‹õˆ¶+šÏ}]ßvV@ÏÄÓÊp¬×ÀÃkã µìÓAu=Ö)0лîè7^MÊè­—6°å3§$¼ [™$”%gNÃ{*Éàl×Õ-oÂVº?k(ŸQÐÉ %©)<ÚîG‡Þõxº³4Ø,¨7*-¢¶Ä8>Ø3¸%»áË!\êòÓ^iÀ¼Åë¥4œ†—±rúã¯Ù;2ßÄŠýþÐh…Z]ê‘=?ãܬ>ï{6´¬¶«H´¾srE²¼,@Þÿ6Ç[&‘½s¥Jk>ˆ*¿r%Ë„>‹óCØF<ñ7x™Á¸3Y¤Wïˈww >ÐìuŸ¨yé ©þz—ºñã/ý’ï/E—ê™ÊEj™ˆº?HäàÈr!Á¶ÖÈ…F›/Åu/@®z2èmR küÜö©€âK£°ô„@Âø&1ˆh ! DF¿{l)Ú)‡W¢Á
+{é_7•ÐÜs‰†6´ér¢¥bØÿ=ôR©Ý‰ôÙ¯Š>? ’!‡å‰Ü­”Τtv=KåØÊK–C¦¨1ª*dÙ¶”˜Ô>Ž`ýv]l㤨íѱõ’`ᲟÎáýõFÚõƒÙk9øþYä
+ 7¹ø ™Ö ¿îŽãøÞš ´.ïb<úÑßeÃ_¶—#ÙU†»¤Ì†"¡Jmu&3óˆ5áed•©“(HR©‹1ëèÉÀí#DémØøžø,B Sêæi•èä^*@pÿÌá¢5.ù‚z哯ã´_»sÄ5ÛûÏ’×fH·!ÕIgu Zþ®eÎ*ò^+
+
+¬ÔïÉÓ¦4˜íÐOà G݇Î$°Vl—÷Ÿá «óÅUü!]¨ÛŠÁÍ&åmÓ­—¿Ç@ê?Lj>ÚOÁÍÈñaþx¯…úpá Guù[gðð7´ë‹ážßÃ\0œk$2 ÛÕ/Ù>s–ª§eá“ïýÅ
+8=L²Äiµ+« ±8ëÔ²ÿJ1¹wÜÛM
+²t]Õr
+£ÇR6È€7'×_¯ë¾XoG
+Ïéä
+Ò.¾*›ü»'¡³—¹î”±šÙ·§â§ Ö¶I]uÝ~ âõ·²Yõµáœ‹/LH»SïP+v´*½%bd¥©<\Ó÷h1'É ô”åÑb:#z|÷%Ukeââ#‘ôA°áèÖebŠ›¢æ¹*ï— 1äÞÍl°S?‚óÈ…h«PË—zÁœG°žŠôŠ èÝŽ, ¨¦¢$÷Fè!ô¶f)$1¸ß}rÖÓx Ö-qV‹6ÁEBwá¨NÁbWú6%Ä3¯£T\±0&©‹vÕöªØqÞ·Hï7µeG¬8m‹Ôÿ¹×d!Î ±þ>*æˆù|–r…ƒD0rB𠏻(­žØþð'”/½îŽÆ„ ˆ!OkänÛEÂDlJ¿`
+Tþ}òÊÏ7áÑ‘Šp=®·#ÖŸ—Öe²sõZpñM±Ö<2`˯ϕר¤jLpIV»æû! «fm/ j.óÖpk*7m~¥¶oG¨ø]Oáæâ¬Pþæ
+6ÿŽÔª%>6mß“èæXUuêz/ó7’ªzï¬ì¥—%\ïÍ•ø8 BÌ€*áÖ†¦øœ¬6JÝ0;•C6¥¾2T³®˜«V«Øâß· )y™˜éáHb”…d
+‚ UÏz”|¸'oy«JDqݱ¬ÌУÒð˜Ã¦rQQ³R he×Û
+^5ù0ºof
+«˜ÐЪSm‚`„¼|̾Ã/iI\¥ÛDVÜ}¥2 '+…œR–òìi¸_@{q¨Ò;mmW¿LÞº•ewþãæLUõ³™2Qx
+s qFѕ׻¾é¸Z­cY·(¿âwœºJ²BW¡üS|‰/ì3j¶Ò<ºB#î
+fÿò†]龸¹CèÙ•ÜØ”X)¹+©ü{1?… ViÔ˜ŒŠôš Ü”•÷½¦ÀaeZzGÑL\û½°uȯ’ƒóÎ}<ì—%õÔÖÒÙr1ãrá]1ÒwµÛhÐç»’¥ËÚvþ.1Àë:«É»¸û»ùW¨k­^Ž U²V"8,åfåÞÂC`¡Åbvï>õU+ º'\E§6¸kî9OjŠÞQþ©{¬Šµu·E‰»ó ¬în÷¨p3z¤|¥Cq/ú¯ýÎ-ɯùpˆuµmk8`Gß["/«žÞ™Ð?¶À¯ùEZà‡Y¿||œ+rë>ÖŒñ…BüA_tq$ : ÕoòRn]µáýL«_æ9|/§B-Uºü«¨˜cmèݪ¥òKV¾sÂ\îøÏ•~GaŠ™hÚÔÔkCëžà7ãÏ·QÒpB•þèT´«¦[Ê]Þje¯2èËx\éÇh#Æø<¿.…‚„fÔné㱋½ªŠÎgÂd·Èäö|¬lÏe+J’‰áËÀQ™'•hÕ[,‚pSæ×zɽÌÆFkûàXª‡£25Åwx
+û•BNbrñT³«ÿÁ½Â.}£/Vëé<
+;WlªŠ5 ©4ᨲVM_ø¥z“înz+­ù3ì&<Öt£Þ) n•‘4ž½Ö}õóíÆR}›Ú&ܰ<ñæûÎí ¤EW:è}Mª? °áŽêjü乆éÉ‚4Û?*Vû#jÃ;y Ù6ÏÏI¬Âì½dñ~n .ØšŸ+—GŒü·vêlM'”s†|ICª“F ǰv¡–¤ž ­èˆ¬­uäV¸¦N«Ý¿s÷b–µ<)ë³*¿'{¯ç#¨ŠIªõˆÊîö¢¯YitÊð%7™@7k¾tØ‘\bj?.ÞüâÂõ•?p6-¾Õ¶ÖªÆ×»­>²ä㸟Y£Aµÿ爫*)ñå×ëj `\Áf‚»ÝP©°Å ÂO£9»{ùfÃÝÄfõ4³p¹¯»ú¹#Îk¥Â]hM¬Ìº½Òð,ß¿7ÏýEÝT²Sà*ÆÜÅ>†§÷ô è?é]*1øTÐ|òþIGpí™Æeñ,rÎ3—šWRôνáKO.H¤µ—aDZÒW‰ñ”D-j¬ŸP[Ë qHG󉲬©ÙºP½:E1޲™RU^[š³=K¦Ì/—ɺ8Uߨµgš=¥)—ñ2KOS½î”£æmþCÎÆkþIþG/Ë¥_õh/<êý÷ògs)Örš‹É´JíÈ\»õ›oѰ¼é´ mbõŒâ›zÆݼîyµ"]‡T†O
+¥Ä­hXô_ôÜ­Nù"‹Y·Oy>…ä²û’Œ› óL£h‘·-å0à C¢ä ]ßkuN{ÜÀnP$Ë> }u±CÙ“ÌØÜÃAu†­•[ë<Ïy’”z­¦‡ÿ*yÊ%Jb§¸ý³ÝdEQæÛ9QráÿÊÈxIùªj…íü8Ùì~õ
+?罂FëÉ Èî_0¿Ö#Ä=]¬5"¸é®r°þ@j½±I.½
+kc->¼W渴—P9N²ç±jHU-õ;æmÚÝjý¶~¢üÌí6ïNeùn¶ÝåðÔŸ*xoþq‘SKÿ#ìgtJ¹?{6+9ÁL«†TJ^,ølDnÞñ¦Tµ “ÁŽóD§›$ž ã_|;Æu^Y7êÞ¿“-aìVZ´¦Î¥µÉ¼í¬ìw㬲.z¿±9,œôÛùV6G+Ñ…ådkˆ)J M॓9rÔ~r:È»d
+í €Ç—v·‚¯÷•¡€´Æ¸¬3_¥žà0©lÓàktþ©‰ÿú‡B®½*0½w§'-îX)iµ~>¹»©ì@ÎòáÇÒµ¯ ñÓ=Ea&‡eQè®$‚<^åÖtÍ ËAåXÔxÞ;©Œ*µ™q ÷
+ºžtâZÞà‘’åÌ”Ã~u1¨ßZX*ße—ö;?ürÿdOjûcÚ®iÛ¯8\±\×'W»:­^ÞÑ`Ù!ùü,Pv°jÎ7vÐÛäa9gýŸ<Q>‰Üô…àеkáÂKirKtu¦2e°@EœÂâxx÷ Ý‚ýN ±^ÀeM'ë}Ó{ÉÆ?kò,òK&~.03lÏ®Æ;¾¹Ÿ™ÎÄüRÎ>t›>…ô?~ÉŒª&ã«Ó
+NÓêsdp‘²‘æýåÝø’G׺ÜÈáB§ÅŠÕòpØ©¹ý¨é‚-õD-ÌJ2„B?`c³¥Gø»ÿsü&^ZÜ´ÞÏÂh|‚šÏd«ºdãÍH[Ëœp\åVƳò [ Džüá;XìõBeô8ÊžEOêœ;Ù»åðÉ:¥Uáü¥ìÆØÜÜð·Ã…INÙ>ì¹5-÷7v>œ¸Ò|ü.OeâàÄgÈÄ×ý¨øœ~ŠüîôËP¥ i©™Ž+퀽~eW<ù®gšÊjÀéSo%>’D¢—‚yìží¤ª?ãÐ;ôúò·É9‘òrMÞì½)–;ï‘8F¶IÐ3[‘X:b…·™~‹íN¬ïѬ/e’徬ˆ³kJ<Šˆ
+Z¼Gˆ“ÇO¶QÄ'ôª¹WZÇ;,<*"k¤e ”ßh,ÖŒ
+K}ÏñAn§q¬`ÚÚ…·”‹KY\û»òvÄ sý^&¾îŒž !,¸ ½&B©¾S=ïtΗ³A¬ÍîYÉîŠ#6 ¿ –¢n$gUd/
+æÿ拽ÝTxWö—|Zƒ;þøVÖùçžù ³ß;— 
+Í>(TH<+s–Ùñv>Řû¡ÓÕ÷™ñt’š¹Ô­(ÝŠ×EtqÞ£¥€+iãâ¥ÌÕØyA它PChŒ¾X¶õ“˜í¨å²Âk¯µã„ ÊÞ—ÂE)­ßîu^œ«Ú‰»Y{8Ç ŠŸUú}9E”pªuP¡twbãœh)oÛ® ¿šÌ¿SÙHT#ø¾1;É }¹Î¨c
+gm\„„âÿ¦éÄb½RžË>!,²²˜/›ÞtYû ¹}³šžŒÿŽ ÜV}¨ÐMq)]È’îwg¹ ¹¨±OÒ+Îï
+Ò¹wŠ»ï
+ÿSŒæ¿O Ý©­A4cL@䣭7îb–)U`>oNm{õ·´‰#$œ«@]:—¶G­z¬í;j‘«}l-Ý8ROïpV CÁ•lêºPëί:6`hYì Åú€­æµ7¢¤G †gË{»ÛÕ·µSŸ @ëÊ¢²RÇ2õSkØä@WcVq8 P„DÄq¿Ï”Z“©+dwÿ+­Ñ>*=×j+±]øä_}…>–>ÔŽmtD€ÝQúôpö~r¨ÞãÉX#XIþúŒÔÇ Z½C·¡¼ŒÏ»eOÀçŽ@Ÿæê8zÚÁjÖ’Ù‡ŠôgôŒÿÈfi€”‹®|ÆPˆ9d¥­ï÷HN&¶ä½hD2é
+®ú¯¦æÐÇ©åé%ÌWÒJÿT(viã²óSÊÝÕl$)·Ìâ¸%« ûjv¨óƬ >°Üúax˜­¯…&/ožÔUÝ´ñ—†ôt])/6¡®Ø t—@í$ÛÍ÷ ˆ7Éé¶šLx«]É(esI±ö¼J{;§,_—q1­ŠÀ\S¡d¶n¡Žÿ0yE@Ò¿ßiU5Ÿh!Sö8üØ"SM6ž´c=ÃG‘ÁŒ‰FéÙüm1L±“Q‰å «løþ›'…q(öhézá!{€¹û,Su~ðêý;ÎðÜàüïiÏܶTsëõ2>ù€q‡ 'gRÞ“ÿvÇ}i[di÷„‘`M´ö4‚Á2ýÿL’òµús/o–¤É(göNõµR™ì]iàïú5màsDÍŠ$.Á}¼_ìÛÄêîÚÇŸT›úk͇ùß°°}JÍ+?1íO²5›Ã­¦VAæltNó=û
+Ý©²ˆ6=í ú„LÕòsbõ´vK,7F—._ÈQ®´ª÷M‘‘+'›2¨ß 3t˜kÊ3™©éxŽ˜:Þ\ë3÷
+->mÐÂõm`âcFX³ÕV–¼˜Ï¨Ì0Q¥zJ„…x˜¶Ìšû†ùâ LÙܾ—t†ô* ¥ãå±G¨0Þµd_^M¹Ìˆ|‚@Ìæ"‹Ñ†6üs®ýÿ 6((ÏܱF…zm­4Ö+qmeÅÓÔ¦û„2ÐÕFË'§^_+U*þ¼uŸÕåÏ Õ†
+Î¥ð™â1Ç-º#ö\ÿa<gŽñ;Ú…ç7m̨Œ)ÄáõÍ(¶ ¸­ÿ:»Òù¤“ÿ1užÛŠrM»>‚>TTDÅ€ Šäœ3ˆ9GÌžÿfõzûùöÏ£—\L*ÜUs
+Ÿ½ý\fRâç…ù%ƒ‚úÊßBj·ú8{yxÍØSÚ½ش
+ޝ»6Ö ÉU‹-„ÃaÅÞÂËÜ’Ý!TМ¶qsÎNS¯ëƒãÕS®õsÇ.Ü‚¶‹rM–@ìt˜[Ë^·qwÕ¯¾Œ-—æ`A·‘ƒœc?»W_¹L¹ÒãÇùD{
+W*»ï¹>W*þ³wSeGøøæåñÓ¬pšÁú,ÁÉQkÓ¥£gMÞïAëç°ÇöXXÔóÏ~cÖ}]Ö±Ž8UÜsös#¯’°uÒa¿ºÔHGσv˜°Ön8'¢µÕ˜•¢z03w¶µkþÄÌΈp½Åê 4¸÷Ý5‹Þ]÷‚OÔÉA¬\j]2žw^GeyƺÎ.å&äÜ£Jí0·d§4ÄúC dw§Y=µž£´ì>°„
+ˆ&GTÛñ+çºó¼ÐvH.ÞˆaóÜuÉTi{èj6úÏÎûèÓ9í^(ÄùAùù ˆYòë€W§áÕÃââžQ޽‚†
+”¼«W*tµwçêÓ0Øtg:
+–KAym¸ÞÑ?p÷œOíBuZndwQ{â §yߧȺE‡‚‘,–¯r“«`'«y*>b‘ªv€ºš“Ž”_©ƒC\Œ:Óìð]ŽZìkêd!”}3ëΉy#·1¤ŸF1û¨*Õö¨’Ù
+€5–ëeÉãŒ*ÕãRÏTéë™uÙZuÑIÀÌ ¹[änòSêeVØ~!yŠà
+@ç i{H|B`‡®¦@ŒÆJyV”Ëü<N²Š‘*7G†'¥pŠÄŒí*½¥ ×½ésßÁ#S’á žØ/Eä ûš›û¾1Ó}óËê3Ym ã.¾ìm žÅÖÛ<†C(Éœ_¶œ1í­~F<v5™8cS“ÆÏm¿a_%‰ƒÑ­1Ÿ
+3yWöïÒÃL£wàˆa)ú9cUÖJ€¡žÏö ’÷úv¥? ?.‡T…?0mب$šyÙa©í*~2
+óç—%€áDÅ™Ò\O6²Û]¶]Ke:ÝD_´¦?š>ÅÔñ§îl‰©ý=–r5ýý½ôCµ}–!³ÔÔÍ/ëù£AFÙúèm*êÈNãánùUÔ ÕíT»ÓwzÝÝ+G}^5éb/‹Ž _G°ÅÄ‹aª35¯‘µû÷ÎuûwE4òèyÒnÜág²Âû34}
+ÅŒ;ïŠî$™Ðzóå>%ẞ9C:µÕí¯å°kbìj¶ã¿kç‡[²|ÛõÁpQ\Ãóz?”Ùf¤·;òQÝ_¦Ÿ*·¢6Í._òÂÀÍ«¡q+ÖìÜòÕÙ÷žÖç9ó"验t¢V§],ž±½Ý$Úò”Åâ]p_a/V3üfȰ*èó÷ãDƒèU¥­ës'‘uû¯^‹†Y{29µí†–­ïŒn·ÐxÜ kgº»,=ñØ~”¨í–Vêï°±ð‚ OMõðŒÔ m2÷ýû*Ám”`h^¼Qo{ÕÞdBpkÿ$4‰°Î5åÙW{[T¤ÂÞÍÔ‘0E|ZO¯#[Ù_ ]H\LÊ>E©'Ÿ«D©»¶uš0CJûšy,Ýn^Óª‚¾-ù­¥,LpÂ" ­à§“$¨–SûØÏ¸=´Æà`¦LBjk]IŠ{ç£ì†ÖÄ*T›6þ=éMå=MSJ¯ê¥É•oy³Ð0Uͯâ#ªøÑÕeqb½¢6OŽ®#ªb[>°Fw¸T×®coppÑ.Á˜¬Í‹EÁd>úíßš”76¼Ôó;=¿ó?c:Vm!À<_<\Ì~­GK/#n1½¯1¶Ã±¸ùjˆ,ú†.YÑ߇ãù÷ÖÈZ.d‘ãW‚Æ¥ë &”™l§ÅŽøÆ³AÈ ò
+aäµ&Å^ÑÚÝ©(ùäo¶Ð³BK¿ä)¾q÷ÍÛ7ƒgðá2'âÜGÁr¼º¸à°ZFô~ÔŒV%èT9¢þ°ëIúé»…ãá‹ù”®g )$=®n¼®Ä÷H~æÑW§°gô"+kqÞôl½¸5ŠTúW†~L‹¼Îfû°ÃÆ(¶%Ía¯qåL›™E§äÅÜÊa»Æí2ì¡ïå©TíháÃÎߟ™ÞÛ)!K.ýV'>šMb©g {~SÓîaºkÍБ…u·=qvsÞ¼¥Å£h7º™«¹!þD•à
+ãÇÍÐ.†—¥Œö ò 3牙+%‰LÛÙŽg|y”šRÖæ!§R}ÞËÝúC•nuýi\¥©ÒÜ®.?É¥ÈvE\”aó9_¸k<œ(ú6Ø×Aâ4ƒçÕSR"(9ƒgùÛƒý»&Qè-Œ¢‘Ê`¨-œÖÇÅ'i¡éÙN3Ð.mÕG„~†iQc­õÕî­§ÇrËëêåÿ¶´¬˜Êûó-‰n<k…b÷sµŒW|oç^‰¥Ya0®&šÁÓTßn.z^“<ìFƒn˜ö´ßÿ@<ăƣ‰z*OûôœŠN<ÎWæ¼,¬ RàlJ¸VÑD©O-ø½ÝX¶´µÁoªæ
+Öì67ä/JïØ}øÈÝÆøzŽ£vËÝîÙƒ=·•õ
+E´ó¹½ïW(ºŸ¥¿½'ãŽ×?µ…ìqûÐT¹'À¸$–êõé_^×\^ÌðÓ²BŽd®ç¼FžVGBиMžýNÃÃ¥þ®Úöoì ôš#ÅÆaä½NÛÆ?ö.²¾*-M $–×þá«x>‡ó¦ü†' w—&p‡&š‰Ò™~]¯Fœ/Àâša_Š0_òQ´c¹*¤°ží!6[´jZé%zþpñ2ðÓä«Ïùæëf™y…wkLRï×oêÎJtü¤Ù WƒÃN©‹nXŸG~ÿt\·È£|©`3þzH'Nåã%Ó—±Xr‰Aw©<Ñ™J
+ü‚ðèdóµ
+˜¼½aÍ–5·Ü1=ô&øÀM¡ šÇ3Úq»19„ÇÈ!Éævó!œ‰Žþ¡Àƒ«®>âsÓÉËñsž^©ÅÔ'»Ûu¨\¸ÑéF´ÏTWµg¯àòÓ,ž¥Ä(?©à)̯ÁÄ{A=ár5ÎøÒÚï ýЇÚÛ-Þ†}¯Ü™FÎR¨S°Å…K¬@û¢Ì?H >*ÓÊ€}ˆ’æQö‹ï€šßUðü´R%ôQ{¬~Oþ#Jó¼ï´ÓJ£x:‚Èÿ¡È[påp)9ŒãU¦åÇï­oPì
+pm.§èÒò×ÿ?”Åmô[_n…ŠjºÍn<pä±GfU˜¢‡/ÉÂÊ*!·G`¥›8ƒÙ^¿ÊDšÄd•Ù㘔?•&……OK÷-¼?ë]N°ºN€ž ˆW?3Щ¦x®}þÌœäéîNÕ³ŠU?Áµ(îú–\ÚTÄÂ"pZm ÁÑÏ&·±<˜ûþÄæ`Õaµýêý¢(§ÛÈy—·\ìØ;†*¹,n“hÙj€«×·„;Yý| ÉZn”µ;6áÑ?É;±Ï@^‹qäNÜ{×Õ‰Ú~D°Å7ï.ÜI#¨D ©æG—–àØ/‡n±PGœvƒO¿Ý5iô Q¹¶µcô^GŸ4Öƒ F³ÁyÔï¢9Öýuͬ®Ù°ú)ìyÛÂíš[6Èö<¢æ\
+èÆ¼¬rÒä
+L~©bòbùá«:Ÿî×GÈÿŠ¿-øÅøƒ¤g
+Ó“”N‡…¾.•Ö2¥\à˜&!²ýišLkAœŸ´ŠHà¢? 4àÙœÍQ£«š‘Jk€MjVaž^†¤YÇ’7÷þ€uqVZÈõL¬žÛÍQlYÉûü…{iÉJù…×ä9( ƒË—](‡Úu?æÚl¯å-¯N}!×b| ²ë¿ÞÓJÿ@†­?àôèwÔ‹+÷up‚i|¯0‰Ìdmͳø¼u´ºTlLµ1ÀÛ1[¾Á9ˆZÕê{÷7sÇk·dú@³?é¬Ai°=:ãs’0¬|Ú3´‡ÿ 7ÎÜh©‡z¹ÆmÏÆ€é.=ºóüoMèt@Œiêv¶5Àc)¹wsÎÖú`±qÙÕê
+Èß­ybT"EÛ瑨,ÿ’ÛØêéòAî»é°äE?¹ÃGOÀÝiÝNǪïl£C=jÌØV¹›wsœÉ¾ûì,ÿåœ3‘N¾“®F=”ô´Ís4nœZ®Æo;á0šhßn]…ˈ›Õ .¼Œ¼Ÿož˜Ëÿ¡¬?/ôÆžÏLz+%´Ø>¡N_”⃡ØkÈ,¤]—XåR%œÝY7=“ë3Eq¦åz¬ÝÂß|éþ¡§XÍi‰k¡½pÔlÙÍ®H2•Drî:L†>¹ŽŸh©Ï×ÌVÒ[rJµq¹¾N«u®ê”÷™—øbbBjvë(ïE6Ðú6_1y¹¹KjyÄágÄõ³FÛU[C”—
+Ã1[ʸåT-šÍ.Y@ mi #wÁW‹ËŠÕ†W÷íuI &ó9S/†c½¹ß=E2|y­ŠÇ
+Úª;Ýå÷ÒÄg§åxÙ íoK¿³ý”
+ðùa`m7×7µXº dÕœîW° ý c¬klÉAzº)énÏÑÍûºÁìã2Ÿ"åOèÅð)á:Ÿ!l„Çd'‹â6¯®–"}nS?œ“Ä#~
+Uá}
+y®üRhOí`™3;lK5áS9\¥j x!ÍqC/iù<ÞÄ‘‡ô$³Ýd.ÇÆr?•;Ÿ:*gˆà{µ‘*ûê¶&‚©âij‹È+×:ЇãÈüêúÙÞ*áp¼“ì: PÀ¼+ fä¶Yz5 c{Tøa¦4U&VYOçØ\Èä’ãökŒ¤Ç:#¦DÜôúÕž¯-‰'NÅXÂ-hc˜vs;\ï~áé¼Q­{þ½dÑ\a†–sžÂabК?öÍèÔ§ÅH&ùÑðŠ }ÃJ£S¿ÕØ#¯žÑß-™I)nòîL}!¾¢PœFŽØ
+rßwƒ½"œè€_Ç™­G \¯¦bïä¿Ö( Ë⫽{q±¯S¤ÅãB¡{%<Øöv+¡¾…Úêmd+ÞfÊ…ÂÛZoÈÕì^p¾ó&&¦sú6d”ì÷ žÈ£ÌèODqÀŽkã§ÆkƒõÅ> ›@4–Ã¥@ÜÞ3¾kût€V—±n¶¡ž|’`O]¬Þ"±\럾wõ.,¸K!ü†á¸2T¾÷ägUÅ'¥6.Ò±ÈѪ¸ ï®)v©«ÐhTÞ”GEݳl%;žÕK¥¯Úù¾ë£ì(î´Ÿj*ãT7Ü’ÌÓbÓàjÀ³ôã÷ÜÆFÉð¡·Á
+²ý>ʦ\~­@ÿ„Mµ€Æâµ°&Üïv”DÓ1HJ˜Î¥
+djÕçè2· ¹sú â»È`h«±%Û¡;à”ð51ßÖ‹Lég3–‹Y4yK‘ —„ÕG›[mtYøµb;ƒ-f÷Î ˜íDu[ÍX[Ÿ¸V®ˆ½ž™•IËÄ8¯º—JB3Î+f[AÝ“*µŠ4›ƒáü¹ ›ß²4y6j¦ÜÇ«Ò «ÔeÄû°Æ±:×Éf˾ËA9+üà§=[Ë÷l
+çwJþ+Ì—KÏïF—ÈUí
+ÑT|³âí¨®¡ç6fÀ“‡Ô{N.g5À_œžw±R°V'Ußoœß6ßXð×éô§†öz"R?Ü4õ/.ÞôØhÇÔü¥{W~üóAÄÁ¶S§×g·2ÿ_ö2A …÷:ÚUUßÖ™œßôó,[1gÌŸX¥£¡ëtÐãzö[z˜t±èaÊh526ŽIü‚ ÞŽó*GÓ³þ³(î‚vÅyÅí>¼”«À!b<B{ýe+ÚmPj7_a°ÉÂó¡ígçÿ5‰]ýcÃ7Mmæµ/±Òg:{bhF\QOoü)dÚ‰«{M3]ãš¾4‹ƒ¶å‰ò.˜ŸIÇþ_Öº„]"~F`[·ŸÐR”’¸Á…}¬eù3Ø¿ý€xîyMÅ¿“æäóÎW¨ºýhh§þ0Ž⋟ý̦(ë.Cç¤%êÄ_ã\õ]Óld?;T¹;#k¼Pà†âhZ
+Þ1)´ê«Ÿ6°3FfxàLX:÷s®=ƒ+
+Z­ËÛ—d$¨ÓÉ©<´aa4Jcí.^§cG'¸ñ=XRq>½¯%Ÿç[ÒâUѾH—=ÅwDz‰µõT}©Ë6ȽÌQuÆòâÖªä:Mæ ùòÛæ;æ:A¹<^5ûÞ
+Š<0LEå{ÒyF¾ùWµ#eU«¥-ìúºµªênh¡+÷ß‹” Î8%k)Èm–êâµ³±¾]“åùï¤#Å×˪ĕaÑ¡ö¼„3Eºtɰ–Lþ\V–ñÛƒÕG“vßÂJ­…ø@ÓŸÆÞWK !…¸¯| šY䈲É*‹º=è‚ÆqËXfsáü׿ëê¶H¯BéV54ǶÐőßó’da…Úæ{ ›2Ñ”:ÓE­oÌòÂxˆåiIa®œ¡Uù๽?‡Ø’5ç0ŠkA
+ÙÓMä÷gSÚPÚ³¬Ä7è¾!‘ò_…åCŒu¾
+wq¦|?oÿ€ì'.‰~ V9ŒÔÓ44ÎCÂlTk®¬;¿EJ©Néö/He©‚)Ñšé"Ÿöóº|@qì%Á¬£WhÕ+­hõ©‹yB¹["‰þ€r°¢'v¿(Å
+¨ˆË-"ûv“cèEmœH6â½Ú¡càX¼I Qy»5YïÔÊ+h+Ý¡!åhÏ%ý¯0?¡þ®Ñ9ðë£~UFƒfž_<2µÁyvß:.8"Ê-ÆmjJ4gÚ}
+âlË*ÏĤûA·µ×V®íÅäçDc=õ¢«SÛEo‡Roêr#Ml›Z{FPö¹äÚRã/ÀÚî'aáÊNóÁ‘Šñ¬MS&4ÝA®?ê‘EZ©F`í‹á¬w­úì¶0ˆ\DÙ;ç :NWõÆÁýþ‚à‡aÁ¾ Ô×9!ùR·WÚ°á‡È8áq½î±W·„!,Ú–eûÐñ-o¬N#x[ §³ÿ@6€çYã6ß<ß¿^AK n­!½ÝNÔp²ýåk/*«r#>”c ø<ZÚõ ³*8eùg¨{ý¿5)×ÌwïŸíÆkj‰ž~Ö+¶'­zížÓñNµ#lƒã{š‚æŠAÝÜÔ[ÿ^£œ•K’Ñã˜Èm hÝ(;ôNøó¶›qÝfyŠ0È5|Úç¡É¶ž³ZôÓÀé ªåÿzN.Ôî%î€Ã˜Ú=rxš,ù4×Ρ)^·¼µG‚ü¼«6øÙ(·çø#˜;áîú@?û‡"•ã]±þd°*fˆ
+US‡ƒ”ðMåÌŠ¯]oŒô’ßpÙä#nc CüOûfËWûü¶ùn“}&{ÍÙôÑÃ
+;üZ*'¯žxÕ‹Ý9¡ð\~[»ž/ôy¸ü¾·%ù"€'b´Ç?‹N)/Ò÷ä½Qï•Ê#,äÙWÀNÝ?§kŸû.ãÿˆ¥\õí
+uÜB†›£täÿë$ñ &'¡ώùV‹âzùÛÓ;ÑÈ,è3Û<[<Så7;ßêɘÛ'áfÿažÞÑz@zv„:–•âÀZ#Ã>Só6È@oÚŸ¡F¬¹F£±Ç®?DRïÿ Rq±sQO(‡µMý3´ÏLß‘.}ÜÇžÒ¼#Õfeû@3ß? û¾ÖOågj¸V3ªµíQÝù§`Ï´Þl†¢cëòò©åùïñ—ÁáPñú_ô:C|Ðè¬j¢«kÙÔ]öž} €ž%­‘Ö_0|M½ü™"~ 'äWšõ$‹¨Òp¬%Ï#2eרÙÓd¢ºÅ‰”‡œ®©=¼$ÿ@
+ÝÉ÷2JHWP9n9¿Ç·E¡‡Š ÛæÍbk’4ín^‹Õ!@ðOâÎ%Äi–'Š2ò•Ù ÷û–‹iŒ[M!°j*·GLßë[´X…®QÂZoY`wE4ÏûKLªƒe X©°hl«-Ûù8ÿÞ眾yÞO?ð¨K2pv^äÙHÎV@‰h²–›:Q‹K%âZ£Ö"c>ëDÒQ~°}¨ÇSaõ„þl.#µöÆc¥[ßöÙa«£VŠ:³:!±;»ýL§c ê쟗RëÆðjÇ*¬­Ë£'ÿCù´Òæê\$1¨™éĽQM
+6
+Zœ·»wuì1õÝl7q©¨®Ì!Ê̬ð>j·é6ÏȆ×É"¥Ãp”Vîþ‰"¯à•¬I-‰._ම̾ãi½<¸µ 3¤¹Ð\9³6¸°ÁòÁî¥L¨4hyQ9ãôêåŒõÎÞ×t .å²Ï=_±¢vQ8ý
+[áÆ-¶Õø”æÝmΩ‚vš“5èøS}·¨É9;pï’„Ñ]ç†ôofcÉÕÄ {$˜NïHø'×µT®<«Š#+áõyýëŽìÕjž7v8~£œG׋º(?®q©þ
+eê¥E•—Ç"†–xŸ:”Ùþù›ë-/½ÊÞN)ç¹0{óvû‘ »%kº3D2nÿëðk;6ËßýS1­Æ#¾-ª#Ÿ÷+Õ×5^¨U÷4l[\Ÿlëòy:Cyð3B滯زëä'W¸ q¤­ZØ„Q[×±'m:L3ÿç4“…¸ äâ ×|Ï…Üå I;ë>Ü­zîò«"4ÏyIሽs Œýú"¶8ÿ3¾ï¹
+s<ÜHOƒ[,4-š+WP›Y’Å x^” ß\Cp7(îÏnZq÷>ڎɱ¢ÍPu&÷Qtél=føcÛ”'ð¶$igîËAt Vš TÜƇ)1SÆÍF«–õ™úÞæç›‰Oxnu.èSiFÂ?gØ-]šç“·.£÷}‰i–±¹ðlº°¼žáλ»â5@ ¥™Wj#¬ŒÙ«‚8>­2Ç•oš)ñEX.®uï$-zUºâK«®,ìõ( í~³f‚ÂPö
+]ýg7Ÿ^ðdœrŒ§‹ÜŠ€¯vÍÀ¾Z´À…ø½/(ZCµ![)v^ZG¸' µCiUƈßᨩ¥Ûß®þ”ÛýE~/ûÕñ"6°/<Ë›TW$£.:]²½ ½Žñ™Äâ̱V']»¢ãßœt¦a—ÔZ‘z<žÖìá"¨<HjçÔeS©a qa’ÚÏnó@…5O>ÝÒù¯Í—ðöq¿µK ^pÃE £nê #A±A%(‹à¤Ÿ;~'v½=E·öJ s±(Z"ü*V$pÔkLþ€|*TŽjJg?öÖŠÀõÚcjxà·î­:›‰ù¥ÚIغ8÷à –µ´ò¤Añ`Ÿ¸XIKXÆJMªð{µòY
+ ,òÕlRjþäåkcIkЬ2r{âè*îÛ¿«ª§yCÞ£ÙÙ·3%Oü/<ø
+Âã}ÃÒ'0Ϙ˜éù¬{ÌÈ3ƒÊ‹ÈGMwôûu›J )T¯â]¹NÎ}±Ó õÖÙGizQÈÄWÙÌ#¿^P`EïÍ“äOØ‹^= <ȖѾäéÖß51v§p [+ÖÐfk‘ÖAF~Öj®fš¦A,ƒ…§`õF¦)Ãiø­CU›úN›k[ãÿz°ô[ÃÀÎXéÄ8ªQØí­AÔ–Ë«ü¢õ}ð¦¾ÓÙ°{RíHkÇõŠú•Î`ÿ9 ´vùíÔík™5ë¨kB«ñûöU£Á¨!¾Ës%A¸ÔÀÎO@µÝIc¸G‡FÁ½‰j‚Mþ›p6s÷ 1yŽ.fvpwÎ*õ¾)\Bkqb**:hl»rmX =ÐÔúQÀÆ {[dþ׃շÕÇÆ[]cIE¸Q{¿w7!¬ò•‰´‘„‚£d­·ro. pnÀNAA`%[-ÿë7öQµÐ¼¿dŽEéàµxÍ”K\ïŽn“nÛ=NÊtZ¾tz²òäK*¶Ûۀŀgüý.Tõ8üFü¼ÀUÄû‘Í“’ÈÌ
+@’°ß©{|ÏŸTkÿ4×_¾˜ÁÆò¤m $ß%ÇûŸB¦[•ØöN(´Vk„Ž­[/…„Yk-y‹å£Ðd.LJmÖ4Zî rÈ VuŸ ¶¹·¼ù\ïˆ÷¸Xâ$üˆ<Ü”RX‰¾y,™;KõÌC÷ÇïN'e?îýjæX¯ëòù¶»ùÎ{OlÚ,(µ
+qö0ç»l%^[Vðʱåú€I‚¥Ò4Tp6{ þR¦ýcϽr—²†w ›§ ‡•^µ<Ï+äfÝaçãîPÕà·<t±ªó£õÖÓÿki16ŒH¹5½Séýín°ÝçÔÕ´ÞæÏ£Qýd}×ÖÚÊÒíø!$Hˆ(B9ç
+nH*ZÌ|sÕsÛÇFÊüý>õ '/®Å¶ñé )
+£vj­¹ÎC¬.¤'¾€•¡úØN¤sŽþ 7Ònou\qEïÐF+F·oËã¢àÅ>Î׆¾~äv®ÅÏ,~;‚Î4ã*gjtbýØåÑ»UrË^é RPÀîJ ŽŒ„—¼ ›`†c…_§QQOÔÔCâ‹7¸ `輋³þÂñX¢˜J Ç¥±o“L½A yœj&ÈlQ©F:A VQ¨C,÷ÕêÈ tp›çú7%œ²Ê—³Èï_ÔÖGçJ)Ù\ñΧОÖá±Õ#äåËH]ÌKM5~Íå”w§µ¢ö:²ÓÛˆ§äZìåF Ýªryi'á»xipë£&ï/
+ŸÍþ«—³„õåÌ*·ÏÀ üµp’úÀ¥£‹D€)¿°[dªŸ]¡çÌBx±ÄS¢m,çPú YZ"{ £ ¤Oz*·Å¥’î»·¦‘à°
+× [*Ÿw.ÄÎÒ°w©% 6çI•¸·2z˜# áý³Öàns5+=Yà›GòžôJšÒœ®nÓEÐêˆÈk×3˜AnÓíê!_××l ñž.Úä®Åd}ëšØDçüA›Ï‚rç°ónÞ3L`˜Qæ,öLFY· {<D¨Qß;VTÊbhêÍ"•>Ê©¥¼n€”!b’¤Šœh‘Þ½#kŸá/¨´Êé˜U»¦‰ÙŸV3èa[wçDOýNÆg7·IoEa½¾ÞšÌÿYå¥Z ±˜¦TGt÷ÄÏ.倶›[L˜yÏ äŸ£à(°Ç_ÌÏ'¯ž£ŒŠ)ç3›Ò÷:.wÓÙ Ö'Šï¢T_õ’í/·îŒÌIuÇïÚç‘ॠÄ)ñN1ïyx%Ç!İ†Ê ß]#.í·(Œ® †lKÁ÷V&-ãÍá
+“gŸ
+D™s{1ëTT`†9äµZÙÚ*˜¿ª±QnW^ÎädãAøÒª×æô!b#Ã0ÚÉé¼9^<¶è BDî|½©îa7 •K ÕqÈg9·¥ _jyš„ÓìRr<
+Ÿõä·k¬î®çnFuIëõàD2é
+óéL»Â4Õ¾‘޳ËÒ²Ž.ú
+*V¶¬3}­šú^˜þéâq¼{ZÁÂOJ·ÄåÌ^4•‰nôØ'‹™‘ÂC׆º6˜t»öFg¼¬ÏÚ.7®!°ý4¢Ct\Uc*×ìjÀEK„jlÔ¢†×iD0¨´“«BÔ×´"¶Dѱ# érЉ"4ø[{œnÜ( Ò+ÒÎm
+nýÙ]Q5 ZP]¤Ýœs)Ö–ìö$:±c¸ƒî!z.³Êr÷»ù†5Iã~ûAkN¤t]ÎÛíï"»òŽ¿ ‡½7ÒVùHÑRz›&w~
+HÙ(H2µð>N‡~šIš™SõíÛ=IL{ãI¯èÛS ³ ×»TrùöÞ’V3+¯‚²›ÛEû¾:À<‹h¯×ëuM]ñéCy¶ê9»44ö:2¶ê„Ÿ/­§¶$ð!´¬kÌœ‹¬gú|ÀW§4ûýŠìÒyó·I`L[>³âp}¾*³–ºWà_Ù¼À}£¸›N-& {µ’ëÎ òip–Q¼×€•öå¤ûsÒ@Ŭ²a¡B•5ßÈûʊǾm5€×Ë<Y'ï|ÃBÑ÷=/‚¯*)n•ÚÏlUZÐTø+óy3d°³HwËé7UÑtz…Ggßâp§í¯xSÐ_@õT¾Ü0ÿh™¸ô:»15°Æ½öêG[Û¤=#ƒvÜÖÄªý ò–Ôcn¯gó1:[^éZÁŒgk¿„Ã,M¹ë<‘˜4Îý]9öÊxJIxÜe C‘ ȸRÁ»ãõ(7HµÄ˜°8TíVÛ§ µb¯SY~vж`¯ÙÏYÙù1q˜×¡=ʰÌÑ£ÛFqÊm¥RÕ_×ÉV?1 Ašor+èÖÑlá8k÷œ©°^üxÉâ÷ýK7¹{®! µþ£–­Æ*]ÛëF²ƒ x¤œ–Ój§&yg`âÏ®ËïNêü­˜ó™ùcæÏ>âA}²ÿ\#¨m©FHÏ×\ò¸súlœGFO¾ðâµ$Íî'2Ëb
+@ÝAÛ¯Íþž2ÇžQØYœPꌔ!ã|ïð¾mÛì¹0P%£ ¸Îdz(K³¬îK3нJôUæúbÜýÙZ‡¯õâÆh)ãba¢ ½Æ” §€Ýp1££Û4€9× z†ö(uÕÕ9\±§û§÷é±»ôá°—}9g¦ãì•ʬu™³¬™ ëH^þZÆ éÝ zœ­ÊlAýçœÂî¿Øö7š??˜3L;mò4¡‘ly'm§6ßz"“vÇ9íÃá½€ÎëÌnpWñ?Ïþ1¤D:þ’ Øî®•Þõ`í”6ƒüõgäf!ΰƒJUG?dàò(nÛÔ<þ~ú¯ºö:æÉ ¾$%§D/«äðY^9­ Ûn4%¹Ëµâ+öb7é·çðò ®P—ÿkÊáˆ'pyÏÐZûÞg­BñWúì¶ÙÛV¥1J­ èMà¶r‹ñöU憂úÓá“‹ò³˜»­6_=ùöB>DØ^VæxúDÐ ª7eh‰¦mEQ#ÍÑ9l¹ƒÅ¹º8Zìù§ªþ°GŽähpŠ?ÄúÙ‡KB›ÌrI뀽‡åÈfY^•2 C¡ä,„±î7Q¤Þs?­i¹j}3—í… mî(D®r‰vƒQlª½eS.F½QÌÑ Só:7ûÞ:õçh÷a1 ©Ï÷D*îÞºVTòëÀÔûv›åªšãÍòþÖëö掂µþî£ú¬®ÔÖÝIžþ–ùæ“=e”ŸËU  µ½ 7eCÚ©k?Ü#µªÀc aM’ óûìrSÈ1у7ÖîöúÉ™
+ÈMgTûQ([ž_ü,¶‹X†gK§´·>RÔ^Yè½è”—I迼„Õûe-óä]ÿùC‹µí…zvzŸîmúŒY-“[Ì‘Æ]Ä3È“–£Nã²=ns¥2nø+ÝYPÅK®à×rÐÐ{_&9ÌFcÊ‹¹³à!°û Brdéé5vÖË-Oê™BÝ("Ï ¹ý©_ßõÛxéNÙáU{ôûpGõp®s§ñìÞÇ€>
+k(ç'=I­UÄ›ª<ÑÃKnK04ÔPy1éÌ#à»cð/±‹¤ùÁRÕ½W¶Ví‘–#6ÅQGWÙã*ŒÐòy¶V{ä%gP
+Ó‚¤u;yë“ÃdÏŠiÚ^}@GÇ ;ˆq<­iµõKWÕw¶ö£%²Œ eÔquþÝÙnƒþkrÈóùΛgºˆ{¿l6kþÝ”d3
+sƒ5
+òÄä6ÃydtNÙ¢Þw/æ¢zûwýKíãÕ"µÛ=æt°ÃÚ§M£3Rc•kÓ£EžôMWÞn_º¨oR}ý߆ÌÀa!y<ŠÎ~ÕDcvGÓ.<µª³(½_年<9 å§túÛbvõÎÿŽÉ9ºqÍ ´Š$rÕaÑÜþ!€³¬sî:Ç«ç@#C}B¸ ¦eþ&çJÆ»œ‘@Yæ‡oßQ°û¢z×]çÛz×aÏÌdãvµ æ£<¿ßáÝÉ¥%ŸDûtsšWh
++äËbÄu§Á&·LU©éä¹k‡”öîÊÍ2вÛ6\׌;êe¹R*¡¿¤ˆ6E±=FµÕ¦$Öl±€îtg¢ÑƒÆÌ¨4h®;ŽÆ õúy£ôä^ëØ¶G×4 Dìµ’v…ò@*éÕ®Eñk|4&$õ),²üžgq ¬\ëÃE-é  ‘áèZÅ$jã<ýQ
+}Õ›gŒ•ÃP“«Ò>Ù+ûºù=iLJÇ€j×1½Ž'-x¿Jú{pîØÉâMåaÕê÷w¼S¢¶d«-óúö¢ÚýjïÅ)c0ÆŽëÓÈfΣ<”BDž!oíK&E<CÛWšéSü¼I¥\¡§Ü¸öØÂ{Å<°’EVKç»kwo/޹B(ZSáÙ·›Ö¸T¿?ŒvÐŽÁÊwö¡W²­¸Î\h‹Ñ<ržpŸw¾ Pf•éaþú8òëÒeGð ë³Ð:†ùmûu^kÞßxݪ} ÈE¤•³yñSËŠkeñÁ “f©s GDØ=æ@Va‹Jé9éÔƒÖ²708±t{Î0OïëWX$Æ{Õܘ˵ÇoTJg ÌfÒZ0œÊ@¾9ïiã>+Â’\²_i ­ÓVa5¨ÅãSyáiyðͿ٠'—KE)í·3q¾nº«Ÿ2_Ŷ²d)1²é_GŠd݆Y`곑ÃЩ)W\FÔã –¿ç÷í÷Òy­Ç>QFâ=•>5´Kù¨Ž;¤(‘BküSD':cæi‚K—Eý“ºð+î’«åfZºiæ*à…h ÑDÿОèåÊ{:çg‘_¾‰cÝ,ÿ›K§¡fŸßÙG ¶8­ô·Òˆ¥V&¸Ó—Y’>Þç|ôü°Ö_è1ËŸ¶+\N³>lî­\Ÿn,¬šcX!mû[&ý±g"’®ÍÞ61ªµøÚ=p Tm°™ÄBuR€tŒáÖ»_ûÙ¼'üà­’GjøNgËJÙƒ:~‰æ§3u ѥػÂsËxRï‹rÆ,œmÙò³þg/ô´^ïvX³/á([ÉåkÀ´NðU|åëNãh«’I¿=á(ÃUˆâó®Ô‡ÐöÈ{¢ý‚÷´srw Zé€óíÖ8Þ}b<ÿ¼1‡YÚcx‡Ý—ÿàlì±­ó¦§ãÙõWIš¶¥ç©-ÙÔð°àf´Q@¸ˆvÙ³ùÎiwWóÖ†l—ÊÅ«H…D†-Õ 'EA¾_üÙä$®ßÄ ŸÛ 6;‰Îs­QÞô„Áµû–=`%Ó“ºJ¿^*áhz©Iôè,OJ*Ðï‚ÜR ùÆ-E[ô²\éí’øt2·†ìž¼î!Ž\Ï5w­xSË+7ØÊ7]˹ÆNeûù/¾AÞ¬ô•m£PèbÆÚè£`©#®º'âúJ稦©;p/ŠüUŸ[j¯ƒ
+G`a­ùeð âÆãÃC d«jÍ^;] ô@iRº´¢ÝM­àUãÔ­Œ<<¤YTü{hWUØ*ú–Œí5»þu s5Ãx|ZÎsÎA‚È!‚/³0KF,×y¬0
+˱S9=„um×|EÁ mSl¤Ð uÚ_©ó6ßlc!ù~oÞZ=_õu ªô3Å=y1k‹Ÿé_ÂuS÷wßípaƒšksЂ€y`º^„~žŽyoÖðŽÇë#?7üîØ8êÙsÚ·YÒ·„n¹uzÁÅ’º“–o¹c
+… >j7Þ€Wµºj\™oÍE²!ÜÕO(ÍÎõU”Öì×</å'‰„ÈÑþ–ZFÖM7¿ Qqåž<‰kKÉKʰ´9ì=%uÉ}'õ©’šœª•ü¸<“ó¶r‘Sm‰û?Ý
+M6< ¤ †Ô4åý((sš®ÉTIXoÕî9£Imáÿ‚°Ý-ÞjlÜ •~y2Î÷NKGsàç^³ ©Žé0›"âíPGÅÙô9Ó“{ó%çмÛ)._±þ ÇoùHµã²¸o÷VesW^çOýÍ úžJó¤g^îÞ^÷¢¸5Ú³R& ¼9%é`ÇÆ_nE ˜é± À\ÞÎm8«0ó~*ÈMùù$…‰ZBr ëbÕkªŽÞϵÅõËvˆð$g1ÙÉ;ÝŽ”\«P¹,žLûVi•nóó§pû ¼äð˜Ú<¼ Ÿ¸oÂÂ©š «ªO—߉¸]]+w»ª¹§G×gô£”ר.áz@ýF°fNÉ@"¯¸*?'9BXØmH-ËuáAípWÖµ ¡˜„8ö=‚؟ͼbnÜÆßº‹Ñ
+Ð^]×Fidy¿5Xõ½ &¤½÷òyþI9 8p&ió9¥ß†¯­B971÷zPÍ>›ëM% GzªÿÕÖºž´yëŒøž¿#{bÐ< )ÁJîdóè¨{ƒ­%eöɋ£(sȃæ"¼gc  ±Ów©Lg$íF‡»ÀǦ{Ço]9yND<]V.?èoä…Œ30Ñ}¹¤Q:ê}ÇxýWƒ`ñ–"±ÇÎ:Á\<óÒ9$>;ý :¶e;Ù5zƒ¿|¹p dªöA%ÃB6¢P}ºàvèlçfÀÈûy›”àuö-µKlóY—÷” b‡÷[¯ïîGßm/vˆ¤Oãp­¹æ»ä„|îs÷f}hU£JìeÌ’r;Ê–e3¨ ·D<®É¹ÕïÅ7a–+åQûøû2~·?×ú˜¥v¡LïJ‘*Þ×ÅœÉwÊ#ì ¢C¹ô®æàq‹
+¯èRýÈÆÁýÛJ’Π³^ñ4·qä˜M‹†XÁ¶¿ ¢½óŠÏp/c ðr…q¹fKäÐú¹oFKâgßOòW-Rb좫ü‰¡+å¢ûûG_&L“+ÑVÙº3€Óú]XvuUÛÔ¢ï­Y|þ«®µŠšUº}¸ »o¬4-kÏÓ{Ôž{#j€Þ:ßÀ
+TÉæsÑÍùï2"Ó)âC€Jýa:1¬5.Õ!5¶.zö+“ú{¡…ÄÃ
+RW›”´Åê)ÍkîX³…÷ã%0Gül-ñ%ëy—x®Øm7ŸÅ±¬¶›²ì ŒÝ*ãv-4µ‘‚+]Ô6ÞZ/ਮ¼“áßãîóÇ̵{#hÝ]uÕòFä,»6œ7·b
+, ZÁ½Ü¸®Fø'ö_oõ«ßΗ•úÿÔ`÷ °zN«yI«Î@B-¨ÔÁ{æß^Q·ÀyÉnöÜÌ*¯ôÅfÔ²ÓJÞþ—!íÕÉî›|Û)ûW?Ö­ñrmZRòu‹cA°©áˆ©b£³×íÉßX”òÿ}ú÷ËöÏŸLù~s”ED{Vyl}޶åÅqò´ýÓóì½Ø97Ûà¼Ý¬•2žÒ ù`3)
+äyõƪU!!™SŽ{Hµ=Pwƒ§S®.L“A³™-vß׺t× `ÚmMVƒÕBÖ]o ‹
+'^ß
+ã—ŸYÚ‰Y®ÌõPäR¯Xî C¬()h³%äO“|É›g³uÌh“ŠH)ZÊ<!„·9N~`Ú5«ÅGAª¸5‡‘aNçH2äžÊœ²¦&+4”ǰ)rï™fèÂÙhu-¬}¢­K/×Ò7‡Ì¯Mx‰–UC]¨¬#ŒÊVnÄ ;AevÙý$¥ª[Óµ;ñQÍÖP¬…~;GéEk¿& ô|yêAy4ÿQíú
+‘­ê‹¹Ò·Ú¶¢]†®‚Ã'SÂÕÏÞ!®¥ =ó9퉶J²‰MA®ˆ&{öqj?•è©#üLMŽ»Bj¶B HÞ¡/õ¢M¿õ7yÙr­n®{:[•ï ŠIõmbiÎÒ+²n9éDe÷¢£r²æéœ=­€;—Ô#|_•6‹ÃŸ›…NÈ’\£§‰†
+%Ý8ç¥6f[<2‹gçÌ^G‹]ÂÁWí!rTÄÕ]Ň:÷ëö¿4Ô\5/W;åV#ªÂb±>ó±­¯U†¦Kn§Pkì&UöØÔZºå>FíF¾†IiõÏ©¥S¾ÙA÷'æ¤F¦»õZ6†RÝacì«&åú£ú‚ñ¨ë
+s¹
+ëÂä¡Áœë“„—çuGß–ñßÕ|‹;²'¬Ú9;M¬ãÁ  ‡:NW’z˜aAÙ§¥mý»ûòû±ß>Ü¥r…`F«ù¦Òy›©€ ohnq%ä ^´>4Ø¥Tž*Opôh„¡‡ò§4mªY¨¨çèiŒ ûNÿÞWaTÝf¿²} „ï}¶/Üå·d¼n!í­{+ ëÖì¥T#òŒBõ—(•®§•îª7JóP •B{8Tåj‹J#ÜÓj%
+SÌsëª:3h¢mñÅÉÖi •o’ž*6ÿ‚VÕ’2…AcYwZšŠ­nLîioè Yä9¬­ uYÐbª¦W]+Ñ
+6w_¢ŸzÛ‰X+¨g^0Ió¤sÈâ7‚ÕòMnãªe6¤O7lÄÑ[/rÁã$óО´Ú'¹ê‚Îp2K)ÿpúEËÐÔƒUÿcHïIagq)UG€åoÂüœ¡>œNn’ônjûØtÍ0µ§ºyzÆÙ©qÚ}{Îe™niG€ÿÓO%
+CƒÊM¦;uWž¬z½5²Zò~#xsöÈqÉÒêlyr€‰íæOÏo®–¥?ý`õ}T4 (õD(ì]ÙžvR”Ç5v;‹òáÞæÕåªW“{+Í„ÐÈSˆ<´—*B;ùz¼«47§A[Tn¦›™àKVØ+…‚íàÔÅËœÔÁ’r’ç…âçMŠÒó!]Í;øÝ<JâDÊ¥PCù@ÒQ–º‡-ÖA„+÷èÙ[ÎÞT«J~©8ÏnM©ùí#@½žêäcä7‚•ùÂÌT´…‡=Hè«ÑõU*_¡¥2ÃMŒª!ÀäiÞââª{«kø=­šAy÷÷Ð.Sà›œ˜ç Þ}ëœÛÌ #ºÍŒ>¾·3¤çš8HÝYeÓÏRJ^†P©›6‡
+JKE`eî6KX07—.61Ú}sïc °üÄÛbhÎ_æCœ–M—hý·쑨±ÉB¹q¯Ç­æ´sXŸÇ1& õÍ´oÐTêû¬hãµ×¦Íˆ!.ø€h½ò–´)ß ¡æõžwÖ9™½-"–¯xÞõ,¿ðÁ˜cÒ?¹›4öyV\ÚEæ –]¶;˜ÁyðwL*¼7]Ÿ=]¹VGc¼LÃÖq4¶Åõ#³
+rÖ¼Þ~w0/EÊÏG<Ÿ¼<­>^8Ò
+Ëí:bçPË"Ì|2Ë}9‘êù ßÕ+*t”>„å÷±^ÖÉöfù=0ñ§6øp–D¦',é¥3ÛûúÊ”¬Aî…ãÄÞ5Þàd!çÉg¡´îy‚Y¸Ì*Ê­r[ý,wÌ…;»Vásb'|.‰7ø¾Ø%ß*Ip›×†}ïh=IG*úŸ~~ÒÙ—­{d¾ÿ!Vìçü‡Úli¹u
+Ý ¿•²ÙW†Šê9vè@œÑ»YÉkzO®]H¥¤NâÂÆPÍ%}§÷´É-³oÒƒ^”Ê¦Ý n> 6IdÞ &ªžE~N§‘÷÷ÊšNkº
+7¯‡û¼Žs³­ã{‹@<“_²Í%-±Ff‡…u®ÅwµW×Vßïâïò3Ad¾Y5¸Êî
+çsø jç¯`žŸëoÓ¤òQésÆC _2·À Ð<[âË IÿŠÁ1¨ëŸ%kQ,–ãÁUÿŒuª±ËAo‰¢êEC°{G$XÚØÝ Ášºu¶þ“á{ýcJ.9²Ò^©Úù€Y°=äôú ‘*µW¹¢f_ѕ⑥{u­<»ÛÿÜwp‚ëßô¨ ) ²Õ`‰Ù5:µõMƒ‡ëTãý)V\t&„†7:yýµš7ÿ59´¾g»zÄfÊ Uü °-ý&j ÕÏ«òr‚›l4ud3&£ÌO‡ß+Pÿ×O E^¤Ó¸ƒ6Ö‚ª©¢ ¬tU&9ÓüÓ¥²Â`±èKÐÊ“…±¾L¦ä¿ )n_¢#D‡ð{kFB%x®9§h-…\ŸÊ嘽À¸¦B%è}ž øR}»èÉÿTר¦ç®v:¸ì¨ò
+:©8õJ24šÖÉ+záÐ`ófó
+[kKþ÷é?†´›|­Ããã$*¼òrm°¿)ÖÑ7QÇØÉ…û(d\Ì
+\‹,qIšø>ÔúA
+%á×j®Ø°«ó.ÖÌæ«_oŒÖXýF gÞ(aÜ„qšorm®*¯NöÅÍ´…Y>к†Þîj ýD0.<îä&Ы3»äZ¤Þ(xTÚŽ¨ƒÔ\ÑþntbÚê›yLú›V³·rã”æóö¸ÅOOFIPÚÍ—ˆ8×­¸Z%©d<pS®œ'myç¼NŠ]O„,VçÕ½ºk¤ÍR?½vrúÆ’yàŒÔ,Eµ¥¹
+¼@Féî¬ÆmÅŒãCÛ$:g™ª 5©;@¤e8,±Þ9¯Lû¶Ö$S´Ú¼§sXH3ƒ•C†=böD|]F³ûØ÷kùI#™a“éSûÚ_Ϊ÷Ce¶“Ødnõ†¯EÅßÒ¿ Åf].ÍVZ_•7Ø`µ ¯õµ©§›
+«“›mF×¶ÖÞòwµvÛí³Óޭ߃C£¶Êð•;ÚëK÷T%çûÓ&4rg“«N.U{å\ö-¿:iëø ºÕçUív˜~þEç¥*DÑüE0!IL€ŠHMÎ
+æ€9ìÿ3 ˜ÓÐ]õ޻ǡú÷TqyüÂXXÈ‹Æk¼§eðþfIÿOkÅN¾%¼ü0KïÖy[©t Þ}öûNÅÉj·ª—$´jÒ/Ök¬××Mþ5&_†KB=ƒƒ>š½-Ti^+,€Ñ†Žxè꘡ب¨Ø»Þ@p³¿ö \œ!ÄNûù­éq…“¥¶²"}µÓk÷šØ¥q¥¶C¢ ?ÜGÎ(ë ¼ìq©!÷‹ƒ&ÝêgŒ¢êËõ`–ÚHÎU¦%·šÌ¹8Ãr·‡M ·¢+Œæn³Õø°ÎuSxŒÇ¯ÄwÊ…é«?a–<7y—œ·¦Cê³.d:³ëâì͵ÂÃæ±ÆYå÷åL˜ßæ}±bUëbŒø7i T鵿9ÙÜ…@(a°ÓWæžxV¡Roª†sè­õgE»‘AEWVŠÿ2šm®m¤'æhŽhþd µÜäx·»Ì4±¯-ÂwTµç»XÏMÜß}xÒíøMÎŽü=J“dÎî!ªÜ–áé—ø‘Þø-s]¼Þ‡æÑÊÕ’[ñµUƒè†«Se³¦Rš>­Ö%¯-­7Т½\å½mËŒ¿}s³Þ.:·wûÉ6nTÈŽJÿˆÏÅ>]K³áÙ€× ÉFíËEcÿ  hz…»Øòº²ºï£Îž·g“ÝÝÍ&i?ðúêõØ ïì9ä~óçµS_ÓÓŠyÝÊÕÖ{è{ïd0Õ>u¹>üSF³ˆÄK©”üÒÚØ]ʳ¸
+å9¥¬"äÏ^ÛJ‡Œž•cïþ¬Nóv©~îU3¦['s¾¾WªçÆ”H¾ a‹@
+M¬Ôþ5ÓËà‡ ÷ €<ÎIUáŠ5â˜Á–%âô%óþAøÍÐö`ûÅV½Wí¶b®¨’´S:’÷2µi¤tà¢yí¬6/º;Ô˜U÷Žxφ~ãÏþVÛÔdY«Ó…mhÓîgPc:å›ùW•…°ÙÎþAÃʼºFUõ]G]¡õ••å9® êxqÌÖ•D_îªÍð‰<D¸i­¸×§‘3‰gýêý0»ìc.8á›/ëÇ7ïõÏ,dWa—ÜN"×Ýæ¼%¾7a$é¸dʰM r|+1 Ÿup^.Ê\xjaržªÖA5´¼xj«²Òé[¤êÙ¯?2Äj†›eέ˜nNÝVk6¾[;drµ9Â{Ú±˜gaÇD¢¶‹¡:p7²wò¸n¡çýÖÖwžê4hR8¸ðG×mÔ$I´Y—¶‹ÙÁ~ÅUF ã„_¹Ëi‚WÕ:‘¬Ò•(%óm‘Nañ×K2þ§–js'mÚäb½y¾rã ÂÜgÆqíëýu¸?‚êô`vÒþ‘Q7½Se„ŒN)QÎæSÛ^¸âº–‘µ×î
+ÅÀ½æhfÝÖÏWrÿ£¦‡Ú,†Ï ÝËÙñÕYsÛw%ØWßY¥M¼ßã¿ôyO¹ªé¶º®.¤u³gÉ…lӲѧ$K^mÈ•è•M;NKI||É=¾z%mw*£ªõ‘©B\”ª ª=ÍwLrÑÔŒVæ(VˆèÞ”ÂÑ“•Ϩ&³•2“ßÓÏ&£ËßÈ”¨Â/ëÔ陣ïs)uÊ™¼Á›­®×È»ÄÀ+ÙbµjIƒ&ª,ojOT½Tø)ÉÜ.IÇS…l­Õ·í ïj:O’³dÇU_Çò/Üg;ÎàÂDjN›°\%{”Þ³
+‰o—ôxD*ü¸¥ÒK—ا°°À±$_ ñ—Ÿ 
+:†{ª<êFo&
+šúʆQ)܆Zu°Uq´°ÒÖ•¼Æ¾æú¡¸³»6®ÁM© §3p|¦üìf
+
+®jDEÐ-£Oð8‹*õp=^
+Í»¹ä´kR+'×Oý±
+R}ÒS ñw
+Ú,“ZñDNÑT‹ŸBBéï¾ìÖÐ[›¢™ËñzΕ‚­OHWƒS-_þ¨š£Úâ¨ÉþÙ™´t›Öé ßP1Ž|wRbTQÑJñEÅ_w—͹¿ÖÚù~\çýÑÆÎ•-å«è¥t½™*S·ðòo¯†¿dRJ*ëóAÊ)cº¢`[WñfzošCkDG×Ìꇥ¤-é¾kÀÚäín0‡q=¹]0,z‚ö”Œ>«m·]ýƒ´`a߀S‹iÜ0¢å¶ˆŒQߪ;¢3o‡«Ùô¦m e)\çAæ&(þTùaIdd¯&š†zØ*ÈêŽaq9?ßëÁÁÍF¿%Ebl«ÙûZÑçèIý©?9ñÃe6ª?\†8]0ç36{ʲ"I}tÒOï帽šOFˆ8ç©‹“¤ß‡¬®¹Å|ÆeK3”OµC]ôy71-èÓÈlIÈü`æïB×ëOKU¹Vo4\+ú¸Ag¹ÈÏu´©j廊9ÌÉJ¾ç&Qíéc…£‡2;‹ëzó~<¶Ó.IÉ3&–”›½–Öïhi™9)~±óÎkì£{y(*ß CZÁÆÓÏY_ÔÐŽ¶¨žS‹Çˆ­ƒ’@™nñû-úÚ©å! [éó|O®`iÆ2UÅc™¿Ü­êªƒžÉkñUÏ{¿­à§Ô„›¢Äàõ¡¿fÛ§PÕz¼0í5w2eÔTç˃±Ñ…v_ÅžK—¼Ú[=.–oU¾3¬¹é Àá÷‹gôú¥@\âñþƒ¬Ñ.áç}wV°v ZýTXÏô`ƒ8û–6ên{p–ªe4Ë
+ÍÜá˜/½È•ΤÍèC`–ÔÃ’&o¢¯/9gšz×nè³¶Þ‚Šoj{èey%?×ÜXmSÒ«7÷W…wö7‡-
+²gÛ[;ê~o«æ¨ù§KïïŸRJ9ÕÙQ¸žXÇ´0Ñ­Ý&„¦:JO¡ó4.‚ñpõߊ] ÇXNâí"ݳ(㵤ÉyÁË›£ÕW>zI?”ëžV÷¸]ÔJâdXe×é)×ʸ|öÎÔéï&’÷ê—†vq%Žz½£ ü¡=꺳TܤܑÍ`Ž‘‚î$PÛLÕ帳 Ù¸…È÷©Wó{ÛÔÒüÓ–§•åÔQ]l2õLî)ã2Λ¼<ô}îåÉ-gÖÒ¡°¸GÅr½ kÔÕfƒåÖÞ†§\-ÕâöÖt­¾_øAUP¶x³žÓd8¼1}˜“UÕè5JIu'
+Žò3ð<xɨZó›?M~P§0ìS|Ñ®¨òoù5'R•ýõÙ}o£L¶ªù°¯ òþš–n¯üwÙ}ø¹7wÇ×ÝE·-òxo©é¡nɳû{¨Hcš¯©~´BÝ!¸bGRUù¬îÝ`ŒW¿®pÿS~o´(­Õ…ÙIìÂÞÛ9¦&;ªɼÍ?2”úHûhÂTñ5š‰T9µt¥ØÀž¾+^9ã5ªžñ˜´}ë犵)€íh›t,´,L†ýýDÈÏåÎó˜ñt
+X´Ìs²©ô¼™¸¨Ù•^SH;ó¯¯5‹C´TP5> HäºÞ\ÑšoÞU¿0os‡u·±ýƒîƒ½‹7³‰o#"7 U¨n¶ê˱Štìèïô#í·ðjAö×\mïH?æ&¬ †=h §à VOKº]žùÅuµËú±@«ý!Pä5[MFvˆ)‚¨´…¶–r§äM+Ñ›ƒíïä»ÅlF­d]½èœÙ’[öð{vÿHü¬µ->÷ïŠËÓ‡W[6»Ð]–E[z²ËK؇Q£Ò¡~£Ý¾&_;XA*J_¼Ï÷ûõŠ`Cð÷ͱÈ«»ô”V{ì)—â¢{ùtœ6© ”ÊÞö~¯EƒÒ×+ÔÑEÑʨÀß4\”3‰ÕYéFš®|½hbåØü» wzC?ê²L¾‚ÈüкÊÒí¾¥ò¤:µþ×C%2fEç€p½!
+uAÔ‡ÔLTy¿U2¢ ±d#Õ”ó~ç™\vßÂË·®ÿ ÅÏHŸÌùÞAEjµüÁíQ#í<µŠ¯ùX0bÂMa5äb‹Ýoè~Ù7 •>Íe²|Hæu\½qm)\=›!·ƒžkº´VÌ1uŽ]²ßß×À°‘'„w›¿ªu’º)ó¤N&{ýö› ÷¼
+g&½`#.eÒZ°eÄã<ÿ½“N”)ò~ïLiåo𻽗÷Ú÷±ö»œ#Æ×P¯ï.)èú7íûW¨Ö•]û«²ç÷ %F•"ÇFö©ivñ¢”G/n|¼i bbjU©£F¸]… Ñ‹èÙ€ã!½¨¥he^fé½m5ºZ-²½ŽE^ÆùŽånØ+!åÁƒ9rî¯açÑs“6£Û€Š—ϱ2.`Û¾Ãο
+G18±c(µO"Z ð9«YØX¿Ì“Ð×g*MÆO^Œ›§Ÿ¤JâæZê̳†Ü˜Í‰;kT°èkb_é’Õò7Uª¿óØ3¼Ýº+GÆ%ˆ*ÂÍ^¯¿/œæÕqMR^Ôb9ú(÷‹ÜSˆŽ0~0:ÑM³™öX, ½—¿¦~'u¼ùP¢8ýfbð’—ÊÄkñ“Ø+Êò†w0»¥´Ø<­ÞÛ\¬,°Èè´ŠçÐÞ81¯›ð`6}}§ð¦^qç™È?k¼‡¯3•; _©6*UˆgìƒY«B±?Ç÷ò9>ºÕJSf-wî œnIå.ÜñNxæ†mT6Åq¾cÚCyS \¾ªr6øH£÷[¿’õî=*q0Ì.ãêr«Ž9Ï}Å5aÞgz1L_ö|TˆÅ³?w+ÁÒ ÃD×}_&œ ”™C ¼óàë»ÅFË»r¡8/…ÿ™åU•îÒé´‘
+#ßSm‡#Î"é,çýÕ»¾ÐGÜb/ÕéØÌ4öí¯ëŽÚâý}]“&SY‰jæWÑøîGõ½i “æ¾Ïž00±¥4URëÜUãÔÖÊåù ²vv¨g÷Ö»eÖç“8¹ºê¦[Šé‰Þúñâï“æöñS,°×R_°lÚÈn˜,>جs>
+")8íûþúEãc«fK88Oþ­qD+„õ±Øn¿1ùp\Ù¸°úˆÛÊÜ‘S¦6#t}ëKþÚAäw(¬6Ñ#¥ät=·åu¿:»P®Êœ„ ²²ííxÆsJ’[Ï—/ ß*ݧ€ô“qÄ¬ËæÎz6û×E›|“ª<+ v;zª½ ñ‘;µSªv“`­Œ'} ÝÖŽ­|”;°ÏtKîétÀ±+ïè¦(ȶúZuÁ72fJŸÝL°oÝ §ÓêQºÛá|@5±eAú6è^ï´šd
+ÙÄa¹5»/Ž;Ñ]$:ÃÒ–} Ñ´Ûí[k+%¥à¶ñ©<ªÒéÿƒ¤%î#Ý‚
+;]2ÒÕWðôðÀÞëúRä†|º5õ_¨r}Idm»¿§$ò’$¬äõœð”½‰¤ÉKrfÖ©'×+‚Vy¯;ÒƒxÇÇr!Ûå5–mžC‰ã›Ÿ0ú9IÐ:.ÎR¯š9àˆ–ãè¸#±Jå<‚]¼lÜWVr¡GŒ/ z®x¯-¡“âÆ±½¬€ÇTBî”g=¶b^nIHËy–jí¯3^t7ÒhH"=ÒjlõàÜT¤åÝ2úÁ¨mt’¦$´ø,…˜±s—rÔàÅŸÈåY1A:Á„â •KÃîܱÑû„•æ=·ëÓR»öó¤Q›ˆ]™ºZXúªÓK5ƒ‘% +ž(‘Ÿ)3n<ž¼á”Š ½ÂÅõ%(í|A.În@p¤ÌÉB-¢
+§¹z1·JpÌ‹:º½ü\^q¡”ákȾÌô Ì&NÞŸò!Üý¦Š%y¶ÜDªÖgýâÇñ†»„Ò•P_)„:<]Üêpœ‘™ÿíFíæ\×Åóç &WümŤß16×bA‰­Ræ‡+Lïl¹ƒRhJ+部õfu_@¶ehpv÷EÅ»Š
+€®…ÇaÏä65•“/vã»x™ZW¨¸V*»¼?g+§©’Ÿ 9¯7­·„<á[**-¾ÖíN÷®r¡ý©¼ZÒ×MtÈh>ï)…<–×Í^××+ »Â@%ÂÜÿþæor`>àk-WGû*''샎ðßœÔê ÁìÔâîú¶‚ÉsˆòÒµ9й#e\MºQžx«nÙ‚,žÌMK5»…ŠhÒ\!dY/7°®àKÞxÌÈ]²Õ Tì:è$Ïüâ“_®¶â"ylZƒfù14gbæÈ`rU±ëM••´=›*$-/‡d(®DŸyc½ŠÖåÐ…ƒ†|Åä÷¤ÿ’'“¦¸¬=¬fç Sm}³ñÄ\a›·žÜ[$Æ_«SÁcÃé8Kè-W_CT,ˆ,*lÖVŸå©Ë8—g"‚[ÚQVûTÂl絑\¥ÌûÓœ±•6t—×gŽH¨·}gÉŸ,öñÝŠœúÿ¤øðôThTýu¼‘žmà€ëéd%HýûV.?¯¢§H‘µÝԖIWÕ—À± (v¥ŽüBÊ ß 9YQºÖßTiL€=I>6o4˜rV·CI'”³­m`RU;&«·ØVãkÏêÎG9^Z_ÅP¼C§Ä~!°zÆœ¹^ØŠobu1z‘Ž\é“u½.>yc_s 2¥ÍhÑÛÖe¡d5iB¯_èN¦‚¬ù-aÃd<ºNuŸh0ÁǪ²Æ`¯¦bSžau@c â3«m)Ò³t+D3Û»ã/OêèPÑA§K”—§irìKÊÚÕÚÖ
+ðÄ»[ÆD*¯óTºÈKÛ¬l råJße;g£ö«@
+ Ô+¢¤2$»ukè½nöåQ†Å6IˆJÅL5Ê~ɱjZrÐᥞFÖÏ­üò~Aë¡Gr,jImÕf}™yÑ|ðjÀÜÜï±þQÜu¹À?ßöÚvœf‚ý¦ ASßs³£¿½ØøuE6ÃÚÊîÍ‘ûQð$ë9̼ù»˜Êñ–“…ÔXXpšó“ÐßþnR£\@­Æqp$}³& â6«¯2Ó2[®!¥Ú“Pë«bÛ#:œß$pÙò÷¨tN$CZŸåñÄíWÎU>~v´éÔãǤ+»x§ µ^jùÊH&k`!yÔi˜E1=¨Y'@ìK¥DòåöS®&™z¼c“2|Vjâg«×& ÄXQOHs—;6‹×¿é’ÎNú`eL7ºlUW–XHÜ„®bZͪ. ì­Nz$Ê÷•ä]ú©{Èh¢¥K± µŸwï'×°B,wºŸÛ3àyŽëì ЧÍ:µ¨ j)“'XmI ž¶t™Ö¸Ùº+1YÝ*²†»ƒƒûöÈŰâ:ócqvµimU[¯*WYo“_Çú¾·›öÊçG_óéOC•¡Ý­œÞ?ð¨ë×0Ëùé½5ÿö;p9³á+ñÕ«8èpûóf.iÜ1+÷)§©§k­Å ~oõP}«
+¦·Œõ¨EO”R§S³F ¶‰ßGK]óT~Qã£=iÙ¯c7çÝâ­ûÀ¸A¨6®R'´ŠÖ2ÊS†¯ß; ½šî›+Þ^M\Ò$j³~ýhugC:¹ÌÓ¶>Ü çYá`²’¥#¹°³Å½¤ÞîÈ×jù£¿yœÏ#GÔÄ—2ª‰';¹ÎƒHÙ™?æ/-.,wµžtög»'q}‹×' ü†P!Èåo:“}ñ«Jzjksä­GßͬÐóJrðl4ìÃÙν’_®_YÜs™±!Á–tÄŽ<—û uÊ׿ K‹êQTüƒUD»E”cFTŸ÷DØ"Ξ”j­Õµ %Ùë„Pù
+ï“Xo¸ÚË$üs5*|·eéì”-5.·iu<ÉÓ
+®GMÃUáIS4÷àÒõ¹¿n-ùÝú›Áoè:¦6åe÷!'¿¢“[ùÇO¶SDkÕÊç¿bV£ÞxI©5U~ù­ç2‹B£óí }›>‹C0òõâÀ’<ÿÆ#kŒ—äs¯Mö¯Æ¨G³Ö[¡ûER/æéå$Ÿ°ÐÚ
+>ž®ð4Pí¤læÂ z£D=·ÇýYo™×X} ©;{6ˆ'ñv¦ú¼þý&Û5º5%ýôc…–uàÝÅ€ZH¢é`Âi âÕmJêϹ‰ú|ú›…;’­ýS’cFÇU[À’ùY÷ï¿ÔbúQufyì k»Ð
+Ð;™ç
+lX”pPqØP\á¸Ch½ %Ï;3÷~•xãéÌFʪÀÕáÁI«ESx¾& e—æåÆÅPñB!ï}á÷ÈÅ…ŽÊ<8œ_ªöQät~7fNwúów½Õ´Jwçç—*£¶~û¯þ®_–ƒ˜­ýìúª ‘n€Ñ.î@'Õò¼xèêë¥ïæ¾ïÑ~ÕoQ+²³õß-gëÛ‰wöui¾ßWÆ*Ï–‚ù銽;º£Õ­jkbšW§Zc/Z̉¹)Ì^õ£Åù7¿1Zу™ïƒ{‡¿>þfXŽ\= ;°ÂW¯“DÞ¶‘§N³°æÛëF}6Ž jÌÍ'«˜¶»n=÷.ssä;éǦlÅ©u¶Ð7c©çdMÙ•ë»~t¾»ÇúÓ~.þA³‡£ÂÉC–õ»t9£”ç)À¥â‹wù;7ê׉»õA/aýyÓøyÐ/©h³J§ío’\¢Ê/y[ºf]+u¡9Ÿˆ
+póݶ€4“Ö^üR3ñåb DD„$yw¸W/;²Ó4¤O™»E¶½èiÌ¢">° z¤HMØTqI^9ù4›ë*ÍN&s¸É°1”k…ƒBøÇGÞ‡Ëã;!ß5)ëVÛXo¿‚Õ˜ù¸Hå nÖÎöË耬'š\«8=¶ú&r½@üeÙ]nWgE?5ےÔŸÛ“wÊéÕ?©ðH½Žß+}Pû"GÍ“ŒÔ Ä“zÞw¿11,Ú;C;aŽ.Õ_Sîf
+.gº!x¿Ua9È›8h<ï_á`Ƭ1¯>xçI”maç4e„¶b&»T}‘»óèk½Ãe+ ¿íËø.Ç3ÁÁš·@5&Dï’°+Vû’k)O·sþÂbûwØ¢2fš$¥âé½3p´Þ(+n FòÓçFÕJ|ÃÅxù†¢® mAQŠL9dÅñF8wTaò@›CgqkÅé´»î¶~ ¥‡µ“ÐúÕ[S»@ü©°Z(¹®ñ-òApW¦Ö’êÈuŒ¿}ê
+W‹ßP¼´„¸SÙäñÞXô¨¼+Ä·¶ZtûªwŠ5¹ÆTÞÖ¯ç!ÓÖ]iùØGün,?Ýg±§JžÀÍècò1Õô I ‘ج4^+Áùé@Ó¯þ0ëZs-RmOFJýGUò£Ï§Ê=RS6¸ my.*ö²»i;G¾/NpÛk²ø‰i4Ū1ˆ4%¤#SK«ÜP&¼jd»êüµ™%¢Sê”ø§:tKˆNTÀmp}‹ÿ ôK¢CiÕ0ͳ Å‹’÷Q¾tô¯êW›O ¾]Ù Š™²]¡ »k³˜ÌÁèoÖP@î#Î
+t«ì€YÌÐë›äeêê:î—–3£5tåkÝ`y?¿µƒJ•KUIÄç¨7Wl×ÈMî•à·ÙìåÇÖ¬DŸÊøcgU_m—o{ ùpVŠ2B¿Ù.¦ý_Là»Ô’šý¢zå¶&€Â7W{ûJ¨‚ðöugöqéBTLè²Îû¥Q별#°;«£[„wŒ!G.
+¯“›³Ñit‘erUâÕœÝgêÚ´vP¢ÕK^ðʽ·Eµq ¦ìóxÝu5_ŸVû ¡¾„{ÕTtú´P|cà²Ë>V±:&­·ž<…ó· B£¢Y)"6 >z1<¶‡ ®$ˆ^÷þPíuzý‘5ûJÝÇø 4y^öMå,+·ÞÈ8ÑZIµ*Óܽé‰ÎM'üäºl'2,î­!Ÿ>úÜäº?ß)î»ÐBñ¯ïËÄfO:¬¡¢°Jv<¥y_3»‡X`G%kXã¶ò}ØG½
+0ôº¢Vjɵuÿªüöú;¿Ø¹÷5'°QôÒÀô[yÙ÷–¶0[mÓîV'ÓéÎQµ˜¨¢±Ùö®l£žWòØ¢"_%ˆ•BÏ2–óŽj˜÷õ´®·$Í¢Œ¦_óÔlË>·hÙÐØšÒS<L!ZÛ*byu\2]zŠ~8æÊ¬0úÖDžù*`¹&ì¶õèi,ïFÜošƒÄųlµ›¹™aU›%q
+.À-¢½ @¢Žc2È s^®Ü½ý FµûjPj¥¡j_'Ì\5¶ià8i\áBíï~Ú·ð­£.ˆõ §wÙ#­Î)ÚŸ
+„®?ÂZ³Øº¶¥ÕËè…‹žßÈ>ZÚW9^Ó[OjƑѸwÜ*²~+$ ;î´U˜ëX_öòk¡ŸÈf¼©37öÍ 9·ŸÉ$wÑèA¨8‡±ÞŽã1qJNqX)Þ•ß|=±Õ[«+SR´V‰—û§,¤ì²a~6Ä~<­··"ŽDýmïsë*Îû"¶zŠü5"žZšÜÞž©KHÛ„M^z­g¦åNÝÓ{öCé Ýž†ÏSw¦ƒã?éL‡CÍë3"M¤H.Ô1Y‘¶Ï«]w«‚âw†óMwÛ´#G ð­Wu“Ž6û{iÀ»HÃa¥K­8Wþ~ÛEux>ˆQû4dk$NY“ëZÕûæJ[²­"ÑÜÍb[
+Tƒ7(£RºùôãBù¹hצ¯Á÷b…ˆÚ
+à'L´ ÔåEܯ yº5!‰œ,¢ï®â£•Øàß7q8í¥î¸àqfËi•çÍ·f¾÷BÖ¾T~kÁ©ôzmî·› }KðgM(Lï@g,N„!¢¾\ƒ¯Ò–Ý…UƒÖ[[©w ÷DYd¢ûÇh7Ö<'²šÔÈÉUízêTg2’—Ä@<€6{üRÀtIFÙ¾¿ÏâÐ_×Ùܲ@dR‡oµ"£@ZáV“µ*Û|Ô»?¹V•¤Ï‚"_+´
+’Göt.šxŸ””.
+EÚ—³xrÖýI©Š;µSMvZ¿Ž²ÅUíÛ'ÚÚ|ºî¥w£xæy«¨óͱs˜ G?¯øWcÍçÉHÚKºíL,’eG+ò^A¾”Û{À\Ö·À7jÖIwØùàÄżhQ«*š»šõQ)¹tl¼Dp‡~ {WÊÜ£[ùéÇ(jÛÍ^úr¬îóe–ÔÌž¨iVvÄ“!\]u¶ù=t²ñÚFÓû¨3ñ-©þrÝCО
+N±F 8yuÀÍÓç<ý™Ó˜7t½,èÓZÓÒŽ3À)7Gñbtw5°ê,ü?}ÜÖ­ϳ¸);ÇF½c™ ü=xcðØÜ9Û(d ›­ÂQ;”4EQ_!—M!Oçô=ZXÖ‹ Ÿ½–1Ð:”÷‰7?Nì£;R*R8Ï?t¡`^ö$‰ ÍúF¤æ’¨•/¥2°ž:àߥÁ3\ƒ(š:¼ª
+Ý“^ôœË¨ñbèzi*ìžxÏî“ó*IöR4áÜAσŠ_$§v¿G¯ðCF¬dJÿ§è¼ÒT‚(¼‚»&P• 94MNbÎ9§ý_æqæ›Qš:Uu~mª›áaÊMy‘$=Pç´kiAÔ­½Ô2–êËoN¨AÓ[HK°ö«YG¶ ]lCÕôšÐDLvTä£@µSj¾ÝÊ!¾â„A·€4Sñœ"îGµ[^5ÁÇôܲЬaVµB›ªíïÆÝW ê\êv'Þ°9ŽK|V:OØÍ£YøÞÝ÷ãát7µ½h”Íá`>ƒ%áv² ÃR¶
+òÄu ×sÏâïÑ\Ú‹QvÇUC²ìÅ¢¬?4à)à6!gÅBAÔ_ÏWÉïo;’­4é]ÈóF£Èñ;Ì»tkxíTt+¬ðÞÙÙ¸høªL@­˜*s»ql*í×þ¡_¶5¾_¯¥¿ÏùÑ)­÷qa9̯Ì
+IÛ‚‘ßÝ`M¼ñ†U­Æ/fN“xú]˜Æu[§‚ñíÚqµ2Vþu‘ƸÀõ®‹µ`¼Ùæ9òÄ«ÐZÝ·y özû`^¸\¿é6™-‡S©§~Ñœ<Ú‹1öòêlF2]s€¥¿„ò¼=Ùî)]“ê ?A™©)ÉXPMº7ÝÛÉéùÍß¼mçfˆ2
+£À¿s[hõ3¼ÎÞ×ÞÑ8÷ çl©ðúœÉ­Ãu|…eíD¹‘…Õ°ÖbH˜Sß°fõeˆ)•aséh¿‰8²î›"늫ͼ/–Šc²“?n,Æ;=|ûjgý¥q`
+(u¸’MUYDßôÚu¿P¡“:» Ì[>¹Æûî ù›?éÊWÖ~oÏPâ§›Ö# –ƒónë@©˜0Î\[{¹˜O·ë:£”ÁÍÿJÎ?dTaÎ]Ö-ÁÈý ^LHv¾ZÝ)K§_Ï_6ÇköµH¡Á[¿Ü|¢ÏA®mµ¤2> 5EàÈv(Ì‘H¾Oè,ôì~£b9DPØk=ONö.öH²Î³Ú03ó)0¨f‰OöÏåñ¶•¨t$4Ó¹ CèTx5zw7:ƒVŸêÄ(®µ‰Ä FËï<»o6fôç j€ÁPêíÓÁÂ×£<ø;=mgkÙÓ E¿áÓŸ&‚–fQþñiô`¼ÑY«Ïà‘åã!òÞ†­Ú‹Q7Š8Íá‹Í«ô0¯Íù@Uë¤ýjÛ´Oõ…¦\g35?á&¸Sr-úU鬞™¨¾Æ©Ä,w¶]+;¦· Â{ºÙ&G ‘P…~¥µêlg9ÝÖbõ,s¯Á^€©ËúúeswðÃSؘ1¹È`µæç‹–yÊú ÚJȾ?ÃnWo2uu6믮ªìKîÙÕÞW3ìVW²láõÆÃгXm#™Šc%^\äE­1^£R.,nOåøÙÔ̱ºùzK\w´Ò]~ÿC8mívÙ—Š:`ûy<ÉémJéÎÊþì÷ÍÌ N¢R,Ú=ï»iö"Ú² •xˆOyÝŸ’~p¿šÇœUj ?]'W9rF}Ñ>nóW©2­ÅÛÎß)ë´zö§=2µÒ §È Çš5ù¾Vëí\æ‚+yß»IË—ŽÑ'ìúÁ2îOŠâ
+™>ã6ëCj-O½vèË”JOúÃQÎÕ·Ϫå
+Ž©^6;#r>{¿¦uÚÇ!í¹ïï—ó«EZ³1ÑÙYk€ ‡»= \|ø¦%ls‚›PÁîžþ©ìÝþ›ª1ï ß “äëY{—/ ë  åop|?*N}
+!W€r*â¿ B>q¿ :ôJB¬ôŽúx |è‹0Ý´Ú§
+Áé{T­ž”tBKj—áyv=ÂY#JAq4 Š}H^ö r´ææÏæõSmñ*Í2…|à›c­¤•ú™tÞº—‡qp[;ÈìØprçßbø`%÷Ù*e}¯ ÓܶžUÚy𠕺™Áæ¯!_rš¹·vŒïf>¸úíc%F‚•Gýœ<ö®ç¦]sR!.-)Sûy!˦º¸mB©’"8ŠÉU>ï¡+j4Θbåb[øKFWyd¦ÌLöñ"ôt†æ`jú=Ê’ÓHk¿§9N¸!.<ý³,ÂŽõiz›%]Fj›³|‹Ó¿¬y a•ë™ô™<r´4ïx1,ÝÍšZ€íò©$Ù.¶Yš•Çø0$]æ6&{ÏyzfŽ­þÊïW§UhrµÊV2” Þ$g"Ç~­Wˆ÷‡Õ ðu¦ ! bÇB›ÇÔ …KÝsµQ_Þ¶“šË­;m—7{Ê')!Ù·W¬¾M  <†6zªdwŒÈfM_ÿÎm«È¾ƒh_Ê#êû²NÓÇûÐ}™!rXË‘² ªjGggå©ÍöKt·-pÐÀ¹ªÕ]Äz¾sÐW;c7¥´——ç’±¡“Ìó;¶|Éký—7ž/ÿaƒгeò·„.zàý)AܺUàG9Âo~Q›À‡2÷¼;ã8™ëûË·iÍŽAõ"¼hõf®ß’d¸B=¯³~{FîàÂ*€úâ' Í>BÎÙ]WÚ®U{^ÛöÖæ^꜄u뱺µ{Ÿ×̬5˜Xˆ0¹ÁÇ Y•T1!þ*œaµU„ó\ÓôvÃ~ê 1-eï¢`7Áµ±to–þ[€GWsj°'Yèv©iÜ­Iqñ„,¾Õ×Ü=ÎÄ9^:bño~Hg ¢ ý°Y‹6Ôž!ga–y‹ÿߌøíÉ Œ³¬´þÙúåRÒò&òËܵC<“”F6kO«áÙÌ3®ëל•*îF¨¿§S¿5Í»p[s”ÕççŽ+ORX¨ÑR}s¸Xp2ÖwNÕ?^4Þ‡™ÆúûÜÈ.£¤¼ð$ŒZ§T±ë@0زy_ŽUaf£[­ÛéÚ·ã3žuCÃ-x'“™È%ç(qy~2ºkXUzø•õ>3s6xbl_µÊBÞ¥ÇPþ‡îyÁhÕàn»â|„½´õ͘KŸOX-Õ Â÷O+vö´/;ÈU6å"ª-éhæ®»Ç) ®‡mŠu*ÍjÜ*ìÄH‹‡5h`š@®môž#5sã‡Þ'+›óSÑ
+±>‚sŸ{£[5ê¦å϶¡íe_n>(¹d.§ÙjP‹¿™]e®ÛtnÌs¦$×SJ%«f?šÏÛ—™ê´a]ו£Ë|
+a; a^KúÒ®Î}l&Ê–yÚM¦¸ê#gÜjž•/?<žù•$n-eÄçµÖû~ÞO_ì¤ý»ïÈæ«™øðþÊóðžòŠÊË"ö[”Ï6û~”ýòvóa¬\¹p_œ'áúí¬‹¢¶NÎCkí†'l{bY¾ß×%ê ÖZ?/*td—«i€<ñ¼¤ÿy¿÷”f]¡_K\qZ£íÙädµ¬íi܃Ɋ—@康¥µÒŽ>ÅaÍ,+9²`ÅîMþ!ý#}«Ì0awó ÈÍÜÒŽ3Õu…ÎÀÈÜvÐ2f¬rQÍUW”Q­Ã8LÜÅZáæ÷ÚU£[{$œ– /ò¶Þ`#/~U´Æá%O ‹ù YVòM 7ïëÆ`SZÝ_W„ÁÊÑçNÚ‰Ÿò´¿<œH£¥Q”¿?¢§Ë[ã¸Ç#g\ãÊèoÔ{qÉÿ¥Ê{&©µ½ÉƒPÜx™Ï(Þ>Ì`¦1÷Þ*÷Ñ'Ï\‚ñuTrŸ¿Òx¥ë7x€ÄÏšcdcª°Gª³L–³áIì8r
+ì«åν\?`3@úLœ_!¢kN?æØ‡:™‘ÿŒèÞ,SÛ¡µávv#”›ÏzvÈÆùUmŽ`bÍù0“ã•\§: oëãÚ•sQ³Yh ˆFœa):h 9’½Z\l'Ý䄉¥uûà\²Êò ¡º¿õÕùòi{ªvôÒX)e—’šVW){L8¿,T7µ¡">lem<&¾µúÑ콸æq®»±{'ý›0‘ãUsnyêæ¢øÛ¬ÌÆð/_z{ê϶Áóè,ªïL'~ï±ð»Ñ¹ ºìB”9LûƒmÍtר)çõÉÝšè–8Ñv Øêõ¥cZ:Cµž—§#õt'V’)eÏéCüCèv`XB.Iã|—ÛßîT?çé-èwb¤¾¸žúœÐÁÎý‚* ÜWy‰12¾|n`ÍŽ©o®Û¡S)t»`Ûk>©ëáAôgø~À•ôUôÞýCÜý¹
+îÊ7fñ˜îçºïe­Œ¦«ºs»ÄFŠ0¥KŽ
+’f?ÉЗ›Í0.ë]C‘í
+ÂøyËòs|Àiüå¢ábÚ³àjZD¾ñÈH­jRòâç:yålû€ Tží«~G×”Ž#U º1aŒ@ð#!ò)è½jš‚qxs -Ëjý¾â´Þ³‹‡j~I«¿¿ÿÍ׃­®KbE;Ú\’ilŠ.ÓÏ’Qßœ{ycÓ"Í¡×]Ÿ|e£v˹JêŸam=C«‘]É™–1JˆölS/±§î þ~úmú)‘qùûºØc f ;ífî"6¯ŠÚ.ÅÀ˜.
+-}ýø>ÀŠ¥ë¦wè³F»7Ïb÷˜ÂÝ>Û‰vh¯‚NÓl½A ¢ÖÁMïû¾øË²Þpü‹³nCÙ®{fUÉ¿}3(ì¡­®ÑLè½c‘ÆÏ\!lL¼àf†‰°ÔB»v
+“DÝy]5Xé…ó"ʾådn_Õ=€ u§Ûú±ŒÞa¸n{ýy>+Ë— –øBʾ‹åp¿‚uÍ9tNNóâþ©7ôÜ+ZÉâ[í»¯Wg ¥mÒUê—Ìq¹=ÃcZ߯ðY z÷I­r4>a}Õ_Ä€¼5׿,Œ0Z¶3}GÑD¯MÁ‹Ût-ÝëÕG¦þ`R­­t&›ÇÞøŠ9yø»JþÈ k’†3AÁ^‡ô-ñz2¦%-ö$ç^Í^Ø–Ë}ï¾zŠ(]2ó ¦ÜÍ<#P²Š£ôoýîOVS\âÉÛZ’êÛew&Ÿ£ã vwå×ú’ªF¬ÔÎSb¿²zgÒúB«¹ñ³ bŠþ;o!ºD/–_<TÃP³šlÞXþ$uš¥én®n.“R»»ø‚î³’2Xýב¿Æ³ÃáÙàççG‹½ôÑЄÕÕ›Íôf
+§ìBø™Â5X<Ÿ¸Ç=…³µÖÔZýþ?DsöÄÀjŒó>ˆï ŒåPŒŽ5¾dJN-ȯ‹yAȪ‰<]7Š+…¶{Û1à¥X‡ä•;›‡ÕLgQÛ…òÎöŸñÊzÑï2·°8äþwÊÌø¡ÿ]
+s5ïtXèê½uwõãŸwaJ*åkEî§zuÓžƒ*ý)1ß‘§;lU߆›™_BkpŽ?` *¯ŒùÌ}Ì–Œ¢qÓos7 3Û/îbœðîIØ¿joïoUïrõC2¤`¾§°Ÿ]HÀ Ï†Æz3>»†v{'‹Ógm̸,ÍÇ8m‡ûD( ãÜe¦ŸýÊ[Šur›Ì'äú|´á3Þ
+Ó¡;
+‘š¤]1€Ç|ßðõi}3ÁqmaN7RÛß×4GªTs¦é¶½—Wå74],tMa× ž˜ þÞìÕ‡¼aûÖæ%³ÝlÕ®ÿ5Õé§æY ŽU½Ñ¤4 gäbÕÊÍõ?Äy¾–
+Å]kÙ«ˆë•Ý>Âf¯i›g9q  5 ´ŽOÛm'«ß„¸]íÇå9¨ë &¬ÄF¥÷Ú>±šNŒwûü–' ïãuLjÒ¿¬ZžÂÎÕ¿&0U-â=÷6MÜô:¨ßäN¾ë»økvw
+åxÚo­¥Ñœ®ƒ—SNBÓ˜9L
+¯7p×ö~í‡7:IŠ
+ŽNÊ™åVv•“¯Žög¼Ÿ~`–÷ŠY¤§MT×£–Y®T
+:3‡w„¢«%yñ‰!lôëø\17Ý7L±Æ—xuÐéuç]®úV=VÇOSseKÀ£¦I¾]ר$¹4gw–mRË–ŒgøîqWòó*ànä²ÞIà¼yÿ$™ÆZHµnO2v@“ÙØ0‡çõ:ÍŠ¿Ï[îfêø:NÄmB¡ú¨`tmMÚŸ|šä³{
+ž…\S ­Áªy‰Å5¸O`7[íçð±]µí:EmEÎ”Š–3a%KØÇ¢ÜìûMaÁ{'8ª”m•‰”l}‹¨ù`»ݯ5Èé ­ß½¬6d㿤°õŸ#!t8òÄcóVÖ8ù‘:ù¾0•MyCÏtbÅ»×j*'5×´ ó÷3?HO¶qóKUOð´
+/Jõ~ʪ$Y]U ßÌ';±Q¦!zë|<³æÞ~¦wž¡<å£.:ÄD·ÄsRzðÅnGž‰dɨ‡çìàïkEæé‘KKßÍÑÍ«Ž­®iÂkNfë5¼¢\<ÀzÚá’ìÉ×(eõ"”)uud{ŽÉU«$!ÑÈßÊÄÜ?tNá¿VgóY-æÎõÜüUŸpmäÃfÓª*`­šw¿+„xNêg çy5é
+M1
+ z$V}vžë3E ƒy®0Ñiä¶kohJܧóãÙú•¥ÏÃ%ý!环˜B8¥ËNU´}zXÛYÏ:] •òdû:ì`$¤ŠQséê›_g(íÄâ·P£àužúØŽ~Ê¢†Ìå5®eYé2 2V~ÞÂïçråØVÛ;_»†ä¹ñ¹zeȬ¯?á£íº¡ç;ÑlAG’\Å-ã;éÊ]ÞOå{Pôq¡ÝIuÔÙC±Ú‹ó¡mc㜠ÁúõIå‚~OÇo¾$Zœ9Íâ}ÕßKw§ø“E&2‚ñáÑV"±È÷–Š‹-©ÅµÃ¹ñ¼¹#øëŸU]C"ðJGN8Y\A?ïIÀåÎNšXþß UedÆÜ²dO÷£LAeåöÚíÉIÐFßømžKvñ`+Ý“ë[{¸i3mšU:c¤­ÛÛAY:û%G…ŸQÇ é°Þ£ZM»%+ÀβÁöo“ŽB[ª·GùíïüÈÕØ"ºÕ"
+ôãDšë§RѬPçúí±¬oãÒàº:ÝΓ1)þ| ±Î,þF;Ú¸Wô–:jO#ËúFOPóßìß#ÎÚ?D¢zt
+ÓèåŸ{h>¬¼l„ÞÆË ¯$7Ün©ÐÝjÍÚ˜ ©rç®*ïΖ­¹ªæ‰9 j›j§Dx#+oÂÝÍÐJŒÖHþÆoÓ» >^¸2ź½ç…¦™ñK!@!y&®ìF­Ž‰Ï
+Kjîp¾ê¼Â4T\·‡Í–þ¶‰è‚ïÆ æ"ósr„ ï©ôÔf”ñ'0€O°F-—‹Zid¹àRˆÄÖ…®f¹¯§íéÆÏM
+èþÕÐëŒÔ©z§´ ŒÒy;«¡ÿªûÁìš3ÔßÒ—´e
+&×RÖ>´IžÇFÖ‘ÏŒG壹jˆî±Zÿ¦0±Ìåeu\ßÛ&k·%§«ð„0ßöD¬j†úw¸NœÓæõÖÍÒƒîq_²Ûù›gç¹Â„TÉn6Ji—‹Q.ãÊC3(Xƒ
+ÌÑvþsth¯ê@
+ü‚*æð \„€2ª3l&NÿqàÛ\¿†vüªæO‘†¾dªÀˆŽ•g¼¨»oÑ©›Â#¬ñö¦Š¹!»«8ä‘~º}Èb3«r\Ótâ?;ÌÆ]Xߘ;ëgµ袛G|ïâZê†Êg_(³‹[xÜGÁ¯>”»8 ÜÝ ¨É´4°Nï…›kHg<o Ìº?›Ã'KUžú€ùKÖ¡W­µeð¸Ï*Î?Øxˆ‡^2¨£ÿ uÖÈ\\2?ÙøSÝA9”ÞŽ_߈º_SzÛ¹KcŸ­Ý'¶ãm
+k=‡>æ }÷&¡ƒ~ºRÏQ_›YZu³0B¤Á•·v.é/¾t4ŠŸJîÈ!á-êfÖªìb`Ó¢£³: {¹÷éJO7±K:zb«${h™Ö­%ç’ÌnñX-6nhIóóŒønܳnº» 
+ßœ.MÆMª×Ö?¨_<_J]ï]UÁÏÁ`X6sè Ïzk6:êÝñ»“LT'0—|Ø IÜ£Q:Eò[ÃÔqØl¬”Ù9—r˜÷¿iK+Ì,Øñh­“D{5Ï0Õͱ*ÙI±–¼d ZQãS$oÂŒˆ¦ÐwEç•t³²óTêÅɾ³O:€®úZSæ¨í¬!££7ÉáÉærÃ].j!‹‹hf:+ ¼u³‰Þ0>Ï–n¯,¬tqù&|oÜÓß%ɵãuueßÎúÑl×7e§ã”6â+íÖO{¸û½q$YhØrÑóXóÔîêâm’·*•ºn±—­äkmÜOl=ú;¬>í0·£æ ¯¹Êë‚ÿNèØ9à¾Ñ÷Æsg¯c›8ù.·ùÖ-l_;Ú¤—¦î‘_y¶»(TDWÄe`vÔ®ó}/0²þ¼è^íÿI`AìŽ@¬ÆÞ91ÆëL/.>ÞóØv,¾œµ*¬«ÞcµoÙ[Ð%™´(¼÷b£¹íÏ8@ŸÙö‹â·¤/Ô\§«†¿ÏÜl<fu+=µ:9Ã|Ç´gu›cG„¦û\Á”¨êÈyák~ÿÎ0ìç³0.åO{R¶â$BcÊ(ì…|aÓak……é¾aLí 6ÒbÏ´Qæ›1rZôÂfýe¸HŽÿ ô»ÿN§Kí9{M"ÊŠÒvü¾Y†)) )nØätÕ+f&ðCa ÙøúÌtûömÍ›·Û¡ÒáÚ¸ ·%mÚíÝy]·³2èâïEgŽþb쉑Ê×Ôù-–Ö|”£ ÒYÒ1¤î¯Î)îÑOñ÷ âÄRAá{§C,û*½4` šg:£É]7fâ\Ö}M¨Ïâ¶ÙÏÖOÏÔÑ&<}æOqPöøz;Y';“ék÷[Éq5¥Öuûû0z9ý»/ôc+©íl-?@ w³Š-ËisN½sž¿çf(ú®y€«¹ ‡Ÿ$ZàêÍâËgÝyï¯ù<w`ÛÅÛ´;ñom~9<µœ 3 SXÁÍrãÀƒÊ…×¼U9±¨Xy;6™í׬È:÷‹uuás÷ãÌíÑZ©ŽÅ‚»3kkw¸à -RpýÙ‹î_Œ5:6J»òݳ!~k=úéÜ_¦øÜã6ìÐÞ·›ÁptËsêhÕ™•?{^}/W^×.Úâ=ܼ=|§=Æ®NbuȬr3^­ rÁ¦þnÉì@͆½½—örOò&®¶ŠÎ}} èo«fø[ töW(Ïx¯þHÉüú¥ƒßlé\Œœ3ÕzÉÍ•¾-{qÔJÀÙΌ킯Œ¹ùtÈ+ìÃÜ,ˆ[½Z¸»#ԢǗtÛel,[hý]·¯mÝN¶…ÅÞFÞ…»ÌÐà3˜qŒ$é}y0
+ç=‘vîzˆJé ªÐïbê‰ï{ç¡úèÌ®ë?È2\ÚÒï!Mgt5°CrOqGß¿Ä]&):o#Ùûé®ZïNƹ†š±!•Ú‰éú1úmY»<|4~Ç]üÄ’Ž? ôŽ0kDÔ3ÿuž<^¤“:Y6¢/#wÄV&ÞÚj3ÀÊô¡ÞèÊhQ³ÂÆäÍó~Vì±ïW·« ;ñéõÍÇ_Šuë»CàOƒý X1bʬñôªÓÿŸBß„±Õ‘ަÇ/V ñç:cû8·æ^÷JݨuG@]Ê̹ҞµØ‘à7V€½0swùZ;Žˆ§)h{RM}´÷·ÖЖà‰VÆìp Ùî}Òý;Çg uÌG!8éß_¿•í &c]àÂñÚ=S¯ÄìÞŒ"Ø‹³ Sã&ö’fÝ«YÛX¶cªQϦ˱çê¾Ú°w‚*Ž‹­ìÌm.‹»m?ßcàv³¥Ú•æÕÏSüÔx·Íµ~ÍO“"ØÖL¿1_ÎW¾7È›pש߉g^äê²îú¿È$^ÞIjàzCÛéè‡(v¿øñsþA”ð 06Õ©W¨A²ý|¿×vîŽý-®=µLcâç]õ¾j5ˆ×1q7D<‹Äì7¼ ¶ä¿Œ.IÐQr¡ÖëlÕç ‘ï`%<1ÇÃ¥®qɨC:i¾dç;ä2á<Ôø¤ïyw^qÇ”üòÏQ¡lˆ`ÖaSd[:/ŒÄî'é„Í×Ï¢Š¶?§pó‰Oò}Y¨ë¢ý«„Êdé2uCÌýÅìä;x1ŠÃæ4mPFf0}iqàk¡¨Ù®îɸc0÷ù9xÛ­ìV Œ‰ W½h¤¬"-û×Ëü@‰öø´Š.€•ÑQ° jyŒ­ŸÖÀÐÍ$ðï³…J˲õ·±€Z“Ö£¢GŽô9ì‚âD™c7ëe»Ïn‘ó?žuÏ2gÚú -úÙ¥0Õ^—ûÛac›÷÷Zâ#³´u1ã&å³?Þõ:$mÔÛ
+§C[Õ¸sØÛlGÄíޜOtú‡u*ÃV»½[㽉¾¿± KƒgË–ªÍBÄ»ƒ°u¨r^L¼¿nóÑ[fér¥qtµ¡‚=Ûi¢?ö<ÝÛ{4C«o‘…sæVúß^Ô`*'ùö‹2wÔB~¡mîÖ,{¦M@Ÿ1ØT"Ÿ¸z
+ÕtHóËÖÁ¿þƒyãYû}äøã¢’å™o‹02¶·{ߨ€6܉ì~ÒMTLz§6Ë2Îðªv迼 qq¡Øu V…ôÅ n   ¥±ì±# #ˆù]‹â”s÷Èv#§ÿƒäQ€,´“•åòUd w¶´q»?Åÿi¦±˜uûù˜sGê׎ ;-l÷šõá¡V
+|Êr9eíyWD³s(á¦÷^<Lñ‡óût&¬)áÅ)qÅŽÀÜØýrMUrÑÒ§ã¹ÔLNâL¹uÇgà1¨d¬}¨«ý‚,?ÅïÊ!P·ê“ Ú|ä®ù›ã2+_Œvßiû“7e¦U·–àì0R•d¿5Îz ¹¡1À d 7¸8Ó@:§w›s?/¼îº˜§8‹¿éNÄôÆG§‰,­ˆ¹;8OxÏ:ª‚’ Y»×r‚õÚG©j2§Bºyû®8…˜÷GÃÂ,ÔÂd/rÝJòÎP E‘PÐáún°4r¬î*Ð[ŸÏ‘?Ùô¿äaÍ/ƒ=^9ÌOS¾Ö?(]ž©Vüà ¤ÔÔ/_·:Ñá<¨ ýÕ¥,íðá’Âc8-ž)õ¬.ƒnT{;£–zŸ:Æà~˜Úxl¿ú)]ÂQŽƒ@¿žRR
+ÿöÀkƒ|µ°¿M’FüžZ’Ú“qèU®Ƽ:帟ØMÅ-µìÕvORt©`í¬«ÑÞRiAÎBZ÷ÍÄ›Ý ƒVO3ŠõqTªŒhÿf×^®¤ù»HúásÒÂçUU^— mÆ_ö¦÷Äž¬;¸Ùòö8aSýBYõ‘Ysí{³€ñ'¬ì²ÕÏ]}l^A²Ÿ˜ß§®¾Â²£:LhØÖ21s¾½äO‘©‘`Yr÷goÆ ÇdÝqFûS„OÜëŽÓŸÑA¦ªz}$™L:ægÑ]ë—PþéE¬ù°•ºX¶*ž™±T{;çrsºaýƒ‰Jƒqež˜|Vd=Þâä.d«­•7¿¹¶ëKç$Â!4n¾¸ICwf„”u¼eŒ¡˜Å¿Cn¯çvgÐÚè ûÚ>þ>c½SæÔÀ¹/µà„Õ5³÷¨þÕJí ™•BcÌÜŽ¸ê¹ê²l¿Gß&»¥w CR↚}GÃMà¬"GYÑLÕºç @ ýHgN_Ô/øÀ?”¯; î‰}RΕÁfDz+þý?¨}OWtì~wCé÷· ˆé¿èhˆ'œB¦¾¸j7\0WÓ@6e&èbìS³n{«™ÉªäûG¦Éšx‰\¶ÑK{×P•ß$²\Ù^%pÿàæ1–‚³XU~Q?yQæ@IL²µ±7aùh*nñž'3ßÕÓîg¶Š%rBÏÀ5H_íl‡<röÒ!u°i0ɽµ=ëÇI1>ƒV£Í–ØØêýÆäÛHüë=ø¨)¦{hÜs‘^MäyïúµFµðb•铦‘±°¿¥_$ô8¿i¬™¿‹!ʹ?ê5»Ÿ‡¦§7_|ƒ¨­*
+[7‹`×Ô^zèŠô‰-—ž{yE®•&‚)ñÌÐB&m§¥²ízZÑ}„ ~¾›ñ«¾Îòð;Ìáthdcëk\®‡¥iõwžå`ý¡ãý»@™éÛ~mÉÀqÓê¼é@ë§J>/ —êqÒ/I‚ ÃöM§6ØËµ·KÊ©ê‰ñ]
+J”@'“ŠUëõy9­ŽA±’¾\òþª¡¯;öècw©·å! ¢]ˆùp«›“ÂÁ˜Ûë0J‡¼µLª]ø-{šƒ¬^6푆iMqÏ¡ž¯üý³A|è¸Y3õ„ï*Î’!GWý´¿„J¹ÕÎ×n×·j&^n)7¿áksFËKÎcèàYªúÌg”ÖÅ|ø}aþ‹¨yÖ¾W2(|ø£Ù0T]ó`O—‚Ý¡U²!&
+›­E
+*]lå^ª‹I,2)E¾äJ¹UÛoGXž:Iq ¯r/¿K_fY4¿®žfÀPd‹`—×Ð"íÃÒû:ž;Ai¸)ÈŽÚ©øŒ
+§íœ©rã8Eý
+’YZ»×·ú§mž쩘êÖH¢ßM,›©yEp/rû>Dê¾Ù”¼C|sZfE±{KðP2¤8r뇬/C¬mè^íVõßüG;¤úµ0¤zJ}ÖA–x¹rÇ/Z›ËÔôZiöÕè¬7·7O.g~^¿:-?QçBOÆ\Xq–°¸ý
+(”@ÛÑ·3Òî?ØŠaŸk_1Š™Â<dèÑÊ}6=ñ^oMÊcìjúCœÁc7>ʬU7}0¡²’…íNªýÝžt}f7¿¶°“«Îg޼«ŽÃXg÷Zú¬ÉYèÆîT|S^íò\#ü2Þxp< ­M*ÖëÜ_ÿí…ó’??ÀòòC´Ý© _ßÉúá”ö@uP¥â Îõ¦=Wнï8(Mg5«ý6Öì~âtbR%<s•|ìídZâåÛÖKôâ–y5¯bd Þf›®@4„Gˆé<»~ÉëÍþì'[¹ôhA™µƒö@–»¢4÷Ý)ÖƒVF1ÿ"¶:.ÈöÎÆÏŸ²âÔCzƒ‚Û«ÉhtŸÚàdëËž¶wú߈-Û»tYË|hŸ&¡aIÕYŽ8¤vŧ"~…3ýÙ¶MõT³0½æhSF-¨Gº'åX5ï[”Xk7²ýŠ2ãVñ4À[5vô { ÜØøáPAë%…ÑÄ&oX_eMù´¢¯ÿYË!P\OcÂ
+Š·b§i{´Eå = ™h›ÇJÇ5؃ìàÚ0±õ!dÔ7ìà ߓéQÏðJÍÞã&½‚Ä"°¶æ'ßskM9/“žéÿXâl·‡SS—Oô©„:7GD^tû´¹æSk[ýpÊä¤tÁﵦÆw9hoß—ßHºÙ ¤Çe ØÕ»d írj Ñc¦½“.S ¶Ãçôæ5˜î—n›—sè‚7R…'&h£)àÔÌW ía}Iû¯?Liºù¹Ç¦²Tú×ÒÊ9¯3 ;;özà]w¨ÝSð
+×äÉÄJ‡÷&ú†ÕìE÷Šä¼ÎQïw74hoûª›ªà(V_© ì¦ðÂ2þÛkm †ÇÞ”5xs³!¦PÈÄ›íiÍ»2µØò‹Îïð‚³wÄXa¸Ìð{r÷ã:kAb{òÐò¬dƒÜGõfK@O¾Z+€ßcø%i{ŸýzïRI-ÛvÈp/ζö¶„ý,…åWwƒÍºISŸf3°íŒŽîF˜ævŠþÙùüuª‡ ÿŽãø¾t~öOò÷a?bh_­8ç@ Xó>ýëÔúh?qw¹„´&†jLpܹ]p®¸¤XÞÃþN­¶¢¡¶5ÔËû)ÊCÓ‡¢feôàûýÅJkD¥0\¡ˆªÍîEcû„¢ÙÎ\”F,ØÞRÅ_ð|us7|¤¾Ræ…“öèE(õKÜQ[w.ç·¦å
+ã9Æ¡à&æD¥^ulq ‚î徇 Þ…â|\,«jñFßÞ B0g¦*Ïí½Ä ì#’³¥[ÁCòM›¶
+î‡ Ç“ ª½öŒk_¸z€cÛþ…欉Ïl=¨W‘„R«†å© ÊÏðCëkÜ>öiÓ-øÇë‘$Êþcꪀ»ø‡ V[ËIëï~äºÖõúh¦\ZÝ>Í÷­M4æn¯të¬U¹éë(’‡Ktw7hZôm o£ Á†~w¾yè>ŸlÝäPhy72€òj÷,€zá»öžïþj-:Ùô·¹_Pä
+Qd”­GƒOÓÏ
+l]öŽÏ<á
+}OñK2â­hÑ äç‹ë~ž¹°È¯«×ÝD>¹¸äáúzÞžChüxõýZFä4î7:Õs³W!ðªûN3Æ<ðzéÐ#ï·]«#âïž%èQxU/þî¼Ýò\…c?S|[ÐY¿†
+;©W#׃/à.{;Î팚yŒõìåË+ÎŽÝz°ÙŠn^êÆ ´#cÛ ÝvÞóDnÕ€eû“¬ä}ª/…Z¡¾TfŒå$…nÓ ùþè®Kw=ç= [7f‹ÊR#¼|AN° MMæ®­"BÒ]±¬4ˆÚOÕ‰Gíxû¸2lu°èë=b•FG¾h6M£P3!C†§šßÎspøsú=á൮HQ‹¢;;“®È/´Q)Ç™^>ûÑ(N4L~ sík¸‘ÆvòÀÑaä½|x”lL'm„Ù2¬KM}w4ð©GÓ/2ÛM}f­ˆ)oJ›Àpäguܽ׌îY^‘k™£âšÀ•Î6µ¬Éê£FÕÉór°píÅJ9†^¹˜P¯A4rN½!¡‹]z;Ô&ö5`¢)h<Oׂg€Ý7\tUF<Ž„À­ÞÞíPt7ÿß_9ì¹ë¦¼æºn²_º
+rºß¾ÚG¾çUwrÖ<UBo#=OŒUîÕ®ìþzu.š ÜC jS˜ŠÓÛ6ýönV¾ٽîgÕõÕW/ë+L6(Øš…QW¬fþéK¦ç©}{ú‹Vîêfßik–¡øý»ÉŒQåÕ‰Ýa<ýÁ EÙ‡Úq^.mA*p£T©”ׇöGŽ,ŒªZ³„?RoL¿IéØbŒ”/!ñšñDt6õwö_'ü¨àA郎›iS{Ó~ÝùÈ\…Ú‹:ÿ 4}Öô¤;®gæxN
+¸RÀï2 YYóy õñ€Ï|éìJÌ Ë_ˆS}´ÓŸ’–‚îdÇ|*·œévt˜àíWHð´mãmÅŠÆK³Œî/ w1·¿Ï9,´> ç{>€7T¼¤‚ Û<) úVàsx–¿[ÚÅG‘½XžgÑÝ‹xü>F.üÒÇa¼/âHÉΠ?/¿‡BÇ¢Ö]ìÓ–Ý%ŠÕó[ØkÙћK ß=š¬}O;·×M•pOÞèM›y`ô»ÁHÅ„ÆøkԽɮ%9?s<ÂÝŒyc±r'z´Dp<ðŠî ª‹~c¥»-?Ñß^ëåTA0ªúôfìjË­žo¯5/º®­8¨˜-¦½d‹ÞL¿B‘5¿<Éùþxû“†›±ès{¿ú¥Ñ"H­úk6§–O¾<9öÚ݈ۗWéßß´>ŽV·éfD‰õwZ“ѽß4…ó'M耒?@ZwDóƒvÉö—ˆ[é#J¦ =c¨È¿†õŽAm*{2›jÒ¶²Õßß©)Ú£j¼>ÃÜ+Ùw{èšzuHæŠå­±ÕdÛt^0؉|nÛiqÿm*ïãó,ß{)êt()Öéêk ¢“$õaJ7S…¹™‹kŽã¥ÉNvð0b\j$»×Á» eÀ¥ÿA¢âRÿ §´1)>F
+Û¢û:ó>,­7Žs»Cwl Z”C1îÒÊ6µ3¶ÑO°ÏÙš×iü´skò›âöQŒì•ŠQö6ÏÔr?N$è´Lr†ù4z°1_yjíß@TËž3Ìèqg¡ÄK·åqO“¿­÷3ý»>O¯R¯‚)Úà)Ïðnn4fÞ(³dU’À‹OG– =}l;l½¥…˜0нP\Rˆ³ÏŽš]]+¸]Åj±OÝg¦|`vˆ8©>,Ÿ4¦Uýì2rv$~kú™;T¯5k®±Ã€í/á³§i)&ÞW4jº¤5ÛàŸ|^TãEál¡Q€ ÞænxL±Ý€ü­ð»Ó_äóŒl¸'q}¸ï8|[³ÒÔ‰%©³”>ز.IGðQ<êß2Èxð‚ `ö³îèm¾Jÿ Î¥0œ2±p^Q¾LÚÅ+ei
+Iê¯PcžÓSå08^™ VJ"ßE¦»8̱=;àƒ$¤FjœÈ(7ꦄ”—_ísÝ-ÛòåÓFff-9vÿAB†d_ýò„L;4»“æëdÍìúCWü6{ír»Ý–—«mKãT ¿WH+=q\û”z5@zCn¶ Ó÷ÇK†E‡¶µÝŸ»Ža÷—èûñX¸‹ñnÿ’š¬£íèƒÿQ}Ê4—]ÉT@×Êh[ˆM¼fãàcX²5 šûzüv©ãàèNzÔ1y9Ú\’b$é¾3rçe=·³<WÆå¸ûë×ç öªd4ïF£õx © ¨”²{ƒv+¼ðW‘
+¯uÅ
+¥_=j$O‹œ^ÖŠ^(ëõ.guߨƒÕàf¾*ƒ"Èx¶°!·¾îœ±ôÇnBˆ¤ÑœÔ?‰Î}m…ïf|·ýs£}‹Ry—6“¤Æ…¸¡·œ”'1ý9a )½Œr±4wšíêFî?¡»9ïs‘ÓÝÇלYák÷NÅ„¾™@4Á%)º'æ±Aù©»åê訞v›‰wʺ}å°øøú²ßûèƒÑw©Ã·i®¥qŸ{¿¹¾œ?ŽS¯1±Ú §Ù07ß`œ!ÓE¨…ÓõJ6ÃíYâÛ>»Ÿ¾×·W¹B¥¸ÕG«y»ìè‘R–œÊ¯:Ïe/àQS!`ò톬¯Ncðè.eóÛ©0êæØihßÁ(§>›ØWƒéš€K £ŠÓ!° •ž/b½
+Ï¿úmo…R+xÉ ¢ †5ËzgsÄü"eì·Çö‰ý¤‰ŠAÚo—¬\©'-U*L—íýÝøé&Ñ|ã²C)+Õ¯0ÉqQ¢ctdåTg©OòPÓoËÇÏîèþƒÛ#ïy8ßçníûz6Cµ_)—S]Íý³WV] 0OžqšÚ)éV´JУ'a•þËß‹º=øz­êÛ<:”j¬\ІKØ(ÛîÜO§`xnµP¶'³‹n}Æn2’«¤S°íÏkY³Zá\¶`9¿R¿î” on¯Í”1 ¡o§
+Çšçå]æ5¶OSúÊ­nò¬@BÝØíÞyN6*j&Øóü‘®¶«šv]wö”×aR:îÛ¸Ú•ÜìYÑ g¥‹Î³àO<åØë,'à2Jh«%BCµ»­gzrB=d|¸®/œò™?;;¶–¿ %œ4ÅeX­â¡y6ý§À{¢ð6â6vF¦Y¸“'míƒ]åݲív¡Ñd¸Õù ÊïÉ×­œž
+a^ô5¸4>X*û<,u;Þò0üû—´æ‹? AË+ïîŒ%Iç›öuç±·[b_J‘Â2»ã¥ ‘uÛçv="GÚ{¼é¡§||:þ>(¶pÏ,¿º] ú«wá]·&-zÄ”5öoòh?›9–ªS„m3?ª(&à'Ÿ!¼ª·_wb^Ÿ‘rìàÓðªB3hk÷øYˆ÷õŸM ñ’*©c:ÌóÎEøã=‘AÕ¿÷¦¯æÜ&yŒ õ(di' jä‰+ÅQCÝÞ— y+­r<já˜ø‚Ôð–°MÌ1ñЃ_ né4wñGiì½Ï•§Ü[„¢øÕm¸ÃÞÜûO[5Ý<’}¯tý4^3•~øÛù%B¶¯ì´´ïß%ž­¾âB5töç!ݨ!¤26Òµ1qñRD(Ò¼ˆ™ÿv½Ìç÷83íàöö³<øißý5®wú÷W‘G«¾û€Í›\°>ˆ¡—¡ˆ»Å3ë'«„
+E¯ÑžðÍC÷‘؇ÓÃ÷võ<Zºíî[è'?:Q•_ue®¶³¶ëPl*”ŽkýTsð†]ÿÑþÎÀ¾ˆ2àý8›8 o§îÂ^ÞI+ÞàºjÚëu‡ô²gÛ‘þëãœ5êqsqF@
+pF£Mœµ@•ð½…žÙkf‘ìXÀ—Å@ngHmlDßÇÁ&Pêã†Ôiƒ®^æF±èˆÝœ»]cÍ\<ÿ qor‹Ô»VŸÈbÿÔ|a2ŸéΈc\ÿ9iìÀ`À™wyÂ:Ÿ‘òÔ\GÊGFr¾*[×®kcýbßg©`Hàà,@{æ;j¡ûô óó‡úZjüÊW»ÊCOBk¼…‘ÓX” o¸6ÙMFo—ˆ¦ï±!¶¶ü@_½;bÖØÞn×8ÐNåûÕãšC8ð
+‹Æ”¾Ž[7PRâ½§•8šhâ ¬[5ØÛTž,"{å¹Ï·‚Ïg[H\»“+øV‘1`¿“/ ƒ!ù×Õ|ÚÀk¤Sž7/§7ŽeŒpK„w*Ìuïà<Vç`àù(¾@]£Bõyu9îúû×É »hÓ«ôÀùíäØË Ú$ZÈjæ»B[óïpý!ÖëSðCËð ›ÍA;\èöA;­M¶ ¦t.d‰p¶ |<³MµØr5)!ZqÙzgHÖ¬9](*ŽOH}.;’K‰=+é~öéÚœ¼^¥yÝÏÙòMyí[¢IÅŠÝŽ/l+-gÐ[øÐ»˜g‰—ÃÀZbÿÑv¦kŠ"ÝÚ>ÂçyždAEÿì£ÿÂÜÝ_÷ÛoUíÌ®ª?\A^éë ˆXáR'ëcÏ4m¼Þ¯C°8\Š-â²Oµ‰Q ·'ɯeíYbžÔ4—0·V®”iøkWŠs^!’©[>ŸéžU´Ç$1R­Î—,iJ³ñbµ$Ûs–m”S˜>ïÛi‚Ù…òF¹%˜ø¥õÅÔW…u5Ry]ݯ½Óµ7kÕËðä|_za°c¬ïìùå iDwŸxTïÜ_ÒG!·ÞwòõRéÞR‘éb¿_w*éF~OÑÒaí©Gïä¼µð†½­ðU¿óù*£ñðzݬÞÊ÷QÃA”áÁJ)Û,M
+’h'Pp üâ¢^bogV%8n­Qö›¾Ûy®¢®ŸXw¥G²êë>rÉOüVt¬R+{Í¢~üQÃÕÝ>›'O;îƒ4n¢ídÒ`$£µUÁXÇ«ë n™TÝnã<~(‹Ãòµ˜Ä°FÏp’Ü ¦<?`'39Ä  u­BÊÂT{ñ™0¢›{.“Å,ƒ[àüÞ®Wä@Å.[˜ÁáåÔ™xgµ¨á{UÆGz¯–—ʹ&Þ²ˆuÔ¥2ñ¿dQVüåÿÄ#ØÄâñ|&ñ?ÿó?ÿqŒAàPéòéÕIr„‹¤. e›.ß('ƒˆp"kŒZõ|*•… C=䊛•e–®%ñQ©ñRgF£šÕ©’t”Ü7'ò%hIS=Û¾g¸n·i-щw¥Ô·kw˜óŸÉÌ;±ÒßÍ'Ï8ÉÏ:û©3‹F4?2åeѾÁ˜2)Dkn4,¢W¼¿æÖü¸Ò#|(QÓiÙŸ™C™Mq)oÑäGl¬†±›6×èqÜÖw§§´žÉù˜%&Jæ8¡Õ麮ïå:tÓîÁ5o´„Cÿ@ ÜtÈî˜×úÞi¾,C¶ZŒ·œ§s^ž»”ºñØ}ºœïð+(Iíj º®&¹éM‡îÌ=q8º AóP|û JïP¶“ «š$玦è㊅jÛ`—.ÛK+}2oÁ;²ñìâx+äÒ‡E3Ÿ¼='ùO`©’Š›ÑN;ܯ{å&{ŽWÈ
+_~‡zXCœ«”-7S-ùÜÞ™ô;Ô½.SßQ}üp«Ñe¿
+ö¢o?~&Š1è[qüÕQ3Õ7âø«£ƒ¾ÇÏF1ª@§FTŸÂüà*ϼø¥X»œ.vn[úÁƒ•üˆŽAÈ75½µ ÏûGi©ÏgRT|›Úé|R^mˆ¤TN<ÓbãˆWÿ¥€1ö½KŸýf úiÐO`Æ Ÿýæ"öYÐO`Æ ßlüúò ÿÀü×ûR<Á“ÿ÷ÿ¯Ýÿz<cÐo6þÛ}ùÕÆÿÜýò¥xÆ ßlü¿éË¿Œg úÍÆÿW_~‹ñ¿ÞýïbÆ ßlüûò3ñŒýnãÿÞ—ßgü/uÿǘ1è7ÿù¾üt<cÐo6þ¾üfã•ûbÞùá_ƒëÓŽÌù3ït¿i™hƒ¸AÓ×ö:Û .Z×W"õE, eŸxew j™•Ê—yrsm¬^œ" _—ôãë
+n?Ñžë]<œï
+£.pK··â&lµ÷›\=ñ;޳¸ôå©3ÅC]¦(h+·I†û‰Aßj*Ó+
+Ù„r•"8¥
+ñ†gr{}¤³ù¤®ÐëQqKú"Ë¿ëÜÛyöW À¼ÔÿÀT§§†ÛZxUȽg–ƒK­KòÉ«t6a.„QZcx¥¡É|jºÑXÃÏ*L©Šo©ó))ì‚åí±?–w_þÏ?ú‡DìZ*iˆªºwx¿¬eùz,¶kqUË7Sâõ5© ‰.=ã4¬¹ÏäŒØØC½³cŽÅ»L¯ˆHQjš'Û!z“!ýÓúòŸ Q…»P¼ÌDÕƒ²¿ÎŽ ÍV9ËÐÅèI^zcZ+qó 9bW‹Ê‘ Ã`Éë†vzê†òF ºI–ë;ðOÙõÇ«€Ã/”¨rJÔA(ûxxçGŒFIÌ•í¦¤îNÇ”¶‰£Iµ†µ,ƒÏïšLê3º«]«š4Õ,,(’›U("o$ÑówÄZj¯?#1è³ù!Q5‘­Gµ 7¿ØÏVÛ< ¬æÍ…(†æ†=òC‹¦6—8ÕÚóò–û$¿b¦ b⬠¯åúy²ùö WKì‡æÃ ¦g{³_#1è¯ó¨:*T¯ê‹ÉLãZo{¯8Á×7†ºN"ˆ„U@ i$½Þñã
+¾_&k˜ÑÞÕ°ÖP«×Ñ"R­#v2Ó@ð}¼ù!1è¯óŸ–;Ÿ6äØª?Ci‰÷ê3~5+¬ÀüÒN³1F ×›#ÏV:wk«„ÈΞ+o\­B<¾ ºQrÉž½ä²½<¤a^>+±Oÿ¥ß´WFï”2d¿8΋°Ú§¯}&”òrç"I@íî\Gêm×\^Mü´¸ ÅÓ¢~81h.7ÎçùHÙç‰)2Ê}íÁSõ¡Ä ˜ÞùlT˜HTÎ=wÚ‘×Äööð–^q:®=8B«9`\Ÿ7²¯ë4Ûq5nž¸î…ëÂWºV»µ§È›¼·S›Êe7奇t8fo1èãðÓòcL#ÿ,è'äS}ùéxÆ ŸýT_~³ñ¿ÊýO`Æ ßlü§úòkâ Üÿ½Æ`¾Ýÿ­Æÿ÷?O0ïÿ^ãÿ¯¾üÂ)ýfã?0ßîÿVãÎý/Å3ýfãЗ_ýA¿ÙøÌô›ÿ×îÿ´‘°ît(‡×„q€¼“y­8Ýò­œ>ó«Ã)¿â ¾ßI¤­¾£qZ-ÜoFâMÝž“²›,/;Õó¼tânr–ZÄE7ËøÿRbÐ?þ
+¡Þ…«Pñ~üs˜0ïԽǠëxA—â¢Pvn£Aï¸O¡ ÁÆp’ª_\ÅÕx ÏìKá‹h§M„»l<«/©Tq3owCî•·Îß½þðú?€2
+Û8í°º½8P.9Ü|Õ1¾ßö~Z@&þ>
+ú£¾,\oÃì:á/£xé|)+=çî+˜½Ãª{³Â]{¹Ò8íV­„³õÊ9os³'>IfM‘’
+É ‘¸æ[üyâÀsìãðÓ•ïò;žï;þ\Ðä¹s½m5#é„v)yä¨:poW–8e桬w×–²3³YUªõƒØfØ“0z'.m^uÎq+†<þJLy|<›n?! /ÿ÷ýôQÞ?γÕõM‰`.oKüv;ý{ðâ*­Î'FWÛOV¶QʈA|
+Zlî&+Ô³6„ŒXü5¢Y<Ùís¿FÞ+¥»ŒÁ­TšjT¹r~¸vã×Y§[¾TÙñð”E£¹6ìÁKñ¸Ø™¯A‚3?9j{.S¯©ï]F‰j@O
+÷Ê0ßp:u{zTÆÜ<ä7"È‘íWY7ªí‚­SUÓÕüÂÌÑZÐËÙ_{gÏYìyß)-/j¸C%BJwex
+o_’ô†¯ƒ~3ý4è'0cÐOƒ~ó¿"öuÐO`÷¯ñÿÕ—ßbüf úÍÆ`Æ ßlüÝÿ™xÆ ßlüßûòûŒÿÀ¯ò{ÿÀóþï5þóîÿt<cÐo6þ¾ü/h9'
+ν•:=¯Ét”½ý†ËÄñIÞÙˆ¥#Cîpj8ŠAèÜIo¶û¾Ö>èúÐoE‡€ðY}ìfÅZZ$dü,p^Ò:Ôíƒ>ÿR>@£r]¹ÌmÙ¾·Jîó:ª¹ rÇ®» ³°F=¦û;ÞDàÞÞp¹ÐÖéh~"T¥tý¦LÚÛ»¼¸†÷­'^_͹ܓÛ8¹#ŸôÝKÿz¢²Q³ï û v;{±0©~n”
+cûqâ±ã+
+VN¶õQgÝOgB­ã4ïÊt˜|ß/y$ão«ùéeÓº¸WN>Z6; uzµµŸ€ù¾ßëâàãÄ+
+n©;•ÓÝoÔž×—Éfý¥”oœ=R;¡³Z÷ž|‚Ö›ËÁ±ð›Ö[„þΨ îÖeªÇM«™|ü†˜ïÏßhÔgÏÔz7¨j&Ô?+1è[­ÿ­¤ûÇ»?lF·aßÊù‡Þ¤é|~êò í ¦C㨇ñÀ úEUJ´+—âÅ“ØÂÃ/Ïï`Œ±s¯nÐåu /)Ê#‚ÑQ'´÷S•ÅŸŒOòÉ¿wH*£ã£nŒ·­óÈ™›Ð¼”|f>ïD™c¡ç¢Ý%ßU2ÏÎiK9gSæ7:k$y•Ʊ³J2Ø ºYø¼Þ¨Ž¹¯Þá/‰Aÿhø„
+SŒf2é/i->ã;|Ì{>îF'|œö¬/É{wô?þj$
+ÄëLéÈ1Ƚ÷®«âœÚø>KK—R!-ìB¡ÈZC¥ÍTêµ1uYŸT“æ1âžÎ¡ki·CÖqg‰â)3bÕAÑUú°âß±ï]ú‡Dˆ¾qÛQu´Z„bwŽ{tQÅ,{²—5eÝdÒe}AñõEÏZš¦å*ÕZëÝDv›ø˜¤+Óu‚ÝMð©óþUk³:EÍD4E Ýã Á(iDYþK‰A‡¨Ræ
+6À›½A<*ù—}TÎÂÛ=ÙK´*yXOí8Á⟶˜8&ÑÆ\B„z(²eÓÀ×§£î®“NœZöÓ‰Aš'><ÎùFù÷K ¡"%«Á¿”¿c²<ö®·Ü¿wH†µ[Øa–É`ªH%wÜf¥Ÿ"‚©^J0—SN9­¦iTÖîI'G J¾bk4i;Zãexû`² öl¸”ø,ðe­’þÿ/ïÝÑÿhø±
+;}Á»)b"pKéÕRT©¬¼;ï&bÐõeEßð&oí†37˜i;¯vG˜s-ã\h›{1ãê;Ýiªj{‘šBš Hõ#;,5f1i(L.U’¾%1è»—þ’¨<¨Ù€ÖßÖFvUºØùîSHâ6ìߊAf-t.åº<ÓÆ’wúd^·ò"cè-×cLM\IU.ôÛ²H7:[A¸ð2·Û¾v,\6tÚj·uºG2egBé_ÊÇ~ò_ úò]È(FœvõŸ¸mïõR ±éþåT}!gfvì„•×Ĥ¢ì>·Û-¶EiKÂpOÝN+æÐÙniÔ‚UªÚ/ ß§FÇI5Ò¿”¿cÌ廌`òœóhTäxØ ˜2p*’à >o!îmÝ‹µ&Mø&«­#J6j ~K°|BæYÆŒÛ,åø=ž¤ç)‰èãÑæ5…hÏ6ÛïJ úñu€W>Á‰WÞÁ 08;
+¦4R*cÒÊÖ•‚èÄþǃ¾{ €Ö€ñu€º×Ùû#òLT•B#+äî§Ä™‡²õÓbU×Õf©Î›·•Ô`V¨0¾ø([„6ué™cñ†âʼ`^CÐ<ÅH ÃØÕÚ•vK¯®èÿR€ÙÍÌon©$¦Qµ@ÓšŽÔëL¡-¯YâÀBãñʘ›×ÜCa·+Ѻ'Lˆ[ŸEãÁ˜®—S’/ ‹µäs|:.°l"Z¡ù³‚ XC#‘R‰+¢ì€'ÌÇáëìîãLxžFUZÂÃþƒæ.k«¼µg3õtðÍ(¹‡sd>IÌ„Ë Óö«È–†AòÑl‡äòhý<çÆø4\ߌt¥†3Û*ó•p«U­³YqØOÈ{¯ï¿[£²> ºXˆ*×Z÷áyÎ*X– Kg\ ×f#köZùx—|ññfùƒqŒ I±V6O<bm½KWZøŒ5:X.;ìÆ ´p‹÷¼âôWdž,/ÓÕxÙÔçðâ:˜/ÿ¥€Q9:ýo=p•^µbÐMÞ–[îÆ×Ìó†B5¨:Hß= pz~bÈîãL†¯LH =Z§*­?Š.Ñ줌à·ÔG©åÿVY.…h.®÷i{ÑŒúó»ß|H úëü³•ïS
+v Õj)¤(¶ß¿0=Оy”èqbÄ‘õe’\QûTjé?¼ô²ÝP² #r‹´*Ì·ƒN~íjx×.Ô>+1è ˜ó#¸ÅKaÓÊÖ±ˆÜŽšB­Š2c?A'`Š¡û:K;”ˆIÅÙÔFÜÉé¼j"]oÙé>üņ²oóè¸ ç#}€ˆÅEø +ƒyN±ÍäL+ÕÒ3Ø)gþ¥D•`£ßžúJ>-æ¦Ç<ÈÄe¿xªlÒì~Ì\ŽS„­r~h78tÒŠ-¹»e—A´Å
+ô
+[Ê‹e3‰âê_—¨$ËÛP04É2Fò]
+ÒÑó*¼· $t•ד²¿(:ïZk­<u›3·{¡Ow7xk¥M=˜~„ NÀëö>`³Î…Ûnrv<)¯—-‘héú††Iõǃ¾ÕúÿA“
+@yÍP³k:ìaL-ÐJé¾çðìÂmß3”eè3Ê ¹€Ú÷iœØÍSR*u1R¬;&Xó ]¡Âp¯¼¿á’ÐYe¥Æ–Ɇm‘67 áëò'f=ùþ@÷]ˆ¿wHºS9ªœŽ—Çù–Mß"L¬ú-Ýõj¼5·Ÿõ8a¢Vn­Ý®¨³
+&¼Gã£bç&yƒ`ŒÁ„¡­vO¤ðC°¡p)€Dü‡Ä ¿Î?!QérÃÔê¦Þ…àÝ÷qbQÇ䨚ðΆ
+çüõþÖbT•6ýÛx8{ÍŸrÃgÜnÆI5Щ/1¯G(̨͓!3í8Ù/AÉõDe±ådŸGl¤ZZ‘,–°wÝ¢>mÏï¯>˜‘çƒvg?C…åÆ|]
+¥Y&[¨O ¹Üùº€P–•÷\ÝðŽ…gw³%·ßéQvJ´%~$¢BÖœ0LsØñޱÅsEFq£c®B>.;·–³èoýó|Äí|XÑÑ+œz ÃÙ¼]f™°Ÿ®jÙT šæÍlv‚¡â×Ü>)ªzýR1¸Iõ(a?WÚ‰ô¶Ûla¦Õ™ƒyŸL4+$ž×ªZqÛåM3¥Å¶8Sæãík«oÎt5gÙ×ò45YØž79 R63×±;Î<ÆÄ-õú®Ä ï^Šªyš {qf•Mc?gIȬ€qR`ZG¬Kì3â/X;¹$Br¹™Èùä¶ aXÊ1³%ç SK߃µå´øbå‰Í®•I%„÷ã31ÔÇT¶}×ÍÞiäÛîˆM¶/_—ÛHæ{¦Sâ1HKîå:`ÌJ€1íÆê‹Ø÷È
+^4rCįQËå ±YÌ!ÆœÏV‡ÁrŠñIdB6ö¬-1¦'ÂÇN(;âúK~Ô¾ ùaÈ Åá¦Ø–†=»2‹A7´¯‹iW¯Žgé:¬t„Åͦ6×ȤÐ÷1{a@[ƒ«‹ÝPjÄ x™+t¦v•éLÈSw0fÄÇptõíñ¨[Ú͆wg÷ÿwB×ïÐ56[0þÞË$‰õê«Õ!ûc‰AßjÝ¿BA‘k^É
+’õB³{K†5&²¶5žw@˜½év÷öþüvúô×ù%*òÆì zˆŠû3
+ÍãƒÚÖš´®;+)\æ>sub`̆šÖo{ó8+ÛuÛÜ¥âGýKU<ï÷ +*¥’
+˜‘õ¡˜½=r¹Z0[ÁïÄnÆ.}º,OÃË1— 5*=ךú~­öç)R‰«FÖSQ:”Gûmi—Øo¼µ¼¸ÕlËoðÜæƒ¾Õú—DÅëcî!æ@»àùûãÒF–|†î£
+îÜîlœI]_Ì«äçÔ^ó|.möýlÂV6“Ÿk+r±ý »Í•gXZí C¥{Ç·å¬L‹µ¤(
+ÁÙñ>'r’î ܈ü¢|`¾Ýÿ
+ïÏu÷ÝÙuT™ÑjTQƒ ¼)dâšJÞò—R·WsÙ68î¡Öð€Ç‘ÆUñ‘òz¡cÙ(Гíɺ› 3^
+Üã…ñ›àÁp’ë‹1ˆU^[ž…P–côÊ|I¢R2¹˜$0ÞçÁÐÔûQ%]ã
+¿éè-îߢò<ŠAAF¸e<¢‹VžÉu­œyîêÌbÚS%5ÝÝeˆTkµá†yއü#íO¹qg½`g9xÍ,J(O›óK#µ¤ýM‰Aß½ô¿ ¥$ÿ.­Ÿåßõýw#UNÚîÑèzç[œìÜüBõ–:Ómõ4핇õmìÅgSH£%•X'o‚¶Þá·ü£Ëî§ð€É!Ñ„.ÂÁŠª¬"š<'B†t¯$üšü…ùž+_³È—Ý.ð¼FÕ„³»³ãé:]L¯<›Nº<µ+[füTуQµ¦Ž#¹&SêؤÊU>ºŒë¬ÖZ4˜Ø»Tµ* _]Áë›A¬×·íû·ž‰ÖY£ 6} ¾$Q©˜ Eù]o¹ÈFe×mϧHTmÛ04+˜û£‹w®Í’`®(hÁÄ!²¨õ¡cqgœÆ¥-ùÌ”½–9Uè£Û¨’4,µÖ!^ëâRšàq¬†`
+4¢ð­²"ñG@|_bзZÿZG2QºÐ¶ŠªLf{“IóMŸpÏAVŽŸÒ—2.~+§*>–•KF.¿x-Ïíkbž)6öEï3-§¸ £žÜ[¨ž†ÈárZ +6K¢ó\™ÀRdçk•ZÅÝ÷™þx7!ÑŠª…ÍòA3üUÅ6òÅÞi¶»c‘…
+\\{ÀvRÉ:ô¶9±Ò¼2\¥ë6ξ8̬·™M›‰Ï"b:bm…#xwI®Aº_¯Þ©UY¸ƒY )´¡õ—$*ëHTB\ÜCLê]oÙˆª«ÃüÞá20:‡×,E¦=õ
+×Ýl€|ŸJ OéܼąçM²&TNPô*™\KíLƒÕiAY7·<_˜Ê‚]/ÚsÆ'p”p‰ùmõ¤ïNüXbÐ?
+ȃ4 Œ±ÅÖd¸ù¤Zá´rÞÍîN›"ÖÚœØ)ØžTú½ó˜dªþ¸^¬†#ÎD£fTN ¯R'ÿY‰AÌòúA/•µ¢½’òså4wŒ­xí¼xä2©ÐB™3O]`ød‡ MÞÃ’žÀam=[Ï–Áƒš¢®ÅMì—°™TÁ:yLeä›ï_–±ã†9j'Ûöð&´½a׫^ºýø’DÕFzš¹ÞÞxÎWÛÁñ®ñEé€5ŒPnBDZZT°uî1XÝÆ½Ùb<NáEQ‡gz°˜¸å×jì½öè¸Ùf×#ŽX’£Nf‡›q›ö“½í Ú»ÔëíC¯wÄWíã_ƒþÑð-±äÔ5Xœ<©Y½¼?É¿Œ» -”B –oXÅ •ÐÊ—I«Ï,äÞšV$¤3©ãõ>¸+[‹×hÔ“¡¸fÃA] d^ÆÅ%6Hì'ë¾ÚRýi4dú=zzs"}IÀýÒªïeå0*Ûž÷ˆ
+‹c1*,áuTðrnT´_•÷¾í<*MÔ}p7§®Ñ½¾ï¥oÝW
+FöµñÿH{Ó­E‘°Áö
+¼TgEetBqEÄYd$ôþO˜UÝÕ«*û]çû³aŪ¬ÜùÓ¤F”{ªÆPÓVûˆ¥iÎÆí¹d/ìjÒ^ß½±½»/p¹Ëqûy¥";8TÜ>LÆ£óÞ{ÇûŸàs·
+»bü’ÎF6T‡ÉB ¶Fôt€¢Ê; F´Ü¨m©äûâºQnχ¼½)ôæ|kúÑóÜå(ïp æ·ýÂó;úT3 .
+uø_Ãðå¾$ÔzðëUÙr^Ó™H%Ö–ßB÷
+®ÃÞ…_œ…ÓQnˆÇKó3?Lü»j9…po†œ~4 6cn³ÕFyÎ`×Ó©·ü)þut(zW¼ï…ïËÒì÷uþï&Öþ!~ã jžoïgŠ-d½“ÑÁao9½)U»›W˜Ÿ¶ÏynMÇõ4g¹Õio¿Ô
+C³ºNvt|]|ÒÝmº£¯ïá[Õ¤Óv©õùâÏH ÿÜCÇLé/ÑBú|Ï(|ßBþ.‡¡h<Þ¿¼ ï†tÃ|ù%™r‹V=oñ¼|åÕLùt©<+‡9›®ïW~»¹§eðNû®'/ßý VyŒuq™×47g*jÀΗŠVªÊ+0ÿÌ~Š¿5ã½
+Ç÷ Šë'óý5!nÎ@©¥™QجߟééÂ÷F¯øíTûüµüyˆiøAšˆ}k.w\ΪlûG¤®_­ys=#ªŒ&çJu•ªÎ¡!¯6ƒXZ%}g¾ìú$üºü
+-«—üs^kŒÒ¨B×ÄÂÒ¤3pn¹8AS>W’]_ÆÒƒ˜¤s⣟–+€(9ßw=¨9_£ßý–-P2WpÜ‘ÌX}ò~jçåíõ±º¢ÏknÅFG©æÄ{º<Fž¯¾õY©ðÑÈ>Z}ŽKdÙ}`˜|7i2ÖçóÉŒyÎI„¸B-âôÈ?÷ÿ‰¿E[>õ‹Pt½Í÷[BÑçNŒ´äpôÅÔÆ×o—û[»¸ç1õö­ð%>wûþýߤìDZð±c…M½ÞË\§œ” „†Ì¥z5œTyÚº¦Z½Ù&ñx<œ¶VãÄLø)¾¿&”È*ìåÓ
+ì7+Pô°ÍÂg^'A¹nÃ͘ù£LJu|9ݲéöõTáÌÇž¹Üíe}öÖ>®Ê[?—}}Êżæ5—MMÕ——$É5‘@ƙʺ.ö^GÜ_paÌW냱1hö
+J¦5»‚½âƒâë 5oF\S6ÝçîU%obáˇ:¹í/#7y:(åÁmgí#{S'5>¶ÝU–«xò¤Óòçõû2œ¼›A<ôè´­æ†WµZssf8n{ƒ‘põG§Lêÿ@ù­à7ü-z/)° 5=P*>2 œV_ͬ̇5Õwh,ÝwØ@?9+ÍÜooÙãöVŸ×ÍJõ¦dÆÉ;l/…ìÓž/óŸÇäS/»ã,º Gø»„‡šEžœ-êOº qöÐìÓíÇ
+@L„ú_/'ý|æ¹Oìn,üÙ]5 n-ª×力ÀÌâ\¹ë²•¼¿”­]·š.¾v+TÐ÷ÒbÚÙO·ôà0Η§¡ý‰nƒÀ:}ª\ »»q˜ìXƒíôk4ÙÉ'½aÇJÞF0@á7ÀË¿”þ Ì%
+ÞA%dvƒÍ§EëlííM·×zˆKi]åë[a²ák˜ôsEé½×kY:´õS¯c!ó]ë:­©5P¢èÜû²Ýí›A-5R<Q£qn²š7¸R¸I Ÿòxó=þ)
+[ufÒO
+©K>s\eÌ6Ã÷z³}DÎVº”¬ÆåÀèQv'îŸU]]î©¢¥YGö§œ0ã|f<&&Ùpa›Ëþ§œV{Ù¦±ëŒ–ü‘/UÊ6W%°]´óIæ)ãy¦ù¤*;–~©µ9ýêI›Cù·RP~hPê°‹ØÕ#å»1´w™„{ñԥˮ˜~$õöçH(Ãì®!£;ã_÷þ¸°‰FCòZœôù;7íž’ñ²#Î ø\ø)?59©Ò>²>W»3a;ç1Š[t\ofèõ­ƒÓôdGÒô4Éÿð™XÊáÈ'X”L W|r-BÇÌÄxpÖeýœ_
+®¸yYASµ¢Ô¸h °ê9»AßÌ?‡Ý“K‹q3œòåËw«¥%¨ìÒc6LD{¦Ý+Ÿéš©×êód@¡ŒÉ8Kñ¥þS€ùdýsR'.…Â8<УjÌGÃùš››ÇÕÿ„rk]ÏͲò¼,:âÖ1!V1¶o¾(¶;ê-¸NñÙèqÞÀØ'©ŽØæLš1ÚY\Ðï4¯Òl»½¡“.³§:xíHeLòÖ¶º=¿Ý‹{Ñ"üV©]:ß\ójmjôÍ(Ç%bžwÇ«ijoHñ,²§G¶  £ŠzëŠö{H³Ø%î‡
+/á\ƒk¬?M¶µ¼ÓÌÚÞóô'¯Âñ…æ8Q L•©n™ŸµCRnpFmçM~Û:·zVk çŸâ‚³„±ÿÌÝ(NÛ–¶¯øÕ²*™dOÒ+òb*$=S”
+­»@Ç‚Ó;ö¯AgZ¦^œ¿ú$Y•>f×QÚèO tú,T¨Á×ÛÇ)Ój eži]ä6ßñv¿EܘQóÞe&ÍÒ‘‘H³¤ó«ŸbÿÞìæ[ëÈ·¬ëK§c¡íå[Ðp!ˆÕ»3t°è¡gJåé¶eÃuw{/i>Þ¨ýÐwÛÇÉÞo£Ž~WBA‹´äwiN›;”tÖNJôÎ{>»ZÃ7N­Æ’¿ÒFùç~;‰ÖvGËyźÏ?²üž¢;”-Ëdu6¨z—¸)-@‚eâí±å„!ÕÙi[(•å–XK®š÷e¤5K‘»!ÍÝ‘Õ÷ öü {=6–ÜõÒ Óö½î]·®öÜ Ÿì"zÿ
+Kî{ëj^PÓøžm—gg'"Ëhø¤×ÌÛ7{fÖËgÓ¸#FÓªí˜vãÞØ±í+LíÙËvºîMv2>Z“%4¼AÃAÃ4[mI3¸1XnŽèj¾AÅþì$ß
+þt̼
+Sjù«
+ñ}ÿ»jÛœFOËÓž\®dÀörLOÎìt}ÜÛöܽôjüóx ©Èò:ó·…×äŽy6#S} ›£µ(ê—Ũ±¾«‹Îº3u©ŒDE=6F+=H +föœ[þK)À.(úqáƒßWuPð뿾úB|Ï eâÇ‘„ë³"Y6»v'ao?oÚõŠÙû©Ð¿ÚÖ;¸æzûy&^{Á:vºÈ@WªiÍm]òj€éE%*[J[ÊB3Ö;Xµ–Íè/,jÙýþÒÄkI¶!ZÿõkB=„Á4±ïVêåcEái4æS}¦WŽ$J»Û6HíÏÃx:ÈñÕÔÜÝèºÂc3XþzžGžê3;m\N­X4ƒ.;™\uaõy~‘ó1ù´íõd<]ë@?|æ5
+vý@ Å̓^\¯ý
+\™96fß좤¸±_ÞTk?UiÕ›Íò´6YÍÂÆR™lwîZ<,;»ámè„о¸ ê«Å³ß\ï3½—Ш÷WŽBäî–J W1Û?
+R>&ו (]ƒkÜ»ɯN³|®gÒÉWãaÕì]?ÔFn:ª‘oô—D1)ÌŸ’7š²Fz"žnåÙpÆË‹Áª¨½Mmk|×`ós§w™{¼`I[—ÊçN'ûP˜N÷µ 
+SÆÊtkê÷Ý·Û@H=N^ØNY))EzOj@¯S+Áüp’>u§FÓì‹—²3Îul<hQ²ÔÛN‹J¹z[þ<8œ¸IÁtÙÇ,N²szTb«XE`mÿÆs…Ïæb¿.ÀvÞpb\Wq=¬}Â}Mo‡õ@>ù‹êÍâ<Øà ¦·¨vEdÞ¤ìaGjƶW,l/ØeÎåÔ¥?hïjb_ìf#®xQi°ZWÎl­CÌò0O1¤”ªÐs™Æu<b×ÎðøÎ”º™,(j·(c«i˜òó};µüÐ'³—¬ ‚é°¬c«VÐŽÊ .θe®6&Z•Öp¡ÍÙþÛ¹tzÈKuF$6çòuÅzŠm2
+súîQ|Ý’!M—šYê[uê““ÆÔ».,hê»®óÿ(Ô§p˜ìä{¯ èwo½I' ±ÖÞ"íÙÖ"fY3vÕY]kí·Lªtv–¡B´SrYˆ
+·zŸŸ¢Í®°Ú³üý#8Éb¦,9*/iPÎméd;Q¼Éøm+;K¶{Ç+ÑαX«¡J»Ç¥W¿ð]Qü_÷ÿPÌ…8¬—ú–EÇþ©v®‡û¾(S[ùøœh»Ý®ÆGÁ–^Õkz@îˆø¨áÅtJ}d¤Tº¸\%_Y_(öÉ«fzKôæ”™k¯(¤^Û´ûòØÎ—„Gët\F­¡`gZx
++7¯&Iþ
+³îëq—ÎÄCЛm¿wì­"Þ™{o¶õn§`Ãg]¤}¯°–Xí—šS²Yƒfx³áoªA¦Þl]í>;µøøè×tÁU?×Û¸jŒ=©ÊŸ«júñ^WzÕ—QÉÈ?ÿ†òo¥
+á‹f9³•åm=tq³Jæk¥Ãàéo“§ÂÕç’ýØ¡n^&ó¸87:ߥ`oÞåäeÿÈo |Àˆnš/€ö‹-ÀÌàÉ£ pË<Õ†eJ[
+yUóðÅFmÖÐý
+hÇórGHÎÂÊI¯Enh£²2”ŒíJºöÈ–Tp Í
+°ÏjšNÿûåA¨yÐü¸:x$ŸÆ¢…xbpÁÈ#0øÚµ·4›§’.rû×¾ØÝ!Õô`SØC-Èà%Vº³Uš¿,hº H7)0敃|˜Õ ù1mZÖ{òÔKãÄ ‚×þAù­à
+sßš™ø"~ÞÉcëµ#Í#—ïo«ÁRû Õýjd­nrÎàÈ,îx›Ÿ“ú‡æúoAÄ{È Yî ÷’{¤: +›fu¥$Z{ttê]Õ.»KÆTj¼+S©®(RÌôÿ äŸ{@qJ…Qé¾YSöyâÞîMy?MŒ=T7ËG©«ñòT^HËTýv™ö¸Ð§]ì)¨}êÕO;ûdW(ò9þ¾ÄŠ\¸u&4¿çZÓñÑPŸëiN¥_îºÝcrçv›†­ã -A¹÷ÚY5;mg'ŸÉŸ
+`þ±Uëu8•ôÎÒÅË~7|Ç÷¢GÖ½¤Zž_ÎÄ“$Qmd;Í£­³X©/Ež¼žµÇÃÎx¬
+5óþ^M?<¥²—R­–«Ê½Œ¾ê~é²î¿
+«@E_/ÞQcÜ y‰Sˆî›s%šÂ™~èL^]Øw§Ïeª¿ ZÎfânö}°ì2o/€L®NÎÎÂë=ù3@>ÉAÑÝäÐöá»7Ôâ
+;Í\öþ ¯M¶ ÏœÏð‰çs¿~MÀ:
+>}¿£ßîiNÏBÛXÙU'¹¾µú¬qyßÞ‡³Y[_OdäžòÕntÄ |ú0îKE˾¤»æ3MLö¶0àôsÆèø™¥œ[þß‹‚ü`F'C¹@Çïrhãýü./cQ€ß+O}W ý^š¥ÜÑXé<*êE€SK¼2?g£åñ‚¤×‡ÉÈØÁ:6oOOûÎØ¦’m‡»u’Mïήj¤$¬¿É[2·¾m›Œ6Ÿ¼ÚªçšÈ ‹-@ËE¨ù‘Q˜(…_+$Š
+p²ÏÈn.Ôº:d4·ì^á˜ðè.s£[ö|Èç{'Ì åþ~²÷÷mÙl]muGÏå‘ÎPÖy×ï›A£éçé:¯ãoŠÓæÊ FLmJKi­¸Æjý*Õÿ —ÖP´×¼ýuÄ ¡„CPÝö ð釯:…|ž¬ÔC¼Ó
+UêHùÊœ)]²K¹y¼™[Æ’íKÇÔzp4Mvdq»¿T¤P~(:¡LôuI´êx`«‹Ãö­„5º¾Š; rù™§áðí×{n_ù7$îÿÝïß“FøÌÓPôüÞƒ"=óÃ×ðȵñq' -c+ˆ‰_¬I­x*T*ukM“&Ú”ÑÙ­¸ÍÉHõt(#ÍÍ3±åÒY*jÿ¸]éÜñ¼4ÚwÉçElÑkè5ù¸—ª²ÐÊ2–ãKlC¦Ðpvÿž‹AÀ'^}Ï:´àø2û çû¶v}´‰†NU¾o\!›>ãU=ÔÌCÑŒm§ftu—ÜŒªx{ý(/X­ÞBûŠÆë£snÏ–™vNYäê™,(ÌCºÄVJOdenã98"ϧÏ\é‹âŸE·[€š ÛP®
+'ÂPÖÍׂ,^žéÖüî¬k‡'2ºÀùs|ð›Ü>r0Ùe8¡´¹Œg•µC<Hõy«R«·(±ËWî-òY[”®«•4·3}抛óL.âYíu-Mƒõ®
+l·<ŦŸpÜoïúõm»SqýönTÚ™]’móü®ýg
+äÊg(zúŠŽ¾¢ú
+ !k@ÛÙÚî\hë¾
+8Ù5¹™]7wèz
+ +ø
+®ù÷ïþ± 
+%Gqp•î®=\ÝVüúªÁdö²IÞΗdÓsϼžÿœ¬¤R;
+[ÇšFƒžI>/¬±˜ö¶Cr‹d¢ÿÈ‘þ·½äks>óŽ °Ëª
+È耈Ž(¢Ýÿï÷­õ¬ï©¾¿?GR]]ݧI®\ƒ!Øë4³¯4.XåÊÒnýühÛ]°·6g¾Ø¯ËíZÍŸ®ßz²R̬î‹%åÑåI×í³ µ ªSrvÌPÕÙèDÕþ
+€³×ÙÇô{‘õ¡
+å½ø}¹Û"Q*ÚéátN7¸'÷‚Üó–Íuß—KoŽŸ(Ô‚ ¤²)|™$·û¢OoÆR»·Ö*ˆà7¼ÝhEi–îM÷Óùr±žn–iâywû²^\pûŸúTÃÞUǨe§qwK?èŸæ7
+ãÇ÷åDÆâyÏ·vqªioCûz ˯üãÔb>ï}ÿôBvb«–Ý(g¥´®ðƺrrzËëh%fÙë½y—«\F !½ÒçbâÎgGæu˜•VZfZ o5;öŒŠõºtŠV§S+˜ïO6ÿWüGf»}ß)œ“îȇÅ
+€),*óÇ´…­¢µÊo®Ådw87ÍuïwøûNõ÷¦j.3>•„·Hõ K„ˆ«‹­bPs)†‰Úx›á§šÉŽ¡††m›™Õj·šFlÍ6dÉt£LÁX=¶9ãçÙ/ˆ¿à…¶Þ+¸4ñ;œs¦
+ŠdG|]ÃŽ»Âizƒº{¹Õ¿O=S»räŠx¸Ñ"ö[ùöÒÌ åè>?´{…™â%•i=ëöcÊt¬yçÌ%ŽÍŒÈÌ îè Ì1³º
+®@ßóÑIHO¯÷ü/Nó#ç-0ÈÁÑŽá ðC‹Ñ‡”:œÈ©®¼ÔFSF×´V)ÁH(|ALfwô ä¯
+j}p~l­á~[éM¶šÌÀxÌŸ&Ôf¹–ʇE±_?O#_Œì„Z=¬ ’†_d&Çz•ÐÔu\R¤Û?¯Fw޶8ê–_ÓQ
+ìî£Ô»‘u«T‚þ¢û
+ý0ÅV}Ìæu+@ùLsbiŸÏŒ©•U+v¬kn!œtFö‡KÛåõIn09_'õ>g®c0XÇ#Öé¾¥­eD¹ãç‡åó¤&Ô“§ýˆL…ÿ˜u??Bm¤¿…Zþ…ôŸ>Ü•s÷=(x
+1hºL…<CsÌb2äRRyÁ¥ä|ÂÍ£î‡o׺€og©ä¯
+»Jnmu²;ÞùnYÏ8ï¸[î‘é^>¨~zyŽÉ/øAÿ4 gô]PͯOªãǧyOòöù4æíj¹]3¥Bè Û×ô¢v¯Vœß¡Í â…‰Á ÆÊ{ê£Á6œJ¥Ne1l´=@—K;Γ/—>îJwø_íΧ«¦Û9f’0õÎqú+OÚ«P;MGÃ;‘ßË0µ÷w€|u½ÅÜ#:ÛRÖÞ-ÇÞê{Öñ?ê÷í?Þð! «<¦ b“µ†^Bêõjª«2¹Æp4ÌpŠT¶¶†ð(æ¦ü'?u9nHmØ`ý:öƽàÞ­ûÍée¶C%~žu3\û}Ô¬öû4ŠÚ.ÃÐö»rÌ¶ßø†ø+àœç¶¡Ê´6jª[½pUý^z¤,å¡j-ÖÕŸºÅÝÍ
+*­´®ï'%u±:´F»Yº/U®-ZåSÛj¼Gà‡së+Fë^Ehì™;‚^;Îî èù #èT¥C¶YÝæÛH¯ê´ÖroùçÑ‚Ia¹Å£Ê_wK“´Üc÷žî*…©ä§Ñxàž•8{JÍáÖ]ß*(Õ«Nj«Ús‹ù‘ä‰u1Ê\a¶ê xŽÝŽú;4Ø2^œuoÞ}ÅØÂîЙɻˆîU½±•qË·6Ót³% FÃV¶†Ì¨
+Tà5q*ajÍ£Ö®ÿg«•ヂ$ò>³1ºî‘.tgm,êÛIF˜a¤4ik³›âßêiùœ²òbSN×ËXhqC3첊+ºw¼2fœ#bvæ° —ÑsÛöŸÏKkû~>[9¹ŠSE•ª4•·ÕmV\ WÙY“בð!¯TTþAÉkéQù+öD@uýäµÝÃÎoN“:CYÃO®£“Ó¡¤u…q0ÞÝ
+‘tË9Ÿ!mÄ
+•5'ô}BF.˜5þsõ½ôðƒÖ™B.®-*]Ë\ÅBÕ×w­*OâB{8Sˆ“÷W,‡b³4£äEÁÊ:ù¼öÜØ¹q–îåÚQž Ÿ©7ã‡xÉýžO¾›ùN “Án´?Ü¿ÚÙ‹ün¢>Ò §%âû$‘lfÐZ£ÅÍú|øb¾GW¹ï£û£*½ÍÊ*yåýŒ>”厗 Ÿcºtòò¥’ÒÏC{ùÓü†™Å޲V¶|Áµg§ƒ=ÄÄ8¢øß¥þiT{&¹•&ÝVh>ûœ´öê¢r´hÖ
+kèöˆ2ߨ/È›éaëùøðC,K±“ªL1Éü@Ò¾@7[{%n5C[£I“éõêÉ&Ó¯}‚‡Põ•å°²Õ'r%+jYnÉzIa«zíi±Ž¶—ShC!{~–|»8ŒrsuòÎ~nžeÉ])›y¦(œþ+
+? ²ë® dý
+€ÞrÀ%6‡Ùrð}£4qýžÐ¨6]ÄÍó
+]Ï¡P¯µ€£H\@–å@v­ö ÎÕR>Èf‘0Oë׫ÔÐðïm'a§ò¨[…vܤìÝ‘B1Jœ½uv¾u[¦—‹[ÏÞo®«äq¹bÝ2…Õ¼|) }ú¤mäþ¡5MqAfÜïn³û˜^+´Ôü~ÐúpÎëòê¶DNrA§¯ ûl@Ž=³ ×/ê WbÖ¯“ô<?kÝþ=¶;IêNOl”z4Ë7d·®¼÷ì…Cm7¸ì—ÞøRxzæz.߬ÙiRX¬&îNÍì;€ªlºKo·C´»VRkÅÒs¶óÚ_°jÊ”ã¿G+9Y-ý©'ç8O)æë¬Õ½‡ ôí}®ÝNQfÀÅW{¦Â¼geÏçOµ|Òùyü†a‡ö¼ÓÛ3VM–›Âxç/1s‡#­Õv»vÃ,gð5t5¿ÞËR0oO&u×++‹uõ\ø+
+“%s®Gk <ðJóÜCÌÏJx5û`&þmàâ”GpÎI ®Ëüf*†Sß—=͹çíÅŒï|¡ß6ÚÛ G·Öòt÷ØÝQÇ€G7·í!_›±xO¯«¡Žû ‹*¬(¨ytïÝ‚ñØBy±®'¾d9UÅ âæb/ŸžünaZe^Y§1Zá¶uÑп.MÊrP¦OA™ûÊäÃJ9oç0¶|´"DˆøtžØ•v&[áôîM»uÛnÎHö°®§ëÊÉŸ^G=|–=RC]\‹ÛLk6Z~úÓ JªÓª0óýÐxÚ1eÖ,KÂLÜ|›…ŒÙË|ÒÀ›þsÉ‚s.î@¾Æ¿¾åÐò«vçé8U¸²·×\ê´ –¥Š€r6jÄ-}K\ÂÝÒ ~X²´º˜_}Ì÷¹#˜gdªÉlÎ1íªm“•¶õrxÁ¢ÁÞ1ÝžšéS˜1¸ö6­ovó”.ˆÚû¯ø>™"ï¡ÐÄþ^=݃¼6I¾åÐòã ÊhÝž¶CUx3G0>±Á68ËÛ²uÓýx™z‹¤¾t×Sk³È9èavN¹át"tyJ€•t=Äü´¦8b™ºÕ0°Ò´§ ÚSѳu?Ù§;ïIaL¾µS¹ ´qˆ½þ
+€/#dË.”i¬¡Ì‹@áh–b ¥j7QÔážÜ0ÝÆ¡Ï(]ÑQ…µ)Š÷é.Í%ù"/5¼Y5go«–ÝÛmgZ©¼ü0üyå­oG(6Ù é²vbѶV^÷%u’M­ÔZ4ÕÚ'L”h¹‡Y1YÿùW
+4[òÒËúBZ/ìY »ÍPÇF;žõ.„;³ß`>O]'‡âú©)î8¥N)§˜ú¸>~ÄGnÜnwq{P¿@•B a_Ä4uLý~÷ºz}«bà
+ýÇûUŸñh“á—ÛeƒVÇ=±WŸµuLw=o€ ÝSTf¡Ñ5ûnØÖûöY˜è‚÷tqsßLJÍéI½ý»bµó`ܾ0/Ê…ªìÕ;]™½CÇŠ¬v¹Ÿ)>döß!r ˜‚
+²bòBñ
+«Ìûñ\^37©’Oì
+ÕsF‚xï­¤FÂ¥¯ß†%kúw
+Å$ äu —¦>­šm—s^ó‚âì±[?¥Iìù¯
+[]—*È(,†ëÝ»8òù¿Âú1mŒ)â­2íq"]²ásð1†O® ›0NîEC1sæÜ¢}aÔiíÕ|*Ç7žŒæî°áôÇ£ú\à´ÚR&ª¯ì*vËÎ+Ùøã•åÖgS:-2ÇR%Šu,
+æ°‰ç‡a5ïŒ|&—\¢ášKÂò$7Ÿp³¿ÂÌ[P; õKÙm<Xf*wV¥Öw&™S(í.xk¯D3´3Y’ül õù@.רB³Zå+Eò;/÷{³\xíÚ¥ògÚ)NX«WˆÖ:_hê0ïHê(7WÄIösÙY¶ay„o¯÷–Ü#|ØE>ý,žKë•¿â[·äèÄH
+ )»2÷`2qZ™#5…°¾Ï‰güÜBÅ ÎeYç(93ÄWët‚½qúYlYÔ㔌š¿ ã.”iëPf@™.e¾O€Õ8
+R3þ%Z+ùy¸¸Ús¤]¦ÏRöí?Îê_/vên™³\4]oj7ðè[·g´ÃÁäCžK_ýAOjT)ÿ8ç:zÃÅÿœ^ÆûåÀï— ‡(@¤ß"ÀÔx¥ºM†Òí%%é÷óøIˆg¹ø(?´Á£ רÍ8ub£ñ÷˜¥»MdôèÕæÑ,Ãínoþøº® nÊIùÔê|,9ä±qºW‚÷ª‘ ze"û ùxpNéæ÷–3¼¸ÿ^ç y€‰+ ®A7¢ý­ƒËÉîAÙ¯âaã>®µñ>Žž½ûýá–?Q¢ xÔY¤J7÷· z§¯«-Â]±"……OBâÅ»©³áˆ]Ö§÷‘,¬‰ýkg•÷ü ¼ã„8¿‘¦=|}|Ðß
+ç1¸ÕNZ¼¡ŽF8cöÁà÷Ii£î;“´ô,ñ¶ãfvz+,](n-äýIú{‰œñœ: #¥?Í¿
+sôÔ@õòNß44¸ÄWÒz³Þm"•;¿nG<ˆI
+}”\˜„‚S2®ÉÏû‡³îWYH?ÎÍn'Ùó…lO©qiÝxÌáné½KWf‰œübx\I³Ó½¦N'th9¤/Ïì–Þ^™,˜éÏ34Ðç=Ñ…ZÎ}³ZŸäŸgI Ê
+âjå:vS53¼@Dá?€™Åÿö¡)pDÃŽͧá‚|'wzÕšÅ[äO¨KXq7á1¥í£ G¢©MíRέf;¦¶ä²Vk!.âÞLvÎ}°•lçÄkÓÅ-3s9,`¾¿eU²¯öکн«!ÞK©5qVRŒÁ*¤óœ+äT>Žóáüpß4yÅ„oUl°ùYýô$}
+ΖBn÷kS¹l•dñò7ļŒ*-‚'Õ˜]ìíÄÍkÓ=E0=1%üeªé9¶kk§ºPÃU¼QL&8Öí5JæXv4glzÄäUhû#¦€òçT<}q€¨À÷ËÈr3.ÎziŸâd7Ý_‹g{u\Üýe§s‡M#'Æ+æ4ϸÛÍ!?WFJSrØiÚbÙ1W•Jß (O˜Œdj¤UÀ[WîãÕtüd 4 y ±Œç¨Äk)ñs”øíz {×ÄýöÿÂú§Æ¾…Bí ú^¬@¡4<Ç©|s¹¾[ó:cfÛK÷¼õ_¥ÒÕC®½÷BŽ,|ÅAÉžS™ºég†-#›{ö&ãšÈ«·AZR¬ãLÓCɆ#–zÕ\imb;q§b71´ÓÃQaYŽŠpÑŒ(za퇇Ñâï
+Q^L©{*4 ‡O¡YLcðƒþÓÿ›Ý染[¯ýþö*Ö8ñÜP?u#g¼î|ä8CcÁ
+oÉ)× íZ‰4¬^OÖ{²ö –íª¯ÈÊVgÙJ6før!©‹ß3½L}\ ½º^€‰¨]hfë˼#3»Ü\å¯ÙÏÍyg—(dÙfû{juÒ'üéi ‘L~ƒ®B¤dF~(êÕTÈŒ`–Ô5ÛœO£å¦Ug׿„’ §÷‰ë ~Jj™ãüSÙNÄtY¦PR‡À‹õÌ…(˜ü1ŸwD·”K޳jŽ)Û,[S„oi]{Ù<û™±ýòãd¬ˆkô´„;€*Ã4QÊÿ6MÙ¼;
+¦Ñ(Ê;åúÝ·ž¬7€ï7@˜z¯a“‰p; ?:ò ^N_ÃIÉzÊ–¸{œ^^O–‹ä~GôÞ̰èñ–°Èq{Ø2ÿÌ+¿ @Êh  éT±¸
+#ù³oô¬fw _;Ô+ã2Ÿz-Óß-gÏÓÅý –çÔíòqÁ=êûß
+‰ñ=¬!ãV#ÏŽ[݃1J2øâ‹ùü ÿôÿÀÙ&
+woÆëìç§WˆÔ³™Ý ™G^Ø¡ÏX“·òÂ[¦& }ÿu^ZU_Ók¦m:0Ó«r?#Ê?¨ž+ÉIØ5´… ÉÑøÑ¯Y£¤SóF¥t–Ýç>%÷6nGÎL(›A¬ÿ`ú  ±PfRAŽL›?è}q‹FgcÙgŽ wümrá6“½ ¬æ«ÐYb›Êz~ªXç©¡Ýîö¬JÓKiƒ@Køä`\
+šŠkU†#-¥9Äz#€D¢ü¹í'ræ´ŸKØõg_Ü\"fŸYR²¯‘8ìˆÑô¯
+gcÛÚß'§R3ÑjdVl±‡é$SüAåUÑ©Jë·ÜE§ÝŽÄŠ,T1ÜèCÆçïžyå­1[à-¥6àï§£ ñrÌÛþ ÐØ8žG{Ž4´zìˆã*ôÈÃa¡º­<£ªÿ*øM¯¿šp‹ÑOïWѶËÊäƒÚA/2ÎU«gSÅi[ŸÑ‡Ë"2·Üæ¤luXý")\xw`”îCÞÎÞun†<ý·Ð†¾²ïfŸXßÍ…lÿ-J„kqôbeþ@hÛîë’ÂùF®J{¿U+oÏͰ¼¶ÜIi•ÖëUWªãÙÍX‹}¬ʺKh/‡Sf§é{R¦…ë}œê™×>iq¿6ñ¡B6 BíŠÔùGÿ@s³Ï‚ï»OOeW·õŒåÌÊ©·yz£¶=+×Éd9nüðƒþÓÄëÑ‹ØÐªgb-d?aaSËù•‹#Ùe¶t.εõ´9m)"o­6±bäß©v›l}e–-îGX\e¢\}ˆ#6x WƒEV%—ûÖ“½s…KuT)êlo{Óǽ\a=ëÊ*rè«‹t·'´[ÌeÇÝbªcveFøp{Çza‰8÷pw$¶UÎú´‚?èR F™ùÉ£Óv†¨Ø™Ã‹1öòLšÔNYSIúw´z<7RpObéó¾ ={ ¨“âÜÀûýÍ®ÈÌ)ÙËWGÝn© JLµ²°;ºvi¾ÐN£¨Ð’ÈÚ1;õiü†gþÞÉÆ;0b1M|mIMÂC==³(Ë©ò*°Ö9-g:5™¼INÌVùQZ¥´¿VÃpKB‰/üœß¹Þ•}lœBzûì!߯§5Fs­N‡l«m/vfîÝ›öÌ_&íÙzÙnwª±ÝÉvÌßðƒþi®¹u ó²Ç7“[]¥™EfqŒðÏÔÕÄäÒO]uCD‹+IUY$ÛÎh0c‡ÒhšÒ‡úXŸ¦#ÂçSÁfß_óãÓä³Wœ}>̵y€yeçNŸ«´óðÚí98íT-Öi¯[H;zµ:¢(?j‰”¿Ô Šg%ó7œ
+—Ûž,êÓ­+âJâgF^­ÄZÃðÚ‹HwµÓ-'ãUØ$äàUiˆ!’b…GÚñŸmrý^°3[÷Æ]騭&µÓ¸g“õÂÑöÛ¸ÚiP­þ»2 „ £7~¾jJDíÑ „õƒ6Åh%6óòÍ Hð+öœ¶ÄÖV!…,¯ægµ‰ÍjïX?߸›Ú©‡Wyo#qâ¶Á°©¨…Áü8hòHæúÃ1þA{§ÇÑêNî’irã]§ÍôÎ4Óï<Ú¬ÐN·x¶™£†ƒv£¹&ýf±°×HEÍzd…ÞÄd¥d%×6Bo 7&T×ü ?è¶R­ Þ*¥§çš³xÙËÙën”jÌùgy/×ÏÃæqwáûžqƒË&ÝÏ/_•Þeð¢»F!êzÔè·í:tz>[µQKÛ·ˆ™t¥ÄšcijO^Ô°Þ¸I¯a4{j=^o¼º-Ð÷º=¤u»¾æë”wVj/êjü†Õb¾H/Ê™õËa°ôÝU¹«:c¶P!‹ì‡Í•=2·A‰[ŽÏ^íi ÌÃ5J¹bS4ÛÐØ6–pÃV.'M(y¤ÍšŠ&­È0ÒduïæÀOÕ Ì×@³@Õ˜)7¨¥è£Ue÷
+*Jôü?¬çÞo±Fí?WXUåpH2“Êùó©Õ©sÜNçË×9@‚quœqÙŠ+‹
+ñ¸®Ê…ä —úê¯v’Î_
+7¹”é
+@þ»«óÏb#¢¿i€lêLÊJÈ4´Òk†
+`¬7˜ß9ÀÌø°9™
+ð̪ðg¥2Ö•Ù£%~n¿‹¥ø³»½©©îv÷ëå(p¯ l}У¶›æt!Wñû  ¼ÑñØw}Íìýۊߊ’üƒíø‰¤;å8$²>nåñña?ü,nS»ku+OÎì÷sš±ÝÍBÕÀqoõ/
+áå£f!ía9дÍùj`Nâ,·) ÅÕºŽÄ†Ú> ź…¾<žH‰ˆëw1˜¾ÀâÚráFnò|\TjÜë=ïÌ?ˆÀÌa ,g[ããÌpD|Íð"_Oy~°^Ê33¼Uý' Ó
+"Ž'×@ÿÃèé”!_üœn^*£r„Iò
+™'´»–ïí†ë VÅò?ØFàý溉õ«p˜Éû¤¸\žÎ†&Vã’)¨é
+•¦£çä¼­‰i0ZçÆù”¤3z²ý´çÿ£/Ã_¤m¨V»ëI½¬räOâÏ{Í[o§«¼,'òcn3zm#’êæ†t•1»d¤y Äev±Ò¬Þà>%yû]O¶þŒ©Äg6¿ÕnÌ…'“¨ÕÃÇ¢^½Ð:5|g‡Ì°×_ÉC-ú{f†0æRCLJÃÞ¨üOÒ6D×B§ҙÅÑÀË7Ù5®ô˶žáÃÌ9“‡.°Ö)¥¸-é8jŰyæ„g¡'ñtYÏÇ$c͈¾Ÿu÷ȈW!œ4†µçèõÁàáû ç‡p7~°d‡}¦2ûÙðéö³ÑïgóFŸ!’áÿ1øÈêËæEÐä€õ?É^<ÍŸÛ =L¯? õŠ.Úús«¡ï?˜ÌzB}YfzÂó-My(Ä„ù3WìqÐ5¦éºÜñ¥7Öìöy´Ñ+ñpPx~ã̃èÛÙ›g°ôZ]Þî¶tÙÙ!tyÝ®Ñüó0 ù³ÛÿÁþ¿¸'x5zr³·7«¸/'vÛO]Ÿ›sˆ]4½ËW£Î(–gÂ_’åLIxá<k_æ,÷á—LTÅÕI‹wÌô{CãÝz5Oƒ‚F}¶‚—¿dè~©t¥Ü›îÜGU®³Â=»³ÊxPGyj%²z)î £øVÿá4;Âöÿ_Uþ`[ÚÎ=c0ŒÖjÉ VøøãKî^ÅÛê
+ÿ¯*µeóie"ã®ËÐã¢öçÔQ
+Ôå~¡½r>oш˘%$í“‹›Ga*aûædŽúc¨O‡N,Ó•²êIÚèUŠ•]«ƒN|÷vâ¾ êsŠÔ
+5 Ròv uï‚êrµR¨ÏªÕ_¨‚¤àáú‚±‡v3^jYÿ‹°xU¤nÇ?ò-ÿM>‘ëê¸é×t¶y!M•ŽÓë²Ý´ßU<Çâ¢5É— Ý^¹ÄØFñZd;Er½ä·1Ëk…”K„»‘ë¾kGâã[€¡üw×j÷»9•ËŽ»33;¦;¯öó—ìO!ÏÙÛA
+Æ,€BEL9¨) @éÈ)úã{.þÐ)'ö<ê¿v:6z&>óÅ’sãhõEK_‹`»ú€å$“ÆE
+qêå_fœ…zžê^ÚW¼F^æÐYÚ%ß8ÚuÆŒŽ7dì¡»³ ÖÍ¢;qÏvvê>÷œlÇ šWç|¾ç¶’‚¶ìÕº8²žï c­ 0±:½pl¾aoô €÷i R6÷]­ ÚìÌ`3}Í)r6¡ã9$²òƒK1ŽXî} /ÞH¹ß]fâ½÷¾é㻓¦çw¥P'1¯RÛh$÷¶õÌØ*¤ ÖkèkÖ:Á\óM/^&̽˶,u7“{u¸ÉÚ¥¡î±Áö§øEZ™—4аšûo9t>N½ÓÇ=9¢áìÕ¿p|tje7³dT$/Ãìôý|ÑÛí*Höä×G´mØû—­¾Ütöjé70?è²lö'JÓØš
+m`±Æl˜¶+꾑ú¬Ï\Öé”%«]¢~S#|_âQO•5ŠVe§ßûÀ¦ï,ÀÛldš¹ïÿ%enJÿUš´û7³”m^Nl<¾ÖÓ–?¼‚ËêÒl·˜d¥í]õ { Ív-–æ ÿ‰Œqûúܸ¬金‘Ñ £´æïFC»¶v=SU¾Cò¶Š[ƒýŠÚ ²ÉÍ+J÷Ðògu e«euþÀ¼<pÇ(ƒŒRk½ŽÏfçÑJ¸FÈôG…ÓãÓÌøÛǽêJlÛ‰vpšÛ/^æ­Þª ˜è;06ž°ÜéÇ}XŸ©Z¤…9(ÖjCV›(³ÒäGYI‚WKþx<(d9ÉXCW+ñÒ5w…åti-sÅ\û õ%­±?Å_
+ß…›Ÿ©£ý™<?œ¦¯Ã–§òÆÔºÖua6H3%íf ŒJíWœb>«’<êT‰¡Lk9ËÍö"uO‹Å‡¼ ÒüʇpîU¨TçïL­3ï±Â|pkö}sùÌ~ŠÅÙ½Ô¿Ôþ’ÎÄÿïàÚ0 2¨|Q||¢"+òÇRñêÎ_ÞyW{²¡mu`N($»)žµŠvo£-µÍ톊¥´X÷.üò@‰’ÈIm!®?– Ë7/­1U8\8µâ¹92Ù–6
+3lå5ÙI’aØìFÓ§~oOýY!ŸÂS_æÿPýEÚM2Ïj½žMïJò~°4úâåV÷t޼ Üi»6³g ££xs"l-™í‚ºžžëŠÝvåÌø5^ÎU‘+9r!ÜÖ7…×Ûd¦Û¹Ó‡Ù6iF3¼FX¢ÔÉN Ddø¹1bÊ¥JšM.W䯔© Ë”Ûåê—Ê¿HÛ—¹Ïê;¸5?7ß•óþ¬'§µÈû6Ìï†?ToºXœBZ+ÈäV=Û&åɼD¥­’ëmbå:š
++4áøö÷WÓ¡ ­ÎQ¸bÎð$ã±ÄºN g0eÿO–h¾:¾M&ƒ±’}Êã4}Çã+¹¸ò¥ü‹Ÿ?•vH};2°ù<&¦rõX;Éšì·tÙ›L­¿ß”ÉÆE{¼ñdõNÂŒ¼‡¥åiühˆUž§…•8ðvع-Œ…Ù®WQØéë¹™ÎoçÝÆg/HÖ.+ÎÅ *Úlw¸Ñ8a¸YÏÏÃ^D†ÉË*)ý 1ïÅkŸÅË\Üæ¥Uy®ûÖvûýÍúM€&ÖZޝGµ{rcet\ÀÒìP/ˆ¢á×uÙmsïÝ­?ß6f6 ižÍ ‰9¼õ$<¯ìñÝ_FO‡†Àà¡!¤ŠùÁ 9QÔèpý]¿˜æcÔð
+†Û*¨Ô4e|…¶Òqˆb8{¾„'k¢<D7òóL' Ù¹&´§é¶?¹íŸÌXØühSåäa?¨¯#¯ô}â³ÍŸ{³MþIó+ï^^|½+êo¦KÒ;»KÖ…¤+Î "¥XüRøE˜}l_‡6¾î«â.°Í(0æ4¿_7eÇTvÜ9m•R©¸Øˆ’¿ò„uÈEœ}­¿çÙâ'Ö
+Z‰ ½qsÒÂQz¬ãx-$v°+t}/SU{G8oÓ‚þ>t—6öèÜ^ ¬ÓìŠd[³ã¶fï̶¦*ÉÖÖx@´µq¦ÐÖÚPþçk­{l=wq¨"{²ð>ê˹S{•©!q·…²P{o…7—‹Ã±w0›ÍÏéÅw¦‘™æÇú(WýÁF½Ïbëuo•9¦wªÔ9úR¯*ݨœ7;\Öo¿°lD½‰Bõæb…‚Ñ× e_ìuËÞ–½|fZ6›)ü‹#ÝWÒy_ÕgÛªö‚Í9'{ZÚt%Ë£º(/ê27º“óÙœ¤4¶üìχ“v†JFŸÃ†câ^;»Ö/øën¯ìp£î­Wg;ñ¨ºl'tvM}ZÙôêS}½¶F5÷O³Ôdô|¿It«&Qsî ÿ„b [+4|µ˜ÿ…çIµë6)JgsªÁ§õŠ)ïW˜4µ–¢²ÖøzZ²çìu–öÉOi)ŽÁÖÙŒìipNJÁmÀ’×O¿øô²t(K¥î]ãÒʤ3Rµ5nC›:O¡«’Ò¿:vszëcs•4ŠëG¡.|:ÝúBåºpO¢z…§Ó ¶âÓ|íš ~³‹ÄóÅrÌÎYW¢¯öœƒ#ñÈ6³Òjžãú ¦ÕØáäã1ƒü9\öKŸœÕƒ€ndéG—* áN·Ò#Ò{Ì”&Uj«öZ­ý¦ÕkPoÚÌ¿Zbƒï²F]H‡Z´;Ƶ‘ϒúMª\S$Ûî•l%
+L¶®A¾úšnr¿ØvŸëëæ<õivѱWž©´½Ð&ÜzîÑ[™y®ü»<'=ñÝw’cÛŸG~Ù¶¶«m-‡g*S»Ç­©
+ÃÍC’%šEºTiT†ùV}¹¯ökwrÂÕšŠ´"µçqÿƒUAª´5ÁªÐ lTì’ÂWl\*ƒeô® ¹O¦‚BâfNËŸµÙð"?ߊ͢gp7ZMë!ŽF|sÖ».ØQ‡^îû?X‹%»ƒFuQ˜Ô£êlY—Þ«^?TO5µ9yÔ(t’úä”#»\«šJ©S…ŸIe»3¤
+ž ­23/%ÿ¹‚J3­”
+ŒÃ”
+Ó½W<=²Ï"?"‘¬ÈSÝì/ÖŒ?¬ðÌa/ÞÀâvã¬6• J9°À÷®gƒé@S“nox»ö0œ´Ïkóª¥u¥*f›Šãù^%S]Ën\J3ØrnÜEJÇ Ÿ/%,žë—N±â`LAJs÷B-Ümó«F&Ê·5ž[O F®Suø\‡Ô°^/¬ÊÈ/V[-v—K:góhÐÒ°i$Δ‘Sß,z!zdÛöÔï7K³„ªQ=¡Y}@½ìg(±œg)¥TRûFq‘0»B4…ú]ó•ÿÌSX çtæ“%Þ÷B™è»Tv»çGYœ<¥yrÆ…:Û co‚ïeñCùLá30ðÙ›òS¶_HåksËÛýɆ­^äÕ1‚e/l>çé@ ¥Ð%»*Ý(O›»jqqÉ´¿{zÕÂ2»Ä*ßÎzŽ.~LbP½m³Ž|s³x|;f¦íøš!ÞxŒÏûÄ œŽ ãy½zV­åc¹# Q„èÇQ=5YÏÁš’§àvs’ÎÅþè÷Úd/œ¯û –ú•cEh xÜ•F¯jQt³4_H•üÝ©–s‰’é–
+w~>¾øãÎcöyk·lÏÖ6À®ŠéœiyS£›uM…<µ­Ce0›¼$× Ðeáâ‹ë`Zäw¥ÁSü™úÁ¸îÔîÏûÃÕt6ê²Ë4‡êÔ_°sšM¦¬“‰˜¹ãhV¥ÆÑ<·GJ|G«Q’2“ê]þMâŽKÈ•í^ƒƒ¹\9?˜[ð¦Ód»kË&4ÉÈIwaNwªúÔw»´“ÎrF«>–EÎÿ,–‡9.4;•—œÂêÜš®š3§5ì².YMÅüœ ²˜Ì”ûykBŽ«Ç±ltÒ{lÃjfDQQc˜dvËa’]GC<SH0R÷ÝßÄK±óº”›“?Z ·»k°5lÀš9iÎ%}1’fZ«5‘W½ùÐ’'rýø]O^¾n‹p¯¡ùl"œQææw]že†Ã:›#óÔ´Xx÷™J&šNjði9V¨h3Òj‘7Ü´Þ·ÁgYFVM'ýGo>èÇ5o`5‚GJò|<ùÁþ¹m€r–>x„^²%ä7–­'š‘ŸÐ’ö¨µ¦«žÿ^Èû¶YžFw ճвú1g„ŸÏ+Û8ëÙÃüô¨×IfÑx5¿OЭœèñ*¿eFk\_ HÕ†Pk» äþÚ+¨Ÿ)*=7ɳ=7„ö)­Ÿ©^_ž¿ˆÐmãzìR¡»°½]÷fF:"O¦‹ÌRíb0#ï=¿<=ô•HÒº#P sâÌ5q›c
+Þ×ÇKnLJ­ òɾlpƒ"á¥=ŒË^/lÁUbæ:Bù–ÍŒ)áTH[Iû#‘ŒhfÐgZ]¶7#)‰æ›}³+V™S'š©ÏŽ\Äóúy;èÔ/¶Ý©»•kGJæÏ/ñ~°¿Çg±èœ=â5ÜmŸáÖ6÷KÒÔÅHÐT2D©˜aÆ‹&sòôÚšÏwWMCw;=SÚ‰©­·ñ“,‚Ñ&x i÷ãçã‘XîOéQãëÍ[.-tLwÙ$ÅŽR%õv<ùm*ÐcjSùT7Ò{T÷°2¨ T¹Rú}Sú‘~üâØ _±J;»wm£À!¦¦‹«LÉt[‚X;‘cÎ^7;3VÈŒÙR¯&2Q™6&«cš‚¿ÉÌehác°ó¼Oß;m³=nÊèÅ"»á¾ßéܭƨý\“<ä†FAZß
+#¤V«¼µZö¹"oÖø@Þæ›;© Âo¶¯ æš„+ï×Mó`);hb´È§S#g?[ŽÛÃ1-v¨~ñAUº÷;Ñê¶ÒvÒYïÕEûMF›6Ü|ì©¡Ÿ[»OöÑÊòïf~Bà Î/ë—Õ¬E•î Vgg¹:mõê««ëÕŽœMgâi. ‰•wqãUŒµ{­‹ÃíËý/ÖèñðõšˆlW¶ÑÞ,/ÊbÅFæ‚ ü 3«cÀ
+V÷1E*^h—š‡Ì¸ÕœyÖ¨Ô“4OnðqCo”Ù­/3ú¹vc÷ZãtúZù–!©¨T7½n»òÙŠÓÊ
+BùUÖºdÙ¾…²(Ý©ÿ`™]¾­dƪåfÆ£øú …É,±±ã´¹—“—Ì*p¹!ĺñxÐÔx×ë¡”K+ðÐË–|é„ÈåÍ=R#×ùž+x°ižLô¨Å,k»k1= c•ÙÝïVf²-ú¸7¯8{U
+ICt¤PLŠ(¤œTˆn”œé’† }R¤rf.ýw-WIcþì¶½¤çÒî¿H>Éí üvK‰ïÉÇ Rh7N>yMÞº”¼·›Nòæc&y))y·e#åá'ïN=Já’”}:K‹‘¼»ùrJ§ž²H?ØõFÉ›†æ)ƒUJ°Oz[òñº4üœélì·…˜}5ÿÿP9IÃ}¾O øþ ù
+Ð~+†™È¾Õ›»¡¢o+n=ß–ú:ƒ_¤êi˜çý5 ®þè]G¾»m¡K6>;ì¶žÖÐ5tšÏ²åSí𪴷ÙöiMzi6sÇüHÜ»n_ÝçrŒµ›G{'ØŽN…`ÂíÕ_ÛªÁ¶2qrv3ûiXÚœJG139Gãó©Ÿ í½6ãI5Ùd²Ù×t×{?ÿ’ÆxŠ
+1—ù…JQÞ½JRP-I•,iwéTÜÁµ(£»ý?6d9ŒÒ(_Îû[W½LªVù]W—‹éŒsÅÆ…š‚‰0¹?Ì‘MöGŽé9d`”ÙhÖüXo»ÒSC&0DÇÕâ€`îµ/œÚÊjqÂä`ºf²+Å{UªÜ³”|Ð|{ÂÑ@΂Ž,òBëA |<«›B»p3>Û/›¸wñîîT
+¥6@%Ãu~@jËë/Aò*ë?N»nÀÙiæ[•¹d,FwH¤NÑÞ‰5Ë
+ÞØ,‡\†ÒP•m±SF]þîƒ7§N;vz1ã‘“½î)º¬«å~UašTƒ§P²-óÜ·ëGø½xXâh¿xÄ^9Ä^ù‡£=vŠ£õ•ÿÿÇöMõÁ™§+`´µœ}O'”¬ZżÈè¦ùlò转@Ö½óJáMŠã^YºopÄ%“û”ý8—›·7:ìŒ4d™÷~MßTSÁ d‡‡k„Q]|âQ öJ4 #©­‹.<è€ÒØ4‡ÒøœAé6b¡tÙö~@´ÿ6¼ØÁêÂ{V;Ó…`-íy'_Û‹WC1jn:xЖUÕ,‰ãXçŸ8p©l20\ ØÂسè¸ý]gPÇž¸ËÆXKçb6É'ÑÓà>ñ
+æì{0º {Z¨hƒÏú³næ·]Iš|º’<¡ºR·mt¥š±êJyÃû‡µVóœU¿ ÍœýÌšd³%æD#34/²~}5¨Úá¶ÁJœŠ,ˆnLYüà´|ÃÄþàE(ýCOIãÙ]w‚ŒðÝÍVùiç†õÝæõr½R/Hàf¯LñD·ŠªÒ·;mø@¬¯æ ¬{sÖ5t ë¼»„[©íÁ­X_ýá÷\ÅYŸd>»vqÛοXm¬ÅCqèZV_Þu„Ÿwé
+­Œz
+î”Îcì#'3Œ>ä=Ôo·½ÉŸ{Фøè*çJ9À½|F©.ÜÉ&¸Žy=íÚ Ú“—÷hO’ ܞIJÞξ›ÓÆ`õþþø½Øá4u>qœÔ2+ˆ¦£\WT˘»,‚°\Ú­Qcµ•Ç©‰`É€P~ÁhiR¡{R‚+½Jh™ÝAÅqãÀ÷³k¶;Øèœ/›•¤Þ«RjÏH¼ÝJ—Ý¢ž­¬éUs%^nM¶2o4™û`Ð, £Y³ÀÝV ? ¸[ ¹œN§øÔ*­ c„Èsõp½-/’˜äú÷hRÃÓè–ïÉ+à…´
+„/?`’žŒ\’®oõ$·Ð$•Æ|ÆUOR¹;Ï›}’*¹k’$)M£ìßÒëjÆ«¤LËйÄ-ì´áÕ« 2ûGÔ¦®?à£bJé}Góé}Ç3jÌû’£f:|è˜
+“P
+’÷Þ%o[½%ov—OÞÍR#y%<IÏË,̸8Êà²0coýý.ñ뤗ö-CH3&ùŒ=”¸æ¥ž
+ðßjs û$ŸX-¿‹»…“R¤z
+I¯"œ Ÿâ²Ê8ïÜ÷7È¿íîövzu·Ñœ¾^/úy5—Êçò(hàe8Õ &œáx¾ÍSçOž¼··<†µûá Ä§¬÷÷ZnrÙ7½a¼;s\¼±ó)à…Y ûe¨gyi.÷Ù§¹<yÏXæSÀ¿Ö&N§NñG•謣páz=ö—Ç¥g\ð.5ûV­§Ì2¦p-</ uwfë|ò'ÖíÄc~z Ãoݪ’Öê°ÞÛkc^ØÁl8Ý<¡m¥½Ç)¢fþ1ˆé1\¾µÃ?dqm×ßz2rÒœü‰Ò܆>eLinqÍ¿„xZºŸ*èz{ Ñ 1pˆsá̲§Ò••:<Ô¯ˆ¹×{èlψå~ë­MÛ-š—Ï›¯g«X”Fö'råK5 4dñ¾.WÓePmUöþ`üÜ{Ç[°ýº=û²à’0͹IæËä.Íæ9Í ûÇ ÂéçírÕf#ù îæÀQé˜Ð~Ôî6¶÷iÙ¼Æ|CÜ‹l´À )ʋýAFÚÝùZxu—ëòœ‹ù£ý=cÞýa!Ë«N"ÔôÚ×µ¸B^¹éÍÖÿÅœ)gûJ÷í_Âÿ°à7ë?²¶+†YŒE?Í“Cšž. ßö®·ëœÚÄôžŽÊ3K€¬Ö0Þ$1蜋óze Y¥V K=ÿЮP? ßTJœ7:T”ÕµU×WfÛËG_,'{i½Hšãã‚/Ÿî)”\z¢aåGsqßYÌ¡!Δ¤³žÕ(ø‡4ßn®Óü ¾wÛÖ»{ k«KJ¯æñ2B¦M/Ž·ø8t¢‚8 ×ÒÑŠýcWzyã˜È­¬}»´|ÛE
+”áÅVq÷ój3.C"’ã[í¡“m@íy8“³@ÞFøÎÔn¶u¬€ÏT„æä¼-)öõÉÌlk6 ì¡ùÖãÍ{ÿæõ¦—EhF·› ÏÏ‚ôÒÇ+¦în‘#f []
+Ë`øÇ4v³Â7ËÙjvZ¸sûé²=áã:WœG
+VWün{6(aèôØïRS=Ç “㆓ïªl0Ö“]o­Y¼›©$–Mj_fŒf[“ܲœÊÙe™Ò8¢å?dÝù¯Ê¢³ŽÙ©`FQÝ’U‹<2[R6mñÛ1¡¬°"a/èFué‚[p3ßð—Ólßß?¦'ÌûL.°U˜ ƒñ÷ž^{­µ¬çGíZ£‘¦;ÓxxO‡Kûö8ð¯Þ¸\†Ž£hcåF²JâúîyÑõYu2Ú í5ÜH …ax}¾*óSõžÿ€Ûy|ÃúÁ¡ÜÇüö&–óÓ‚uýÂBs l#=FâjÚáºÑ¤K5O6†ïÙ~¤æB¹ tƒÆXÌEÝÑ‹ÈQÕ‰E}p{êÚ «8? Öv¨ÍðB…Ï¡YÕ:ƒ»Ƀ >™exÎ
+=,ZJ¾Y›}ù>™%;LþxîÌnx^OgÓm2ë Â
+…ó>¼Ë”ZÀ‹”oÍU×ïMÇç%k?Ñ©j9×±eÚøÜ(ÕZþh¦ý°:õækwÓ:W7ù‡V}žLÛ&¤&ŠÙRI‚)ËÒFPòòk$s‡†+…­ÁA/`A‚Ì[_‚–Ö8c;ý2ùbÿq»ÚòTëö†é_•àÈ–¸põ<ôÉÛ;Ne:®Lã!Ó±Ÿ+œ´úPO4½®åÜÓÕÈÕbT÷†·»öÔþi€ í›ê,©²ô¡‚’O¡ªÌ÷»])œ´¸u8UT©u¶Š Ü
+7„Vi@­v]Ëì/– écûëć1ø6#~V}CˆÙ¥W ·Lb™.Ã¥ ÚIV_¸¡Fð¸±ã*|+ðdz=ÖŒ0¢P{1
+` Rée£0÷õQšÞ].ש2X–Dõ´m GèšE3(JüøDÚÜ­39ëÖxqÖ½eìœõ¨X_Ì/Æø{øå²[‹Ýc»‡•³£xöåÈ-<$GÌ÷j­ó}Æh-
+«š±îð¸Ù}}Td Y¡ÃÁ‹,›ªû8M•Uì-å`o®¥(ä¶Rµ¢d¬Ø
+,¹ìqs©’žØX57‚þ‚޼qùܸ{¯p¨^¯°³ ÕeÞ5•eúš?¢WñgùÒ¬i^iÖ2ašµ)cfЫ[ð/gH‹œ séêþ¥ä%묣œNä´kø-Ó ú¹q ;åõÎ` äG©ye!~ŸÈV¤mŽ3Ä¡S›#ë³à/¯|ÈÝ/×=‡ÕÎW–lRf!ßÊLÎ:4W‚éþºÏúeo™­Î|÷Lm„n+ã%ôËëñeüÇø{8j}}•yÍ»ÏayÁEn®uÖ¤ ù¦™e@Wïᔤ‹êçH7ߤQYÂIFªçºŠØLj#¡sýØ|÷ùp9ì²õY§î˜ekÿ€L^^¾è"ç‚}1Ü5©-’TƒUrè¨3²I}d³ŸÔÈ&í±äp‰ŽÈ¡…ü¢ÿ±ï‰o{½/^µÕ|[’] ‘ùÌLH‹‘zpÔjî>Ãé|¨ÌôšSJrR•vx}¨/Œ_‰À[ŸÇ›>&ë¼Ã9³|LWL¡eEt ÑN}i¨=(U’ò°›ÕÉaÄX*Høu…Û¸Åv? Þ+:eÜâM·ð—†_gÕ~ÕAý-õœ›Aûæ —ôã);uŽà'³6MÒóVÓìÊ죲ùùU–‘a*ÏZQˆoN‹¿ƒ[‚{Aßg>²îÑW™ÕÆ1`~<¡Ë%uѯ樀pÌžÔ)üJÄ6õ!à—^Å{W£‹ÍzGÃO-u;Óuaµ€ºU’BÓþ>[úwM‘Óð,ƒ .Šé=–¶(µbe;ˆ`çò>:^‚ÚÀ]÷ßrU&/ÂEÊ?ø Z
+fy¢Ú]“«*¸E>ÙœÜ1GÎÔ›7”)ú»-µK¾Ú­”†×9òœ5
+qõê#¢›Ô§ø”j-1-¬ÑϼrDékëÙãž(Ø ÛB³ ™(Ï‚ŽÔfc>ÝlCjRCjÔBAjðFû߯‚öôy{ mº½áÇ͆Ú|ªŠ\›A
+ž·ĽÖ*ˆ"ö¡{~"C·Ç\ýcŠ%©iF¢eÙÅíÎIêZhÑÜ =Ö׆M*îXeð´ww ×êxû&WNÖµ}<«‰º¯˜‰WºN˜B(BÓ*Ü€°»†–“‰Ï”ì¡”©f—>ÑÄÿKËl%.1ÝXÊ?Á^ÑVƒ"/R~±TiåŠ|¿Fùª(ùÜhø‡4fÆ(N7ƒ§xi\|Rc¾»}+6𾑂Ý= $¬f3L³Hv¶5­å¬¡g±â•VõÈ/
+ÄnF›øÊjážÑÀjÝ€2npa÷‚û…Á\S
+ ñ:)4$:ÌŸ*ûG¾=!«¹ v§sF:0sȦµ&µ4Û¿
+Àey àF¸û|
+ÙnªËI}ß^¼ôSo1o¿)÷­¼8wQ¯fèmÃa߂뀋`; ¥d¶ÑwÈ8Ý[%aÚxâãÉ)è'#¡>štÊýÿƘnç·k›œ}´H•OìO·ìüÅzm4ô96Pn¯¡w&ï“•U¸¬–Óò}³pò§l¶t—¥ÍÕÍ÷7‰Ãy‡¼Säå¹è\ë³-ðèÌ"¦‡b“›6%Lœ+#{Ò¬=ûÚl\ìîx_³0¯Ý7â``’g]3>caøß« ‘3}Öœ–3Ò\:h‹ÚâkŽ–þ½÷ÖßB»Øª§vøEÚÆtwùèN¬£—si…3u”즇[þ2mÕ“Çd¬>*¶¥VªÖãØêü€ÞÆ 3ÙHß[Å,ÍXjÛ™ÁÝÝØ_lsãR]ÁG’¾‘G•ö~ðEÕ·Où#Í·î§Qí¸u„-‘% ¡0[Á€Ã /V¯Õl$ÏèeÇ]SÒ)i¶[Y£©.jÓ‰Áð®m¸o=ÔÎÂøÙt[½» ½Ô`öha4ûÕ1ÖáQ»Ô¨bï$]M
+/k»;¾d#p;CƒfnÝôíµ¸$L¦è‚ýBu^u
+Ýéè ²ëÅÇ#óÝl3Çîh°ÎÊ7w3Š^ÆaTíèW}wuS½Q÷@M×7õa|¹÷†H=Ç lªÏ·íª³eå¬LÚPÞ1£¸'_ú?Ä?îpï–û§è¶nÞ{Š^ׄ~Ïë:Jcñi[  žŸÙ~®ƒ“Ëqд1z€™ŸÒ€3ó9Y¯t4ÚØUkT}5½qÍ­´Qí /Úû0D¢÷m0©oõÅC¥P%¢nKYTEBÉ ç²Ìžï¶ òNZÓlEó7B =Aë”øEøãú"ëp"9%âœRß?›íÞ2Ù‘ —¯ñà¼ÆRɵ×È}Ÿû¦€“>¿;FÑ~£mçÂé‡^¶a9·×£áµ6±’2`²²Rçž*Ÿ¢rThjø½Õ4'ƒ¯eUÉsWÜ¬ŠœXé±°__ÖÂp|*fgŒŽe;ñ¡qãÿî‹«æî~ÌK!´Œ)Ïš¼»‹å«Ù˜«ã47í\žW+qÝÄÌŸG…q¸š7FuhÔÕ[õ =¼n8~ðð1EMÜšþ*ûc*ôë3—ýN²’‹v~+‰§|,n;µT¬YxI¥n;}¾c>†Üµ§xœr9Î*–»÷…ý‡ßë¿—~Ààp ¨žº®
+ ŒqY
+9ìM°f2D$ziÛÈâÒW8ÔÚËd]@qœ~„Ó;‚øk>jq÷tÕc“œK±$·âš2Ú[: ¾ŒI_$g+Jér? ndãrÉ£^µI´Ÿƒ_æÙÐ1Éå7á)„_ ϳ#Ú‘Bý±YºÕ7ô'·ðä5oªtÏútnåq 3žCŒîlΦV’²­GbsV9òW~q) rX6‘³Tn
+ñPé䱑ÔNa)ŒµÏ=s«-›6ã ôs
+ÍŸÑN?ª (µk´ûT}„¤¶m«D\G ÞS›úý#š–Œ¥ú›ÊØ[úe¬Çðö W(Ã~!q0ãÜí1pžè-¥âÿ°nTTù÷F§vzõí4·…ÇÛMN×¹»Þ Ãg.›NÚs†…‘5¬ç¬Å¢Ó,éWÉK‰,ßMò‚]âåH¡ß,n9{•A#hÐF]¿±ê­ÜÆ®æ„gWÀæån)QDî "Óõ%"÷ê9D®/;ˆ\¸â¿ü€HÔ{à¿ø&ˆË . ³¸ób,æ<îè1€¹¹Iõ—³eßÕÁ‚^w^³~õøòÉÓâ¾%;õö0u ºÄ¢ŠOë–ä÷Fò']D÷¬÷Ñ|î2èqÜË쮣ò² å¤V$nðPšá&ðB:#®/tF|É팈â§3j;íΨ|%:úû€ÿ±š_늳ËéâïM߆̶ZJ]²9Y¦‹»¥€;†ãt7s¢{à,‰”5F!‹ú¹¿”á›E´Pâ=^Vn¸]i¸àíÙS:¼Ú}ŸwÎçܺÃè¥mÙf–«½¾wA·¦ã>×Âáç´…#Ÿ¤…çÇÍö8ã-ì°Å¾ü¾Â~
+ôÖžôB9z±\̸Ái÷—‘!ä˜-Èâ¶1Ö÷çmÊè,+;qsH{3¸egåÂí늿 *Vãé–;-–ä—U4çFÎXÑ€Ž°i†²
+-Fn?jG†<A&EÅuïö×ÏuÝœ àÀ=Cn{
+zmYNÞÝŸ¼{ š5/jdƒAvþªà¯îf½ÿ÷ƒ÷]‹à­…’•Ëõ
+Õãdz؉qBÏÎ"Ï@­Éž–‡®x*´ºê‘7ºú!¼cÖ¡‚ÑξN‘ÞN[ ÷Û¸qI·°3‚7våÎEOÑ#¢YÎú%LOïápG—Á/ɧ#“÷ɽ¶¿ô‡ÿ-†«®óZ]dïQ…_ë[šl/¶ žã™¢<Ïýý9± ¯d
+nìì÷)
+ÖŽÁ0S}e°ËV1¿Ö¬¬¼áX:­Î¹
+´4ϺxPްÀvï&žûNgI>/[É…@5)tôÁSÂ_òõÊøý3=^áe‰ÐÛ+ò’6Xœ3²~ùØïùÚß~ª¯M
+Aƒ‰ë§¦o+ÔJWî=@;`NKÓŠ\k†ì—px–|ô—ǰ.¨q~ÕewÖ-¯ùGq›[ÝJçÛ" {WÇ×oÏy%ÿ§šñnNÆSµ{Ô»oáì[4ù{`@Î067û³·;.~rÁ8’p«ogàYW ä¥\AkåGõá˜_cC¸\––i̽ÿP§Á­ªNתâ"Üý‡›¹©*§Š˜g7ýÎ
+L“IWëï³³ò}"—ãõôêjÕÃV;%×ëÐ\]߃É:-©ómÚPÇ',{›*ó)‰|ĉ›h§ˆµ\¤ Cî8áOásÅwª¥=gñ'×-Ev2¾!,†0Ù~Ÿy=ò‹Œ÷›y=GõŒEëKóãnMKø‰pÏjzíEþB³È¿¬Ämï Îü¸£š<vµŽÃƒËT§#=”eOÏÉ>¡CY‹…]¡.ASkцw‚äÏXŸã;cBáÌ;fè`ÎN^цIÈôÆv5OP½MÓë'KÓárNçeóIÓ›n5Clþ_~Àý¥öà×Û‚­¦ãIÛ-Ñ4¹~*“a/ÛÑ_ε1Öš¦,°R©°QòO –Kåz*Uòå¼X/Ъ¼ü˜}u¸ëúq½JŽf§Ì[`’5˜e 5&Ùº.¯i¦îÆ}_8}j7¨Ðr*´™ N¸GFQá´ÙìÆ;4ž²ÁA±¥s¶ç;œ)Ù³üín”ÄÅFoU¥lg1x|"CÚ®#ó¾áKRäDuÜí¼ùñm]ä¬×¢Â>;ó&‹[‹.ã<=’ˆ5G/ß÷]辬~¶yTøî©
+m¿Éíã^#Õ@¦ÈºXÈÖ}².¯dšIÕI_êl>9•õÇ
+‚.V(מéýzÑê—V·ÑNU¢¡uþ>û@FrÉG§âNn,kG¼A¡gî¶$ìó¤ä@–€”2ãªÃGͶM¢(#¦_:LdJny¹3. ²~È툣ƾˆV¼€ðجâ¸Ñ tÜèmc<¶µb¶‘ñk¿­Ý¡þKä{ÌèY‡*êkº3Eи`‚ÕÁU×õn¨¦ÛçDè†.Æw“¿€›w‡N>ûê½,1¯Þ™þ²ú¦½B§HƒB·Ö6X›ÚÔH”ªjtŸD´@œªŠþ­Á'3Ü×!ŽÈÕ;fïÅ"†/=ôÕÒ‡è«=>b¨ç3Æ5 %Õ
+fÏ­ê/!tèž¼Qgu;ö‹tócù܈‡wä( a[âàü}Þ+o‚â}Y´Å¼eÁä.“ ÍÑûKíß“>Tú(EÉCäþPl’ÍÔ%F2Dâ—}ûÍî
+½…´@ztUzt ßý€=:z †©ö=¼òG Q™«œÐù°u,ºÐFëëü[ø–”®`ñ݆¨²ómQfòD~Ü«ŸišvJ-)Gò Ods¿ød-vžËDç|­ãü€±‡à–äYÔ‹JïcwÌýR¼®ß™ŸºÜ­˜CBTi#!V1-EHØ:|°\¯ ëw ê‰Vå—ßëîšÝüþÚI¶Œõ¥ôÑÌÀ;)ÞMò„s ±XòÚQi¾å‹Ôv¿ÔHÍ™šD\9/ xRÜâVÒ¹à½PI±Ye\Àð_CÝFØFÑïeYý¶ß êW±[}ÿÏ"m; xW`õxÿtŽP·‘qç:Ú.:ÇÜ4éîi¹sØ?ÿ‡1ú޲޶Ӥ꣤§KdèÔóxKnØ´ÿ„TÝS³9™@€º„=_ð#D~†ºWgö­ó±çùG]Ã@ùrWØJ dS×D>.)x_öy¸¡œµŽ~„œö¥ÍmÛˆ¹NZ÷z­Ö²ݲOÁªe‡R–Á¶ìqTÌ8–[¶•~Yx.AÏÎÛiúË´ªÃð Q ­æAÔ¶%M‡à¤_-5ÂH4}1ßãè›Öµ›‰lÞ•’ Ö RSœ |¨’nj·üÿGןn)«ƒÂîÔADD¤oEZAP±A±ï;ìE=ÿª9ß5×~ÇÚÏ‹*«žA$¹sWŒtB¡Eºua*Ô•SEªóh땇4oTÄÑ#"cˆ[“Ê|ö"Æï/FhãŽBh“Æ€ÐüÐô)Bh¢ðñ¶ÏêýgÓUÚ;yZÌ¢>éD½êõâ÷`ö¢ç¼ÛŸÒÒ°¶8¼9 ïl&-‡f «Iu“°OñíݬòdÞÛÊ Ç_É·¡¦äi@¤ºš–HÐ~2Ä”­¨D¾_sÊ q’ûe4þ.ðuÕ¿ãÅþ-í$C*yŸR§´£+Yˆ7©çK^7,üÇàÜ&Ìîô€HÍ0’ ÞôZHíæ+9êãúnQÅk÷XzÌéoÕ€9k÷¦±d³ü¾NÀÍW«l]sýr¡ËNñM­±ÅÝü茗ÇWioç ’±¥q ¹â©{¬©êc—a0Â:Ê÷T¸O¨Ð׿\ADfAAx/’‚°)e±eAW±BïS/þÑæïfXºÅ‚ÿZ źkžRSœ¬’,Ä_¯åu™Ó°­RýêÂ'¡bÁÁÝ‹«é3WǺU%(<fZ§QA"÷«M“ÏC⎪¬CÕꩌ¨$dÝHÃBjæ­ Ï7ê:ãûM¯KÁ(A;0ŠPIwÙk
+Ëñ?ºFgl¶è¡#xÆëþ»-àïžöz¾9¿ª³Û‹cî³ä\ŸÓÜÂé“PÒkâÞ2ça7S1ÑQãPC`låÀV÷àÃþæ×/<ÎׇÊ"_Rü=´û.ïPc‡@Ñ0ˆ\óÔsT;WÿÁ…iÛ{ÛÁ€ x°M¤ñ9ÖΦš¬ÝÉÿñ·…¶?Ë‚sô6%c”¬ ªÏÍŸÒðµKxìýÙÒ7†ž“:ÄðF£b½Ç¦‘Õ˜–ãx9LÌ<^›;9ºãx€‹øF†n¾·@–Æ pÇýp/±˜1ã—R6à‘óðhp|A3®2à±~ð@Ø
+n~Ú¯‰Úè_Û Œ/W=œ^ºðt}~ØëãYÜ>ŸIÚÑD9•ø4aÊîI»9ÝŒîàõ¼èã\¨õÿH?Ðé7MÛ©¥éÊê©ý³2bÙ·UßÏxÅY õíK[ ÙI’ÜãîÞrß›ÿ‚k¢ ¥øJO°Ê¥›ÇØ · ¥sT «gq#IŒ‹N¢ ÿ4©íÚý#Ò7²)AÚZí=0>ïCwäªÃoçblÛÅO}Kï.ÎæÚØØÿñ»ç0¾1²jõÕôirö¸Ú¿ÏAèºYA—Ý'Ò £Û{L®ôhµ¼p³íá,®¶Y9wóçiB¬¾'¨µƒÆu‡Vüßoå¹ÇÇ#f@£ßÏäJÌ‘²¥× cÓ-íÜõÆkñ\¯b6:¯”G]N$‡^jïHZBë®´˜‡íÿ‹ôãžô¬R !×3Ƶ¬ÔK+{öÇj›ÔóüfÑMÒå «oÐ;šro´ßÎõÙî°®®·É^Øo™¬¡oº±þX?þ^GSZ½a]ÅË>¾ØZÖ¼™°È.L[ ò§>_}«½y±{ÛÍvú ÿOÃ7=¥Š¨89oßÂÿ"ýlöjVZ—K?ãYVPw£eu¼Ôﻉ)\zâ@8Õ.q¿«w´í9¶MOׂõÀÖº«Q׈WàéÎà¥.û›Åbì–Ö¿Ìׇñs^`¶;'Ȭ~ñéñÐÓf$Ê“ Ú²'l}Žïœµ½ÌO.þt2®>X:ÎÍ)z83 Ô¤ßÂEùÍ'S/õ…H
+íDÓl¨ îà<XÞáÃz¶ÿnÃSPXË µ²ÊuLia£ª>wKj}æ‹fkzŠÞ”^<¹L»³ Gëñý´=ŽEúp½²¾b¤ù¬‡‰¿°TŠ5Ûa†³k¬î×,_öh€µ©g„÷ÜB?€û•>¹ôÉ?z‰m¤_» =©%_¢c™8¡œßWJ§Û¶sÓõë'KcÔ~ÿÀs7m¢Ó¤Ñ¬LºJ(Ž£Z¯6Š­ÐŽ¿›׸E'†ÇÁДΓ!ò~/Žvƒ"T>G^ ½¢r¥™ïáï%÷×kϳ{C£n—ïçÎ
+LâŸþÙ¸ÕÐàUÔô²ûv-ÚUç:_Ÿ¢ºRÏ×-ÝÉ:b×AýQÛv’]líX°ù-ï®ÞÍ£ØË›Á0fe=q³ÛÙgH°,æþàãv3W×ù'LÎÂïéiôˆšjÝ,¡A85‹AkÝX·šåk¯çŸ£iVc=w°ñÑúâ¾ßçÔÍåN¹º.îQgŸp§
+Û¸
+?ô§ºŒö˜jËVUµs¬Ú*rɰ?ÿ°Œ[ÿ°-ü¼å÷‰±vé"Qës»t´¥;oáU¾ëwj%ÇSX·VŸ­l×Á:°qEËâd‹¬S“¶ŒµÑÙø'ý·nºtÞµá9‚´/cÚø¾%5HLøêìÔªH•öT{f÷UÌØ¬w ß¼Þ*ȼ ˇr1ÎØ&
+Þ?
+®ÓÙøò{HÿXϰ?ë$>1BR¾Ï=IˆLÈiùŽ—¢{«>Kñªƒy_Óò?‰k¶ñoÇèRP¬G²¨¥¾«)oú¬MªÜSƒfâ·:7$«1dåáêʉYÛ­TÅ#®Ž‚'tWw.“·ËUj‹t>ã"Jô{Im9:Im*Mÿ!i!ÿ°²Ë7ƒnÏ(ÔMßÖ©µ§“ßBìcèj÷Žé`Û«j‘ŸvÕ¸Ú¶…ž_{ç&ý@æ­Ö¾nªó[zª¢Â÷¦®Rü£ÕJ^Ùµ¤”u“’ƒ¡#ÊebJíɵ%Þ`|,v—¿ï°y‚„g±Ã
+ÏR¯+<qõ <¡ùKäµCúËßW¯%NQìDÈò Øí@gr/šÁz5nHÞ í¬VŠaƒX1º‡ŠT{W‹z øWË·™ß},àÄêZo/Ôâi²Wv­äª”¯×T>öò\y¡˜*eRb®’ öxGžƒ8¤Ï5æß“lÀÕ6_¾
++4_Eä6¯¾óÛŒàÉ*ÝøÿóDƒÙqm‰sê~Cóuß̾=r§”Ü´Ë=D7z¸$ÕFÎŽÓò¯uµZÀv–Zï¦Òæû
+a«3¹¹572åu)<Fw‰m®¾bï²Ë¢ áÅqAŽÞ ÿ‘™*_;7}ħ-Ë­”]|’µ&]Üv+vq—î\2Ÿÿ1ëúwv„^¾DoàUÒÖnÒß6ž‰ÿ¥½³æ|üµQ6Õ­.á&«–&uY!Ž ]¦.ºÔѦ¡Äáï‘Øo¢KQ¤øƒ0×®‚¢)?ž÷ò¼f®KÜl´£9$÷”YÛ,;,†¶ûŒ[OÖ q˜ÞÏ·%z\:ô~9XÒû8eJIåñ“·³d‡{(!º9éùjR–¸öªSthïÉ{`›ƒQ]6v¢â{#]Ñ+FPNR³`
+ÊY²ûòõ"¾–›NyhvÞpó xæÌ
+ÿdWa°ÛĘmPaðt-Ñs3iò.u¨¶eQñ»W®£¸Xéj†Q¹v­yåê/•kí™ý€?ßþÈÆÊßeôÑ% ð°0kÞý>­êËæq`µÔMPûB+SuO;YºNˆû
+_k%ΜŽ5vM\\ÖI
+!³SÙ!Ó
+»üª6C¨X% FWLÕ(àVã[6G¦Û÷’NŽ<W)[áC(žT‰.ô­3…|¡€hÁYE´fÑB JòáÙ§ÝË8L`cm`¤¦\ó«|òöâç1[‘ w;ó \§Æ99:ƒ¤øû>‹©¾«9²úåHNÙeô¯üm¡ÝøècÈñKÊÕPäò¦êv*=!{À[p2”tlT䄰˶å׊ ™È…HPyd2¾10š“wïÉÙY*qUgµa€Œ<ÀL!`öÌ8£»ÍHï€9˜pÆ•ùÝÁÃÌ©0
+„2öÈHH -3»$¢ŒÅ+÷‡[ï8s¨Ù%Í=yyYªwß|ñ9¸R¯¢»!
+‘9.vìzÕ¾;çA•òÜ|G2
+òÙ¥2@‘<Pl>ȸÎÒ6Λïi:B”4U·üÿð— íŒY'M5bžÑKÒ´²_®éſնS&Uké5)ì´ä­çzO$¸:û‚F÷-—ïî£<»ã¿¾ùoépËúŒÛµ‹¾WzÊ—.Ú5.ÜꜮ6=G¸2;‹:›‹ýþ#}£ôoAcúø-m×3:ÍŒÏ(+h}ŸÆÐâñÒÖ3ð4ö3ôa'cü¾efÔï-ׯž”9Ük×ÖwoþeG/õK7j\¸Å«}Ž
+¯ÞYÜFI\&§oS>œ´Kø:Îy˜8"ƒ†v°?‹Æ~«-Z°ñ±½ö ÿHߦ’3^ý–0 2&Ìo©iöjÕp__Ãï<=zØ'uw¯Ó¸·+=¶ÓK
+ s„šH’ÖÍb—òôõ=ú¤%œ3Žt4ní¬ /ÇØ»ßš³;Ôœ`G.~w›ßÒ›ÉlÓ-CYürœ×õå×â•«ý¿Hß‘’ÝétE¦i»]ÎJØ­üUêë«'Úݽk×DÊ»—kµÜ9G:L²`}öŸ´ã{}4.·ý‹§=>\vAnðØ&F/ÝÒ«>Øt‹}xÃífYˆ±J[v¥\OÙÓÉ¿Œ%4,ù ã[ç+½Í¸‰Ì±å®’q¦g[{MýÀ‡?Ò÷fÓ·8)f$YiÓ$«[gBßñ°Z:?j³r¢l†ÙW:ÕýV9Åê[z‰n uÖâ!Vq%­” 2_N² k EÐî^Ì%<Y #ò>wrd:ÇfµüÌCœâôàté)¹[)“6‰xãk{<w«ÔmÌçZÐè±¢£¨ßÿ¿H?ù[VˆLß³”x¢ƒ7rß>šYÛOâÊðr4ãµ ÛtÍ]a-±gjùMwÂ2vÕJì̹Ó:º³íõÜœ•ùsgzx>£iE~Ž&ím~1a¼òvÜ=2ɘx¯Qô sñ[›cõðæ~à <{¨cød˜¿nnKÇòƒ#ÑúÁÿPo>òéçè4'~+׋¤ƒdŒÉ×£U7vû=_o™Nq½1·d±hÝÞózíƒÎˆÂ½<¥ð{ö$OXé%{ã\mô‚rÖh0/yñ-5ãÑZèŹ’0Nïù0ëÆ¶ƒEº¾
+ÎóÕ÷$*ùÕo¤G½wªq:@I·#™ Ë¡5й¯¹ï?p§_Ç?é§Óâo´—/&ÐòvmÑ=­‡fºÊeÓßeÖ¥g{îµømÀÇq_|=GJñ Åñņý%Ë)Æ 0—£Í’«E8ÆYý¬Ñz}²l´~àÞéÙz´<žt/ŸË¦Ëy…K§>q0.…ÃÏG •í:lk/iÛšÛ“WËÄâômûõÇý=˜³:Ú•vç±ð^ƒrš7À`<m»ì,“+Ê…£T„¢êA‹ÁÊÆŽÑ¶Ë=û‡”ëewí1Zîv‡é<ΎЉ¶¦÷¶ÆýI=ȡ՞¬¾Q;_¦-Óç¶-4œšN|€šÅº’õüÁ®t¯ûG©·ð›…âݯlʯÆÙÍ?ÿãz£cæX­âz¬n^s¿bîÆ¯Zn8w(.0Ç8‹Z»äÔa/DU—¿{§Ž”p¤v³û‚éiëð´ÔZ´Ö•z]³Íu÷&7‹Ï›솨”¿åÀo֤ȧÊÑ¢6·Ç«Ð¯7 Jž cš;8ÝG®7’úGíÝÿa ·úèf]³ž´0™íý²D–XË~N"ø¹‰¶ Jרï7ÏG£ÇŠS¯3ØõÚau8ŠÛúö¸nYÇó©Y¿œïÁž»½"zA~«
+ëŽ;d§íú§Ö³ðh]Ï­½»c”Ö°?LÑ^ľm[—½aðõqZºf=©hùy…§Ÿ£Z-\E‡6™ÅâÆë¼R+;K¨³ _s^Ó5?Àï”Æ~+"VË[86¸jõæõgzꦈsÇFêßb¯kåÌ^3Ñ1†—š½9×¶§rÏÚÂÍ™…·Ž'óÀ°Àôo»¬Ušäl‰©¬3ÆçÿÅÁ_í¨Z"óS~܇΅Zônåk·£ŠÇzÛxÞÕæ÷ÕÀû–OÏ\ß{´ýž'Þ†W…gëúÄÞà:´Ý_³|z9Èñ›³ê‹XÛ.„[OÒæa Jf ·,#ÏšÝcýz¬îônsê<-µçïžWÑ4™×¢™˜üNì¸W•øý<>ÊG~ûû—Yç=n‡ms×°›^g¨øÉâ<ñ¨ºé÷a×'Ç´YÏÓXä <6±½¶¶î`ycãj!ãiVLûkœÑ.j0õ×o¥ûë|iµèM»5iÔ‰´Qn¿ÔÔƒüÐ@p*i yt«SÚ›f<Žÿ‹M¼-”çMûšÁ sî³jgNçj»¹²––É{
+QÖ*]uÛ•ëù¦W³×ZàZ;³Ý¶Êëx`6Kó©qî'ƒI¯GýÖ~\tþ–>kÏYªÉSÐÞ+‡ÒªÅ®¤ÝÕ©ê$Vó‰?WñçªZÕVA-@zè…|eœï3&‡ÿX×|Æzèo5u÷¥kã¶µ.¶J» |lùŠcÉeÞ.A@¶ˆJÙ2)¶â¬Xé轘Ö^@Êâ±Ú`êm´ì´ª\´=W§×\…€«‹Ï«ÅÑ•õwÛTê;n*ï‰ÎIÞ“4,7’&7Îç¡Ü¸ÔvÎþöTû°BÏãÒTxÆÙ,iàS‹cç3$G­Òô4îæLwGÙ˜íX@ZûÜ‚2›ÉT0ØÞ ª Ë…SbǦöñn=­z|ª3Ì«p¬Õ%_>ªvÞ|ª…E/§¸…QA)mžY{‘ýr±*G¯!š×‘ÔRŒ£D}x™eñ2ëE 1;‰züß,Kl§4Qäv>j¹á!œÓ88-¾7¬ûµ:ìÕk_–M*ÑËÙ3Öצ6´IwuCË=^Õ³Î}9D*ÆÊæp^)näý½É~‡ÿÈ$WG¤V4¨H´t“Åθ⊜Ö2²úÇþWèŸlAè'Võ ýÕyñÞ
+Ýý1?vi q|®Ï™Â®mG“ßÙZ WóEÍv©<eÒd×û“OAûRJƒ¸;WE¬\Uµ7%[Ùâl à‘Þ“Rs”Õ9
+çRRn¥véy¯HÅn…ʋܹN;ñ‘¯ó±¤öxå½Ýpß±˜rß Ærßi©Ë}{½¯ÜŽÛÙ­7³ËJ)ÅYúí¦õßwصð‘5ÄEÉ­#m¤j&¥3¥G€*ij˜} Ë*vîЊǎ%90–º\A^žÔ ¡Ä°ì@¼Þ­™È‹õµðDF'!ZŸü»øÊññž*ñÙÄÿÀÜ$Y›\žE;ìüÞ^²f„¼2š5›ûk¶ô9;_ö6ÿ1íßÈÒ¿ÅïΤqÚI¿Òs¿ë{ÝÞvÕèŠ J›4OXÅ]DñF–ÕXŧI©ˆœxSݪÈðl웂tºôøw\ó#•]òU`¸©Þ¸rÙÀúeö©ÀZÅìV©ÇSL¤9½kîÙdÃîúôÎ#²hœÞÕä5SœX¦Ø­®'* •¢öP}…(æ.ýîðÝ­Ï÷3ç/ñ]{3!¥®ñNA)¯IX
+…-$
+ôdþPá?Y¤ÄW[¢ÁM¯šËéÝV›ƒk›áÌ.ó÷k- [¶` g¦¾Õ_LÉ"tãxªÐD‹R©æ5 (ª&•‹Ð¾T.¢Tª\*J£rAOãÊ¥PXQ”\ú‡q^NñÞ³-ïI/¼··èüÀN1vmãæš²6Õ-Ji˜>&…Ó/ü7[çs¯ÀÁ¯ÌÚ\̱…ÇAe\ók3x÷éýÅèÑ~§5¢In¶¤Nû‘j JÊå#æ*q§L> “ô“ý…ì‘¢µ)&ˆáö] †Óoö[1ñÚ_Vÿ×¹-žõ%½kV·SW§¬¶uÜ)¶>ˆx¹j «´L­ý’ðZ^nv¬å˜-WÌú1:@Ñ"•„•
+ÕfM‘b²˜µÒ5æõ
+$MòqÆ"2
+Å))±î†Hï³ ÷_B•Äby2êðåš–sðùÜà¦ù<à¦5EpS™Û¸ÉˆÙkÅþòÆMÐZü]JL¡½NKIãE¶'Î"Zfø"íZõ\QÔÒ·ÊŠý½óÚ5)°ØGÍSgdû%Ÿlêñ.pb¤œi¢Z,+åi£¢—u¢æ–ó¯(Ìβn‡¸¥¤s¼
+cë2ò9°<;ç
+y¬w)åÈm§ è£Adt@?/
+ _%3Ãòï"À •eFt ZÌgÄ `
+¢óg‡Ï0Øè
+ “Cu@߯#@?Äm†}lT[òÇ>YP¦å3Õ_ÌbìlÛ\±›oP·{Õ&fYPTÚïSã¦&|7g6ƒ°y÷Rù÷,ß>XìN¶Xà3X°LCϘÖ[mÀÖÍ`ÃðúØñ¶
+^¾Òœº\k4÷ÏúÕ‡%VÏQA3’ÔÕ~ׯjÓ7ðÓvî ŽÆ½?;¬Äýa¿­ÂïÝÁè3»
+?î
+Û]+äšdïUlÂPêCÌòv¾.е<&L¨œµºœkk&>c.}\¿78Ye\?ò6®/¢N6™÷þèóøúÒ*£«Ñì©ëÛ^7Õô'²c´¼|/ÉGõ ‰üWLyðžÜ˜ RN¨“·8T¸ÂôFŠÜèE £ ó…•¿…Yo¾¬uÕ2Äl|ö87qc€F8b™Y{)Ù›èTÂÜôSÜ–¢èÓZoùìPÌm0wB°Cn§qNmq¼Öù£ ªò)`úæ°¾(Ý=ólÍ«µ3bÔµ»)Iaï‘çÇyòÍÚ¥øw4É/n0=•¿sù„›Uö^ZͨWÉñÝ|©Hö‹Åm²¬½ø#ËU¢†sÕ:Ìz!V1Ž£Bû\ܘ0x¢7ö]D»@Aù 'ÕY ÑUËzäY´4ä ŽʳùÎíuë´oÈÅ|dïVC×èuºUMoôX…Ƹ(¼‡Pø—>ˆÑ«ÒÕÑ¡vgŬ+?ÂÉæ^`k§½ÃPþF{'®„
+-›F^tGD׎ n%‘§õþèë%<FÊW¸º p®^ò³\ìåõ9=ÉÃÒþž×1¯·n~º<vòÓá±Û¤šÚÆÕë¾EõŒúïþ0‡@S7­'uFCâ/ñÍxnªÜÔèCŒlô‰[ÑàV$f…½o;{Di¶.°¶é<aÈ3h~^ƈ¼q¹<ÒtUhEÍë\Ú¹-OeÑEÎ}†{pooàë$ §À
+#%ÆD/‡ÛµbÖÉzï‰>âÒ Qkû œ·öO¨¹/p¬Ò)¨ìy2V$¨<01Ã62öMPy²#@å×@ÁÕì÷^g
+TÒ®*g2θ\@e3Ä`P™4²W'·.¨LÑ¡;gâ¥MÎQí»åjY¬è‒%Îj÷Yšº\IR ¥îÐS;J: {Ü„4o ^Ïœ Ÿ€:ûo@½WùŒk!; ]À‰ 45«ZëVÐömh§vθå]oó€vé µKöƒZïhA¨
+xTÏ¡àÝßÁ
+'¼õço§—¬ wj ¤Ró¤ú;Ò&,™–~w–•´du¸rÝú¹Ãòï
+"—‚ƒÿè ÃaïæF‘ ë†¡Ë—XP¼Ä‰­³0¥û'yIOŸZnqÈ6_›=Ü>ű};vîkžl™ÎõOýj
+«f0 ÁÀÿÜŕ͗w?§zLsŽ
+^sέÍ
+¸NšùÉ.úâ²­OzW·Çqõ]³ê™u¢Ö-Žîë"3|ÑauÆb?ÏŽõê]k]b ;t”†-‚œ·/MfÓÏ̾Õ×¥¤UÌ–îÁÈ*½ƒRÞÍù“jñ ~Jÿ@Íyw'5+
+d4V«b½aµn½ÖÝ/뤢ÜjûwŽ©1îÌõÎ"Õ÷Zw|àµsìð‹¯¨¼er<Û…p,ok`ö ±·î´‰îÉ"Ýš‡Ð¿ªõë#¶;ÓÞêŸjðN;'ȘrÏ7–긹@õy³²u75éîÕÇàRß©ƒGŒ"P;”ã|™ßHïì ²Ç'®åvÅqÓ•!,ªìÚ¡ªrú9Ÿ Zqôz#Lvÿ KyýÒk>—“Bou²ÝÖ ¯¿Ê]Z:ó¡˜Ëb­'/fZ“Î:šÇ}èòMá´Nˆ•z½ï4êT1 kþ¸7Hó—$Û‹<ÁêÍݽٺrks¨>ùÛµªv?oçc)G_9´=ãgªmÞ`·²´ì^Å¡Û
+rîf­MÔ*[^-¤ ;AæÃ/¶ÜôpmW Í_F™ Òì6Óák › Ê×i.?{ÐDöÞ§ ¨vl¸—Œj¼'fúš«P#«ú<îkU;N4¹wr:™Ø³e~f[½®,·Ü¾ât¼›µytÒÌÂòwÜÜ£„h6.aÅ8IǖтͅÁ­2¯ò¥õ.–/íN£ÜF­±z£þeÒ†úÓþZÕCéÈkäféÆö’‡ëLÁz׸éöâ‰Á6½’«/c‘F“öŽpÊÉ[°çRA³óÒªTÉ« ÷j„šûfe`¦)ÃĤßóµÑÒ·ƒOç–r§)Ae‰é3úý“Óõ¢g5Joâ•ÞÒø^=»éS¬TÜ¥à_,MH¿ŒU‘wû²×ÒËçb‡—TíÜÜBîÃÏêhkœÉ@ºÛóˆÿTªM¶¶g´H½žÆN³¶jœóÓh­iþR¾bë´;®°,õ
+C½ÿ¦zqelKc¬v.•®PÊôB›Z#UƒVwW]´½¡º©³jK8—bí¨¶ ùÿb¨òu8}†¡KÏâ/ m¹îoÂ}f«Ùjæ2ºÛ »pª¸ÙÝÙª½Ö“Ù̀ѦâBù:üÐe9“ô‰¨º
+ fiìNIGÍF)si‡Ú´³i0t^¨‹ea¯ÚAõù©·CŠî”‹¸Ù±•8Ÿë*1•úyÃ(õÄ­¤X7ÿb3m̨Ùñ<p2ƒAûÙÝ;M×%ï6 ñª~b2¶=(]­Ýmq0ý–º3YNÒ“5ö®¶N®ô!hI÷CZ›&=A3£XS—¹£©Úó«"6Ò,nX½WÄC;Râòb«4 ÌU¡.,ý'ZÉ’9°ïJÉÔÚIÉÌ#¥dNY?Pºñ›ÿb’ŒÂcŸ€í~+öíMã¢;¿0¾¼A°‹ÉŽì¸|3>k}ˆ<¥H|^µYñöÒà4ŸUŠ7‚.zLYRâ¶õ»žxC,…ºEž|œ%-9¨ C™cì•”œ§g)Œð¬$–}Z¼“¿µÌ‡f[Tt#*#•Ö±,*m¹ñ.[z]eß=¼X’U» G>½_¢Û­l«yœ]“òmÊît]®JÙ{o§.×ÁIEñÞ½HÐáGi´F°Bó[\¦VºÀwIºäi]
+O–#‰­°)ÞKǾØÏ"sQ‰Ý£ðjìÒ»R1)hïEI
+:8.#~2ÂcÞ(ÕŸ<”¹aÜ<vU®Ò¸6¸J³3ç
+lâ*¥qgãý¤Ÿ¸öÅp.N6¡ò¶›Äùa¸#Ûcí*_)˜­rõ­¿êæQ›‹ÃµŠžÅEÚ÷—™Ür.[©SÛîÅûã~‹x oÀÀB©VÄ… YeùÉ¡“F°¼1Ø<¤—[
+³“NŠÐe±ZÏT™Êª nå ~º-ÊÕɦÌVH™Éÿ@†T$Ÿ¥ÉP8ªXÁZ)͘Ë|0šˆ©³Þvk'”’IÂOÅÕ“7î×,·°3g# Í6%‘]‡­ëŠs›Åî÷&³[ˆ¦nK†B’„>l5è¢ýZ,ЬlV©óó1¢Î¯Ó“:Ÿç"ͲšC³ˆV¥}Ûq{û[aÌ_„_ëœÜ’S™çió¬¾aý]z<´Ê9*~–[Iiú=U¨8tVšñpŽY§ãb7‹v}6®¬;î|X\_ÀL}¶Ãª™áè#Ãiÿl˜4;îרD?u¨0GL)aÉ[å•!{lÀ‘ò%›vÎe: žÝÚ•”ã¦@ʳ»EörD:[’Ý{ÞéòÃõÌÇ:«ºW¼
+š­N”qΰžÙ]Ÿ*veίÄÁv2ç§lÌÙ·ë˜ÅW×%Óàß;úØËœèÀ’žT²R3TXu ”˜IäízÓ³ôz™RññÊ·ZÄpµšËÅÀ‡o<Ú^nFžc…žã
+'<Çc,^®Ž ¼\ZÙx4:9¡
+éQ÷›µêtp+Z‡Ýˆ,æcH[§ô~9â£$¯Ð ?ZsÎ)c¦1 i®;˜‘wÇ_‘}t¯ÎúBŒ¤ë‹ÐžpŽ
+Ö«Óݨ´°+t¦.?·Q‚oj®Ø¨ùO©ÏÎü\zíØZ¹º¢[ëÌœ,.í1nŸê¶ã¡›EáH7vô<æ¹åìR…¼0[¦,|kf¸ÇÈ,Ÿ»>ôl?ÆÐP¬ õþH¾”û¨—‹¸å4aË\¤Ž¯¤¶¯•zòÌwœåÑ´ÌKõèjÉ|±õ§N½ôYÉU—¹}nV6ä {^à3àNQ¯p‚F£ßùÆzÕƒ4¢z»ÇÒNÙZt.7á-<W¾öø\®›èÙ¹ByYëå÷2«Òy©fg½Óï¼·a
+½ìˆ~Ë&&¸iô•œTšÛ²È`0$Cõý¾?zȧ[Úá³áy¦¡Ô¢Ð>SøþŠ"¨4 ÓÑÏAÃÆ:³6ÄM[p1ØÙåÔ‘Æ›9È J¥ð”‰È«ÞIq^òVKÿÉa
+@Ž(­z)Å®V\ÞO#ØVÿi–KÎERwø™”»r6ÿU¼Ø5|R½ùëBäz•#ºÝ>âBëSßÀƒ
+¼„ôêq‘]RÑ
+Ä5k¨ƒ¶KÑ8ê±¼ê 2€†54Åï:¼€¦a=…[Kq
+èÌîèìNÔ]ª¦hvÎø¦tN¬’-­d%Éïh±Œ|PŽpˆ–Ð Œ¤÷>º5¯Áœ`-×=@¹‚¹Ï¢õçЗ`p<Cn)Ø'`x'˜RT
+®§8§ìÑpZ1¸PSlBÀ ÂÂTŒs»„Ž6n:[Jå“!Øt¦È$ª,åj@cDrÁ R»X€uøç©ž~}ÿÕ“Óp7ÿ‰_
+ƒ_7`©#K)®5 ÕaŠíFkä®#yøÂ»ü6)ô ÛªÈ|g#á .ÓA å°~NùÛ¤oþ­þZÿJ·ôÛÂð×?ù¿dÜ_;Þ¿N¼ƒ!»ÐÈ×ê(üëýwÕ´H3²Íö€rY@1“öC¯ H ]P‡«Ÿó?:¨ƒñÕǾ
+îß—øÿ1³eæ_o„G”oÞî3¬ö¾Ãõ6tC‚:ru/uâŠwºì¥þh ÉQõåÄÿtµsRîç07uN·Ê´v’׋à8Äw½ïjYÐì1Òþm÷?ÿ¬–µòþµøã6ð‹tBl6þQBiè|Û½ŸÃ b>ô%ìÝÍ-Þìƒ0¾â!µø.Tß&ì9ž…Yör’—·ûqˆÞÞ‡Oí’9è‡tÈúwdŸÞ)D¼ïÌÎ}Ϲm¬o•-5Ûë› ÿª®—h­…Ø›®z þZÉç%³|†¸¾ŠÙÒôÝüÁïÞÿQB¿ó}V¸`W÷Ô¢’£\ΉV+nFË9‘ZãmJ­x)`Ýû| ¶ poŽÆnºa»å:DÖ›huóñªGìNËgs÷Ÿ
+Ö[n‘í}йùF˜ÙR—‹3dæ;ÓM¥0œâëý}{EtÒ JÄÿàtÐR¿ß~ s¢2×£º\÷æÑ|íÜÛRŠnØ9Á¬nnN\>@]|‚{y¡_î•ùLº»sxp©ÏìÏÉŸ!Ñ=œz¹{WîãIÉ/¢£‡n#vÏÇ!£=ÕÑv õ„=3|ö9c¨¾ŸãÁ'ªŸþ«tõ?õ«Ú8øÜ>W÷Ø:œzYy#!1Z'f©¿|Ö
+ÑB?_sóöÞÏìwæ:ÝèXš‹Mb ¢c5‹Dì.KŒ“:ÎŽ…#"Œz,ä U%<=ýCp‘*×úÇ û³²?ìÃóñ¢çvûîÆƒž]¨cØ'­NƒÝ:ԅބDZý¾›/¾ýxŃêþ¨.£-5"½åÀÍÙRn˜“Ø *Q€ õqHÐQáÆÃg[[>=e7Ð_Ú©?+•o}+ã¼zK³šé9pînœòu=´EtâZ‡í4ȉýU)d““ÝN:¨ßÊ ÕSËàm’@ƒÄÿ,Ë¿~:þÅ ™^’ã0vÓ+k‡ÕçV)(Nâr%-lj ÑÃg0e‘Ô•ú3u\î9¹®ÓÝT¦Í.¾Þu; ü0
+ó,dOçU;i=vmáz=´n½Ì¥%?ß÷à9*|-ƒC~d²˜_†]¾9sºZÞ®¨±¬Ú äX™Õ=n©ã× [Ço¢U‹{Æ¿8wŒf²9çèá¼P”ñ徇à¹ÀûÖþ˜öŽ@]|ÅÁá±Nàí$ ¹Ö­Ã[òC1~ `˜&Zþgã5ý²„~ö>4­bo܄߳yÃI¿~Å“CÝkÜÒù«H}j±V¨Ñîõ‚ø¨¹—"å¹á»Ý¯Þ½Â¾Ú—2DµoLÊÕ"•«;¯Ã½ö}7hv=.3ÏJg|½Xåô‚Ø.¹Nà!¯v§³|´ÅEïŒrEàOŠM¬9×[lcÕè+ ôµ2êµÒÖ©“™{£Ö4ï­Cç{^«•y<OÏ~ ·ÓåÖ®$—Õþ²q«UÖm_„“¡8Ùž<‡Ž iÏNe‘iï*•¸MT
+}L·ÖÅ¢ó/6‰ƒl§ f ³ë{©sôëTëYµrþôÓH£‹æ¢w96+§Õ¹±ºŽnõÚr jÍËñZ5å^Õ³èJ›·V}Ô0«Z<`Uçíó §”ð-'–z¶™«ŒmhÞYVìÂdo­Û—»åŠ<lîú5ƬW.zš‰Í7ü0YþäS0X³],'Ô¡ò/V ÐXŽ{'¼Þ#Z•bë¥<q?¯Þ3õøœ»Ö¹²÷öºòâ|peæp©ªúããèÖ¶§g„øl³-ó•ÅMU*vÏÖ­M¾^±ÜU×3clà›õÝ¢cåóØð_…U9)ñÇrˆõ?ú­ûÀõžbu%ª•žkgR†N¶¤ˆ”¢ný‹h^̓ÙÐ —^‡‡!ÙŸwC´¾ï¯C’ê«üÞUAÁ]8嬼¶­¼°¯8˜p±6]ûcáÏ o6´7ŽéähµX.'‡X+‡þÍ,‹ìê½ •FJºb¨ÝÒðPŸ”4½Ó&
+þ¨ù»¾*V‹ísC²9e›øœR ßi)¤Ñé)¤ÞåC^aRlÊ
+i%ÆQçú‡JÞ¦W¨ûR¾àŸ÷§½P3Ç
+fÉ+³9‰&Æi¡ ËOœëý&½.½Îâ¡TÒ»6™yé̯VV竦VZ'¶¸ºžÄbµ´"¦–²Ë[u¥¶êuäœÊ͵ÿþ ^:7)Zj3ƒ²ÄøP
+st®ÈFÃPÄ Á§åÁgn] î' QÓc]ɯÖ:®û&R¥65ò£(½µÊžŠd»¼–®Îd*dÖç>g+vÈÔ;7ŸJØ N<ût Ò„ ÏÞè.f)ôËçº\i4Ž:ÕÙE±ãÙÜÐâõ4!”P(įc¹ÐXËÍü ïŽóÁ;¥Øòœ-Éð¹ûðË&y®´DóA›¾h4Ò®I ˜ç
+Ù™sH|ÑAWè’½OÜÆ'+.°BÔÒé{yþPóá®ÚêÀÒ§?€Ñj
+ ¼×RIô˜ÂyæÞ‡”û2§s´œË\;kJÇ0 =õUf1¼¾26oS`ݽš™B¼gl"~ÿ@¢Ñãê]½lù;«*cXC‚éS
+·³8¬¢[>ÏÖLãuˆÈû’íbÓÁ
+ír !Ÿ÷f^0š5èi7ƒ\ä5»ÙYÓèg-¦:Î,ÛµyzGèo3Èm{›>xüMÂ)Z À?@OÑh ³»ÇﻤÀodàñiðyüøBÁSœƒ$Ï’q[vèÒ¬íÃ*.ÞÒ=»<§Ï—\ü¾©+¶›:FÂlúÕ+wò"¸· á¢ïç&È(̬*•¨áX7~”¢:O1Ú2ûJ
+Ø¢Ê
+ârˆø¦ DO_1¼Þ€8ic@ÜIzŠWˆ·øÄû0Ý÷PÓ}s
+l;Ü–ä;¹nØÇ㊇ϥ~æ~ŠÏBÂŽoØ9)gé³0ûp§[‘OòÑŽO—5j,»‡ˆÖýýŒ×—ÌjÏbd¸;í¼L›ÜÆV¦¼m䋵ÿ2ø]7kuó]óß÷÷¿:c>ó-^ýV?ôE Üa¯aÜfè]ñö°•pp3NtêÁÖâ8D•uú w‡¬O÷fB$ñRDo»M<wøèõÙ62ïÜæhe_?ØD®CŒV=ŠT—Ï d/‡Ü<\|ºÊn¡¿@a‘yÒ|V²þÂÊ(âžÒÂÿ–®êóUöêž
+™Ä¬¡Ó­<ÀŽÃB—ýwQÜ›§vyç>kKM`gÃÎ?µµ°NšêWŸcw9¤oÃŧ•D ýšÌÙîg37ŸŸýlY‚/3dŠ¿¦¬À“ØqÈIÝ£c]hDì)³ûUáZm0ùi~,\»ðüþøoïæš¾&lÿ¼9ª+y#¡·ÚõüvæsçUË?—C
+Ë-ôK™›÷79³ßgfºÑÏŸ•¯:ú(M¨ÕÕˆŽÕ·±»·÷§‚U8ÂQ'‡#ùj,†C¹>ÃÑmPç\fòlßÊ盽eu¸ì-]äò_¥«¿?þÛÂèk4/YÐ^ ‹]}1B}¶”“æÔqgÒ€ŽãèhV»Ecá
+-6ÙkýÙ•,Ý^×Fë66¢oÁêôôÊìÛÆ³0®Ï·±bÔ—CüSš!ƒ …Š8êQoq8ä3¥A$CöÔŸi…FßR§ç@ü°»±¥ißW!m;Ô¾t¦xÙsåÞNÚµw[¸Õ³­[¯[hɯ1<Gg1вÝ,ÆõËùV·9«îM8oe“ †ÃDÕ†ÃfÛ ä|k}‘¥ÌæÛÆô"”èX)`#ù(dúý˜ë9Yêzyí40 Ê“ÚIêmá2´[=)ªÏÁ,ÔOúŸh3øü¨|Œüì<ž5g•Ûºi!Ù}céeÏ d<ꛦ–«ãg¯5„H¬Q÷¬åEÙ÷Ø÷nî&Ñ8놆$º"Ô1ÓO+û‹Cù$,– jW#>7~ š…®wcškÛ‰Oœ[½t8‚¡rùQé ùÙYoZy’m,«ŒÔ@v¼VßÔÓø?–œZTj5êb4½c§zԸɠ¹¡ºZUoÑéðU(÷pž #ïhHŸµ?[P¶Ë¤•†¦O6¶sy©ÌN¾˜bª~±á­WrHõq<Sæ-È´úÆÅŸÄóŸ;U6M«[ئðØÖ=qqJóýFe÷ðŽçOÎãDuÃ9N¹bEª½³ªT•¶­;/Á¯8Z¯^u€5nÚåÕ4´sÕó¨bÝ
+«J¾gœ­j~š±Ð3F™5¡ª™ÄíÒ0öóÙÆØ†¿uJÍ!’¹C¹øÅbðíÑ ÿR»õŽ·ÔÚóÝ´­Ó±^— «¹‘wÎéSïÔ×—nGg·Õ~C>;£–üp2'kO¦!bCp¬Ì—3®R ’ôÞ·V—£fU;OÓÂdÄ5kC¼iÐôYÔÜúÓò™\ÄåÖ±ðÔ¯š‹ê]ô¡èÒ®V/=âº4(ù=>E$~1­ŸýÆ
+?v™ârpM¯±¢£A„²Ù’œâÕŠŠBHKn æu™vÐtB)¸|žâÅtpñbÁºØÉ/ºb‡Ù?Ä[%RôY±ƒ´˜A)Ûí¢Xç¤xÊUÓœ7± ‚¿ùÌf§•/ë{Wï+†_¡W¯¤]C+§yÕŠvýâ*wžÑÊu©lYl¯x‰t‘÷¢ôw;#Ó}?/ÒëDâÖ;^¼„…’Ø W”r«®Ð÷Ñeš‹ÙàÊ¿Ц˜©ühj¶ùQÜLS­=†ñ£¹DóïœÁô1‚¨“3B×ÎÅQÖ™’ÒoÅ/»2ø5Öû·m·T*‘é´wèVÕ|b»Et¯7•£§¹˜¼»™ÉúSé4¯%>?ŽÅËj—ˆÖû)ÜoÙŒÐﱈP´\†Ó‹"?:½+|il´øŒ¾qSBI8c¿ÍsР,³ -볋RáÌA£ÂAãý}7Ýæjeø•kBy]Á
+%ÖkR»•TDÙ Au,‹×ke—3J=XuÚ¬y}f“óÆŒ;ï-¼ÇtÌ<ºþaß)‚Mmb†:Ö•
+¤ºK±kŸL†Æ– û‡±CÜbÔ!n{vKŠÄ'K††I|ñµ>(Yª%$‡9ævû2ƒ|÷ «I0×àÑv¨Äê»%%½&ÊÓe…8¥ÌͤœÎ.mÔa6îËeðøÕ ã>Ý¡©77¤ŽãòŒ
+ÜpKqøìD&q’æÈdØ'r)$)VŽ
+qG(›èmºBé`kü%^øðÉP¸6ÜWpm”¬ðá{ð‹ã_æ|]ôn>ŠÙÈ¡ó4Â’½/•Eg®"×þ@>FçÖ$ÞÖB-¢gD™E™­ÆÂʤikîPÁi[§¸Ö¥E†×{{ùˆ¸[ÊŠèç=¡¬zWüÕºdð‘€¢x)ë 8˜Å&6©°f¦s,—3ñ\ò Ðy§c¢óîpåbû"F1#›`>²n+_·+Þïe%½È¾Â·?+}é’[·„aœõ¸¼!šÌ–¹ètïkTKi•ÉNµeýz¥úE¹ïã£a?ÄKÚjˆgÀe†Mvè3êʃ¨Æ ?÷´2BY´PrJÈ:sl Õ™>A°Æã‚`Í.Zر¼^¨mѨ°CŸw+9y¤:ê¡FCþº˜'‚¹ëÚ‰ÿß'­Øÿ
+ßüDþÔØ*©UèýúnP¼RÓˆþ+âc˜1±)‡Ù˜ ¤º0ŠjCÕZXt"díoWˆË¾v¡Ó+ì:Õ\¡n¬È£Åüaxy¿šåY¬}„Ï1§8«p»¹ÂçÅðšgaÎû²†Ô.ÜMøºâé|-å6Éîì}™0}áµn·¸ù0©ýêÉ*]¡Z·­A<Z•®S™"ºØJÈ&w­bfçêçk½@…P;™AÞ¿—¦yvÞÙÂIe›À!’û¤¨b°°ÙJЭ#:POZö¡4æSÜÒì5÷lùRîÙ®´!ycS\rP ˆûZ„\…„!H]Çuµ¶
+#ï§â*ô…Œà·Ùê%®ÓLÂ9¤LÏM||­”18ÈhˆG ”|ÐöËp8sÓÌ]7Å·îô£ õÎX)íâ*Eÿ”{ §Wnx' 9­_ã²Ñ{UΖ#±•Í•ãUf™¯Œµø0k¸p3³×~›™½•G6ÇŒ *D´É{ƒ4GþwL¸•ùá|*¼}hÀ-,³ÍPä²AÞDÎÅ?­ùï@+&âEo=*vÓ('[/çÞùº+Uíj.ƒÕëÙÉnÐÎõõ 3?¦™J Úf
+¼”ü@ Ú­€É;`J±˜âTX1˜Œ¥î)
+`ÜØ6Z 'Ò}x5ûµù.Š&ÐlTM_w¶’ŒNfütý²xU©Ä_úİϥ£YÓ‰ƒÔPÉÊŸö–Ú*ÞÆVvÚò«™Â±ŽfÝl3…Û8·ž
+ä“ „í}Mx˜“×)Î/ ’]ˆTÑ¢‚NR<Ó}• ÿë«`ëvŠ](tHâjŠ¡žJH¦3Ê)tÚ g–(ëìì_aû·Iòë*ñ_ߺÛ_ýö¿|„¼þ{–¯}Â\©°¬Éf»—N«×.@îag{ Èû^䇽
+,#@ÉCe Ðó( :}>54ûx÷}“ÿ«ƒþñ¶ýãàÖžÃÌ¢ûˆàÝänn«âwwW<ô–ÌòßîãfÕ×Y˜9ÿY7«Œ‡˜Gþ³nVCÚϸ@‹—R`í6jXÛÆåaøë;Î7 »5¥uHŽ+ÿz |KW¿¬ÿo骆<"ˆ&î°ûb®ú._bþU¾P=Â;‡àÞ<>íyûðñF½èmvFñR0¢ÝFá§Û¸T˜o©)´Ú0´Û°Kì¸Q첺ÕÈçJ>ÙåU‘Å't™…~ç³âµ:‡£Êhæ@Ük†,VÔ¿‹cý]-ë‹W Cov n‰ÿ0n'yv¾"lñٛǎíËŒØÆjÀnع"¬n.¡,Ÿœ¶TO9cñ¹üW¯úl©ÞëW/íß5¹ Пâ‹\4i è2:zt±{í:NšýìX8ŸÈQOTô‘ü`£ásp¸‡ þÿ—Í
+ç7Õb:úED€»ŠÛCF
+®°3y÷Ò7r}å¥#æ_$7~Il÷‹íç…•*
+·êÀäëÃ石ڌZ÷„טfû$Dzîsa#€÷ûü²ÎrÍU`ÛÚWÐQ¸»k F<ĸ»ÞÿGg­}ÖþÎùóv?I:Ì(jŽŒº°â(Òª÷†R5¿Zƒb—Ò÷ÙEˆ%™cêx]Áªº 4)ØÉómNl+/äMsU¨dLf†‹ú¡×ué·´s+ü Zœ9O5 ý¬ÔtìÝ«¦¬Þ”ç%ŸNõÆ ¥8™§,’+ûõvYF\z$MéÁQ
+·"&ᕵ)áÕC_ÂkÍ“Hø‹X‹¡ÂîâOÁåÐÝ–ò‡‰ßj^Ú®«{Z+ç­|óÍèYFeTýˆQŽvþÜ=Móï¡ÚÙ#‘jÖ„ºò’̶âÄf_~e*‘þZö§«£ŒÔà§4•4T
+?1ÿƒŠ eŠN-ŠÔcÙÖ}}'”œ%$”
+ ]à˜¨-”"ï˜b ëÑj ,Œ+—æ+Ÿ©>v_²×ÝN[N¦õ®X…ØÈ™œ°ò ᪙ڥ¶Hg}ê=îjªy;ÊÀø
+ŽsrGÒ4œÖ¤,¾kIø Š‹¦0 ^º§h¸sÖÛKZH•¥ΪëüžøäùêªØáÅè±æNzÚ 6ž´Ì5^l:‡áN¶OQûðbµêeÅDW˜zæˆâp“i¢ñØ
+Ý–Mô–e³tçs¿á-žÖêÝuÕò«²âfuIÎuCšn["Å %+ÒJ¦$”Ú™Ú*pA«#påË„ß ø*íðœ/â7w*\H®ÉÈ
+§ÜF!{í21Û¶†KÖ •óXíEæ±ÞÕXƒ^mYƒQ_lÛó>µl{Ä{Øå‘¥“òÚoÛá(=_&…BlÑçvÉçPkó¦£Z«µ®@ÕŒ,Í”´XU¤ûWC(S‡ß—Þ_ã‰wªÓ®éXqŠÖS©Ý‚½nÀžm·Ík-Œyb/éez ßgì¶NÔÜœÞzx
+´×+DôðYYÓþó ?ùþÚè Cgz¹—½òz±<´g½iòl.ÒUeª½ºà(Ð} K9–Äb°I1¡\_Ê|­5ѹӣgrjnâ±7rŸcÛ«G‰yêh#…ÕczRÂØ³Å–þä^Wz$»í]< ·‡J‚¸J…8™PØ¢w%çE•!ç§°µ°øA©ZÝ©^å}K óA¦zËœ!réïÔÐZa\Ó¨ír%Í(wexÁiö.h–?‰üÁéñÜ9pì­–¾ïsLß/:ôgZÌУê°Hûâ2í+iøôhSIL ©ÐÏ,)iÉùîþ!ó5&©t®J¬.lDDãùˆˆ&¹ Á
+ßáË`­õ”ˆzá…Xí;Ï’*®±i+ÿ¦ßNpÜý‘½P=&ÔHíóPN´—#®<Wãý½ÄÅ¥2Ïš6Ã0NÄK´ße4jJ0&•¶6…ƒ¹P›e2›Å$Õ9õ‰èõžìÒÚã»bãW™+WÙßµÒpá~/`Ç^e€5ì˜bEb
+TË` µ•`ÇX8cÇÈyŸj ·>töúvÚ4ä•iÿ+|k"}eoiÞ=:ÂvkéÜ…ÚÈlçQÿΔ¨i寓w+ëÍA'Jå›ïžŸ ^0\tjŠf;AË ÖØ¢L©¸·m9Å ›î¹›â¼Gz„"Ý'o#S zå’Î`Q½*§í!î"AÚ
+ž\̬߯¨œNJþ ßÉ% ÅÕ{áðGS7Ø{k©0®ˆ4Ê’i…‹„Á÷AGÄNýŽŠ5‘‰©`å¡×õ3‡¶Ë¿™ö¨ÁqŠöyö+¤× Έ­ä ø}Ýðð°£{°gîšÐdh. À;¿¡@ë(Ф5‰¡I[_ÀÐfpÊåKWàßéòÁÁ³æÔênW»[íº2ÆÅTZèf(ìŠS—khƒì'*F¶Dñï7kÎ+Ú¡:Òç"±÷þÔú<’VEØ;ßjP¢Ã(DíÂfíÈ“› ×<‘bf¤ý>¹É”™ ÓIúÔ´"²W¬²OÌSdßXÏyª{k]ÄC¾(]õ5,×äp+‰ë™–åÉÀc­ mÑ4ÑÉåÎV¾ñ‘°Ø éëÔû2â´úŒØ¾n
+ª›ÇÛš¹ì¬—3]}væî†Lu=#öT·ØlJKêPÄ›cÇn† Ñ%WþÐ$ÂÓ±x «¨9ŸËX÷U(›‹ @kš™Âw
+% &ÛP§Þ.Åêlb
+î?îWº}ÏË@·Õ.Ð÷ûП…0pކpÏÚ~W1ËõžÀÈ`4Ä(E·ó•Яåö›Œp³ž}HÊ=>AP{Àù\ç?‘drá»ðâ, ž›“:9ìÆ<9~×ÍúõŽ–ú÷½·ï¼v‰Ô…vXÜŶéÁMm˜a•_óIIYÕñ»Œéi~q« ­…qìïæ}5Ïü °o7狯uõ+‡þ¯2ÐgPè=XŽñëJÈ’—òÕåϵgV?ÆïjYJ¸÷¶ïüv®Ì¢ ÓÏ•ÖüØ®®¤__Æ$/nÙYtX~0VùÉÜ>ñóÙH•63¸cœ¦ÁË{ü掺u4!' 7‰°5ÞÊÕq…)ÆüŽ þk¬ºQßÖÿ²®&„y»ð-z}ìÀÃÝ.Û-'ûuù]¿¬êpü^ª‹ò«Ž–3äl$‹Ì4ÑiaŠõˆÿI`E­ 3…ÜqgÆüÍê úë_E«CõH·EöŸíÌ¢o?[§Þ§ÿ€z>äŠ=8ÑjÝ$ oRäÒó¥öí°¥tý—»z¼yA²%›ëúRÍ¢Ù§j”¦Xû\NÈþ-žD<of6:ä™Í0fŸûAG<ŸûÏæùÞ·o‡Wodî@Ï{×zNÜÞ ¡;Ó™¾“ǵ½*àV;¢Í°µ-G•V:£íÄuù<‹¥«wmž;0׌-¨ÚTßä_êê_ëêÑHøñêà¢Å܈Œ_O¯_†êæ!öŸõ™ü½‰¿÷&^7ølò]lô®vÈé#n3ËS·µ-¾G-~ûHâºpÇÒñ½nž›Ð®©Þ SãÖ!n ãE¿ëÏÖ(`jŸIOýUG±½_M
+D¹ÒÕAe^~*ó*¥Tòâ<úc]ý|ã–çÌ,—äw9-®‰õ¼{ûÙ^ŸG«Bmß±´ï‚f,¥ƒê-^sŽöJ·Ò0¬S·?x¦ö ùš°Q5ÉR•jH*¶ÔÚ•<#÷*äÖ•#Á™•™c´)U”ø\â¯ÛOT7*’^¡VŒÝ$[Ô`©]¸%ïSá6}K…Û¬ü …N®|ñÝ ÙÛIé¾c 99œâ:V ãæNk#—^V“Ì{]ÅæÐ¡’§è{yUâ>åˆóðÒ¶æÑ%þÑ!.*Q]«Ås·lc«éÿ EõÝËnÃYT0áY5ÿL®í¼ƒ¿‡¹ÏRYæ|¦pÎÁÛ=’ EZÈb§n&‹ýn8oy¯O>œ·yõ‹i¢øú×ûÛNÛ²cÃd²Ó´®\ç­Ës£´­Œâ¨®T~•Ш_TŸõ¤pë'ë‚ æ‡üs|¾åôüÊ}æ
+ƒH«Gs_Å™mœ¤sKþìýÃh üzÆ|=e}»/|ï¼½óâB~1:²-©“·? ‹Dv•‚¾GÇΡUÔ&T¹`æZ¹¼ƒX…Ü(k•sðÒªgCÆm‡óJfðƒ†y¡: Éã`DJw0×é9³m¯ï™Šq|û‡þ öëΛðeˆ`½óD‘= s-÷6oe]“ZלçF9ýrgï8ÙŸc_±?§^ÓÅúÚþœ×é8–þ¸ú®±gcÅ9ß«ÙEa•në凘\ÏNï…B¸X¼ƒÚ ý`õ,sá3™Š.åýCO-ûÒÇhxçQÐñb¿<toÓrâv²Í…kíó<mN?¨Óo>Žã3=šJ„íWË¢ˆ3Ë
+GPÞÂýBÛ\°÷¥™ß‡“ª½8cí¬
+ƺ0›ë"wN‘yv–¨DÔ‡’s.Õìå4ÿÞtÚ_™6à®ìêŸòë+-Øô®ÈÓö.ݧïÞ½ŽçšÛ$ç „yÉqŽç†=VÎm;cCI˜Äš†êÒÊ
+¹½…k³ 4Ÿ& mQcÀ¬Á †®ï޽¬^ñªàÂLk$÷›¦T[¬¦Ô¢‚¦Ô½YŠÍ9ÅãÞª©sôkR.¶w¹IvNÉqæ ê‘w“›Û›~lgXŒTêP¢™‚f²e£éwheÛZúYÌÅÓŒÌB?¬™´SiÐé¥q2Ñ÷Üv©W÷·.ÖÉ«v’¦Œ+¤z-U?¾ò¨×jJÏEù=)œåwBSò0¼gåaöÈÃZp’ß³rzEn*—¯Õ]þ3äÇ!s5} ‡Š®ƒÚ;ñËšã‚b‘òN0‹z•3‹è@16vC3¸ÕÀÑkì8ÐÅݺ ª—Цœ. õ#]µíËcÕ@üŪô²ùb‹£›ü>ÝaÙóV†‘º)Möp$µÚ@œ}Þ{q†˜˜›<1—„CqÒÇòTqÏOtî‹›\†™Ã{Ñp_ÔæLkQx˜ŽüÆv|‘ ~VâôÚ™¦tɧ9­Ù%$õúFUÕðKy¢¯ô7/p·$Õ†ì5]VÖc)‘•„…ÔAœ ù—˜{ýÞˆ7dU!bâœÀì°t„á·ÝÞ†¯ØÄWÔâ+.Òã·ƒÑ6ÅöX ¥ù»Ø½×Y*ßø-¸QwÆ\;oå·+×ØÑ‚®×gcI‹c…Uo ¡Ç¡ô•/Ê=Qöm%í÷gbI!Øø6¾æÄy€–Å<¯4…UÝê ‘Ó˜
+,X¤Ýàw¾RΠ¼`¬%îˆ;!W_l[œ\ôVìEý
+â«Ù⎧„d/·˜f[—öû÷WGbºƒ¹ÁX…›G¿,Gnv•v;å6 …›„šè–Êœ
+O
+mnHr¦é™CŽe’˜…cb™{^ˆe~Å˾FÃBBfß»m¤cãK–6¥ùê=ÔKÇžq׊±7Ÿ¡ÖÆd[yû]UÎ, A\beJ(­1ìkbâ”aðaõÄGëèô»á§{çê 4ÔŽTjŒ*À¶!õû}rÖPc2§‡?(IB›5±LèQ ËÁˆ†oN"^Ž©aŠÕçõˆÁy£VÆËe‚o¶­Í×F¦ßØÌ}W¦mk™›•¿²·úè³¶M«ª”ƒ®¼°ÉTzVŠ»ʶ‹Äô*Ì›pˆ†i 0¤{çÉrd®o*$é„&±uŸ(®WE‚)=ëø–—úxùY[à|Æ®Š`uz©`u&[À¤ík€I»ÑùEcI£ÑsÞ+aÒ§ÈnòT<fN¥yòÍ17£fIWé$PÞç—%£oWùjÞO4§Z[Œém}ˆ¾È\Î2^P"’v¾×ôŠWü—ˆ (®a‡™ì¤¨ä°zm^ÅdéÓÅd9˜£q«}Dã £ZЖ;NgS¬{HghîÓ»cˆé?³Hg<ë#·+·Ìåˆ÷Áo×ê{¶qÓ«˜q@Š…oX;ZRnèªÂæ9ãy©dÐlû½Åé‘éÀT¨ €¤ÐÑ ß~ºvÔ8&OTжˆ£‚¶HÊFµ• ÷(*¥Xµ®Î$?(b>r;x`O^° ã¡ ¥e°ÝBþòßyÛFœb8ùÆ|{v쌭¯ÄúÙ2"Õžç3r°[[âJÕÕ?Ò¼úPÆaFxzV&G%óN„fk€Gmu[8ò Zb,üf<Ø® Ø­Ü=©<4>95õê=0ƒ @dÉsŠ>ˆœa¤xWátg€pýßNœðd>Ù„–}c¾]¸KÌŠ 5µŽ‡Ÿr2Ò¼º¶…m‡S¹óº-0/’dh?IH²@›Áa
+DY±ØééÓ¿z2`)¤ŸA(~OI@™Y
+Pa—Te-ªóÒÕ¥l@Mý
+s@µÓÏïâ¥qìWšE ›JF€Ë„o¥Òõ˜w›°¨Ù¨©å%,açöJD;ýX@œ±ÏCSëÅ
+XJ²è³,&KÆ®h€É%6`j·Ìo
+s0¯˜g³XþÔ¬ÉM
+òµ9`&ȶM–+B»µµô|v£²t%Ý'hõo=ÿÌÀ_ñö¿|Àå˸_î‘? ¯=d uUH÷‰dȚÇ¿º%欀Œ
+´>İÓtïìvý?%ý)ä(ñ•ÍâïŠlÿËü'Çá«à~eÓoïŸåÔNPêºþwé4Ó¬¥Xjå.@µI¨Ò&Tª;
+ ãýj×û¾³¨;ÈóóƒÖ>s,¨ò“îÿÊ]ýjÞÛÜéÒ˜ÛÛ³ó\ýÏMüg´3÷b¬C&ºá‰ÔÚVF«B?ý¸ÎŸ³Ísý5ÕË­ÚèèçÆW}¦›µo½ºýyk#ÿ6©ù(¼¨Á3f[ IþT™GƳ’gëXyU eæ ;¥m\,—øûuz4:ôw^tÑ?|ÃþêÉÃsaIt+¸·™y}ߌÅõ¯:Þ×íWãX¹­[ ž >)D5ÄÞle^€¤
+¹´òªLXe怺¥mJüÍF‡]ŒêºR-ž{J³Û™^Ú‰ßF¥¤Ðñ§Ûüsz¿çRÄrŸUWÉJl>çsçIÎ0"ÅÐL1±¾˜}*+û«'wòÐóœ7ž×>ýý ŠÍ¤V9bžÝRE8 ¢Cã0ùA£ºòYÏxWŒMþRT_ö«Ðq¨`B!–ï>™w°<›ûÌ+bn”Õ¼®Ù¤4ò²!·Í…óÚ¹æe¦¬Z^Dúì˜Ùö1(SqjbF€¸tv‘·ü]F@p,…Ã~ñkøÖØè¨™ä[^Z™_.ž[»|¡cW•ÐL”eÝJ^ºlR,u³!Ó§Ø-Âye· óÂý¬—˯CUy=æ†|2Ûdøû‡Âúu7#{çIÁôâ`º·ù±âv
+ÚÀ5édç<·pú•¹ìô«Ë¦Ó¯ N¿ÎA_ô>£Ó§ÏMeÁµ×–ùZ¾ÿ©þ.±8sÂy[ÒBòÀ[sÁÜ̶Ef2üƒùUBŠ_·ƒ¦/¥3oï<j= i&îmÚ[¸ìdãšÄòà<—§«Ó/¾_ŽÃ2ˆ=ªªé ÖöÅ¢l%Í¡k…Z¶°{i`λ¯½™·«ˆ™wÛÌ»Á0Å5}Ì#.íl<kãJuïX7‘ëRXh<‚Lµ®Z~Ã_©ÞÅr/.²’{KåuÍôZá<”ãô Zè8´_´Ge§lû|¾aÇJÛJêqß
+åÑÄÂ.«…9o·&ùxžUŸy‘âúvÜõJæéê–«j‡ùu¢Õ Ù³V/ŠìªÕ#­’b5Òê%fݼ\’k…Î<¦k’o†KË-|eZ÷‘iÎàF ÄïX{\¤ º‚pÁ
+mQ´°¥™yß²L
+ÉúÆj×ÈQuPJ»WVœÖôíûÒÖïÒ׎’hõ¶ÖäjtVã÷è£j#@+iCj*]ö)æ£<”_}è(œ!)ÜqAxZOûóúà#¿&å\ ãõL­|JGK÷ñ€mº9Š5«ïxs™ÉP&} &m”±¾£Ä\9}W9‰ºp¼jÚ± Ùš|}{ê%árj+T"UÇÝšr?6:J·1)–r\ȃ9Ènè¼Ò^ ¦””i"º„ª•‚8C°¾˜¶÷bÚ†"‘×s)Ž]‘(ȳê,\슆HÂå}QõÕú0t¼abZ¹.›G–Ñ÷gˆÐ%xˆhMÜZã é÷¢r¤ÚÚx´ª—‹‚Ò=”UŪ7Mù-=Ùm CÊœJÒ}ץ̜ëJhÞˆÙKc#­ÝMX
+&Ðó†Ìo
+H–/+ý.ÏÝø-·ï0nß½æ~Pnß+uxîq™Vº°É C?à‡…²Û×½Œ%ÃdÖGIO¯~Œ¦v&˜j4lHy\Þ/ÅF—/ùÝÛBòP:`2oIi¢Ý8 ~dq–# 1GJi—$+;ЬWè{»Áoº›>_Îsn¿Ò\-ž
+CRšb<"æG÷u;_)lOÜaÓ9°çC|e59~2·KçØz ¦û½žbÇÑN¤ã (Á¦F—|–ò[ý
+…dž]rÊ{ 2<L.$Þ`0báÎ ¢
+^œ|=§;ðŸè×ZÞÓ`ö?(•±©+9!w’`‘±Øq0Q¨Z$A‹Y_¿^î\œûõpíÙ°‰UwÉ«>Ô&öFzr$m‚emB…ª$È)ÅK@•骈6FçÍ.ŒÏÄ»'†M¹¼!*‘£vWcUÎN¬¸8x„Ài[˜;žìÛjŸoô›ÊŸ¨ñß“3b·%–êî”Nv7¼Œïž8·¸l_„0¬¦*,&Þ2òšþØè)y¥¥då:ªõaŠÏ¹žË¤ÄЛ!üxÒøqXìà^£DÀS"†„ÅaÉYÝ7^hX´¯æt¥í;Ê©*ÒŒ>q†âñô|©îæ0§4í7Ó}®n´»ÎŸÉY9Ëæz‡o’ñÛ¿¦WL*Lè™^ÐææŠ¦ã7J§°$ä&”L¤ýZ¤‚
+üôœ܇GKèSÅîÐHŒÓë>ä­0‚£a$œ¾ O \¡|ÈKâJ fâßÌl¥þÍÑôƒ`ËË•Åe0anLâÜ);‚Y}˼èwu§=¼¤Dð-¼Øcs|LûýsczCn›ñ1ùî~&0ܯïIØ‘ß<ô¹räJZȳ”Éüœ¦&):G€3žâii׎ZK€c4pœÊ
+J…&¢Ùè#ÿ‰^¦ÂyøB»;„ö»¿ù–ĺšz™À€'4Á
+ᤣHҨⳙZ«r¡£ÇVPþA•Q>ü¾+„òõ¤3ý処¦x'ÖeÁEi¡W­žå|`OE^`öœ>
+éË€|LZ€|f6¦àˆmÍ,ªe¡òá7Š…¯/ •5÷e‘FnO–¤Ý:…‹s*=÷ÿjÞ÷%ª¥oqO·br0 B=­¡ÓH¯@É”Ôö®
+)j¸ =e´sH$xœñ°8.­ÒBLù¯xÛK̯‚ûµÿqãW6-;YÖ
+â@„jÝÝ“³/©.H¤_d>n+ ÈÏÛÅDÓF»‰aÁ8%îÙÊú=ù+%ÅÛfàÆ»òdÜì ø2üÃß
+î_ý6™=ÿÛû?Ò­Bn~UÛÌï÷¢<9 ¼œ8Åm ”wû”³¡
+ì½Í‡ÜÎå-·æG·Œñ—°06 inêÌ»Tõi¢—¬)Ö+¸Iî“ÍLV~&;‰R4Þf«µ1¿l´FÒ¶5ÆÂb9¸5^çt`Üt¸o¿ºroäÅ­žäÎ=xZ{|]«_9´µ­G»oÐÀ·¼sí~]îGT¿·æzeqËíks{W«ÏFb¯;MÔÞxŠu&‹I•7ã
+–;ü £:å]†1'Ý·šôgæÓÆ2Ò·ïÞY,ÕóÞßM<íXC¿½*ÄQ›ÙlÛ­Š@ÎâC£|¥/4Ï]·ÝŒ­é<ým·h˜°5ù]•)$F7²¼8~õäïY3ï›fTå¿ëO :üIî÷åÞû´NnïÓ =ï©W»Ø˜htÈé»ÕŽˆw¯µ->†-~{Äuá4‹¥ãuÙŒ•Óæmª×÷±Ñ1Þ׆ñâ>õ¾«bµÏ$Ç×|¬gV“ü5_Å6V§2/ß÷•¼Èêòt¨ýK]-GjRûÚk¿Qߘ¯Vû½‰¿ÍÌñ¡T¿ÇÒ¾òjÆR iªçݸŗô¼êµºý>Zµ‘wôjp² ªIxI7º8F•yáR©äéW£¼*¿ÚåHÀ¥mƒJJÅ\G‡vt‰¤ç*ÆÎ+íÅŠ”w à\/˜øæ˜.÷JÁ$ÝÌ?|kW‹ý:k[ÊÙ6nÍÒ´æƒ^¯ŠÍÊ£
+’rÄ‹½}™Ùmn¥mõñ)ñ§ß,訮>‰HºÃL16?|Q}cr¡ã~Ô‚ cfþ™ð^ÞÁù0÷YØQΧƒzÞôúÙß/Âyƒ¾„y¥‡äЃUçØ"ëóV½ƒôEH>žiÿòý’¿zró\]/¾å•#fT‹ 6*ÜzƒBÁøL«ù¾?¨çdÑÎ}¦—aî3cç9Ÿd×9x¥²!+Ÿ³ØÎ¼‡óªÿóbÉS"µDý skñ™mgªfø×ÚöC$ç˰V÷ÎIìiøsïÞ–ÄíDŒávJîÈí”å—{Û´°/zŸxóþVX Q»Õ¤˜ŽD?çãC+‹mŒô*’É"Yò梆™mË+¦h×3}ÒÎðe߯Û˱/}ö3ï<º®¼Øl= EOn']]“¤_Îse N?*Òög×–íQõäÚð™+YIÜM; +Ô©½…=ƨ…=û {-çö¶nVheÿ¨ò5x\ëFõ†V΄ðÃ|YÕ3?âýº…ÓÞyXOƒÉí/Å5qÄø£ƒ>~ÖéòÅÔqèJÅ•+ Ûçë->tzVROÆV(¯fv¹mÌy;™ä“y«¾‘3âõíøfé• (évêÞ8k‡¥Îj‡U¾®ÖØ\(/=ß¿s˜2cí¼KŽ‹Áº]s|)n×Ã9Ç9T È"bûìµ’ê•°BñA[ØâÍyL+f^ “¼[®±êf#²
+yƒyWKúvتéoÜÒxÝÿÕ`Ãc¢É²VÏKí¬ÆÅ¤Ü¶;VéTMG1ÅUU1¥ÜT1eñ-?¯¢›âËÏ›9©! eVl5âf–°”œßXc¦óVeÑFm.öjÒÄ úAu±ô1¢Äƒ æE½â°.@$®&£ÕCÐdÌSÔó<g¨•s”ÛºœQ:¥v^1¹YE~îO±Ü¯£Ù‘­…ô¹tÏ’¯Ÿ ~º¢˜ôwÁ*&ƒ¨-†¶CcSœrb˜±šåõ~2λ}¤píYÆí‰}ÍFûkΤg¡ï½!¬ñ¥)ùë]½\öOõRZ¾U­W{)w2÷VL߇ä’Ç~P¹¿‹iÙ©ŽˆsE¶†ä`_B|&'NÙ *†»vGXX›DÈè½@­~]‚x¾Ä52<{ÀcnWﯸ]£‚q»fà§ØTJb ³3íWµö›NÎûêÉf1SbôZq}gÊc¼(Ö¾|‘þ(ÎÆUÄÆC;æ)eðÏKB–8$N›!â7™“
+f¨4Þ±ùõbð¥Â-úAyN•ÒRnå1'†Ë-{"¸ÛXöXV‰Ÿ¹j‹&Ózø+FïãHЧË胠Tl Ÿ^X´
+‘gd$ׯLY1øœNkjØD”ÞzË^®v“&ñ¯#BB“íN̾ú)££H¸£‹P˜tné¤vôâKóà¹üãöÔ‘æªë—À‰mAgO†ã²g=Ï\Qi§m £_skºgLî´õ’hê=˜9Ôгê?(åÁ—Må¡}—ò08úÚ¾^å
+ÎÇºÙæJ¾Kº4"(ÕXy„î_æ«‘4¡Øˆ·üf"/ù2O­xnÎî¸}^8râš:³§’üøAYe/˜kÍE™¶T ý3éÇhgн ’¡mÎ)QÃZ¥EyömFÁÀ>“A&ÁHŒ÷Lbv8•‰\£”¹¢ù!r‘l³Õ¼˜‡±|Ë?¼7çÖ¯?¹´5 ~SŠpzAè=ErÊ\f0?ð|Ûßpµ9X°§óxʪlwÁ\wã cˆÝý8O´­®Ôû¶~SÃäî›,MS°˜‘ÉÉ©e“Áð’öb$æ bÎv&Y½ð•]AñÀΌ۶ Ä1¶5^Olk^tœéV
+_wºw«/s6vë[úaÖTÓܲo85Ÿózž¹ô²¼cÏu|•î1uµž3¤šÐO+3¥íafI`kMÁ¡²%ƒ…x ±Föž¢ˆ¹{"‰<Œ
+¹MGßU¥â‘y¯áÌÛbÛåjU¢ô"+°¸Z„ô8í,êõÝ­Æ*zXš¹o,µû\·³%D¦.ÛUAégä÷v9au|¥q>s2î˜ÛùµbLv<§šP>ȤMIìE¬ˆ¼ó^„îP|5á.xTμq–¯¡Øö¹d±Jÿ­b•E!À„´‚Uv€Öoñ•»ð'E"#ÛÈ#ñgßGZ uFZPCB.*kQwš Íhšš»|:ÂŒ¡2þ½¹O`«öƒ«o’«åå-cF—íd«3Ê/Ši·\Óc9?ãÑz9Çvúl‰U›-Vň &ÌÅ'zÌ{0Ú{LŠ«‚ÊWÎE.“F: ­àØAt\[À÷Åüw‹[Ì9[l³ ½öÊz͹ô1láfêÍVÍAl50ªl¨]Vçe¤ÃEj´¼°dîì%蟘.`·ô
+2Ñ•X~ðÎ'‹&½Z{äevðË·ÐXº®~Ÿm<N€àè tý ˆR Dk†büf1ÑÕ-/Å«ˆ$ì¦8¯~•Þiù ˆ)¦¸F€¨g€(ä?€(Æ@øvÏ,¾ |(Ê‘rM]ù'|3O&‚i8ÿ|“ä,ºã‹\0¹rÿU¼§gxЮž T¦÷¿[‰»´.­ñ’½´Þ€¤–0 ]ˆ
+Hë
+ÜÂݯã[àÿm_º¨ª±uûë!JiE±Wì{‘NP;ì{QÔ?÷éoÁZÉΗ“œ³“½w~ ˜0j΂ªk0K¶9Ö .íˆ]Œl ï *¾Å'žBóþ§æ]Ú.=°ÛW@&Û7þ†4ç> 
+aë
+ÄØè ÄÐØHö`v{,O»Œ½
+uü–8TQ{¦WA7‡”¾x…Ñ )…êè7ðÿ)áToøSÞß¶QûòÝ~ó¼ò/¤ÌA ¤N A¶@jç>){M€”&Á%O¯4©u)nNÇòëõgm8S‡J}_²=°QV¯¢¡?ùOºmXAâ·Ò»_Úñ·ê»¡í÷›tøn?K&ô‡aõ†°TBè¶=»â—ѶyެЉ€lŠÎ€,}€Œ§­@f^Á@fôÎýI }”çâãñ®ü>#ö¤ç2›˜GëÌ­sFKØEòçÕ“Wí5Ž&YêÊ{º·Ç•›¸]çìÁf'HòúX­©k~QÖWé5?Z,o»f‚™-œ4¹Z œãVÑŸ‘Ó>íâëÔD¤ù¶s ǰ¸ÀnÊ¡úº¿>ýA ^‘­«aÑÐsÚÖ—ánYûYÌßîš—ãi»ÎøMÿu+-ª ®ÉŰEùòˆÎk÷5k¾¦Ìt]¶¹@¹šù‰ÕÓniç™ ö¥o×ìôqѱr×…dšÙÛxü6cË12©^Guì@nkÙ1Zôz’ÓŽCvî?%û³Ä$ÔAýŽ~w>°p‹§enÍWˆ²®Íܜ֘‘¦ÑvÁL™ìªë‘#“¦m§÷âÌÊë®i¦ë›ñÛ¨ìÇå§pÍJyxåˆp7ÜZþin!¢¯[¨ÎlzìPŒ S˜&ó– ^´kGM?âºbµ’L6kª( p–\]‰Ñúku'‡{d…öÚ°¦i(ãY^¿ƒßšŒ‘‘GnyFAðF åà½oóbe¸ë0uí(âm?áýO«7|«Jîá eüþò¯¾'ÈLª`øj0k’ûAÌÝD·Ûˆä~Æö»‰w¾·ÓjÝ/ë*ì!Ý£T
+-«áæSmo¡Ô·ëôD
+_‘¡£Ú+ç ¹Mr«¦ÏÃ…ì½\|¶÷’SjŸ¥JdäC8Õ\íFŠnkˈäfï¯{çTŸ9²½t+{?ñ¯J÷¨=ê]9iwù-v´bLm{NjÜ6*½EËŸÏO-Þ…M§Ûˆ7+ì³ÞD—yc¦,„~®QOÕ²ß^âË w­ÖòŠÁ_(6ö™6è•ü Ëß*ÃŽ–Fä „õª“ö/û¶!\ο½Éô›ïÅ4+äm"ë'јõP¦QQñ~H¦ë->S¨“—V­ÖÍh½@½õªXäÜ`°[E®’t%‰ê•d4y¬$áPBø¨y‡ µÚÞNìŠôcÔ€ëúW©‰,Õ|ßJåºÛ7ê-În×ÉãJ ÔÑSd!eÕº©Ä¤ºÓcóª˜K­ªœ_ØUŽfõT‘…ö­’¿|™Œ#å fGKÞâ+åÉWFð7Ùª ĆRñ}D§EGÖ®ÅJ*ɯ¦‘{ãxG3ógpuo%GX˜¡”Òƒ¤˜t¹ñÊqg*ü‹åÊZ™M–3H<¡R,y³V‚Ù*ðç‚¿œI‚Ù^k‚@GÅ÷ö¸;œ¾?-Vâø²0Sðm¡žâÏüVóó®nâyÒßq¹µÅsÝ’-æX$1Íîf/»›/ÓY±éYÙ«BPr^vz›ïÙY¨­¥_áŸOø+µ6&˜-:Rtºûw±Â¾ðÂlð ?•Ð:OÇ ø%‘‚Ð(ä]­Wη2J=·õš¹nAéæ˜×hÝÙs-+–÷ãlyN3Ç9»ÎÈú%}Yiï,­uîL:ÃÖ‹)oï R†$-R†\‹¤ eÔJjÅIygcöŶ15ë­m§P¾6*qApÛÑœ½Eò-»ôÈ­§·K®›;ß²;óìgEáöÎÆçü¿)¡‘ki.“Ä‹ÉôÅ­eÓZ«SLg(©’ò6ãfÊè-zIÿ°’“¦ô%…dbο¯½ï ÷/¾’+ÄxÄßW3«®%f6³KÌœr21›$`ãQ`{ân­6Ñy¢^9­ý¬`MŸ±\@à¹ÓxeNîÍË$¯ê9­=¸}:“xRÞÊ?¥ŒŽIå™·—ô÷øBIš!šŠwTå+éœõñˆ'f3ÃõüºžÀŸï~ܵùa¼UîOãr8pëy1Âu‡$Ç’ÇRœ±ÓÈ“cR€ ÷;Ùˆ ÕizU)=rª°
+ç0M,£t*© W,Öðg<{½¢i½M¿’¶Ô¿ñØîrˆ¯Úõ5·­ϧ±ÁE°-b‰ÌÀeOóªË*ꊹÚÙ 3,—L–é]éûnèÓ#q¬^ ùI=_Dœ²VÕ<UêX *’ArroMÉêèr&æo‡ðΉÔ'ˆZlNõFùQ²¾Jd[¥Çh—ƒË¯I,˧·xj<y<ùéºx¯–œ[rýµŒIÅùŒ=km‡Mí£'›ÉZ/‡ÉªØŒ~Я9]èß\êŽ6”õ¤TÉJݨH©ú"§'«ÛmŒXp Ñ8ekaYRtUF(½Op-¶ 8ÞO9¼ÏçD<¶>/ðØæ€á}ºªõéWCxÍlžÉØLZcIû±ò.Ê\âLIÞŤñÝeUM™2ú fÑÓcLéÕ˜²kú˜*ù’ùQSuÈi²?!±‰´ Õñ†hb‹#A,Î^t½h'Y¦¢ôUOâÛÉ£Œ÷kÙÎáçâ“” ŠIj+ƒñéwãÙñ=$€ñ\‚¯Jµmý¦›Éõm6¾ù¯?âë¼¥döŸ\FßW'ôøŒYÔ›^(ïèdMHIìÊ ‰f–EW£÷8Ú)"v”!ù¾]—·x¿Û?ã}mû„㲎±Z“v“Žtãå^JèÕ"ÚMÏXøˆÈ› D¥ÇˆþNùˆÇŸcÕ׊x=ŸýÞ)3UDx|-=âÌrwŠÉ\~ËxÝ ¼+i¿Q˜PïöË¢šn¸Öˆæ„Ô¢kÔS£Lã¢ãýÕn„sªgcÇô{†Iwr…Éhó„ñsõŽ^G½H‹jE)‡¦ß‡âMR*’Y‹ˆ ö~Š˜ò&è|¼·žÞ³í5" *]Ö¯j´¨TF«tÒé3‘D+S»sñˆydµµaòèhA_© U¾L²æKѼraæ­â»ÆNŹ×QTëòöÆsÐËΛ¢Ú
+PˉtÅ ì@×YÂý
+ÐÍ„èT¨t†
+‹W-AXvÏŒu·€ Zˆ¾¹<ˆ^^ˆn–Î9•°¾7߸bŸ~ï@<úÀ˜Gîr§#=áL«ý>Ú¯·78ßN­?5oSÚ.#•®´€¤Ò3Ȧã‚è]_‚Ýî
+NmVøNtr‹Xè÷¥·Pö&áÝ¥¯§PñÆ2¥Þ±ðØÌ»‹PµJv|‰ÊÄòI!Ÿê‚rdk(¦/Èñ”¥
+ßTíº{~Z½å=zCïֿДúÊÃÈá‹R(*ËÖÇÓ ‰€/ýV,|*¦ž¼Ñ€F® @W29@;ZЗK 0 §
+X¼Ölm¡
+ý-`Se°]Œ÷txŠ˜cW°àÃâÞd»-’ø¹pÔGçXdV.!_q
+)…zrÀ&’=9oë`±w °9¦ò÷åx?ÅÛÒp…} Úæ·€{Ì<À)p®–œÅ´
+dSÃÈxqdfÞ dFû…oLJžÐO%´sTÙ¿{^sWˆÜÖñRôÆ(íØ•ÓÛ©³R¹£Iœ ‡ònQÞ¹©fe˘duÃ9~}ÍÏwÍUzå´—cö\3.Š‹·"*ØÑ[ú7ëÖ¬ÄÅ´‹‡‰Hº¾#ÇPÚ¾HZÙN_§«°l¨Õ†ûO…îÐO%´¹=C%ô¬¾ôŧÞøWw./Ø›þsgk9²œ­e$º[iQôìš±Éu>Ën³æ‹~L»Hä5ÙÕÞ‘Ï}³dÊ#ìKïHÙéÃ1fü.aúÚ1cïž0vŠHcŒ8ü`„Ïk¦Ñ"¦½Ëïl¸<„!wE¿¯B9t0ëuŒÐÒûP—Y¬Îk·LfÊ8VrÂÍ­¼##Á¾t&u[cN=ˈïe³x³´qÙŽF3A±Âݲ”©Ñ }Ýl/ufÝØ wÝÞaÈí{MNôîêE¼Õ´7Ž*F~—}›diô$§¾·%d%Þ³î±1Àéaogˆ"¼OžíP É}`ŸuWƒš¦Ÿj-2ê?¦:ãCnã¾ <±!·Í°Ú±_OªZ2™QŒ,•‡œ ½ígEr*hCBæ =˜5¢ý¾B%ÑíPšHî¨ÑÖ_‹™IŸ9UV=1%žºGÝ}wù'ëh%±ÔÉ Ü°íÍ;HXfµevérøþ~¨‰†»x…{d…äB©1”CCW£è6Ó#±E6§ÁUºµ} ápé3ûó£'&ÎïîQ½ ]Þ;F;ã;éç%Öö,/úW[þôo™u²Ü|/™FÓiûMdÛ6àaÖÀOô¹ÞJIXôèÜV[P£Ö-Ö£5æmÆÃ÷÷+Çy’ùÃKü†™‘Z;ezŸrhè}ÏcͦÓÈö 4$#ÂÉi"«ÓüÛ›üauÓÓ¡Nžî—ÚZ÷Z7}Uw©ŠùX´Ê= Á›üT% e­¶o—3ÑŒQ2Úæº”gø·àïÞ9Á? Ö‚Ì"áûûa1SÃ-ã×pï®,TkÛF±QkÌÚ\½Å!©P­r÷V¢riÙŠ\0
+Ü2¿Y‘‹\¿|q8¹¬UÒÃr-ŽKF#m•òDq*ø«š+˜þVýTtıh°ñ("g„,ÌÔrª€{ãjÞÑj¾U˜¬òä»É­'f7·žNwÎçBr(v·Ì
+Q®“/VKÞTÁßM!‚@a‘¢ÓÃðà*1š‚AhðF¹"r˜…:oU
+øeÞȻڦ“oeNbž¼ß•Üz ô\·HY9æ™gÅŠ°ÉÆQíš9ÎO‘ŒÜÌp™$éVÒ—MNMk½Ç6PGû/6Idô´6Ø-C3Ýν ÕcùbHÈ .[ë‰ê=ßJc×ÜZ?œsŒ¿º~*¡;óõþt„Š%.
+¡Ìdã‘f<sœöSCµ¦æ2I\/¦/î´šÎPv#åmVݔѻɩ<G“þ±4Ošòè’Ïÿ¾õ8Þ1Ð_É›_)HW¾R|TùŠ0CX~ÊÞ:]ÎV’ ŠùÀ@ÑHÞ=WÙ¿8gä·M_æ—U:CÌ–)oµÛ¥ŒÎñø›êßRy–yBÈG’æ ƒ%…„@òïsåµ›à+i9Í#žSLÌŒ]-QÏ¿»X¥µ¸kK“x«|ÞÇ)´ ¸õü–äºÍA›ë¶òS®Û6®ÛQ;ÜzuSZþ:[­±M?-<ûs*¿l,@vÀ½¼ô0[>&Ÿª»N‚z~Î;'tÂW’§I Á^¯óÄlx_&f:ºÀîõ\ü”ÀýÌ-îš‚o •wœm”[O’ëV'Çbûtlç!&¶2­Xœ’ö¸¹LX¹g<Iɸ^`.ç,ŘË%sf.×V–M¦ðN­¿*•äÝH«BÌÅÒ½wz8_“–ííøj]\%à”soÇ,›Û«&·~,®[¡-ŽEiBjÛÍS‹˜ØÈ®bq¢´ûÀØãªzdåNûÊ&Åg.»)Âhâd2 ,A{çl¤EªÑùLwAù÷÷ò3Ž2Írƒ2­Ô”2í6Iùo¶XoÓv©4²ó‰üòAà¿éÉ…qùÌWg³m|¡\o1™ÄNÁdO÷ˆÁ*õÛˆM.”阹Òc“Ñô΂îО/MiÃÔ]Ú§:ŸÜ)ÿz»Qæ.@„Z¤
+MùžÈ¨Þ6š,¶Ïxçy<Åx—Z3¹}o*Þä ïëÁöJJtu_KQ¦àÈx_m p.-ˆ&Í’"z‰S£…Üõ.El)GZª Âot€\î6@Af A;Aðß
+¾# çÔî
+•"
+ÌYÉ "¶—–$ˆ¦_z2K¾ÁRpKw4 Êñ¶@knÐCRt¯ï
+[J›H¡å2H.ˆ1HJË3EfúŸµ9PKp’ ýŽBÉÌ}æíShõíßݶŸ6äoºí·š !‘oò·B A„O£­nåBÁ6¨xøg²cN9ì
+‰éS»¢ÿ
+ÇJ ÁÀXñ§ã&õ4FóóC·lé>DŸÈíçÀöíû®RRÊ{aŸßFä²´CnwÕOc‹¡ÙÌ,²°ž"/â帜KØ!Óm—ÀøÜ¿¾G7¡í,v×_‰ém8Ù¥ÏA¹§>°oßvh¤-ìÁ ¾Ý—Ð^q[¯˃ü]nþ
+óóE”›'mîû{µ=Ôvß Ø÷þr;›–Š»IÝì&Tvãºäb™8Řy!§䵚à·ÅÃ^'[˜ÅÌ÷ø¼Cý‘.ô`^ŒÂýuÖKgý0ÄDf«®(g­ôùÕO„,üØÖû…âµî«z&2ò½ž¹©ÍžY£ƒ8Eë«¥ãêã»Í “0”vë9Ns{Ï÷'Ýnl÷ÃRÞj„y\*Ûlk!xjþ >°?ø—°r©ÂvÞ¾.·Ô9½îVäé"{33p÷áL©¾/íµYö,CÞã “¹r‚rÒ_l?œªêFkÆqWíP³¹[d&Ò¹sÿgð}Ç6®ŸÈo£·åbÓæ:É•8ÈOç…u™¢^­ï´¦¹…µ› góØØ=G^îy1ŠÃêAGˆÕZ[Ìr ØÇ:Ânªô«òDN¼£–”ò–æÏ†lCµ°ì¦Së^’Iº—éd2F=v=‘}kO®'æq¿>Ž/ª|3üÖü —ÄZs‘þ\í˜ï‰Òš¶,+–¤j`4Ðʼnñ'øÀþêè?‚u÷rK¯{Bb¾ŠßgüÜb“IcjÆ­½To›ò¾nÁY³[›‘!&ºSm¬‡¸½…Ÿ¼£ô¯GK–Ý‘¤^âú@߆¢Ÿü øÀþûùÕ>c§W t5w‡å8?­ÍÒ;–yÆÆWãÔ"'ÝxrÏ…!§Í°^¤çêÚP'Š˜ÂMYŽÍF’Æ×áU^—ÕD_<©bqi)?–
+QL`îP-ÎçVKI8Lu6gë Úxn«UÈ IŸ€‘££Ù¡«Qé³£ìN5ȱç’&™Ã·®¨ƒ<WD³þTúïåFþ+øÀþöÔw€›¹y™ù ß-¦Â9ajïz߈Læ¯á\›†Ñý£«-‹Œ˜¶ä •–,o]Òkmp' ȱÛREs—E¡AÉ}gy—þ%À¶üÝ©¹%‹Ù–͸vb™ÃiƒÍiLqT®ÑD”CYÊ+ûÝSÙÝÙOí¾!]kÌpp/ºò ïh²h¡uI漊¹_±ÁàgÃ}œ-÷ÇІó±Gvx\Ú\ü°ZƒGÜ=¬_ý…Z$fóì¾ÌÆñžZzó>ã·7©8ÆÍ™Í“W_î϶KÏvSo_m/xÖJNùV‡Jƒ?ÁöWG¿¾ýšØýøë¶ü#¢ßAÎù”èwЄkä_›øýO˜ý_›ø¿nËÏN|H^å×&>¤ WI¿6ñÿ&ûÿ2žpÖ÷kÿmù%‰iþë§å?Š'÷mâÿaö$žØ/NüÛòëÿ¯Ÿ–Ï‚Ð_4sÉf"¤ù-Ôflþ‰>~'êýNôb»)ë
+‰6®ÖÊM„DŸ}åÁw
+{f¾«{§•Ó}k—‚Pe4|Ð>éåÂa;ļòR]™Þ\ég»Yâ);Û¬ž·ì¿‚@û›S {3:J_Ýòëru3dù²:i›Õ&!–ñZ©á¦¸“9™=›¼gÊÄ Î-Gº:ëïlj;¬©ã¥FÆ™™²Ý8Ž|8Ômé¼FǃÛn3'eô/áûÓ}ñ4L_ÖÆâpY‹»üe7׫A—ƒÒ°6«•fw]Ü5óõy¬¥[7ÃLNÃqvµæ®3S™–o+\Õ0e¾)Œ¥tù©›mtÑZ? vN©<xoyr9}ÞY]ØÇÖt' C?’w¨'›ò^œŽ5µµåãÔA¯`…¥æ–§Sµ-:Š`¤eÌI—bEx•èp3öª8~N´¿±ÿzþO°Õhnç<¯Æ¾„¶ÉÝ„ˆ¨k&!<œ¥ofL…eû£{µŒg]
+£Œ,‚%õ3úã´~,“*‚»Ã/O‡ämT¸”¶m-늂VòEí ²jHiYërJÑ{R¶Æ÷…â¥'ZšÙK¬ØÁ¾ÕëOù^¿J${}tÅö{ó2è}`áÇÃbÄÄ<Bu¥ñó•yêt¬Ã+—m¦" m˜—A$žZ‰•[â)¢\šî¹Úší®]®Ë¨Éx§¿CùößómÉ]&Û¼`¥Z0LµÎ×a¦¥ßØwüè÷—Ú3þ¤^ϸ’mßnÿpPͺŒ¡jÃMÛÑBébëùu»Í3ÏÃmfÜj—©ðNÓâZ<|`“7›9LìQô8)=÷‡I¤=99“›rv&Û‘çTkÖÓž›ÇÀöÃD¿ƒ&\ñý(Ñï  ×È?Jô»"öÃD¿ƒ&Ìþ¯M|H3Èþ/MüwEìçÄ®‘mâÿW[~â³ÿkÒ ²ÿKÿcÙÿGñ„Ùÿµ‰ÿ/mùÙOP˜ý_™øèó·
+íình$êºØ2Æêhâ¬æÃ9}Ý—[o–ßÖ®Ssá'oG<MÊsö8A¢×£3Uæ'§zœ¬Ö¸:XnàÛD2ë[í!á}ƒìO¾ž |£=Q
+]–`<5ÏtË@–±Ìø7šZ@Æs6²¶Áß’žS¸PÆÞuâhÝóQg§f-OQÎö¢ß¸Ú ¿t·Ç>0·ìã>>$ìË?‚ìOþƒ¨ã™–Mô‹há ‰®éõ|x]g£ÓÃÿRm%ï6©hòÍ™ ¨«ƒ+ËËŸþ–”}šÛö'Vêutîô/?Ï·‡4ã,¤yª4ûû  "Z±¢c‘Ò"ËyvN\?uÛR­ü˜LšþÙ³-ωàf·hëf“ýÆÍ꘩P°Œ¥ÆÂ¥‘òÅÐëüéO
+ã9?k‹4ƒD»…ëôH#“éöH8M!µéý<j¼:ºQ5`ŒF‡çpbðžÖh“O ó
+sg&8¥Ã§Ú¿ûﱺcïÆxR:ê‘×b«5óŒ«l‡Úô7%tc²Ï‘#ŽK£™8nï—ÿ}ì¿d'¥Þ#»±Nò«|/ýÕÞ0›Ïeq(,ãý^g‘MŽÌI´¤o¬Ý„¼™2á<F9syÒí½Õ^3Ðǘäq(¡Ö”бÒõ_·¶Õ·õ¶ó#àÙ…[ ΔÆÙö²}÷BQ]íórÙ_±4‘ZžËÊÝéC›m!KSn8ÇqÚ.Fñ
+ÃW;\uOO”þfmJèHJwQc«O†âx9Ôûö¶ª÷Ëý’ñ…ÿ®Ä ¼×¡´½1;w=¦[¼¯éN˜•F‚Ífòf&­É8“¶†{9d—Z‹±§Ê®MZò±:I—fËøRBgW-ðª}'ÒÓúåiã{ hËßœ:xÈžó>¶òÊÍfÒ³›Òì "ûE+<íÞ°’ã÷þ(?½›†y|.ugb­4×~LÕn]²•xÉüÃÑ?0)ý®F%®Š¾ñTÄâs£ô’¥ô+‘áÁ¾¨”È˺®O>°«›AØ^!GÊÍzqñÆÙü k”ðj[Ÿ$ºŽV{ΰ>®ÎÔµ·²ѪeyøÖ•q8ð.-e`hEôïqE
+àK"¬¤&ƒ`Ä~ÿþ`;ÃÎèîýÜtN'ºŽî&£ja¡†½qNMîôjqš6ØC][6^‘[oÂÐ(v”}|<–O©¢.]c¾&e¤µHj 5*â“­È¢0ˆ‡R#d{Ò÷B¨(þÇÑ[œ‚m´V­mçÊó¸#c¥SÍŠmí-øø¼”ƒÚO*ƒZU¢-*{ãÓt©ËÉ)¯ICÑ÷æQ —ð 󬈳%ÎK©?!â0¢«¨ô#°îfÏu÷ý*¹7BuáìB^hÝyEå{é¾*¸R¾žõ¾Rf~ªŸ_>Y˼Wê×PYèc£V©×xeJpøbËÝU:Zé¶·÷r—îŸ*-¾ `SûØŸ|,ˇ·„ƒfÉÙ^¢#cá)1Uß9É¿¢MqêUµþ"•XõµÔ½G­/pvÑe2Ú‰—“x[6ÐhërÜ-­5'[0¢šÞdÈ4l›næ ¶áK…„÷àûÓ7Ó×ïSBéÑtõÿi;ó}Eqnï_Á¾œçyž™Df”AD¢÷ÿ†]U§ž§Ou¿»ºOÿó ;tü²VÉÊJŠ6Y:OÒ‚Ô«“cŽà‹ ÖŽ‚‰«*ûÐ[ieÎ…iÁžIýÛyÇþ4Š?O‘Þâ:Ñ‚~8™±8ã›dŠåûØtáÈv9K¿ äÞÿD@¹6àœÎ
+ô ˜È?ý‚üç³üMÐ/`Âùþ?ýæò/;þ7½ÿOìùüËŽÿÏgù÷ÿ‰ åßuü'&œ#ÿ»Žÿº÷ÿ±=¡÷ÿ]ÇÈ8^ƒrß@áp”Ì+"—ß¿ÎÕÍâL ¯]gNzí^¹j¬¯_Ô›Ý>«mïæ)Ïì®G®Òu‹žüêÆÎòÞô}yϸ<È»·ä Oä‡4Ëuƒ¿)p íÙ?y Ï@L¼1OõÄL× ·"À1ŒQ¶lQg¯Ï¸íUkšz îìáEyê7_Þ,Ä»—]Yê
+¤Q¾D˜Ÿáå{%Œ¢¶¡ÚQ†"
+/¿Ùu´š ¹Å³÷ÝóA©ï/‡ñ cœ{H™\ý.žÜËE 7”û7bXäý… -šÏt hŸ"×ÚÛ·kÍpga”_¶ 3,…—3zá]í„«›Ò§‚@>¬¾œ˜%ýƒ²7/DÁIGg7IO1oÑN†¡@Z[h1žq†ÎßH¸\2}…ö¬— =•ºè\ú1}ˆI
+¸Q©\„h%1Š3þŒÛ{uWbó ”G£EpP»±Ë!Uç/Ò¼=¥Œ1
+$Ä"{únäýOP»õ? §šYJêØ·p(óΛڶX‰²m‘w·ò¸<Óûô.>¸ï8vßèÖ[DÇÙ$ßhÌoÛÈúÜëêØÜ°u€ã±Ïâ÷å;¨x…muPúÄ4!f|Ç,êë(Êé›lŽù¿å/÷2h\Øbì[¶mÚ]À’¹ƒ}±M¾Är¬šäÙŠ“Ø>§í;wˆgVyìNì„"Ì/œ‹ý¢ö¿@óm9þÙÓ‰@›ú*
+‡²ú¦W1>“XÕ^ýgÔöÎÅ÷Ø0žITJ}žx0/mACMr£AêÆNKêycôÖ§Í<™S !ùsYé@ï£aëèíT5K#eaP%Z”ªëZ{T‡ ³F\IăôA_4SÒò.;w€¿^{‰í~E½9Y5Òlš{Þ7 ápaŽÇ…ì›MóS>Ÿ×_•ï Æ=-µ¿c^‹/[Jœ*fi­Ì jŒlô%±pt¡ö´I<
+‡æÙ\3½/]fI¡.ÂÙ|´Eþ[êj¾t¸nòíÔ‹)ì…v›#‡&´²õUù@þPñŸ L½?L]#˜s±§-5\ô{8”C(Eж›sµßÜ&¢3²Q84…CŸ;¡„ÜØi¸ŸqPø+ä1EB—(y£|wz¦˜Jñô72ó "ÌJ ööø¾s9 liu*DqÆ‘qî·H=¸rí>†ßnUŠƒÏÍòŸ{Ðù-͇ÛaFqØŒqû Ý$\•ܦMƒ ù‘O†³©OrVÙù)È*þZ~€ÞáÕ¾võ`~J>íw5–7T|`–Û$n0DZRäbÉ9dŸ«hõíszƒH†»Öõ¸Tbnn
+1U‰6tKd¸ÓöÄcÏHD//ë„ 5Ï„°jz¿%QtôÇ5dä– ü6(›õ&ìF»Z0¿&ŸNï‘F¬B˜i±…?SÁ£¾;,ì¹¹'°D V/â=ÚÚ}Þ&º¡Åf;U…A[ô÷ Ýí2·òúê³.mÇ">œð>Ü%¿)_‚J:1ýÄ\޵ {pÔç€ÅümÔeQߦꃃɠôž8U!¸S;—ϭݬÑÔMI¾íên°¹]´ýšÅßuŒÃ‡[8,Qžc›Œüù@~Uû+•Ô‚–c'H;€ û¥vMO¦×zåo¦wë&äÙöÖÙ—ƒ)°¯ŽÄïÔ¢¹E
+¿/ðY¢âYï/çÏúÂ4ž>hÕ Y—J8Ö²žwá¸\ÙYÑÝ›ü}Zïö)y»‹õOðc_"õïI—fOø¾µ{ÎrŸù–ÄSK~K‘Œ"Šù=ƒ©8³ÁRç%û7%TÃö£%còc ‹Ïz½²¿{ck2÷ç¾/Þûì^ñRc+/J³m’k}&]î¹ô| üWÒå{K5{ÑÖîQŸ&D­HÿO¾%…'ã&ƒM'ü/ZìÏný/Ü‘ÉÛË«‹÷^i›¾ÍÂðX:ëCyù.a|¿Ð?qúÜ+°¦¦ØÜÅÆ$]"ûϤKØ[ÒacK±Û&Úá½ù–t)ÞûÔgÒ%~xEABP˜ºWàûòº‚—O=Äe˜bpZ†
+ÄdG]ËŒU0ƒ°¼½QfKg’•
+_ö•n&(W9bôï’Ö_^öK$ã~U¢ÜÑ×
+eè*àHð[_ÿ}„hÏÚ^ùôþ+Ðm‰½ßŠÔ%0Ä)ð¸F#yÒ¼jÙÇéHPqJ:MPIô“Ç“Xâu×»r—ZIZ”o)E›Îw„°'·øpÒåñJÊx¢}=â‰rÎ+ñÁÓ7_ c‚öeØVóqˆ©”Ø›µÎ×ix|œ/ëGÌ9HxNë¾½æ"Nç•È$;¡¹êè|§SþLº46Çéüð™où™jIôöÜæ3ESS3KÑäK‰¾…¥÷Ó_KQüßµ±A­Çs
+K•ê"¹mÇa“^¤2X®id×ÅÌ4·"†µüò¼‹–Õ³[\ ­´hœ¸Òü6ÆÐùYVæma\ ïVý«òü¼v†wq@¢Ñ‘ eó¡4Ms(ZjÑü6“É^šÀ³>³Ã
+“¾òޝÓrÓݺ‹–ƒºsþù<Ï^µãe6ÀvÁ,ŽÐát<ŸÂqò4ùî?&Ú¡&³fLÒnî=6ÉRò÷å4eÜc19*ÂM bOn«æûô“´Êd*Ÿ`vs´\WêwzÙ¼¶6ó¾%lfÉr•›N abp4Éz†2>ÎÚ¸p ô‘Óœ#Üí[#”윆çLÍRRÑV‡åà§| ¨øS±ðýÖRGwÇw –áœA’„c‹LíS1Ì™;•uÕ5& áìNf
+•ŸN¬Îj:¶óÕå2r7lT©É!3b©a=ɲƒ+Ol\eÁšÖHìßW#©Ï':r¿£v´è4Íß’ä³P³¾¸¯_÷
+çôÍ*Óv3S2%J;È8·WÙx/zO9=Ké¥ÂØ!„òˆÎOšÃ†ðn¶AÐï ÚnØïuÙIOÚ,f½ac±ìÅѺ«L;ø‹)݃þ¦‹ìG»Ž^í_@iV­‚’€ð 4_ÅAñ-Í@qmïZN| ×ÄAÏΤ1pn3? ;o£š…fÜ4дtÒ½åü¤“aÛÖI¥jë4åèågÂÓËúû¬ù½›¯ùÕçUós‰»æÇЗʺÎã§ÀÙëWüJþô ˜È?ý‚DÅúÌäƒ~óù—ÿ%ïÿߨzÿßu<(‘íZä}=ÎcT˜BP˺N-yMôL´«gWY`§}ÚYYa©N3chÇrtYt*Þƒ„“l„™p4_\ÍŸŸ˜æ×÷Èû¹,P¯Åà©t6£ëoÉ'(ÄDv D­b ”Uæ ÈB{²»\"PñÒ¹œJô>ûiÕVÇin¿3×ÙŠf ÎËÑÏ‘E©å
+â)èI»|2z7WczGOc²G_«Œ«V[ïЉû¡›Æ.¿’(ÛöOn?¬ƒ’—‰@go9þp9ÝY§ÔË«p.·S=[ܤ—ÇÔ•Üš…wI7ÊÉŒ«Ÿ#{Vë[»lQ8³Ð/°5†::Zí}ðÔëzwQ¯M1„ŒÇ(úݿɱÉý"M¯¤ó[JÚ¼ñ‰ù¹š€C‹V•Ôƒ ·b,вñ‚ç—Þ[”’ ËÇœA¬šQ.f.[®^]â®vQª®VG½¾EW½سʉ@eïì]Ù½¦Hè_¢jŒ·_"-û·$…ö|ä¡=…%´gÏ‚Ž?B{r…Äìe fëÖ²û ˜} Ö‚ë²± £<*¹:=½xZPŸ| Ø«áÜø«W[ÉÑYmº‹P¹Ïˆ—ï!4Ì,"¬Øbñr±þT>_Õ‚’‹7¾ƒêXªÏ¾ƒ6ÐW Qù¼çcϦÝ7ÁÔZÈ­AH¡¬ûEÄÖkËY –¸…ë]u[¡÷¿ÅêùYÑWøJÚ?…Ò,6ó÷9=n îF1y_¥ôß’o˜(Rú†ynAÐ ´ïܪA…LÖóågÝî0¶r\…6ˆK(EIá'½ÆÇ/»¤ÎßÂõ­7û©÷dp‰ò~˜RZ®œÅ:9ÜZ<CDû+wƒ*¿% tß4!hžƒÍ”|Ç|? ¦Ôƒ˜^>í]≪=¨Äà4âT!?ƒÌ<öºÏæ¿'…7—Û‹ºCgE`ŸYà¾<¨»7i–k]E¬k¹B¥€ùz§gìZƒ”ºåOoù¯åùC@“B  ¥Êö(t|)û‚Ž·zÏ@G*és…A!(™XùX7Èy\ÔýcÞÒ®n>PïK!:ÅN—ßÅÎYŽ3'8¶<Œqí-–*Ò™gÔÞiÇ™YcË_o÷jen «ÒoÉ̴烘”=¼_}ïawõ¼"Ü u®øUÔ–Šµ¾µ˜á>ò‚~ÉVL-,Ö•gô›">SWyè–£È2Š˜kßva'ko…Øg<YT¹'*¬ÒcìøMìáì5*¾*ÿ:¾Ô{Y÷p¶º_‘ë9SĤdKëfÇ²ÚØÒðÆuØéj`h!;¾ªBy)±sáq˜’åÛ~e ^»-œ„‰GV¡‹}y£F‡Í|Ô—6ÙdGü‚| ?¯ZTÚ
+6wÙêz%Õj+|{¯-½³ÙXVs|k±ÁÈÎ<ÔÝùv>ÌÇÁx&¬±å¯$Š(þÉ-P^T: L3Ñ¡-®é ™ÚBxÌ;PÒ¶ÎOš¹ KñÝÉ2Qà÷¸>Ûx¦Ý½¯±‘õ\•­ì ÔtìY°g?1¿çddÞ3é™(Œ3Ó÷»•›Øzq:*VÊ•ÌW'ÓJ½ý[rÛ7²EP~Fç*ت2.­‹Ð…Œ]ŸW«£KÝÝݦ„ÖëœFn×:S–AÑÓ[ ·æ½rö4ÆŽöTi‰Þ¡Öþdvì“̨Ž—|î>ÎW’ÏÑé’‰Eù–«LrèBƒü–¸œì9’|FÓöÇ3:0¡³tªö¢{½JèavˆáÅ5lc«KÐ «Ð3¹AÑÓ©YØÀáÊ™=C³»ýÈ9Ðò½OÕá™ìëCº2:«VÇÚ;h¨e¿ë”Ã?•(ßò×Úãcüh4Ψ!ç7»3¯fÔ)K#þ–zoº.±ˆ½z4jËÛfÔŸrûÁt¾ “<X Ç¥°1UôÛlȼ‹AØ×È ExÿQ!¨>o-˜~w8b{¯Ät×ÛïúB/šRWfšêo‰qŒ½œÛÞþPÁù d$Ô› —DV¦ökºXuÏíá6›"Éçd]â#ºŽ"C¶Êv½[¹/ŽŽõÞ[S[½C÷Ðë%6D¿«Ö£îDNº©ébÑÑÁtÝ™3}¼m…úi¯ð÷["ÏŸ‚mloÔ¡>IáÇÑ»¹®:‡Å¢÷Œ‘Sc6ãÆúØáø>Ë ýž YáÜK˜ïkwš>;~w²giék¶½®] -‡sÑÑòk-ôqm5Ͻۤ{`جÅÒó¿–ä pºÝA±þ‚bC:‚b2‹‚ߣ@¡‹;s¢Ÿ¬LÊ^ÇÏ©GjâÊ­ʤ}O›ϸSŸªð§X6¿?Žé9&O=&v¶n©e˲Ôôŵ&fè[ ¹™sûñ0
+âöúUù&ìùÿ)è0áìõŸ‚~óùÇ _€ûÏgùWÿ‰yÿÿÐñ×÷‡ ˆ9ÔO XD+  MèOïKÞƒºu¡ð KAª|éžm£=w/³îpN‘±»5™; 1”«%阆KulÕšNuÃJù‚eË­mÅ7ºšóðòÔ‰Ú<ÔjH*Ê„ŒŠ/(ÆSOP\eG ¸¹8 ØlTAÁ]m (Þƒ.ôãaÏ[/FÚîžq£·t73‘´…rŒý@Nû+¿;ÊBkoi«P²¦7[5ÍôV7 n™¹üÊ6ŽÊÜ3Ž[êjqèÄbõTYÒ ”Îp}þ‚DÙ¶°
+lš(€„ÉwÌõ°醅MÀrá™Þ,âa¿E.‹²Þö΋ìÜÙ¾hú$ù1þ8)a’5 2q§hÆÉÄM£¶º;©º:q.\tB¬Þt‚Fñùà®t6ë
+ôr}¨×âó)ƒFÿrSÀ“¦'î´_<æW¾/Åeï Š—! 2°°:ÚBPöA_ø;ìË\î²Pí†GSï±ý´úø)!ú;kîaßWAJþæ¨{õÉ•ÏEê;Ôh9ñ¹7a3
+#Ãõ¤4¶<ísBÅ‹ÖKÿªü'æ;ÿ“• æ1<=˜îöõ„ƒØ>{YܬªGŸCûùê¯O‰³¿µ²úL1œ&}ÁŽ@¢%…§«1Yç¢ÑÁû©ìàÿ Moo¶'ËÆ ,è*ïïå§| ¨øƒ€"3âkÅ?3Ðñ‡9=H4üóuzø®½èg/ˆWö4;°Aw´<ª†ÉZÙÇL6ˆf&ZK:§G¯ØU[c„às5!P›üîyM¿Dê'h˜‹)”o’Æ×Ó+yÇ-ÛÒW÷ëxQÌàsPÊ– Eø*:Jµ¸õ¸$­g¸ÞÓó){~Øë;Ý$»<%q°±r-ì`ëüI§Óº«m&ÙÏ%OmºëÛAFÍ`ŸÓ[ ’Þ0]©;ŽiËÛG•¶Ýñ[üª|‚‚Re³€˜yhÏã
+Ú󭠴ȘϊákWL AF”Ï•=Þqø£1;%oIÊÊÉÌÞ Ž%S§‰ð¬5&YWyGgHe°Ï¹/—÷Õ¹á
+Ê–—Ÿ÷*En E.ÞSù?Èò«ÚOE“N|­V =ÑRHÕ† b^RµÒãÖkŒRÁãËgŠÐ[n;UŸ”Êš²–ôI0\aiêL±D¹Öhê,K™Øe¿l¾.|½Sµ¶üé¬pû‡*qñ¹¸g•ª(²Kð½¹Ø~U¾cv…åg<¹-DK!=¥sE{zçñã&\èd`Ô|ÔGõ¶{[*s{('¨ã|EïM¼ž>ê—*¨eJF!öØmÓ)‡<Xd×òF_÷ÅͼØÛdܿޱÈ2¥öeE—ü…puhÏ
+ÊA¾#öýJHcÞ­(NB/Æe4ñäé%[oiÕž2UŸõ]’ 5… BS¢jqW¤ª¡ËSUÛÝQtüº¥¨Ðg¿* DˆIZkPš·ÎŸÞß@ÐÇkÐíLåº<¸¶•¼ äMåª?¬‰ï'-ÆëŽËrÓWK=Äæ¾Bo4NCÆÅVYOL|œá·œe™èœHÀ¹"OÜžûèßo¦ä-ɶMî«JiÂñlóºe[(Ÿ4ÄPÞïÁÝx8‘íâ¥|°{ˆ'PqúX:4·Zßî$tÓÝí^«ƒÀ掑ì4;ÌÚX ØÕ‘;YkãTL¾2>¨ñ{|_cxô¹ÑYpÜŸÊò‡
+P*SßA©„R”h;å¿}ÖWë–¨íȈÇ]iÓ®Qª6Òlc,•»­éî]1V\†Ó¨èÔÃmt´a´ë9òôêx•÷KÇ3Í•õàÔõÁ$ i-Læ°ãfÜWå;¦ßœ’08CëÅϳ;êP>s“DL¼AÅpã¡~µè.ÈiCjX’*Ån™%&—ퟆŒ·F–TŸ“Ä ¹Æ±©}¥Ö9ÚÝ.ÝÖé°ð³²±tz¤¼ZÍgûuF ÈZ{Ìv_PšÞ2 ä†TxÑ.ªltä@ûѲÉyP¬\Öp²µj™«%[R\¢(6ú×cAdçS‘¡¤I7Òʘ쉎T:óõrâ.WX_#–®ìo¾p’æ\s¯/è5&/ñ`)­
+Ù±ð×òü¼%â¥û‚zc/ÊXO‚Jš«]¦^u‡ñD2ö ÉÝ]‰æÞ".‹áç;S£J)j×Q*do;ìà“¦Ø][«bUåᲬ`ØdÁxÌzQ§LfÞb5~Ö%÷ÊŒ¯Ì¤9Kv…¯
+ôy!ÐÛçcì@S¦ÁM¢Ì7|_º´Zô,W‡Œ{FbLJå“qRaÑú[§9¯x&÷îEìIOAi½zÊŸ{¹AŒoÌCoÜž=‚î`&¬ÇÓY¯¹'¦{eÅï'Jb)~ ŸÅš²]„îF`—)3ƳÞŽ_S¨½u&Ô $7¤ ‹òH7Dz„&ðt[^ȸc‘>Û¶®X.¸€ñ𯗵 H.š59=ï”Èü¬Ÿí–¦‡Q½:Mð•Ödòª÷¡óIÊr©qv'o¿ ÈgÐ&Ž˯¢ ëý5«êø¿MIm¼È]¥ÍMoð)V]°äµ‚Óó›!T6-`Ç<¦®Ñb/7•·hí
+—Ù+vf±qŸŽ³}0™%Kp<66»2Î1¯ìèÔÈ—Gë[½9*î–áÛÙ/¾*
+;ëvï-r-ܘ
+?åùCÅD}-I|ï'/ë]ªL›†
+ý~â-÷Fé³ÞæÕãÒY ´ÓÉžY¿½®×–Ã-î-¢µ~µÐ™hÒ=,ݬ¾É|#8på;’›õ›¶ï~U$”œŒ?ÒN™–sääaÙjÂ…Øê<§‹4©wzpãÅÄ›6»ê2ÝíxlÒ^WÍEËaM¼…ÞeºIw÷\#8¡ÁùCý¦rZ}7å­z'-»HíišA­Ÿ×nÕ·í¾«ü‘úª€|A~Î%@žî/@¾<\˜Ì|ž˜A9JðÏÛåy”åéûÖÜ<rWqWƒŠv.“à<ôç°óö³•õâ¼6×çü­Iyö´ÈÀáMn땘œè•–MÅõâ} ÊÎwIŸŸ$æ—½ûS>?Tü”¯‚~óùÇ _À„#¥
+úÌäoƒÎ“'§öI§K_œmçò ¿<À?7 ä‡èíîŸHø,á}ó*\cƒyí¢‰Žo°ÇñùØ]ÌÎ…UsåÙ·2á¡)@¹ž8—Êù¼[‘¯·²MênoºPØûé¥CKÎ\s^Fì?Èò«ZȘ† ú ¶Uc½‚F½9q„‚¼Ã@þRÕŸöªý¸34äÙx)xó25²?W<LÎ…Ká‰"æžQ@A‹Qâ•u.'yë\8Sr6¥“æÔuÿèÔòõg7KYφ%Ú¶Žc+ë òB²z.Ä<ÓëÈûâÛy“®€B%K€Bwn>mkîõë>vùr0Pmß\$†pªÎ=bU[¹ç]’p«â‰v6'ž³Ã&ØÛùL²·=ΰ[ìÚ­kòa)›1œï–a9š÷¨šjyê_hÊ4àãš…Ô‡V,ø Vk @¶)Pà·Ç§S‘Þ÷FßÏ\_‡Rå ¥Ðõ—‹Ôø\Ôv ¯¼jbn ½S[“Y{ûoíö¼»?=ÞcíÇuG(ZxŒ¿–7Ãzz¾vFª'µ±1 ¥­Ú¯äùC4fÓd@5Õ (Lƒ(Üq&
+/CЙòº7D½¾³ƒÚE_
+=?O£3œÑ+—I ‡5ܽÃ0îô¤Zðûrêòòñ],™Çw¶áawù0Ž)û¢Q‚£ÜVKîùEõ0Š]_hʾ “YPh¡ŸÓí ZMÐ P,n(ºõt4 Ü ™¹²­òE÷íþÙŽ@+äs®jš²ù·9hqá}ÌHÇC᥇<b•zhfñð¦Uµ·«´o ëð6Óp¾/M[{yoËýšrêAÆ\&gÊÓÕVµ¥@qí™O7y÷f'—º¨Yù2Ñ9Z^X¸^%Î)CF™äN±^I8Ž¡l©Ë´&¦ç™sØZ5ºUðå§3:Fý«.¥Kœ*ƒÒ^,ŽñO%ʶýq
+¥õ2!(~ƒMrººAÆI
+ ŠÇ‡Ч<¸s‰Q*Œ•8ôbìÂö™HÁŽ%HžIû±+nN{™ÙGA\ü¶D“ò•¨‡YÄí›v–Wùy9’z’}®W—Åâ:~Ê;[äi°å¿ sí‰×ò °}Ó °Û@LvÞŠb㎃R.®A©?îWk—
+1½˜E¤yvljêë7i?}î48ñÂ1 ÓL †™‹SžQxn }WšÊ¼)žªM ÞgÊÂ~ÇÕ‡|tl|¸û‚€B‹€öÜv
+  ¦à›sÓЊx”²QP§( ´èÞuòŒ„{¿Œ³Þ²é÷øÁ5…ÙÏ ÁâWY°f›—læö”e¬q<Tî1¦'þ(¥—Æ3­´ã=a{·«ü¶ÛÎî8ðLlÿZ>ÏƬÿÔ)l@±pyFQÆ:(Í£¹½X:€Ò ›V¹õ,è-·åcZiä1m
+s:¤Ãžô]´2D1Šaï%ÑG„ œñ dÁRwWs.mùmVà^È›çsgËÅ«GŽ••ý‚|ÇTFÅï˜käMIE;AŸÑÑgkÐ6yVäöìeËWËJ¸NÎTw@دÊZ YÛjA¾±“¥LR”2±™Îû;JÝÞìÄí½@àâ½ãŽv‹¬wÜF/›ÍÌ]0Ègñ×
+˜»L{Q‚>¯²ðõ)AÐÞ¶Ü% Ž€*'ÿyÞÅS·çcRº¦;NÛ'Ή»Ûp–dY‹k×Û—“É&.⇹Ûvª»(ÛxÏ"ìšßÌ“Óí&£8Æšu6Ì*S¦¡Ô¾,QoÉ hH  ˜hs°q–ž DIe€¢yª0<(ç÷hÒÚ+g{¹Ë ¼^ØgǧYî23ªar.+8:Ý-9Ý‚´¼Ñë“´S)‹4:ˆ²aX­†F1(y@Óh!ö)Ôäæº›æp Šïú”t%
+ÞÕ' æû ¼);·WL»_3‡yʧ.“š#=ú}Ó«jÿ0ìÑ®¿ša³Äˆ!U3÷ ¢cìwÄNä9²ñ”7$‹š ɦ4š Ú'šªâ~$WêS>Ÿ×¿Ph ™†ölÌyPªvïQø.Ú~Þ€òÒg×[Î ̼\œµ÷î+©z<îöu½Ý9·K&Úý+ãMÖâ-jæ;x\,VV/Ã`2Ûâ1l¶ÁÅÆÇ»Åž&v5† n´L~A@átŒ¾•Åü® ŠÃU´9i´åe¡»±>(¿r̽û¼J׬çÚ~½U~;ȲP0kJ©¤LSµ²x9ãÕíX4«´ÏªøhÉ×V…û¥7Ô ±8'+»ÕòXÚÀÙë¬W§±ÑcLc‡Ç€ÄcÈ
+À߬=¶Ï–j22^øä Ú«?Èò«ZP\¢wP|>7Ól¢SëajTÚ:ˆ—eLv Õ·oHŒ¯Ïù1IP¬{e
+,Ñ=nð1y-kYáö¼+gh1Y_ß&³bñ5ÎYñôKäËC·ÓêÉô øÃ¤0Díývho[Ôh©ä‰/4åöìÝ^ r¶äûc<ÃY[4L–mÔ̸|“e½ÂOFÇKÑ.N½&ÒÍ­ …7ÍÚÏ{j[*'ǚ̫„;Îkµ`TÚ¤ŸÃ
+O3SÔ—³v?ÌŸæQD±0åû×ir7¨¡'ö Š`@IÛö@YÄ&ö4…ÄÌ Ï+VrÏíwiòΧX£ÀV ·M½Ù<ü"óL¬×¤•#—­Ü˜™É…=M×ÞÛñQÅ‘³^«Ãó¬o «›æyÐ ŠakVbýö`í
+VrôzÓ)Ùˆ³ô+ù@þPJS5Ðã£mO²ƒ³qÍ{gŨ§Òû¶ñâvó;zÚ‹Å‹:4ô<~œTÛkê®Î³9›)½ø|b¥ç«q©tÃFå÷ÖÜ5û šV_ˆ¶š+Ð
+1Æü,+é ç/â£x(î²;Û°¹˜D oc_#Þ{]u‰Â¢÷<Uf)½Õ;msDÙíÞ°Ñ» m8­ï÷2Ä¢'5FX/NôéîØìì:z¼)ufÛ¾ÞÉ´×NÛ<³×¤½¤O±v>ç§¾ ^³hKô9yÑæ7bë‰[øz•˜ÛzÙ'Õ¶G`μ·_5üói>нiÖÚ‡ÈõëÏ!ÛºÅí·šê½5"ßeˆJwZÕ;1êv²þhÔ^ç;³6=â-üÙgZ(=Ú5Ï5BnR§Õ¬bçOåùym’Õ)Ï{¥pßPKn}‰ï™¶ãÅÈ”Ø.a¨et—í07M“ËõØÁ=jp+¡ûÞ[}ª]ui˜ßÙQôÜ.\×Ñš>>úÀbQ4ÕX¾ÁŽøZý¦ñíúnfëÌuQïdÓÄDÖj$ìð`Àå‡ ÝóK&1 Þ²
+p'œzÉç东'GÕàœ´_Ú;Lknêtmw²ÞiØrXnÚBï̲Iw1¬HKºÑL̸ún:jOsp¨‰Ë™^}Û³Sõ€“~u„ò÷ŠJ+±^ê "ÜRD‹=cmò­0|xŠ‹'ËÓÓEJ FgªÀÚ`'ôF©ŽÚ1°ä©]¸˜^ó¼ã`k{ìY¿)ÃX½“ê!5qÑÈTß§j±:*ÊÕ„WmT¦U´SIíQyÑlÌÊÙûC<¾A /J€¢+-Èg²·¼²»îd /²³ÙН¬‡% œÚ +Ü× ÛOÃÿ.×9?ó^úù(Iaê^!”ÂÙo*áÕž4Bn2é^ïb{xåkíé•õàüåÚ‘çd
+öÙìÎÏ.Ù~z'žÊ™ÖxºT,¤<ûÿÈêÓ™@öžÏƒ¬Æ­@n<ú‡£ŸèYlš…´¬ñ<ŠÄõz•ø-(عpËÌÑ«0©6‚êR,1âÅóì"óÎú2®L”aÆ_’ðƒöÿ;ÓmU•¤]_Á¼zÅAQP±AÅÁ±Sïÿ¤«êûœ5×wþ<äfϱ|ŒÌŒ’ôº­ô óÌ\qR<ø«Ó`ëù©déµW—Åòçò+
+Òõ‘ïùÅh³½À¥nSz†ÓõcêÙ ;­4ê ÊT9@N²@™ŸoyÙa-¢m@Ê‹om<ª]^¹Þ FˆŒ<f™(w—äNù–q2b„V\媉f;ô;ú ¤ú•ñå
+€r¦ OËç¿vƒ3Ö¼ý=ÅÃÿù€ëþ«ßÚ}b #ñG­&³·5æW#,d•°;3µ {"{gN:X§1¿_zö¶ßMñËæ†vîG&%:ŸÙNÐÇó¾ f¦¼4fEêÿÆò7þ
+Êôï@~öß“[èêÓLç7 ² W‹Z §¡Ð§W§^ÏøÔGï+dEG^ÿ]
+{&];=ÊùF°*$ͽ.³Ý‰ Æ[nl\WήœÀ-Mg!اÖQü öôÅõ&@Êt&RðêLRÝPÿ;þ-4! ˆs bú-Þ%PáÊœ¿U e(3b[|ÀHé‘^”?Q;h£aþ±Ìe³èkÃtyw!_ÕM園V’jhËͤe9L™Mï¥ÞØž¡ìp²à^½IÊ «‘uÛãmËÔÆÛÁô?ñƒüÇ @áû4fö
+(îZ†ÎÉA¡ŸÂä†ëïÁË”ØêÈNÆq÷øÔwç›v¤ã0dFÈI¼s¸ß&gÔŽef]«dÙekþ.þ9!¤.ÎFX¹mǽòx"áÀjTÜÞx»µºco¶G{_ÑGíŽÐ‚þ
+…r ´h˜‡]οå»vX5ƒ|z"Æ3RÖî­q2¼^ö÷\«¼ÿ.ÛiÒ¿º^ã}w¸´ù´~.“»ÌgX” èÒƒÇîW{µ¤¡÷¤¥õ^¼ÑêW Dc¤öíÉâ ZñPµ"\‚X¥
+˜÷qþ=ãúÝo¹¦×•ZTž²Á×o4ƒtC«ƒ;¯ÜÜxãú1X7ò´èv8uŠØuì{Ëh8Î8qOÖ¶Ÿ.Õ©¶!Œ¥Ž§µn³‹7Ì­ õO©a¸œã1ãS“ëÿ
+#Zîy1Óì¥BGKÑj_Õpªô+úZ¾Õ™£ôÌç“Ú‹’ñZM]Ukµ¾7­ÅûÊDª¤&†ÝyM‰"Ÿoúòçò+
+%Ÿßý–þ¡Utº©AQb*3îsžúÞéïØÎRe™Ûß„©{‡¹ WÉËÃú#ôe~ëXìí¥Õ?xJíR*òƒÔoV@HN]ÊAyXYðŒ{­ÚåV
+ÏÆFë_•P«Þ0KÏMÃ. Xcñ?EPãÄ)ìäñ§þYh#¢€>BÞ³e ¢­üƒä?nüÁò°wËS—Ï–F‘°¨ô·ö½a°{g®}f­Q“ðv–brìLz-¦n-ã {AÏeÏüi¼¿ULa’”-¹—*MÕzö[%~ÿ©„r\a3d+ôÆIß"h“ fÕ.Û{Vî´/rýúÎËå3I˜Ëg‘Ûß1ýdùÑu”cûhwÿÖ“½¦e¾Ú`ÏnM®¾®¼¸Ñøjµ›?Š•Ð¶eKê´KRí%‚s
+ƒN ÍüÓÉ{£Û*O<ÂÛ­ž‚ÜÉ Î¹¾t»3áêöbŠÈ=Mß< ~ºB’,õ<T*”k*]õï
+ðҥ𥱸}
+þñà@­ñ`Döµ°—Î Ô‹Øâ…$ Dª’t¢‚ a´º³0ãÞæ¾zríƒwŸ¢£}Tß›P/Ýs_ÿ8Àu ðb~òjèÌFa4ðqºð÷l"üV¢¡Ú†ÕΆPíÞØözØþ_¬–˼ônO‚Í;—tÅt)¡#DHh¿¤Æ§zÈ{Ï‘õ óWïQ^Ïüè³ÊyÐbÞr¾9÷ÍÏòûïø·Pi¸ØÁ†&z”a'G¹ÿ©ƒ+
+Pæ>óý5y3q@O„7n}@•N ¨vsxu%ý‡5$óŒ¾U{›R¹{ÂVŸêmÁ³ý[J«9‘¯§‘ä|“Ý]NëÙk †îÛ–ŒåZOµ…ÆLÔ¿ª‡ý벂ÎI…€ìŠ€r¬ …ÚÐÓè£ÿ7
+†¦SéÔ¿gd˜€ÞÎ7ÉåIŸ¾r}ÏYrs×h6Ro.s%p¡|1^‡Ú¹Ð˜ilj5 ïÿ q¬’E´TkÃñãv{Þó©Ölx<*ÓñË–ÿŽËÒ@ô÷oñÊì},À”/Ñ÷Œú»Ì]Ö¿æabúêâ9Õ=ï^ŸQëG¡¡hàl®WÙÃkàcAc™ýÎxñ^ûsSw¹g¯æ#ÝZ¢Ü¶??rí™™>6§åpذ§Åz}òZ•aŽüçò+
+u°Ê·ÊhCqÔÀI9â"½
+ÌÎxÎú¨uWe‘ùÑöÖüÓq^íÃ|?Ⱦïš¿¶¡•ÊlÊ£µš{­’»\mÛ Í5úsvñhO­ê¸e dY™H|F¶ÜÙ­f)­ÿ €X?¾gAMdHúð
+©q–snÄöôyœIýÙŒüMq?¼çë Ó}>iWûvî›%ÁËBèÛÈ¢ÚŸ$³
+Ģܟ\¥¿.2›Âv! u¡WRsÀæVÇĪâGv,~¢\›…cÿœXf%hwòîNöµuf”î.Ž›`0›sÒ@Uk¼MgXL·½WW>õe)骧 gd*9Þ¨wyÞXZbÅx²ñvåbO¼µÿ/ü ÿqãàðÉ9p°ó;@ õ0´ý²4ìäì?AŸÜöÏÂ3¼¥ËÈq¥Øýà=¬nZ¶î’üN›[T³c×Å\oLMP˜‰KylÒ[pò¢‹É¾×î:»›Þ_—é6S:òâšT:^!]ê¢-¼ÔUT±»Þ¸¿ãß2Ÿ²¨Ëôõ-}8]±ˆh½ÂÕuóXÍÄ]ÄæñÓEP‘wÐEÄΞ²ÅÕŽÞ‹ÎÕ­+SÉL5-âxioøÆè-Õ ‹ÏÒ¶>ð:«ÖÑ.Í„¸a­‰9®hæk -¦÷íu±Í¤/ÅvÐN¸1Jò ˪m{€æOo+|‹wFkÇ5Ùºùí‡Z dsle«áÞ\å²?ÈFI¹eoÔ(Ïæ…Œ8Á™}Ý‘=µ/õu½«sC7Ó»Q«:©-Ôå­vjl²!¢fßWÃr‹'‡E­è-9Íô켞gwÿ ø]þßpB’—€Ïö€^g> 7[Êß*£ñLÓö5´çݳ|¦þ¥¶À_¼V¤ÖÄ
+Šämu §ô‹?â¥&w÷Fѯ[QûAZÂk=P³ÃÚ\ÑÏU¿Þ}‡™z7°EÅã:5}é•Ô7ß㚳ɰЪÜÇìß6–€Â¯`JR°L£ñWq%ª·
+õOå¡8ö¥(à2Ò¸‘SåAe'Ö׆³¥âãRCå:5­©¹¿þ`¨]þø=b
+ ÞÕ³x¸?º™úø&V¬îj·»±&¦²»¸qÃól¹¶’Ä:Ž5d˜ÔH¢×2Þ¹N|ŠšpÏWÕ-=R”î1וÍ;2“*óÒ¡&~V¸[{æZª4ÚF‚<Èî*u&;äê£åÿ~Ú€|ÏÖ€6¬ä¦/ô<ˉǠ#ø0‡­§o[¥ËWL5Òœç§<nl}oõÅÙmøšŒAO»ÒÎZZ­wcÕÝE(+½i¬Èc{ÑýAj¯Ôb.~ØÔYÌHTÌ´ŸQ®Vºâk·Pkö°$Ö…GE–Üß(º#gî—òv¹ ¨Ôk±³g ÇK6ÉÜüKeç`*SìØ´Æ×Ál3\çžþ|=;Eùh©N9­êw—Pò®P'8 þ~J•;°ÌË{Uòn½øÃÚ«ú”
+*ët„•Ž5QâV²8xþï
+Œ“¶ü–COÛÖ¦}Ë¡íSý6I"éåŽãZÆ/¦¯OUúçÿœ¶ùÝ=¸ÂÚå
+"Ù¥çÎ JÉ}JBš“Kö±×-G”Ö©œ)®õwœíôÔðÌ©Ü\µ,­î”¯º½½?µñØ¿Gp}ùŒŽH_OuJ]“eÝ©2­ï±¡òs^×>›ÛLlRS·˜ŠÇ÷ù"´Ý‡¼—+Ù\ª$à%ªètŒjQ¢ç0‹S
+Ó€S¸>Îeî¨ø­õy¶öwìOõ–ºÚV2¼Sf»b¼ùÄ®µÁ†OŽÆ©¥Eí§yZÆÍeÕ.e“õ›õÉI¯å´TËìóü÷ýýÜ]ún mT¸·¨•á¿Õ/ XiR”¨’˹½FPؘí{½/3yoòfózU–óÄkÛÏ ¹eƒ%“ú;`ެt^e§ä…‰7åMþXàz¾@¶»ÜQŸk®hØj _æŠÉ©k¹
+^~MA­3—¿ñ§ñ=®p`ö.=×Z¦èèƹÍp™ [, 7ZüSýAØ`Vï±ô{<ÿwý3›Ž˜p[K1fkÎ0¦V¨ÿÎuræéa|YRFç c†çpÖn¡k»©„ƒÍ÷)O`r-³›5ªÁ€jòì#ê”-i>( HÇâ>AyVØ 2˼7znòÄãé³Ýêóœ;9¯ÿ)‚¢)„¶t’¡žÿSÚ$_o@¾BtN:f}ÿƒü¹ü
+[-S„Y™k˜A—BL¯§šW¨—¦¦ 8U ú*^ '/¢ôt§tQÂ%–Ëœ˜bÞ3ã
+LÎ0¶Ìæ+‰ ÝQ)fG-Új ÚÔÓô(ŒH§?šŸëlE(¥m€oìë o
+äó%bVú;
+Õ^_
+°ã¼ñ¶
+ ïÃj½>jQÈ·_žhePyí¶+õ…¿Á(¡³•Íã–¾ï¢úq/ô•¦]rà+òÀðt'­ï‚ϱõ+
+€¡Ýô ­Ó`—
+µÐu³WªÑ÷Hú¥+™¼˜tÞ¥z|*ýïííe¶ö—भPÉN¼=‹ô¶¦£¯'ò­±YFâ¦Tû?ÈÜ
+€KDà©ò¿ËËdÀ$€fõ?§7dT 0r~ ˜jú9«šø=;8°0•¦*.`¥Ãû7 q’¶g¤Êƒ­Ùé[ë);«Úpµ[®gòi±Àñ§ßþ{¼%ûIvø‰ƒpeË-|8˜½fƧüX}:̸™ñØ_píÿ[è·ÊHÚ›VÓ±-#äsüÀ’ç#-I騽\à—ë=ÉS%½çdb&Š»‹£66|G×VÒ}Û[6-jä£õ|Î:êvzãÕ«ý¬Øäe”«Vý}*›´Ã¼©™éB7g †þ+~?è—åìýúw€Òº/@?Bñ[¼.„<Ň}¾i "/ÏIú4ç4ÂßãÜ.|K›ê6¬¹ký /Ú.ÒšäŽ1µ>Ï1tyËs§}¼YÜÞ#ïÔ/H¡É™ÝE%?<SLnØ?bô0߀)ù+ LÚÓìÍ
+õW{@7@
+öyUx]æ¹áÃ-©ƒ¨|¬ _w–Ì•žiÒÏõGÒKìbó™Ð6XŒóVÓrJ#ú‹ƒ/6z¯Ì¥c¤cØ/]äí;Zûˆwv£7Õm¼q²Ûˆp¢»Æs¸‘yˆBýPfm
+ØãÚðû,é‰@‹^„3`FÝoqW}d¥ F–À7½¸²•\{[Ó¹™ë71Ï)u’ë4µ;ÅãCzñZ­i¦çWX—(G”~9Ö‹-»rÕ÷xÓn¬…çüi KF•RšJ4§IkUøÚºM†Y;Ø¿hÏüWÕ–n|‹wØ&›Xx•=‹Æö~ܨ…TÀ4xöÙMKežs1¼ïWÓ÷uX~¥™#§õìË´
+ºz2ÈèWÌÇ[S“Í©J!à¯é´ê]ê4‘OS;ªÓ3•V4¦N6V SSAQ¦eUÀÕ¾;‡¿—ÿèœöoõÒ÷ðöx[ÖýÄ·øÂ:ðûþîñ™!ºÉÂ\y.7»sAkÍ&^¯¶qcæÐÿ ÔÕÀ¯åç÷<Ø@·feFmLÊ´ÒîÜÊòyR¥Qï3–JBt­Ý—Ë‚œÇ{tkxÝTQe—­eó
+ŠS”‘Ý”Ã:[xõñøEu¹|FJz­¶­5w>¥6XÉîÔË7”jýÐ7éÍZÐ<+¨Òë^ k“tÙbV¹Ò´p—Š/W%æsà>![äÜâ°Yâ=­^.†¬P.j7þÀïòÿÞ8 ¢(m^s®²dŽ•ü¬¦Ü 3m†s»Á÷½Ú2è†úÒžÿ”CEDíR;ZáîUN®¾oÕš‚õeÁë›Õn3x6ŽGßzòò</M›û]Ñiß¿¯Ã¿S…͈g ècÕÈëBs™'ÞÍ ,C®€&ÕJaÃWË¿bW6ê•ew13ñ<¢Ç'ï c˜Á’°iÃ8¬´N9ÌÏ´ú{0WƒvÚULÎÞËS±
+Ã9õö<ÃÕ.×ÈVÂŽ—-ùm§^JE‰\ œ’[«ô¶ì剸8céO:Èõ1fÂí÷¼qÆÔm¦Hê>};Ü^‡K¦1z6Û6ˆñIZc¹B£]Öö0Ý©ˆzSG{-%„M¹
+$­¦  +èÌlðƒð§qcRá
+Ë×;éÔŸ¿;C‰x·²t÷U¿É ô‘jŸø#èt.©„“qRzºDÂ}önRh²Íwž¸R¹ÓìeŠ™y–®`Cœ(•$^!\³\ 2÷roòUóæu#>Zí*ãÊd/.Ò×ðã‚ä‰V qÁ<‰0¿ ×s òYY{ßÙ'i¤Üß
+ªU+PëXZ}øÉz2Ùvi ²²áC­ûoí"[n KïË Ë0 ¨±i|5^ͳ9ˆ™gªû(‘m-zãåW
+@ÃN ærÐâã†Zë,@T»ö­'³GØç¯öù5ç€ì¾åAÉ«d—xdÇ+è]² Ñ@žïæäQÒƒè­!Ýp'¨­s/·‘Å×by
+ÊŒ°6@ñ¦ Ðóó»m}xxºŒ¼ô}_ à™ô`›Ü`‚x(€1 :Z—
+ÐB±ó¯ò2.]@í ³rJÜdCøÖÆÉ\÷ˆ¹þyÑ EÆÙ/?GNOyLøj÷>ýã›sú,#wì¢Ì1¼\›èçz²@]ºèF9™9§ïg-m{ЇõuÅ>UÝ©”–ÙÙ]`þ
+®û.ÿ Ð%…&ìs¥ MIX0[>®
+»éñ‘äÁã¶Òsàê½-,¤¯XþÌiH;)X¼êM¿Ñ@{{,
+-ÜîŽYÜŽÛü¦’ZßG'áqe²R]àø¢8Ï5s¹iT¼PÓÒFiü®ƒý
+€e›-€1ŽpS›@SŠ;@Åä[µ-}0’°øY>^ŠäÁ9y}\œ¯&y\
+B>u
+ ‡@–G> j]
+óú?v:åt¬ynµöfOCìÇÊf'5Ž¥¬zý…Õ¾‡ŽÑ¼›i£~úW@SêPfpëÁ|~÷ÝmKŽW æS(tí–Ó{jéÓDíÒÕ¾\'…å1E†žOô÷çݰvynøöë½’S2ºl.w´,Ê…9» «Ó3mØö²kLæ›c}ðNbeÊ!ŒaFfb#b¤#fç!dLÚ.¦‡§*ýù
+–q‚ý¢¶Ý_ª½ëvöÁ²«V¯QZäßCu
+xÝøA&(€Ìûè­{éì;~S<éþroM¶U ¾3 ÑX-òee»¶:J«¹¾4²ÛPSL.Q_7«AéÑ|ròý¿áùsxíÃrÚ‚ã]®#·w̤”¡$úd_ЦήZ ƒµÚtÀ2wK˜9¿áj6BõÑÙæ‡ƒX¬Z=Eï;`¼Zé&Eù-û³ ÕeT…>Ö@1 ©wz'CK—{£åQ>Ž Ÿú>¢^ʶUHÊ,÷l|âÜíWÀ1Äv¡kòöÃ)F“ã›
+…Út[5ëq9Ô¬¾š‘†›Â[îѹ×R§êT\ß‘û¯€+‘f
+›xñ=`và4H*çB,_ë©<bôY¹8ï¦Jw"`K·®B—‡tŒ•ÏýVêW_1^_á^+­Ð}…™ßÝ i£œ™+ŸÈ7Ÿëµ’Õ-èÙ¥>‹fûVcÁG*5|¼•ÂÌÂd~SÌÕ–$(‹Èü,W}eÛæ­ý¨2,ÜòXˆO%»“‡319ØbMd4n”Ïܲ{ƒÓ@8[hqº2¿bo8jqµÁ*´S´Z˜ØGdÄ%µl¿Ñ¼e»ùz¯Ü®Z‹œ“74?U;÷h©˜ÄÒ—§5ª}6ÑÃ5[ †[ŠïWFù
+œªåJ¦Ý( ˜a|ßß·¹L¸ð
+Í"xæ=»Aæ RÙ` Ú­{é?ÈkË9ü..ÿ ëåF¡0»Ñ›§Í›øþô¶íλ}K­ÞËj ×[ŠYx¶å*è5WF‚×íj·pp+át´+[rëøƒ”¦ª%²pJŽÍ|O2¥¾›WKlWT¹~Ý2áæ±fЏ|§ož§-ƒÓ•ÜÔƒpý_±èrÖ^õÁrbzÍ#·ËŒ—4››æK RAžV¯\MAEAgÒþ4:Ö*ÜkX/MÕfQ"ˆ6—9ÝŒz=™yâáÙ,ýr¹|ÊÙ0EÄ=Ñ–ÆÔÔÈbߪC¡L|nŽF¸bJ(¼%2¯5‡orçWL¥ãà=Š~ÒkN>ý1ë?T½¸½Ê6·xÖ”ìÄ©šâÙû Î0åòGOƒp™#Lå<sE±]ÞÈåØŸgŠÙ\‰®àO T"’)„k&:¾™¼‡˜ç 3ŒLÉ>Úm¬b”As$ròf"rÚ3Òoß¼?øAþiÿƒÑE\>{›Éí¦?D!ú£Q‚“ð^Щ÷ Ê+KÏ¥ãsŸ½p( —tªŠqȱoþôè:Ó5Ea®]A³8(Ѝ(ƒ""‚8â
+O(Ù¿Ë%y€åk°-Ĩ%ûc€&Í5@¯§Jª"
+¾¦
+pzÿ8‚Áþ»7j
+çE|ԆРºÊ©€x«& Ko|ôdÙ+!{€Ø´¦€èN¿ß¯$މýø¢Q
+
+§âÖ7­úñ‡ðÞ3ÿêv þy¤ÞvHûÏÌîÌmJNX_6Ê`7Ø2ÊÓØ°Ú䲿6¹n§Œ=¤ª„nn¨yßÏÞf½,%³¹Q}þ†ï î Ð'Û)ú!Ãèε@:«C¡ûJ†Þ)û¨ÕJÔ}}_ˆ7&ûg]dÇ'Î*™®Ÿn²ópw_½:Ótú±­Ïoè¦Éoé5ŽŠ+Ü5¿øƒ--¤L~m!˜—Ò1aÎöStÖ4ªo£m½^*¼žÓ-uyü†ÿȤ±G%­
+ =?”92ÇÿIÞe.ŒÿxXÏóíX©—*¿¿þ§î„|szvÇh½úf-Mº+º³Qì‹yÓ¬¸^3Ífí°2ºhe?¥÷`¢Ìgw­˜ jtdG­Ý±ÅÙrÄ6•x˜¼…h˜Ü„Ûp1å®Ã…ÌžXpçß
+ësãAtL®Þ’ Ò2T=[Nµ2Þ·U³F8Ã7·õ•®6¸Ë©œ€I™”{_½‘8œ Ïâé´¼Héû2¤ôÙùÒ‘ ƒß
+ƒ~õ“òú·AŽ|­ë.Ä,œ/Ó˜'¾oýûSüèì6ô¡êVga¶Mu|ÐÑ;Þ>5î»Y~&*Ôqæpii¿±8^h‡aÙÓvqFÌ´†~zx¼¦÷Ңʞ¥có¦Ëâp`‹Ášvåð¹xôégºûÝîGu=û™? ¦ñºé÷«^ÓëëŸîé7@WŸÀÇ÷ˆŸab»œ ºÏz;m}•èì#Ð(7žX/Õåô:·”11+Ÿf[µŠ6•”¦ÚŒNÝawUåLʼn%:kôgEoÕ#ËënÒFÜÙ'ªC‰÷Ct(iµïÒëÜåߨßýô3~¯Õ'¼ž=O¹¿¤ÊN¤Ñ©fNóúéüª›iìÙýt£5®Å·6ýœ&®òØtn×ɬ÷C˜›Òä5=!.9©ä°‚ºh0µaï¼äälãÕËyYî7°Ö[µ½E—˜¨{^ºa{mîRg›Ü¥1uÚùÆûʧ·¯€?Äñ©#T_^Ç‘ÞîÀ¶üoý@ÊŽº ­ûÊ9¢«52GåýmÖb¶9¨¬Åuu9Ž£Žm¦? Uœ­!9Åp¢ÛÃ
+ŽzØG¨’œ›JõÁ=}i ɇ‡^ÙÃÂhØ k“Ïž,‡›Ðê¥eà;´U»¨ló!𛿣šºµn:z寋›ßÎ]/§ö¼y¿M¬sKUfµÓZÒŠ‡ó.þN‹·
+k±]Ì-ÂR˜·ºcöÖŒhdyÊœL7W}?SñpPPPéÒ?҃ȅåk_ïîεv'“ùHí±x˜pUÂY7ŸžwfÁAØvy]gÛéxóC°ˆÙÙ…Yº5³÷¹Ÿb¿U=ùîo
+úKµOÒuSx Xl†
+|œIwѶÄ¥A³]V³½Ö£JkMûJ.Y~V=[þ4H®À4ÈöpS§Éͽ޿Ja¿VÏõ=5>¯·÷?Äßz„_7µSåó{}wÈmz:B/®QŸ´–Ë7aÈ¢MN˜–œQ7g­2êÃç´’ok1FÊ0ø{GçÞn)<:§Šò%ÜÏr3^«¶š”ÌÿMdÛ6vâ`^?m¯žNRÎú sZ¥×µ³ÀÜjÅ ¸Õn~f²HÓc¤{æø.¥Q>æÖ×ì–§ðÔ²àU1kYý¾ÉÈÈœªí±ú`£~Æ"ä+KW$­u‹4=œT`uåPÙñ×¶k›4h%”j®³¯2Û o­m^Äzvñž1*Ú<Ö&…eR+ƒ~¥V~¯UûÀ'rÅÞ½bJbX¹Ÿî§je³sƒG&dÖ±1†^tµ@uö62C4ÑŽüäKš2Od°ÌGuaÇ8î‰WåN1ÿ¼_,É­[ËÎÓmâî.d©Çé]?-·sÞîKµð
+Y %ù"ºþî_ý0ÙèØ“³LÆ_fÁø‘±Æ8’3l™™¦“ˆ™ýaOþšQ†{×L!{S<;æþÎïfû¯#ÇJ5§)¤$ÐðÔÆ\gW¬f´t%Ù©¹òZæà<VйjQ¬RBöuçò…Ϲ›«àG1ˤv£Œ%Ÿgé…ú„áÈ”
+hì1DRSä[¨Ãš\Pb—F . %öRüoø!fÎçr×b•‡tăfO÷:A)>¶¸îuÕÈx×S~>ܪÅ;Aùs/%¡@]‹tô¼çƒùî‘›ðßU©æ;ÃfÊhš+ÐÝ©`é”À$yJl>*?é­±&™Ãk"¡ö5¿ºñŸ(ý^ɽrXt‘Ú˜1É™Að&z@_‡"ÍA…¡û…H>X1ãöѨ$NÎÉåNziÅrj–‡ç<7i›v–!ùE:9e×ôzmRBÍÚP"«8¤ÒæöD°å<¢Hr|"
+1f ;
+eˆ)P1¯C,wP ~…è$_]º¾x¿ö&óyøÑñõCÄåq÷ñ
+ýÔ3{¨¦âòsEÜæësfüó¬Ü®Ño
+^™Î4~ç ìqvÔñ(e×¥8t¼F\×îý¾*Œïul´º-u7 7f‚\w“ˆ¾‡*yº¸hY©1qÙPÓû Д·ìç&´'86ÿœÜRì*÷ÍÛÎzˆýPfToPID±×}9|h«OÁþnŽçÈ­çí=›¡·öeF‡Ûò¦Ê‘½îálMiÂì<|®}_|ßpª/%Ôkn3È!µ+ÅÎÅÔ.ÛZ%ë¯_Oçð
+yïui4¼=¸|=µ9Ÿúuïîšï|'›Gºp[}š–¿t²Œ»8°Èþ~ˆ¿õ?
+ÙÎÅö÷Ó<8-ÖõÒv~tŸ|Å (†á`šÈßݶ‰mZ­^Þ¾ÄÏ¿GØ£ö¹zÝ}ÖvùÊÉ[Mí_ûǾª¨‡tœ3wçV¸vÂÜ§æ—Íã1~nØ¢‰¯¹Ì*¿Z«©ÎÒYž–‹ƒ“¾ü¶â¡Cß4¢Ìqöäßûë¸Ûß
+µÂ: ÖJ¤µ! ëoß¬íøø¸×_n´iòþõ4‰îÁ½SB½OÂg Å¥Eu+½–ƒmÜéŽ7s­Û wµê¦íãRxi—…^¶r6Èù¹”0ó¢±Z•}É5™¨z1€só $´Ónuàè;³¼þ‹âoË~O¤¨Å³Œê÷¾ó$çt;øhÛPò£ÝY þ ÍèÏ£„{ø¾(äòNí5f6-çÝ^c¨).÷^[H/Ì´ó²ï2/±kתV7³^XfÍt˜3–#K2ÐØL»ÒÔ×ûfë8qÉÂ~’$[ͧ¯«ß
+@²s(tͱ€& ƒDÙêmRóÍk·+×ïœp¬.àŒcíjï»Y#‡xEcoľôZi+Æ×E³‰ëÆ&½kO©V»?ñÎ)US«þì»§÷ºÝŒÝéi˜ Ü×°uj•Ϩ­)+}PÖ,â(ëÂsýÅJY“¡ý ª€Š(³QîǬ+ðçWü–¾‚ÏýŽº¬›÷½}emÛE@ïÖäE #°óGRfó-æŒî!¬èÇKŸä¯ÎXϘ’Ê,z“!È—á­¼ìAvv»L¥‚Œtð.Cép_:Òƨ?„tØÌV2yŸ/dò¤Ï ƒ{ NH*j¶ú×aó–÷_G¹à9=¾ ¶j‡â«ÕvæãÚxƒ/¼ÕRñŠÁ<òûsÁx(Œ÷{Eërw\Ð.
+ÁŒ«Ë7šj° Ÿ˜òN>Ì$‰ZoÅ!\g#ã’, Æåêzpt7bNk/EeßµEÅnZ¿áûi PÍbŸ•œÚ¼”x;{ÂòŸÌÑó¢ü¡xŠ»º±›-(«L]°l]8-ö¿Íð’öÖá<Mj×A3«>ŠûÒ¨ÕãÊVyðrje‰¢Ïªú1ûª¯T_˜î¨P%=aöx-…O­ûÕ±èëÔ¼*„ù
+ÝlNÂgQ.Ð_:e÷¦—0 j$8OV‹›CLòû®›r|ÏÇm£/›šŸpIJÎv$4Dq0ª+CÙb ±vµÉzz®;;©<KI¦ ˜l£ÌÍGri“zÃU_©’RÇGº%¼ L‹|ø©4>+Œ“ñ¶à0GK˜ëÖ>ÔÇ™O ¶_Œ+«Ô|ß_–ßÛŽŸ-/88 -[m9~ˆ&Q¤ 6U-äë>Y`˜ñ½Àת6œ”ê­VšgÚ,¯šmiËó~q¯ÂEX*ôR™/³yß1‡ùB©´Ë]>u:w9á“3ÏŒsãWz”+Ùˆú?Äßú,}~÷´D|›æVÙ³ºÊ³®\8Žw}Äp×¼“]´ZvgÖ8®§Tö 3!¬V‹œtµñtJå»n”±ÀâŠ]é<S”~ˆB6™LòEd2ÏUc›eh眱†’aóF9½Ð>Bš«¬ô'ŽSôÚšóçÝawýß0o ß·©âÒúîrÿQøCô‘õeÃk,¹hò^zÊ~sT«M;ÛJâXçòZÃ’P`ã¢Xa@!ûJ¡ùÂ%³‘ã§2£›Ï°¿’æ
+nƒîTünJ¨ßJäЕFÆémñ÷A*½e™ÌQœB®WË@L«¿ÁH—
+Á¸9ë¸òè;aÓÙ¯ù2…-X§ôZHf†å÷—„|Û,ÒQÞÎÖ{õ=#¢½_gÞj—aÓòþ\j.E´Ÿ"ŸØ•¢Á#"•ö'!‡Âˆ"UÉà• [Ř‚ÒA­I¤w­…'ÇŽ
+×lÊׂ¿øfGÿ§Ç…êÉAq¶û¯­ÆšrÈ´VYÃR—Ï
+…ìÃC]¤œyì'Bz¡½3JJ¨h§ÆyD*ÜR%‚Í|‚_÷Ã)^¡¿ï˜\w$·ÒájMb!“ f%„«ˆëá£3TK¢«Êo
+VòYˆ‡Ÿ|ÔÆâšI>ãVb%$ …×Ù¥épÕ÷§H>ûºñÅ,ù¼ús€Cû_üŠÖ>Y€®k€z=(t=ªCµSj]‰
+$Ý/~Û’>µ‚â§C ˆë Xö1J ’ @ðïÃ_2¼iÓøïíóú_,RŸ¯“M~¹Mp{¼ù Ø–oPù…4úiåVÖÊ
+@»u ùù $ò
+À©À{㬧
+0‡ ªuÚ
+ ›9@:»  á›·}ˆ µè)¿Ö#¾W¹)̓Ûäµ÷ÞÚÅ%ÐCrÒ]bŸ³ó'ÌÆ Œ÷#àò›‡oÖÉA¯>³ÞlÅ_äÍÒ)ÛÁâs“­ *vvÒ¼p©Î­«–1Mƒwõ™¥jcc¡—•/ä
+-Ù8Q:´EC¢ÍÃAT(‹•Ô´#*eT½!‰^ó=½RÜû¢û  ¡Läod©†ÌO~wxÏè:yí¦‡Ês»Èçѵ+—YÿÝžGumhÚgÌ2z a£ÃU›§]²ýë¸Ê½ãÑó²ý —%¥tnLQ¦¼.žÐ ®õC„Ûö'ÛìÆb§ÌA¬ä~ˆ¹Ò ˜Ý÷ElÕý € fY8KW¯†!'îöx3ºxßÝ’b¼]0§hí¬v`©,÷i{²X±feûÐ_°£7Õ½Jn¡]Þóý¸V¬#»ëGCTá_ò>U$$É|fÅ|úXë‡ûMG¸¯£Þs–½&½~ôšé€íÍ%Yê=—#Q`޲ á ó?Äß:ô¢yæ9óÜŸ>÷Ì{ì/[öî4¼ð¶ÁSãpybÛ;õHk^oTg êNÓ=a4)2À3X3jÝã£âÐÚE¦›ÙXôý+´Ø Œè”Ûýbï¥t›ÝUª+uQùhwºçέӽõζPƒ¿k”ûÝv¯Ðë.GY¾ ¶…(~“ŽÐç
+ûÑb‘Wï~”R®ã¤2uþ€U<-Ì­¶ìV®myÝ©þ°øˆm×K~@'Ý1Áø1HÚÿàÏà nõ6òõ?¦Ü.”0Zkí«žV'ómd3âÖzNeV¤'ZƒaÆõg½rÔ$UIuo3ihÈ«Aè’û~<œ{`¦Ü»¨Št¨qŠnŸÐw•;Sù^+D»zkäέɎ-·&óÇ 5_NÅ£.à^»e‚ÿ °-ßâ•6ÿésèâFì<8É÷8\ŒÅ[`ÙtÃ5Kg¯‹« Ÿ¸š(Ç“‡MÙË¢ThmµA­T6{mz|závöƒNÈKÏô Þ%?EÉÖ­ð.5ãjžoÖGà ;¹;¿qEv¾zôÙùô*°Ïé¡Û4Üu»ÝÜ?ˆV†Mœ€–}í/~'Þ"ø)ZÙÌe^·ô—ž&×In£‚ÒÓmðÜcHÉ%JVŽDEœì÷í¾µëÉ‚‘“®Àæú5qÚÑñ¹I©xoÕ‚Ê&‘k|Fu®Á_—ã¦wܺ¬Þ³°_ê=5êÕ·ª×i´§ÝXkî\=¹€Á}ýp¬
+~_ù~hW‡+ßDŸØQW GÍùR‘ôÔRÎ÷š¾xKÁ ®ÉiaynÀ1Ö#jt§#M™Ïç- #eÑšY¸<Èæ.l»Xn¦©÷ :ËH•³Â¤ãÉžI?‚\íÔ¬ukÊîÕrík§æIÇÐúºõ~žçÁ^3¹xÓÙ͕Rp§Ù­º[Ïóz˜¢Rs±ú˜ÂêV[ o_z¤!Ù÷2|ar`¸(ouZf¤*Mû,LY~Ê­=«ãÕzçþC0²©“µÑâX­^Ñ¢XÕüd[ÕvåLåv3;•Öà˱E·+UóÃUª½kë]ÖSù‰—CµZvkpšzmç0~2ÏÍ0UÌÛ?„8m Ó¯S^ݰÓvÅVÜ}𸶞ÃÒ“m"ëÓØõÙNý(³
+sR¹Yí¬w6µbl\ª•äð©ÌÚébù¹ôÊs•_•ç}3UÓ¸Yâœ-÷C”VãŠë犩Ðü»Ú•y¬¤št›×/S߄ףfœ¨í°¯Öât0š Ø2˜ðê)§6Ác¦6RqW¯»þy[—U&¬gÙ|V2…ö)WÓ¼QŠÅ^åÑfÕJc+ZåÖÁ8”Ö•Ç£„™,]H¯U$²Q8L7ŸÂ!W¯è~•Ë»o‚ËË’ßÊg3Ûæ?Xïš;Ø/vuãŸg½9æjw¸Ìî‚îRœjS³çèW½v‘a³-”úõ,qk“d¾¨Þ¹ã¾ÊøxX™* ¼äÚTÙ %ü¢×‹ýÖ±W8®ïÃüéBYù|ù{þXn¼><s¥.›Ëê—k7[]ì×Ùªþ¦3÷ ÊdîNžÉÌ¢ža’ó%¸šX®àM& n7"²Ã•8µm«'HÑ´­×ñ»¥Jƒ¯ÅZZ·Â–H¾Ìç¥UikÏÅýz~.Ò¹Ó£mÞ|¿P9-xç³Ó7QË<ø<—NÔ¦”n™A¯ÛÏâ+åÌ©BŠ|[}ê°¶]ê0Ûç(‘¸A¯¤—}™ìW¥`®Ö­“^,(ûQWž­Åˆðíž 'wájc¯çÅZÙà;¥O¼hSNvPÈ>^00—U®‚ºN–!÷n†M¯‚"ÍåÍ8åæ›:Ì ‚¢ßNŽTøˆ!‚]A Š)mŠ_½”‡O”=ŠWòý:]E3ôÁ"Hÿ=ØZM¾Ÿ‰—»ñ¢'¬°ç‹Zc ×åjíYëDdsç3¨gîa±†˜ærâî”X5%Ôr%²ˆNzKÄ$‚ÍÝ&Ô¾Å'"rÄ ¨¥!w„cÏ7ËŒ WOw!ë»o®£X>pÍ’€p-Î…XÜþºœ?I›½=ç…óÓP˜6h©©T¬y­[8r›Õ§z%Ýz„p£±pƦȸΞýfÈ‚¶µsx%e¶0&;àP¶Øà®ZèAôdˆ•ñ^"\­ýÍÚc¨µ™‚¸³Pëbµòð?0hæX»ôg·í«ù
+u¿YÖ5IÔà+
+}“W#C‹¯ƒ<ˤwåÚ­¶yù£ÞaÃeßuÌE†U6ó³_t­p\Í»>gϾ‹€ß¦ ¤h»L;Ò–Ó…‘/Oa~>IWïM±ô‚–žìø\¼7Æjxª‹ú¦¢†ÕYéü·Ïàó:Quòz¼Ò×`ÚTou¸Ÿ\9C9x­M´+ßlb“,åÒinI7É œnÝfϹiÒgÖœ÷OF[®†Ó®z¿Š)Ÿä´!0ãqžÔpÀëjÅ{û£ûì•ÍzRcT'Ûµâ[T‡ÏCµ4œ‹™Â?
+]ÞUoŸìuŸçðu¸òv¡ Kœ• ³(Jýä°–RÇ
+ƒ‘ZÎþ@îïÇä5û\Fi,ô°EèîÏåæÞ©Sûͦ=j-W©!\zæ½ìu^½¥‘Ù{rÎÄ@ªëÒ˜èjãž)+÷†ÇØ<™CβÖJGW]™"™«”F0ì<Õ¿Õ²»Á(™P»c/A®EÁh“eÁ¨|
+B$ay!â’ì?ø34£¥pyüÁt¾¾›åq§3©Ýæ]57«=G.ù·±˜×ØõÉl‰Ñê´.wJí¢ßÚj̵„QsüѬcÈ»¥¸”¤Bn'æ< Š»çC¸×þcw¢¤(“=€þ ê!P÷ee÷äýoZßÌôܾó¸?¬*-[’çuÈ^õ8úýËÓ}znè}Ú
+ž}z1©öíwØè'Õc­¿—åQ]—þ"AÖÝÜ n›ÑÞÎv|Ö¶ë.÷Þ,5ÁsÊ;ˆp^1L*"@&>[‹òS….<¾QM}Ö”®BõË}y5 †Rþ*ŽÅàh
+ZúXòmÍÙr]ɹ÷?¨žö3™E™ÍÅ¢Ç3]µÇ÷ä{g‰ro3Sëlî>¯±l:«üÀ¬W˜”þ"ŽCô"!½G˜½BûÍuÙ <—ÙmÜ{‹ænbéÛÌÐæÞÓåÍ t³×úʘE‚ð¶Ì5žÌ0ä&¼¨få;ƒnó1åÌ“g¨ÞpÓïü™…#&îå/Bž)íÍ­Ô`™Vê ­äsEúÀ³5ú@f¸aËÌöDÿâñ’KïÓx–Þ¶ÅÑ&ü×]+,æÊ·k¯t9'¸Î|ºm’ÚD#»‹q»<
+UK×^#o‚Á2º‰kÃQv‰ W†eùÎ8Ie£ö—;Íd9¢»ì‰íîž)_]Ûã0Õ<Û?09k~DrÖ®ÉY)É“³l½JÞ±V…jbÅ"5î¡…¿¸3Üû½·1îäk‚z®ŽÜ6VG²#ç]Iƒ¸)ê1u…yì@–4‚ýÏE’rçxîÙ¡ípMΊQbÀÌ®ƒ>ÜpäÞö8ԙѰkÓÚ ½¥ÚPõFvÞx† ÒqO¼’ˆ;Ð;µ‚;h½ŠÛI±‚'(Z"$)üÀ¿‹?\vÊø¹Céä°6+L¸DV·Ýbâ#+Úvú>M5<ã*²`…¢T–RcX³z[a–¥<¾%2ƒ%’+õËk‹-ÅžCî¦Çz{DMͶAâ­ÒŠ ªÕÞk2Ÿî 2®táwƒëøv+èøÃÜá£J¹ÃPÀ
+*usWþ/ŽØæróOçÍÁs?½}‚p½ÌÉìr9ÝÁ×ùËÓѺˆÉb‹¨÷y¬+œƒÌœW }î>Ù¢ìe™SÃ(Ó“c¿EÍ®}š$”*OГÆgGÕE—“aG”‡/LÖVE¬BõAfÑ>Aìª}<t²mgËíZP,¶kv®Ð® ô/v´s^ã•áÁ-"»Õù¤ë™Ÿ©Û“feh(½ØÔ†ÇO8äïʲÃM—ínšú3çÞkIßxô@ERýE¾ÕvŽøèí2‘QªmžT™nÞhHÒ¢k`Õ³nk/çÞÒ?Üj/5¢fN3•´Ù'…ÆëAäÖ¨ˆ6¬~î~à?¯ýÆxsô²7æ´Ð«½ÀÌsO¯5O %]Ýt©2ÎŽxKoñìŽí346pIbÙ•úDï½SñµXuq®¨ºb×yvÂÅ!‡—‡VϽAï¢ÝÊz­™ÜPšÏÓÐnÄ7ïР;hÚ€r[¬¾ô×F}9ñãzÖ ÑÚº:AjŸ…kUÏýÅŠÚŒNö‰^…æ{nôÃ%稶T½ iߥ@ªp‡ý|ê hÌ\Döt°1é´‘J· ó"·ºn5ßèbÛ´®Ÿ{ãÓÃ2 7«”ÕïÔû×÷ æ÷H½–?nƒêN#ÞÕÒçZ¯£öW£Ã­2º p¥JsåÓÝΖ5YÎü…[TáãÜ
+º[àxäûs¹¸Ÿêü›[Žúê!ÐXÑ8g¥VOˆz“\œê5ÿõíó×D¸ÃÕ
+g{R•©»S­±°rÜ+÷Šj\2å+R(—'€—-_(͢Ƽøx‡¢Uïç
+É" KûCZ»g 4ÌæÓùÃü»° °› ŒºÒžc![þŒÏ*6_"õÊæY]ºmÝ!^sS­†v©Òh¢½²ÃRÔÇ¥®5™ùõªð™Þ÷…^®ðȯx,korz!†rMÙ DZŒ®HYl—àc©8„eå+êb™ƒ•ú<ûÆ
+'½Vn nÅýnÂy¤{®£…÷»…Èt†øá£—aàz.êåZù]?Û)»|†¨O‡Ý«€ó¢±Æ`]!ºSú]ÝbÝUˆÆ©íwšð=2ß±v|Àýý ?:¶ˆ³¾8P5Êr-©sðÍ~“Š&xU¶¬Fñ5ñ‹y¸€Àˆ4Êðq9®å.›f9{ßÞ ™¹²,At‹û2©ì& ì
+°çâXTêâ[ONñG_gbCªï´ßpðl}i¿|ñø ö¿øW„Çf”d²ø#žßù,æM«ªæÀ>^Wâô“%¾õdãâTšŽã;;qÚéîã´¢ƒÓ¦rƒâ´ú­1WùÀÁã´›ôãïÈ€Éðôâ,S8¾‘šy†­y9:v—X¤¼gXT[ÈØã"ˆ Mþ.º Åvã´ƒýá_1bÔ=ÉHƒ/°H
+î^ÿ%ÎNÃgøzŸò¢¾ˆN…Iø˜œ.Ÿ{g oV®Õ¹&DÒ¹ÒÛSç
+ 7
+.”“lÓÿ³~÷“Œ×V“L´xoR~óÚ¾øÃsãÏ*¦¥‘6_ k²ª>ÚÌ»G™ˆºwŸÁíVå9dŒë§´ñà‹—TÞg~Õ-€»>Ó>œ+QëÐཾêû¶DÕBãX¨„Ý9ò_$Wº'ÙÆ ù€MôLrð ÷-/W“lœÁ“l@roá Ë?ðS9ú“¨9t÷'bnn14=\SØŒ®=jñ¹¬–zArÇêYà/­Óν§r6¾¬ÆµØOççkˆ÷²¹mŠ"õm¶x¬ƒÑ¼æûg®êû> ®ýÍ6Ò4)ü!ÉæMžµ¼n÷$7"“$·_!€O5ÉüUÙ¸äãA`½[R}ŠWVÇç`7›ŸdN^UFÜZ}Ûw ¸öÃyŸÉíâYØQi¿¾]²#|Ëæ6\°æ“q€„¢¿ ­K²V±Wi5!¶ïîèeoÆ K^'ÖD-ºñùƒü!Éò+\îý
+_„Æ,>އ$.fÈÄ·«©‹ù«Âm\YŸé¦±ºuËK/jf¯+#祵Ažî'_ɸL¯^p¼5Ýrr‰ÌØ<sTÛ£y°öךŸžHqÞ<óæ 9¢_#*ªàþb{ú83 Ð}é–À"ÿxÎÕè¶„ƒð"œ*Á%¨ˆÇà vK–yb¹†nÔbÜ\Ý…€òÌׄ_RSVu{\ËpLѵ}³ò;©\¥}îi)ÕFv~ºåy#vÌie+јóŒî#ûžQ0Ÿ~ü:u…"<í ¬¾š^¡¿
+$#œryž¿$•»Gïùàyùøýø¹²iPJY¶8m°ÙC_a³GñȲå4fÙìóÅ.l:C"6•ÒÇ~àhúœžÏ£Ž¼Û!pÉ]ß·Ô|é5—S§Ô¨­ö¤?42Fé»vMœ(5ÉÔÀòÕÜËýÕ î ­,ŽdKÐäÉ·Ñ[Ÿëfr?±]pUö!ÃõØÜØ?öP\sâ¯2Å <dŠzýÀˆÛõûË“ ’ ê¡õðñåþ‡
+çc[‚ü@ºÝœ••Á,——ç³Å8óÒÌE¿$þÀúÎ~’ãñ"h˜ü+1UöF½yzü(x ¥‰•Îg²Àß–ùg·Î€Œ\¦Ï\L‘…G‚ÖË ’ÔØqH,ÿEiK¤D5ØG5º-¥™3Ї¡4E¨Ójò +Ñìþ7/ƒÃO`­7—ãÊñ s:wJ›‰1â­6[}θ髸Jäz´œ²’¼^/R±4Äc ^„ÈÛ¾Ål ç6q.õ—íU“\À9+ì…Sð]cq“j’ƒ€œNͽ²']–À›{Ÿ0EñE˜4}?éÙÄû.Cêrüη½ug;v\¿™.c—5ŒÔ©z¸^ê<ä™;è­á^ÜôÅ“™›W»åñ7´$îëÞ÷ˆí‡Ý´]¤'^„2#ï©6­ž)†jÉ5‘ìðÍé·ÛÃW¸½^Üp(ŸE»¬ä2]–=mºl•Žº Q}t—]âÞ]V[·¿8NótèÃÌ?c~Ÿ!ÌA¾8¡Í†2Ö37ò¬ù‹ ùSãÍBžä•®8øX–Ù_õœ5ëÖ±'e´ˆÅ|J7<¥Ú‹ZìÎ
+]‚´Êœ™ÖÔnÞs;¼>>aÛè™Á†s—À†Úi‰ 9Š÷È,°bX¹abZÿ›0K;þÚ vžëÛøÒºOUË` OŸœÔƒ¬’㔓¤Ì‹àÞ¿ÖG†xƒ-˜Õ“‘ÛwduiA_”†OÝ'ÓL>G¥ˆˆÇå žÎ
+E<«e]dZ ;µ,aeš·•È ["ÿi“s§5¾CN«Éª÷æ dJÍÛ¦°Š—¿*Twã1‘ îb¶ZÍ.M;©Ï§Å´?Ѻ+I’Rf…¤ãýõ|Z§'w%_Ú¬I·#C$KL!˜œbá¿Øt7’{ì¢ÇݳS¼†lÔ¾—Úc­«S4«þ7;±»nXÃÆ«A–ÞÍ™«XõD³/uû#^êô´s®ÓRýo~Ÿ¹[ÙÌ_ØÜ™ñݽ1noÏêh0KEqB ½Áòžï0'³úÉ$É·ÐÎN4ì`l똂i¬†LFí±¸6ÚÍ6²nMÍ&Èdc*jââ ä_û ZO«)YïM(¹¶z¬œ‚W¯Õ
+½å¡šÎN•G~àÊŽèþb9Xí‚E‹=£ß.Z“q…™*Y—ÅË‘pÐbDõªÌ´M.*ór·€làÖ]ÈgêË­™­çÚ»RmýÌwjÜzÄÕPn?þk¨Öq«[rV‹nø¨ìåJ¶2ªLå“vgË¥¥ëò³/é£ZÒ¹â¸ÔºÚÛ⃙틶º/Îtøv½ƒN]tt¥à˜ ×—\m¨ºK‘cº{¶§ o‚¤ØK½ÖùÖL(eêì–+ûÅ'.µ7!Txß.há‚”h±4™Âb
+”ÁÏóiwãçÓAç‘_Ö<8ßKZX>ë„ÒŒ®%e…rn‚¢0I£HÀmPdbŸ‘ (ÿAþÂLŽörZò¯–j—Þÿ”æ9çãz
+_¥Ij_nwd®Rn¾¨.\Ûè£Ï\>7o¯"NJQ>«Ö^è¦S¨ è¦ÖD¶šD!ÆËÃ{÷¦Áû´áÑÌ s§×+Êiþ0ŸkLrdöFzvZ3‘Š2f^ŸAo¹u†ÞÊ9Óe‹àªü]üa–_Õ ßUg¿¥yîÃbOÙôY"}P]ìToUÉD«»¶™)Ý@Ý”Š¢ÙRâæXÙôš¹KcÐÉ5]‰ÊβJ¬e^!»­¨í,ˆzV€AÔ«[„h8ÇBÔ;?ˆˆJ\ ¢ÒЏ$]x~þbŒmFs¹hcžÜÊRïØ±8p½ÐöŠÆ4Nn6˜»Vª3;v>i>WÞð¾uä´¨gïz€fž‡e²'ò*¡Ëà‚Ct»Ü2`g@4Ööÿ=t•/}«£Ýúà;A¬ûï"hD@4ÕÚ
+[¯"IÇ‹iÏc·‘mžê•Z
+ö9›A¶\/†«íÂ; vô]K+ýi!ÿ¯âíoÈw°úv“ûWÕƒ—ßén Z¦ðÌRÄz~XÍ:D“º
+xX¿µñ‡õKüy‡8…:! µ‹?Fnr×mœhûC»Î%Ž{$ŽÙ~ p%â¸Oˆ
+HÁ;EúÿCÚâ@˜¢¸SšÞXë Lö|k°§k'cô'Í~6ŽïXéò…DZAð€%X©‘ÚqlR`z@ Ê;Žcµ™EÀªÇL´®èf`«„8ùßéìøy§Ë{úNÃz§\6† bîoR{vŒÓË È Ð vê£ÄŸ>º?Uö'gDäq2ùž;âr'CdöÝ–¡à|¾?·8‘Ú1@ÉVEÀ|Fn
+&!Ñùô½neö_v_¶_‚/þ’»
+£¶¸\CÔ°Bšl*Aq"
+k=ï0ž…¼;K¨ë’:[‰à«ÜÆ\'³­N3§¥¼¦LC:·,æ'¢ÕÆá êjÇÕV/Á^P[ÐIS:ðc1šÓP Ûýú]J#XZŠ,+±¥¦#e£Eøe÷ô.¾ ÿËæInïoc²:Õ"f¾Ý”âÑFë×yï1Ë ´ë8èïX§[³?os½±aEÂrÆLz{}s.Ü&[÷GÄ&§im ô-U½6j(F±,˜ìË ÅÈ2Ôafkó›Zg1x~2¢hm±(–°/Öv_±(âþ—Í—õ*]n=ðöÏô0ýƒ!~­Ûã¿të ãðo³³¨‚,{Þ^]hcÁ Ê, ]ÈÝ7“’œ´31{ªzÔ‡ãÒFGÖ¾T•?ƒIžÑRîù‡èõ=‹‡#ŒêÝãÌŸ†zÂן6wµZ3À1à믧ÿeóeýeõ‡w‰\®ˆŒ.C§âNý*=—½·ûàÝÓ`‡°‡Í± W6¨×®Îóo}LX†Æøð™{Úx.‡*Ö&n£÷åÏ2ÊI+Ò-H°ë7‡ù¹KŠ%ÝUÅTù†¸°8Ý;ïÒxºâ®>èú +Í‚/›±[®¿¬~àïÂûCt#²Á9T¯î.wëMÖ·‘,/Ýã–w
+
+ÌXM«Ð6¨ T˜Î(¦Bš« z¬5Ÿ¬£<õÖvD_g¹·ÜFBêé°€  Ÿ,–ëTE¨•šm¾‰Ô+bòÀìƒ JñÙÏò±n_©°=DרÚ 0ÇÚc?sño‘ý NuÆr‚í³<^Eoqîbkb#,Žþ§g·—Öt-Ÿ
+¹gÕÇuâʨrNQæWÔAÕëZZ‡Þ~(ò£›(÷˜XP ,ÇOÚå7«e›ƒy!&ûv.@›‘ÕÙl[õR¹þÀŒŸ– Œà5FLÉ­?ʵ+^o w
+’Ž=»@ŒqhÖä~îô*lOd·h“¤[Q;Ñ%Ž¾ì¼©Ä wXÜðpê5Nü•ÈGÜìqKós€ôíЮ°ËͼÓ[ï–ƒš=jL!‰]ZîQg긾 Ôчeê¸Ø,éòûµ¢Ëçë’–«
+Ù?äG™æ»%ùp‰îš}Mº–Ðò+.ª}öƒwñ|í`?îCé÷ ²¹×¤ÔCor‹)e†®\ç#ª~X[¤^/ÈÖ5Èi'vä­åÆ#[3Ç%'¡çüÅ9|UÖ[þ65Vóêr좪,¸.ýÀ†ó÷&‡j§©û3,Ã!•Jä "*SŸÆóHâ1æ¦s8~vú)äýÌÛ<²ð]yôò§Á‡)ÝX”®é:ÕØÒ$Ù^q"Ñ f¾(Æ;œ\M ¸ûáƒî‡ì/pR3<œä5·,Ýù‹ÃÃÔ]_™÷ jÎ&öþ†jóî£;œúsŒÑn»¤!¯Î>"Öœ]ÂÏðKž›‘Ö > @O©¿´%…]‚Ñ Â’ gÔ–>Zø…ºLk/ò®¢ñÔ‹%<VŠNÄ ÛÛ«zg]½ûÁeœt}Ávcu2ïv,ãt½!
+ÒŒŠw<»Ü>»Èé–éãv—BªX¸< ˜<ªLÚÇsgÑ®7ÙMk2“ÍûË~5;äil®Y·7Ü åFmJ§;7j,ÞײÁȪ®[m³º.Vÿæö˜fÛ5Þ¶Lôõ=ö©op¶H»^ŽäM?ÓdƒV\ îÇÂYŠÚ' >6Iôh¼"8mX+©Ü ~§þ©çuçÊ(uÆqæõ óX×úkôôW}”Ž«üu_®ì°])Ííqù@âÛò$²åÑžTÊÕîÐ,Î]£tò€Eiög÷(¸ó¹Ú2gèb4Ñ¢ÒX’}kÖÿ§ìÍqã:hÇ&é$O@‡YŠ,ëÑxOüc N aùX/åÚ4û,×\)]za½4©ÈRó4ŠwË׋3¡ä;…Ñ©ð|d2…¹¡·
+^æäâåím˜ÉÛÓhaò6˘yêU5òÔ&3û k&M¿ƒçÇ·z¤ŽòS›qÂô7壯ç´Jd˜ŒU_¯¸£ù[5 ¨C¹^¶"hh¿‹î`dsîÜ~,'¿x‘<Ýjûâ¼ïJY FÊ>3¸´vQ¸´áéÜaÑ™çyäj%©ŸSÊEî€}ÉÂó/Ö Aç Ïîºò-¥ÿ¾ýC2 sÐÂWn§Ø¾TPƒFÉgµØ”Î¥Ö6 óŽÝò¿óÚÀåÆ+Ì^°×9ÛÜ@Ìo¥™YC/BOwÛ‚ær……ˆÎLû!¢_[vˆP¦ˆ˜Eˆ°ˆ5Dø"h³סÿ¿ÐMËPRÜTËÞÏwzL3ãvñu~Rý>ê„ëËdZ˜Jå>½»z|BWy‡pq—«Ý®[è…òð˼#àb¸¡oˆøð9ˆÌï«I$DŽ/2DŠ‘×ê"“["?N¢
+s¢ZÓ¢˜]l %£;€ý…·£‰Tác™ËTO}fì|("ûJÛí–áê¹Ä…ÊZz~(©yGÚ>Ø>ffÏí"} QúQ¹Îí»–Šúl2Õy¡Õ-–!j4ëBÔ
+á!*r ]íÀпÚÎÇßéaß™
+ÿ«J‰à-Ôåõi×ÎÍÔЇ¹4Ú=‚5(¼Ý.åª5Ÿ‡ÑÒ½XJól™zÂ{(¸e¯cî‘Äv‘óä¶tòü?J· W
+àÞ9(¯
+Wçnó]ð¿VàÀ›…ãóg¿kù%ÛÏò]îA ²µˆ?9~'|Õú3ccðî Âj`ÔAp `‡€õô[^v@p˜PŽßµ Vàíƒ(áÕÀg8¡Xµb;ŸéÅÔN›¼—åëêÝ;µÎï¬&¾^ëýü‹ßü!N‹è:N /æ}û;–qrjÏ@´$ØÁy}Çîæ
+¢M> Ðap{§ßâ
+Vl¯ðU !ÔöÍŠ½ãk½ï]_huð|Šcá…wŽ*•yåQo„ø}2ëi·GÎ_^_äçpµš™ã•ø|~9ýòÿyýKœ^ZK@ΊSÿaÄ)¸)‚_6ØÕ?þxcü髯øS=!ñ©7bèQÁß¹w}¡ikø²ô8Úo83ªæe÷¡Iêæ~4Êóð®«öùÞ®Ÿ7CA×w¶R¼ZkÐCd†±z†‰šsÜ•°ÝáÐMƒCÕ¿øû³øûqÁÞü¸mÓ. µH ZÎH 2¾õd¤v0èØÅ©½¼Ä)}ßHAÏ<%¾Ÿœ×êý¶ívnÑd®ï É_©Vtq¡~ɼ5ë¼Z8ÞN½à$°ÛãqwJž‡Ã=j,_ ï+havó\ÅÜÚ z,1È ²æ~é¯qÏý_$)V´^¡zE² °Wà{Á6fƒõé)O“Óý~^¯ñ¾ý¾,ÇÈ,Î\§Y:óÍæáx,ãû˵Р¥î^Hoº#Da±µ÷ÊjK»c°|GQ£›°ÏíoÔ¦h7´uÅ.Wu¿êzzþm{­Ýùàzù]ü’d2óe’)qf’iÓÓ$£/mÀ}÷WëM¤IÃÅ„WöyÃã«ã¨Žì÷íêó¾{¥ñgël^è6SÈU¸ƒb>oU¨ÍöÓâ6¥!ÿÀëCVÖ×Un²Xi[{½j ï'O?Ö¯=ë j.œŒ.8v?j.lØ,X6¬dç‘dÛq©F’IÂÅ«L%ú¬Àq¹QÅpeü¬p<”ZBhv’ÉÖå–’Ï…›Ý³øX+NdªˆwGJe£KÍ¥é57†»}wá%'-èºÓ“ÛÉV[{0®Üþ]G…>;øçÇÂ|6¯É¶mŽO¢õen65Úü‹$³]/þ£]7®KÛOáçÒ;´Š¯î~zµwÔ¦MúW×ÇC×Xµhr½|ÁääûqÚ}¸P®ûqX¾‡Ø\+-‚5ÕZˆœBY¡£ ¬JÆÍÕÁjf^‚›gN†“q?˜Ñ©3äŒho´)8ÍSÙο˜ú2pfø¿1&YYÞ‰)?>ß1|E(±õfÝF°AwÕÍQZ+–zK‹ª.ÓXÙNîéú‹­c­½9~ÌO¶ò™×*lêÝYÅxŒg-£s·©xfU–Ü4юꔾ¥ Ýë4·:ûr_“ ÕkŽ·ìMÏí¹¶—Œ/3mÔÌL¿èH²Dê>ëj\sI^Û›¨Ýß
+|s@k¹nLÊϰŽ×y^pžfÅ…„"S«z@\ó&<#’Ÿg£»y>g¤{L§nöŠN3ĵªÃÌ›ðæƒo“Üp\jWÀêMÖSOñò¢j[PneLü•¶º0GQ}<û¢™üE’µš«‡‘KÌÓ¡»RvîŠeý²ÆwWÓxZ[ƹ#êfÖDÞ.dˆ¦¥Ø2kÞ«ªb˜wÕœ-NêꞺ79ÔWGö:ñ·×¤iCã2ÒÈk
+Ý®«g!”Û‚(ít¤ŽÌ޳ßV²—”••>ûI_r•ÂLêÕ²ú—É—ñíß§ /Á¿9Skv˜Ê˜4Ú[OzÕÎ2~ jκ'‹°xÊÎ/Ô¶lb¯1‹ÃâpºÔŠºÎa9{"Ö’ÍX.^š
+ooêÜC•;»Ê)˜µ*pwÕÙëây`$÷zJY 6†ƒy?"D’ˆ‚{¥Ä‚<lýËï+M–KõÏc¬Ú×þz4§é]ô+•ã½ðOÇeÏÍÚ"L* µœ_|.1ž¯~~FOulšã6Üd[Ú¨cù☚º7–jKèÊlÙ?æ4Ãnã#¥Ï&*e¯åêkS„(/„“éD(ïTøˆ¯%<Î×>õ büEû¢ò*ÉýÑ•æìóNoOwl)×׆E/ñÜqЩYõG’3^Ø+šAÓ ¬óõjcRÊ0Œv
+IÕûm]1„¶ý,¶´‘âJ^ûv•rÊù5D'ǬXEAö/m^ c–»Âe•k ’;˜…ÑuÐ½ÛƒŽ²^Úu0;æ”/£?<Örªu£I\D~õp Æõ‡îbt<VM´Ã3èê<ô<bBãt¨hÚlo{öáFoò­ÊìfJÞÕu†›h ó5ó$–ŠÊC¨Âƒ¯ jžÓYµñÖœê¿ÜPÅ&–ybjÑ`z8xê…MÀmìËèwë\±X8Õü}‹æ=ë8dv»Vã:¯Ì>2éù 2‹qÕ¨ÆêmÜË+&;nšß—{eS’àëXæÂB,ÔJ¨î±ÃÍ+§§å÷Àx¡¹þ›lUÙÖÁYF•Åžw ÍŒa{fóÂ«Ìæ]zpW×K®G½þHÿEþù½¡­aÊßú¼-¸ùÌȕ逬¤ãêý}Q±Æ.ÅågVNI¬!­³85 О JL” f‚slìenºIƒé^Žý÷óò`?×0e3ѵØÔ!ŒJŽSD–jéŽ.]Ó]ºE<]
+«#Z*tGô0mHôŽ"ÿ‹‹¿ö¬]ŽIG µü>Hø§B¯¤&aPA¥4 KŠiîyD{i=’a`®Kbhß»ÂÑ‹ûüÅOGÜ}y×ÏÍeÑÝõŠMçó›uf×Þ š'Œp±PZzlš?0u¬¾Xª6éŒÉñcí“Ms‰MuÊ’ãõgôE&ϯŒôeø‡sY΃a-øçA‚#DmÖjU$bºž=Šã:²ùÈé?Jh1ÜýÀ¢ìÇOáÔ"þÚŸ4¹‡`Óƒ—l ýD³µ>$M 6ÇqvÛÊ#®Ø#-ÛDD©^?K^ÞZ¸§šèt’>_ˆNÐ4„Ïu¡‡Ï‡;no ƒ?ÃÀ’è¿8tšçéæ\œÉK÷yìJ­Ú7çŸ&®y§ Î£D⮵“pZLv|»2¹q]rHéUê;¯V—õ>D¿çg ©—'ˆ È^KLkNWñâ’Ò&Å©•aŒ‹üuï—»Éi„w™ÆIêBwÎíxÆw²&…b:Þ`%w<|1ìBŠ ˜ÂBH“&k æd¹: §d]'Unæ™ÏîXë
+£äÝÿˆû9Þô{™ÇžŨçoGH¯PÚ4I¹’´2ypÔù‘ŒÈÛû5%±æË&ðÎcƒ/´äÔ]­wg¥å‘|ã±°­ÐÞY­E[¢²Ï¶TDð¶‡í]k
+(*b{ ü‚áº0ö¨°.Ûàò“æ]æ§yqÙQ#‚,Èð,ŸM${e×ÐhÊEfýÓµ÷èLM„mBÞÃOžœý6ñ|k AP†€ÛKOë.7ŽÑÍ~<·Ãõö;Lä®÷vèÂÙÖqÝ­·ê¨Óo^v-³9‘Ò[sÒ¯áÍæY›ÍEOh6Ç8È÷›cŸâÿðû ÁÝäZꢡx#£×Øq“ZUîŒÒL¾ Ñs2ˆgáµW¬íwäCȬp‡óä<—nk|ºpvZìlÖçv'߀l{kƒÞVÂÐ|͇óÖ)1ÖÍ[Æ95Ûø5iN£\g ºNr¹qíƒ^@ï9ͶjI8kNçjN¥ È•ðìñllï?•±ù~ôå©ðœr?°†Ý;)Ÿ ülå~ØMc~£ÅÍw³ç5¶S
+vë¼G‚V³‹_š·÷4iNýg¡‰‰µfãY¨†ùðÅzÜ=ë‹8¿¨¥4Ôzžñ¨®á7ZEÄ~÷®Å—RëmEXÇJ¡!ð•üçÊ»i¡_ÞIÙÿâ·>oMÐk\S°4Q7òÐ&Ö_‹â S”R¦º–Äb¬:ÂÖ÷[ÍûÝùët¯cU×IdŽz¬¢ÃË»²-áŠøk•â|‰—÷ÄsPåq­\Ù-œÒIÎJõÿ)^™f±•‚®¸%®
+sV.<8Œ+Ì®Ù~aÄ
+ÅýÜG…ugÛÚë{h–@ê"«ËôŠ ôZ¸²ŽpEøÂ"ðûÉ!øþ7¿Ó§ÛB[Õ äüÜ@Íwµý³"S.SF®wùVîÕº>3§:ã#~¥tm9ÅéÂ0ÑTËèíf¹=¹¹ŠøÞdO¥ê1«ø'à
+áù‘“;`m;3œ-÷V€Ç͈L{ ¥Ñ "˜}R5ó­'»V"î" Á"ia ‘ÃÒp.Cät+BäríC¤gç rQ!r¾ rþ ¸Aï_eïÜ$évŒ·ºF”›ÏÛú«ÛêíQj£é1Ÿ‰-¼Û<üìø9\ADñãB„Þ]CÄuüS¼%ë^‘<t…HŽÁ
+¬,D¾¡2DUå.D)ˆQ‡Ý¢³Ú¢+äwèj¥ÿ­Žþ×@Ûs¢)ȬïªÓ¹ð[ø¦oÑ
+ǑϲÑêøC´º=IÑ${wt`Û'¸üî2Õþ‰ëŸæÉ ¬…|Õ¯¿µeˆêpʸÔrù†h¨òr«Vÿ=Úöw–iôïimÚö‹ ÚÂ,xÿŽuÑ€hr(ÿá[…{õçÿª(ªŸq;c9ŽEÌ8îµ}ÀæÇlã[°µÿG%´¹H€{ÜÞqÌ2€^áw´í|Ï€òšòï¶ËÛ?ÕX»ðåà?¯þŸ
+oÆŸ\[“õGt!q°˜€óßù`ãžq1Â`TùŽÆÿ_•Ð#Ø ²PßaÁãÀ›6x'±r1µÅKït8n¿—¥[ÿUyóµ®ïƒ×àV¼½iññ? ºÿŸÄŸ™eÉ ˆVÀN]‰ F¤÷ïú¶A˜üp}ÿÖÁ^ —cþÙ©6<›|Çô
+sóÝ m畃õk]¹ì^ƒS|zôþîµ$
+;I/»ñ8ReþzºÆ£¶üø÷Iî½»77çíÿIü¹[Æ·ž¼d¾U{XŠ“ãŠÑúC°oZë·ÿýß:x) Ъï”ױ׺<$ŸA}ØÂv_ˆ¤HSG\×JìÌïÚsïÿ]ç¹ (³µí#èƒ@ň
+"9‰ä¬‚b@1çˆzþ:3{žýì÷ûsÓ=MQU]µÖÝ‹XÏNçÖqýž°øy:Þ8’ë |ë¨t`Ag?Fǃ}¶§ÿ_$ïFßKÞ%ÕJÛZT’¶“êéÄEÒÉЗ†É“.?£ÿºœãöäqogüÌu¥È…‹Uã«çƹœ°{?Þf"w¤Šrûjšv`!ßÙƒÈßgŸËÑ®Íæ[Å›ÕQήw™¹:R’¾B=oÙãÔt\–DEr#Ã9‘¼ëNòv<3mm¥¼ÞHÚÚ3g¦í6:i“{ᣥqÑ­<›YîN—¾9öùÎë0lwÁ}«éVviÁ?àvvñÍzbÑ›è
+k[”Õñ<µV^÷æ//üdI0äfñ(€ùÔȸu)3970¦KIÖ§Z‘Ñþʲ¡~‘¼Ç´?÷¶úñ»ˆi1=÷´´ñC;y3}÷^Ê,íóɾYÇAÁíì£<5Ø.‰êt½×ž«Õ©_8-¯Ëj²$¡fv1Zåùë©Âs^õðy¶2ãbÑ?ʳ8J³WN3¾éªbÅSÈ<\¢úÐEÆgs%Œ±$ÑG;WG*ÃdâÊÿÂhÝ'@ Õôl¦=ft¢Y’¹Âù:ò_(¹ ßÔ­ŸŽËÍ¼ê ¶üêͲŅrm5SzÜiªè{´+i™ÈZù¥É±:ªOP;ÆÆúÅñqQÝÎQ3o8"ÏÝèKa..³ƒÂ
+Uújúåí%“ƒµµÿ…'0¹›O w4îœPNôêLíãÊÞ®×Hy³=i¹®–Jó šÇf §ÔšÖª +Bh>˜45-–¶õÎaøâF÷pÈEóLåW…°5×q%Šfë/ë²Ö¯¸ƒn`\³ æOÞ>² ð^scË]ŸlI]"!Ú‡[þ…g&Suž™&åI²#þ€ûRG'×XŒ@‹7ŒecðŒ‹D{ªŽ¯  ¢t4x7pB±öñcõ—ïé² «}`ζנ.€ï¬
+…Þ¹
+×{™@voE«Õ%µ©õv÷ü¨Ãô£‹ÇEÄVOm7oG¢3GüÖ‚#/eî/žNè\÷3N?LFkݽ±EÎZVfkðœ™Ö·ÉeÒÙQˆN
+áÔY ?à@.|_w
+z
+Óî=[^u)ª|è„Ãê­dˆŒ7ŽÔ’—MlÄm³Æ™O.ªSÎþ½šÏ7ÖöÚ…,˜Êræá™L7Lø¸ðs`ÿâ™Y„Áù¦Uô]ut–À[EgF$—'—»ü=Må8¯ ç²¼¿úÕY³ê;-²×kR·¯ÚfghÚ]o¢H/·V§®T3V? ³ƒS^ywÛ¨ŽrÖNŠÓö,¯¸éU+¢q±%×è!Ϲ~ïî
+: *´öŒ]^Ê
+û0Úè/îPã➀úU_ÎÄK©Òø£ •F¯QãJ“¡¿Û
+ãdûì×^“ï%׸;¨]4÷L£xPL£q·T2I{³ÓZÖþ¤iVs\ƒEßð=y¤?6ò\{ÄÆ—å«ݬ
+îçUE.Ý(yi4T¹² ¤ë%’ÉI˜TÏ Ù©víQ?à÷ðÅÍ-¼á0Ê|/¨è,ÆÈhlŸËhÝM|,¶Ý4ϘzÑÊÙº%iðp Ñ»lÚï†yÖòŒq³ò’žxYCgÖoO×󡚆þi £æõwºsÅÙ“¬­ò/i›CKR]R°¶³œKâ¹
+õÄÎ>s1焈˜K3ÐPbgΑ_œïHßÛ4§‡v¼ªö©IàúȰ=š—Óṵn¨Ö6Þ´ÁDÎJª¯l8‹],¶r&ÎMë%®HUV-Mhµ-‡¶2]y5sCz¹3ÉâÃ]Û¥‚›xîò"vV@±‚ §^š‹…¼¹ØœYOñ …°RBˆ¿ð¿8±7ÚY ¼@œY´A‡@§–§nÉÇÇå{'G\×ÎjšŒ­cf;7»‡óÁ ¢ìKgxz/špaQµýEy³mySš´£qW‚GT¿íÞ¨HìÑUëÞ.-Ê´³ëküx}ayÁlÛÜôYZqÓSJQ|Ù|Að¹bŒ}ñ~G°Ps–¼ßnMQž GÃAû»[ì­ãÍ]È˵ulCãî
+}ØRÖÚd0¨³Õ³ ,ö9X^Ÿ ¤´Û>9 ArJ»Ù|ZbÏ|öZ÷cnœöØsý\\>wLóäê››µ¸
+Wˆ‡«–s[^¯ælys+³å­“þ[¶C~€³ÊÍÄØæÿÂ^½\íE>„…ß6©ál/£·):-oxýèɸ³2î6Ô3¹c_½ eY¥Îò†¶3Ò^›T%d»ÆÚÍÅŒýʺÝ䇚ðRfžiõ¾O¦Üì½Þ±ËÜñÁV˜b‰1ÆAïÓ…Œ¶cwúÒö|Z¤í!I~@л#ƒýÍ/¶ûoÆ«uN?ŸE*ÔJ|³÷pÖ WôÕ‹Õ)’K=#<ûjqdtåM0™JûÅeß>ÝÊÏ6Ö–ÒÌB$U n 66)Œæ/dˆÂçAÎädYê±Ë±:f63cÅÔ^ý+ÝÈn@ªCƒ(Õ|yé Õ!é·ô‰7N^ýR
+3Û¤š(E”G¿ø¿NÌàþšgT˜]ëŽÆCÐ1äl<nÙ…Z*.úr½MtÛks$uuÕ
+ÙáUxONYAÈg ~ªTšÜÜ(Ò\i_Y½TÑ«Zt胤™8ÝX1…m¥ü ‘¨„`¶œˆ¿í\ˆKå þfÎ8þnl›ãÜRÈô/¾˜‰.§™0Œ'/&8Kùf'oK9ëŒ?N:?_Í@ÙÊöúb`ì½PŠ|!‡ï#¾×öÜ¢¡>¹rwP`õ{\gvôž`àщ£ÝÛI¢ºÏ½AÞ¹G¤0"Øam‡¯­'ž#ãÖV8¬Àíüæ"›¼@¬P£X!7nbù{ÐÀÚøÅÚÕ°ñÅ\9¸Ê„¾õ[¡ö¸²>ÕÜ¢®Îײ&…,Oê|ÌÛ—‚1„³ãr* Ùl ÂŒ8 i_/´·,¥ù>uŘ2Õ»™ òAu(’ö|^G>IF.²û!&õ^«¦ÖnèpJ¨}ìwóÃNc÷Ü&ÝRG»‘…6vA«éÇ¿˜!úycô´Å,¿„®0×ÛMN}ù'¥[ ‘®´BN‰}2ç&E–§]’!¶#âo¤‹ˆ«Ýˆ,:ÍâÑåVÁób©‰Íó$ƒ•X¦ÝÔ&²‰nA?ø‡R4k4ŒÌéìå‚É'ö/¶ û›é öƒ6 ûívö)}‡ÿbòX>åðãHôºÑS[0lÞ/QF+ÂæYjêÔBh%ÍáÈìO|—L”±çÏ€Þ\íª½f¡ÆèönPkt¾¢°ŽfǪRi¸»E.ô–Aºï¼ßÌ…©™9®‡¯xW¨MrZËÉ„^Ë)î©:½ÂÕ°:P¤:õ«pujê1ÎnŸÚ
+™| ÏÑpñ6ë3ÅÀ)¤‘R‘j¸ÃBr) ao^*°¢)€‚1Í¿—F¡À¼Ž³œÁ¿1Ö¿èwÈ…Õ¯Šk©–!¨=DFG¥\+·»ÝXD¹ïH>ûˆ1ˆ¬~@ä&±½Ú4ôìÊ®¸QKø±.€ýVʃ¥ÄgëR7…6%#Zƒ’Y¼ERs‹dƒäÔ!Âç*ÜÐÉn²hœ5¦‡g¶&¹hf_lMY?³Æ°—®–Ùõ±f× ¯‘+?{¿Ð×hÃÓƒ‘bõ™4[4B®!ލl«cÛ„bö»#¶Z<V |’j3ëó.Ç=íÓ_k-‰Y ,•VR¶.:p†ÚætnaÀ‚ÃÀfØ:Åà`{¨b€XÒTRC
+Ç@Óè m;“¦ w`@—±aŒ
+
+il‰<³³WU;ÇR?
+Vy¹úŒ³Ȉ~·ìÛ¨…ÿÖm­KÚ³Üþ·tû­ ô+Àtô!ë°e½öÛÛö—=BÔùèÉß7‘áÅÌŸ—m}€%£3ÀRfõ·Wð/ƒÏJžÖÃNž_ý#+ÖÅüçãÈK¾¥è“D(f¾’]ŠËÇÝôÿVBÉù·‚õëW5è=B3ýo20Láî’°t~&Ìêðúo}þñ"ø[¼úK¨ý(ŠÉ¢‘6”M˜´P+…“¶{¥?‘7¶ ‡äcáPN1k¦+ðäåÐMqîÿ)
+þÖ¯vö齄ùÑåñV¢ä1.²¬‘”ïÓ:ƒßóîR»Í›ôð&_'›[É_¯+rrøžý_ø-Ón—rò„.mæ<½ç4#ùôدnýêàôâ¿tp¯ž„¹5ùxKGþÁ/ïò#«Ì»¸+{÷¼7ù„o¥Ž0»®0}u…÷p1ŸÁíRÞÁ³›Å§sû ý€§n‘ìoš<=aKgö=;ý É ÏhÉ+‹É3Ù$ ·ig±+üÏð¯žÂÛ?ø…z»‹'s“Nñ¼j§vÙ~óR ¨³óñçÆx)º¹~ÂfWç½C¢½Â³+Îöc$ гï¹ï5t+¿¤Ö¶4Z;=Û·7ÐÔ¶ÖÛ¶dþE'É«[T“ i·òd:°“6yXÿLXQK˜hлÉ{hpÙâùÙÙye7§n6{:…ìãV
+žöÖæ9Uƒ7mŠÕ´Fc²‡4f‚‘2öž¯î¸9êo†~³¾–ò˜Š ¢ ‰þ‚ƒ¿øèɃf+y3î!Þô¹[ÍUoö‘@ß÷-êu_ïÖû’èVósî#³¸ ÓS£¤È4ÛÖdлã3`…ãÎznµÕzDn§a–’!³Æs?`ø6t(ä÷As0eü  `Vn,§Á*43þv²A|¸¶D{‡ý¨ÑsÒseî¡%±þEò~¨RòÞ·Ú狼¹ë ÚÀ¯ÖkyGÉý"‘Æ›´Ç²0¼®H4™zDqÜuTtD 3 w9¿V˜Ó.½A{ûö0í+§ê*XóÔ)0²ÖÓßµû oŽµÞ±S¥zè-Ý/Ámй/£GúK•‡~@£!Ä^YØx‡Ú¿ð¾¯;¯Ã3}_wìGqÁ³§çlÉÏwÓ°žF°lÌGw[=9ÈB°°„ú‹ë 6ùÔ¼_Ÿo_«w"‰^¯Òa÷ªØ³.±îm:ƒÚäÜ¡÷÷·7B‹%/Ó£Q·õðx ž#—o'[« EkãêuËD…ê/Ô.8ô™üùkÂÙ¯‹üv[¢‹±Õ)ÌtÆL&Ý
+» td4ä†›É &÷ë~åòºûûC£ÐëLH¸{«™T'i˜B‡évTot÷-/Kº®8ŠB7ÏŸbGž¾¶öªX¼þ€6¤‘ËÜšUó
+k›Í@=(^ª¿@.וßÍÊ_ÜÖ÷š|ä{ynum$V3Laré7’áø}\æêfØ×æLš‰ûΣ1ëagíÔIf·÷~m+^.{FÝBáL;Šš´ìÕæ­ÚU8kY–Wꙇk}d¢$çÄ<8;zêAo[Ô’¤ÔÔXV“4 BGª`5i–¤L­ÊåßXÐ¥/®RTö
+}à!M$ì0› ¯Ûû@•¼U
+ÓHú«µ*¼’«2଒,/¤[K.¯œ¾´6Œ‡d $U²TÝŸŠÿAá‹ ¦eÔÍ‘Ò5y^¤õɵ{ÈÁy÷Ø7dÑ}€Ð ÃÞpÛ©xß©äÙØ®¡Ý“…´æo³é\JFï zÿþ$´°7.Hj”+*H—=EÁy™ec¹BéÇ47Ò¡jï3Ïji³Œx*¼;¢·ÊÜÄfwT›º_ú øAAl
+iXþÁ)$—Êê¾È™Y‰jãÁlš@zéùÊ>îd:«À•ܧeñºûZÈø26®Nncoá¡3UÔxĆԨÓCUð6¤™šòr8SäÊufI&¹öÛ{÷8O½çFl&è£å³v©EOJˆ®#0…ÎI`Šd)Súú…>ÈÿÅ1Wvä%çLð©ãê£12.8w™^»²¶Y›˜fGƒ¤íÎh‹¥ÆÏ‹*.+€2‡š%y7ë2äXÚcæ¦I·uZl7\N;ë–׺Õìa‹t&+a°:^ùw­–çyÇùèR0¸èŠ8Ñ?R$.ºåSlÞà_ì„(/À±‡GX¨Õ~Àa›w ~—_Ÿ¼)#Ní-'øfgÌéúàùúx¡{ªø¤§ŠR±÷òÊOÙm¸%8UÚéŠÓÏñ‹ñr†o­Œ,$qÅáß«JÀ 9"榒}ú¹ürŸc•\¥Á–f •YÉÛ ³R0Å4Ï–Â9ø侨Ußùv¼œqØ„*Ëõ°Lá`j÷Ž®"Ô#Ë«==„_ª–Ý dEaGî(ë½ëHÚå››ö±L_ÚhÏɈ݇_lÝ™Q½EuG˜Þ#F
+ù“,
+™}
+¿ûþbz,z̨5YýuF¼x ¼Ìë‰_ÿ€šØ‚GÒþYôZÌ8¯r¥¨ÊÓ‡UŸ¦º³I› N ƒx
+¹€æ¨)Áî‚>A¦W\8¥ã—ë–KXû×› š"šå‘" F64»ö-lÀKf‡xÙÁ;ÍÄÑÙ„/݃/,z…/e€/€ð†Ïê… hûýÅä~“Øp¹)ŸÜ¸ugKƒÙÖ×ÊŠFmìrõøipVØòÞ7¼%>(LòŠí°¹ÌÐVSzýfżÄè.ïPãÙ¾£µa?Ûp2“R‰î(Ò)øªR
+Ll¿>¨'Ë:}4Ÿµ–EjÜíj׸ÙýTãô]ãäê«Æ1…ç?à÷0Î,nPGL7W®V­ó¥õЦó÷Z†±Ý¨Å"+/:Õ?E<‚šLFD£!w8ëú|Ã÷6ì?Š}˜Œ¥¸žÈý]=l\îuæRɦWD¥Æ?<¬–[Õ6
+‡œ&Yg­[³ÚÞXhA&mHyÖM¨¼0ƒÊZ¢ŠQ¹®+Õ[óZÞf¦lS›j~UÈ’§yZ©Y- ‹—]x,öêS;z¿dÛE"Co‹øf ñhò.â~ÿUÄMïùÅ
+mŒBÆÏK`ñ¶2ûWŽ°Íšþ\åzç
+5/?­8fNo¼UàRTÚ
+î(˜á€
+PàÌT,r¤¦¿³þòn—ŸÙ¼epbó´jPÅ4x·6¼õ2š<üÔ/7àñ/ÜVO‡9fh]ù£(—ù>¾Õ.‹afáûƒ"èýò¬ ø;­ìöštÔydÚkïUw/é¸dÒ°Çm­wE'®]jN1Ìa¶h»2AEË)ÍÜË‚c:qhœº‡…Ñ|dºÏoxÏëÚ E¶Õ׉Ց»NÔÌ¥ÿøµwÿâü®HFfwËm]dd.:æê÷¡€q
+¡®ß@ªj÷jª|ç¡ôM‡¾ ¾w3§Tö6ĔâÒd·*•͆­ÖÎÉÀ ¬Ógô »H3>-¡º6x]õÍÕŸ¶7Êž>”|§P‘çØ˜—KýÎDZ1ÈUÒÞÈýƒ›‹W êf._œ‰yƒ]Ÿ £+›:8î7§—A¹Sœö.¹mçì$ÑEõøI‘só ¨Ø+¼æZV‘š.o¯ŒóvqÑoÇÅS§¸+¨…Ñ ÒØv¥¡Ž5BÍUNi›´¬ê¶#+J¿/­ÖÇ…T­—ÏmË5òm˜«¦Ñ…èNóCmG§§[ëüŒ.ÿÁù‹ãh’e—ÏR›îâ78Êäõs€äãIç9r=wVî)N¹¸d-ËòEå|ÝÀÍ} SÃÌLVš»OM/ÉÝÔ\(½•Û)(…É *«à•ÖrDJÕO’]½jmxôD¯¡ÎZ—îìØÂ`V¸†¸@Ý@ 3â‘Fæ•úòù_ø÷sdL/„Û£yçJôÔ»¢þÄkY”k¯MÉ‚§&cô*uV§’†¬kÆU[­ÑX‘äx)/Γ\!2É”²RÁJm{LÖÅSŽÇĦ$³?`«·tdáÅ®@[?\Ö6<§õ_Üä^kp­ÜáZavÇ(tþrþȵ”úé‹]5«PóbaS¹)Øßô/‡.ý GÎ’Ë9ҥРš¥ ã9¡¶ºç¶R”n¦¬žÓÞÌñ3©¶íÛ_ÅSgó›·Øò©sEx DðÅ¿¦d›çž¢ÅEÜ`ÀÑuÅÊ"a‹‹}Yj;çd´j¸I‘\-w>}pdÔûöÀ,ñÛqƒzb¶iŽªcF
+ÀÀéôwÌ;?´ÍÖÎ6|*µácK(1΢²VT8ÉäC­íXÛ®x:¾Ç"–ÆiùSæØ"-'Ç Ë¿›a‰çͰÆE‡)ÆåÑÏÊ~Vg– é3šëÏéÍåq£ë¤\¡öÏ–Ní_Â*ÅôLíOáñ º^¾XQaºŽMRƒFY­ûVS»óì!´rßÒÕ•¸ëâÒæ”k´i;G¸ÝºqºÓ"7ÓP뇘³;^È”.|6Â^\;Ï‚ì\*li­£ŒZÔHoå©L×WI‡rkäŒjìã+ÙEe»‚2‰msóú‰Ä¦íÙÍZG²sSö_,3%»uær5œ[p¶› ‘£ÂqÓ%SËšbKÚáô^Úh´´ ¡±æùwR0xÅ{Üt­O¸¶1^±óÇöÈ*ƒw¬\†©¶Ê%ÚTëÔá*("ÙM]âöJ&%0ü™;çñp¿láá|—Æ0x8DxèU÷xrÄx²€w_Ìc¼Õœ²:¨¢G 3ž¼Ö6\QúzØ'LEa!A<G´E±6,
+S=»;ÚÆƒ uì_”Gï@2Ýh«$~0¢OÃ<þ)&ÎeçCl"A{¬Uœ‚XnåðÍYËŽ°œŸ;`9ã¹ÿ`‡åZ—-&DÀ.†Št €Põ ŽDøöÚñre^êé®ãº\ÕßB‹
+g(/<šuv¡-+ŒÑ~àôîTh„— ê”ö(o{ŸW$·${'ô@<0êFôoFO[[ĹáÁ¢Ì‘ù1°]ÕšÒBï£ËÌj…ªS@+vƒF+27F+Ìî€V‹Z)M¶¨ÊÌ~aêfèpÆäk>þfŸÎZ×õ~úÊÂÜèmžóp6Yµó¨}ª Z™jnœé“#Šx̯ÁT@ ][}|ä{΋» Í/'LT I3Þ `S6Õº<ôIB/rÃ4«]dè.ǃžðéºÅàÓö1€O3;]“áS_Ý"ÈÎH1“6_Lîù}3T;ïz—KC¡sm²›õäÛÑD¢3¸¸ÖÃè}°¯“~ÄTðÉ:6­åQ,? Ùæœq”¦RÚ¸èJ¿¦ù>ªÈ …­Mc;è]³~#‡ñ£„ ` …ϱ-ÀxéèÖƒšÕ’ýüV ]·Q Õ(¨…<·«…²©…Ùʺ–ðÍÍc ]Ëû{k[÷äL?Í+Mbs˜©Ò«×kŸ YFm…g5§GPØ0†ñ±Rª4»›b[CŽT·‰x…3 Ë’
+_:¢÷0?„ñÛrV¿¯m."·Ús©ekCmY«;0Ý+«-D5«¹ÓsI¸r†
+´r* Á-Tp_¨ \S°û5Ô›¯%¹ïo”ªc†ÜUÍÇ3ys“z¢ó:ŸWY>t ’`Mi.ðú¶f•êôž¯qû{­±¹fU(®
+N,Š[c’kI*³ÊÒ•·]>*P¦T(o"+×óW¹´_¤[˜£ÅÓvV.žf‰[<9ƒU ‰ã4G.!ƒÑ¦„8þú‹yvÒÕju/[]¼hù‰5“\˜ó/élUÄE²Ð&)ÚØÓI& l¹=ùŠž“
+¥Ãˆ«–\Þ@KhvÂÏÛ§ü»6lq¤n§pZÆÙCøWþ™[UóaLryV>uò@Ù^ƒãMK1Ò`ˆY‚ï™¶Í3w3ÅNÞ|\„ˆs×H¿d„§åYYƒÖ<a¨¨ès*’5©+Phã­ ¦áà09ÃÕ©C}<,/b.ÿLöypr9UÓüÕ
+ð½áеÜâ€Ujh¹žöØUBù‘U)‚yôTÌBΜ;_}­4f{hÄ5%…íýs4Ê>Å
+74…RúÂRš-/ý‚(¼€f'¦äÏ€&cfS,+ázVoƒ¯²„“fÂ+7xGÝßà,]“—bvÐÀ$´S›²œQ±:©H­W\>÷fé_zr'
+éÏÑЀw(ñ—‚‹Ÿ À_g >@ÈNƒÇ ó½@¶‰:@O û±|
+ÒÊ-ŒåY£ü%¶ ‹
+ªQPQáµo\…sÏN„¤{RŒlºØÅε7«wˆÇ¢é=ÚcÊ…¶ûZS¦Ë–ïDƒqäˆlvoÇkñm—j熥…ÏÜVZK³î·6? q ‰õoÔ‘Õ·åe‹YuXZõÄým¶˜²‹quÂñ^‘ùÀ û±W#­ò½ýºft;1íwü³yaÎ[»o{vvù×.q¦ü)ó:ùí«hÏíJÍ.½ÌZTÖªB}ÙÜn/® #å¡áŽÚ+å·½;£ªÚ½Y5ÞÇêSx¬Õan¿RÙþhñ—CœNpU!Š‹Ì¤“ÎäI‡XÌÂÖ¦î†u{G³BvÏoéÜf&í±¼Ùv+rœ‚ºØÊ-[kª²µª âbÚÍÖÓ8úrÖðÈNI¿Ì#Xï)'B»¯_¼Ö÷9C}&žÿª»Ž•IT>+-sV’gÇ@•‹Xc"-nâRRrñVcþÅ©+Õuäçf«s¾.
+6ï­Ó½n†¢÷F¸;&5gfÞ0[e?¾
+V,i&¢]û˜cýJø ˆã½6hì¯}~½Ô‘ÕL\¯(‘Ì7pe2r¬Ç²\ܽ<Ik4ÇíM×ÛµÍV-×®ç -ÑQVC±QÉ.Z§õ-Í,Z§É!þ‹Ãä…6ﶘ‹»ì5Ì¿ÀI¯K+–7.‚#kXÓ®‚ªU›èˆqrtR÷WÔý•­¾‹ï¾Êë¥H‰îØJÉÓÌAžû(—´Î[ZAó‚YÛZÛ<Þ°v½ÓDç&­3 Zز¸nZÿ-Uƒæ‡ê§˜.ø°Ä|Xrg¿0xëÓÝÒ¿#óvÊŽïܸܝšÑèì<!Øtæ³oYp¶iœ
+Òý屬1ÁUùÌ+í,«És«Û•KÇñPÒ¹m,U3ïmÛ²Jç6 c‰èzl®u¾¶Ë­®4„[²bŠËj|‘½gÅxÁ•'¨ŒËA÷;ݱñoÌðéoôáhÓ0ZÕYšØgG\^8ú*†žÜ k‡ X£»ž£Zr*ª
+íÝà”=}_jõúD诊4ÿÂi‰åúŸ‘f!ψ»À‚qxÏûè•YÒí7SyEÚÌÎÔ¾}g)d‰›äI›¦sŒlî qµeÂo<,Â/{Ñ/\Ù^
+Ôž|1ŸQqeâWǹ~mém¼ˆŸ–S¢Uíeæi¥hçàö.)æÅ¦>ȃ¢ò#¬Så3Ó ‘ö˜ˆdZlÜ©é¬Ì«]f9µCFkOf _6´é=Ot«>)' óäé©ÖI¬Ù§ß¿kIµø`¸Ýâ ï°wn c¯Çÿ£ë?·Ue­môÆA”$‘œ3
+
+bB1ƒ9ç¬çÿ1œsîg½«­ýçR£i VÕ]½uzé£/dƒ¶R J:‹}f¾ß!’~=‚;§­µKZЮJTÖóǃVÜ4wLáÌ༑µ¹!s¶ qmÞ([†AVOž^=¼ë:3»ÚÁ*Ž´0Ó^ªg§·KÏX„îîÊÍ{duCò€)(Ò'•$û
+w$¨­Å‰¦!¢ó.ù¢sÎ&¢³BqüÉþÃ9OéN™ˆ!Û„Ú#±j–OöTòJÑ í/nc<bòFzÑ,g’¶ÇOƒ1Ùß(-4ErU'M;*§×D¹=3¥;à¦ò (ky0òO2@Úo)™í³œËŠ¢[ñ]a¾_·„œ ¤£%¿ºÜ¿º1%~µ=ÅüjrJøUw“>«Í†¾ÝÇãÓÜ @ëÓÞ,jn•1.–ÈiMu¤C“±\H…ŒÀ\¾u6]ܨ<€”»\Õî}Χ×åU|ògвy.ÕduÚRþ%xšÎôÓ±±•rA r» ÇWxÂævQ=äê
+<cýÁ›=†[‡=ú­{´:1{CŽmöS¤ÇÒ¹² $²·]'ï¦kTgU™_7}ôi;îa©)ù(2fNãam?ª|æò°ö|J£C$„”)qº $1« ,añ:x‚Cu(«]~CVÇ|e;^ÿ ܞݜ¸ú)ûaO’C±bw¬3W«2J<Ѽy§ûHͦ{OµG÷æê€¾?8Hýøó@ÉÎ…—5%MÓa±Ò(­s¼ãD7J±G´Âü*ðG UÛÆGî ‡X¼Ö.qˆ><z(·íh®QÍ)Ϙyöt¨ù¬dÌ zLgŒNÕ1¶ô“Ië]#|f©XR(»³ñÉñK’ÎðuùAHT†5Ò žÒQ éPÏþŒóŸþ°ˆCtôš\nµãð<.ÃK±þõ{tS-ì“¡µÁ`ò;ò€8kn|u ]ØWFýd=8ì¡MªÈPŸ][¦’Þ1Oå Ø§ ’“¤6 1x0#æ{èDx 7“[çl.h.¾},;xmàïqÖjKx­\kâ5{ÒÇ·­yŠr’Âîö¾qß?Hs–©_*&L<‚Ô+wvòó®U(mŽÊ YÉé™~ë$O6j 7ÚŒG'[ø7‚È´@n=c9]Rønþ–ðºO›8'ú^öx…Ùfç5üA²¢¦­°ËkpÃÚK@db¢½ÚDõúc¼ù7êÖ@{¾ÛG{–ÙCïÍüt®“hTMâع|R¢ŸïF¡_ÞºŽLU­u\²rbB¹_¾%ÃìÅK>´ÅÃw2ë¶/ø¾Š°N´¿¢ýéþ…ög8‚ ›JËH|^ZH|!Òa·]ŒHYLàñ“:ÂΠèÅÊPiœ®©ãÌòM‚Âùb *y¹4P9°AÑ:[“z³ƒ_ša±{®¡=a»®~Hšš2ØŒyчo$'jJÇt;Câ0xá±yǺfæŒxsÝåò•~J®Öúd‚t…
+vbB€Úõ,
+°³Ü+Å‚ìÀ+vÈ'€mež€
+EÀVà` Ôè?|÷e¬˜vãðƒxŽ
+ƒYšÁµVR{õº)ç3™;ņþX2Aâç6ЇËLÓ•WZÒ8[¼ÞA«>·èfR¬0Àp&…£
+#ªÁ
+(†^)ý.B¼<þuÿCµ/f0¯Jç‚mífÆAï÷”dÔmuvè3o~_ ˤã­u^B?ñšC0ÖgþêÉŸÄÒ‰bãA1̰¿zò@W€8oZ@<¬‹@|aU ±aHÚs¤z뤙K±44hF@º Å& ¤D
+€4=€Ô)r)zÊÚßdÝnÈ߬5sIÇdM·÷cqÓXuÙ§xücRÆ£Y@GÀ€—«ƒSÇÿ'%w†*®GÈ™» dXE\Û,¹•Ô¼|¶ü1 ð›-PBÿó›Þ° ¸‹"PNá¨xõÔ\Y*Qˆ€*7 Ê ¨Šô+µMƒÇD©ØC²Ø³œ®×’/²l®—WD%&ƒ®ómͯžlSy Çy(ÙÿW"­ßª}·ˆÚj¡ÚþãtU7™ Ð 4ÿxÚÒǀγ*Ðs èft¿šz«b=ò†@•Ãú¸l
+áCñYJËÄ_¨ÛAm>`ÍÙ§9èÌìë`0¨Éxê<W‹ÉÜ<ì&øèvÈç5&ç26bÖ&“„t׈Ï!Z#᱋¥kvUþƒ[§Š|ñóçáù YèOdû¾®˜ááÔzwf{{ý\¬·zn¶ŒÐõz¡®§Ù§±xÍ hƒLæú87^Ù13&'cqTÃfZrðgVÂofN1K?–§Zú)]á Õ+1 TqÖÿ
+»~4ï½IaƒöÐ9'uç~.êâ›hÙYUWÇÿ i¿°GwŸ—š×þ£ÕnÊ·v})ÍfÁïuI—
+“¹zkŒÉѶ›JÛq|«á­~>ÕÓó>0n·O?Ñîp¾ðÞÄÆè:ÁøîÜÔ.¾­Îʼ¹‰êí3éF‡ð6ø+¹o;èiI/ÇlÞb$jví⤩Aúø‹ð9áF_¤;į4ä}V„V¢ûêÍ
+rÕŸäSmó 5>—‹úP=êNÿÓQ‚>4°Z=4­Zºø¬0ùA:î¯ÚÛ ²o3÷æ%
+õö3²K¨•7Ù–,#l³Û#ä¦Vô á`Ù«„@ºtù5ûÝPýVw?¡XÇÖv½æ+³¤F ãÿ䞥…ìñ±ïuã)m¦i±%-/, õe]€çŒìçG-®‡ñ†Ø v¶Õf—Õr$Ü:ÍVÛ]š÷ãcÖÔÅ÷:N²Ç0ƒe¯ÑB|7B…ëÓkƒüAêYµ/ÔÏ“Y#¬¬_­l
+Q•îìf•úK¸V¸˜’q»ª—;:Ô/+¯[ïá²_Ù—o¾æÉ;\$² –ïà!´÷gÿAÚ\7B¢ã+¡ZJ'‘·7·Ó…ú¹Ü@Ò¾S/åÐ^mÙæÇ5â!-ªÕ’½«2xù\iDW…—çpÐêˆò Ç¥rgU*øOyV÷~fè}J탗ä
+éw)MTª\rÑn¯„ÎkÝ:ð:ÿá¤a |u?î“Jøš3¼ÞÙ ÛV$'e®iZ!ÚÛ'„ð ZŸÝ/T­|†•j f •Ó‚à­$¡°_¾m瓲ÆÎþóõÜû¦õ¹ø
+:Òˇlë/æ™æÒ¨ÙÐX`·ç¤8~óóœzµíDU‚n‰"}ûÈeJs¤~/.ÏÈÉ­N‘§Ó8‘p!‚T*«Uå¼zìXösxòm3s­YŸ-Ѷ’š[ÐÑšš“a{kb™ÑŘߙ_ ã(=ð+šN÷@«ÕÌÆ²Ï“z8£ŠFˆ›æ_äg¡zHöÍ4䡤{­;Çß § ^ª† T.›å;áa$S$WÜÕ9Ô–ÇôŒEV|Íw«+`g܈³’¡[°ZuÍɳ˜˜·l‹Üµgx›ìØÈU••<ý“¶3Û/­„ztDEæmO¹øXO‘7ݽ|¯MDù^¬¦³XúÐo¦%i+üB‘ëp6ß Pl>µC»M}: Ñ‹y}HëøÚ4W öà„ÝÇ%Ï”ööà©­°Ÿ\ Ó}¤=nQºÉ†Ÿ#m}ÝV½D¯(¥šN=:mm7ÛćŸfêqMìU‘VîÊeßÏ*
+å^[qe]Yw¤áÀÜJ–år’eKÉ’œP²H£! Ë¥PÚvcì™Á4ºl#[µÓ1™Æ³nùµˆ¥Å°sÙsû“¿[ý“m>¥½¯Î{àwƒÀ) W¢|N§åˆ×êÓ¹®qÅ›£žx¦¢6ÏFS£j_iß[cEé-×rÏ\e}e§Õ…S1/e™‚X8û-icka¦RŒ0cžea†‘ aúBë"¢1 añú„ýL2}¿7Ú´2d­U=A›B9Çb±2.à…+ìì´j>šÓÕggøÓÉ&=ctkrÖÐᥞŠ
+^" ôx@‡ދ„S›„c¬BæO¹tL&óK8 “7 ðÁöÝ‚¥IUZÕk^és3suÁÙ‰>ÆLüpý¨çåí,æNt÷ë_¡ôÛl! ¦=!öô£/tïXüÐz³ƒQoqÃP1„üî0eMD›Ê¸¾OíDš÷˜˜IÚ†Èv¯ÜR§è
+ø&Ÿ«ô,sÁ+OÝÀ7è"À×ÏQ9G¨ë G0Ór÷°Ï?ÂŒŽ¾¦ïE­ÌBwÐã~k²Œ³ú,?ÊÝ~^¥tßóëUoÅž8nN÷ÕxLŽG­%1Û·vD©1¾8ýäV€ÉæÊ#;Ï -ß"Ë<^=d+8»ÙÆe8Ëò삵žõôêcÒ0g 7kÞD»Pç€vî{½!…2&)Å -¿­MÖ—ÚÑJbJ\7|‡FÞ\W8Ó»5²šðè|dË=Üÿ;pܽ½¡Ÿ² '¹FZ'çV r” »ÏvÉB[,Z2gL.;oô. 0´{;0h÷Ψ¨Ö+»ÈK_6Á‡ˆ3iîP `x„L8?Ï× )x.`ض98Ñe8I¸
+Çœ½¯
+8mNÛl
+GKu8é'"ýt˜OuT'¶ï™—ÕsŸ2ÃkD¿îÚ߆lÖú_ùWµ•OµÒ_ô¿‡Þ§öW1Uª VNã_OïFß
+ñºâ×UýO®€´ÿÝ‹óûðµ±~…Ñ?7ñ?Íô¿åPc$äîÎòH^ËÇwa:=íÌF¿Ñ«‹Êa€·Ã=T·¢í\âz›ò+3\×Àu´:®iÕ·âg×Õ2ÂûÅ-xœêîý˜x43ûD >³ï*;èU}ŠÆKâÁz<^•rïq€÷˜1¹jþÁhøtr™\‰cìµðWç/º÷KõR<»Ðò¸·7Îe[ºÏuõ}ɬB(­É¤Å…?«kiö ú jì©ó¹“Ò§íMðdTð(m‹ÓΈY.†É¡|ÿuöÁÛ8â¨t„‰¥Sª·5xö({`¼?I?ÉWÏÿ—uµ7)–îÿµñ6'^ç3ßÛ,öö:ì­V1Z¨«}4³Oþí¶&|ØOWIã ³¼Œ˜yüLøuÄ`Ã.å†ê9a9úŸ^¢ô}eö¡xëô&ö'è¡S²Õõ²ê°‹/[ËN@n.ímAÛÌédG¡ÜìEüý¾üâ‰B^ý‡ÿÚx’–­Ù†ìVj‹.n¦±fŒÉ°’0»(ÄÒvZ<›£°ÿéL{}û¹÷\°^tç…㮋ώ§N€oímp|ý íõ†¢C#“Bž¦[çH[‘ì›Í[¯ç5Õ÷: y8n|¦ùm#)¤­N}RqÚ)ÊÝÿ… ïÀéÒnž74ijQÐHG˜Ñ΄•8²6øà–‰¾ý`¨šàBgUDõö¶LºÑ¡FäÃV$ªÝæ­cÄMõiLÂi,B”Ò/”O<ÒyÔ'¥$Swñs®6¯ BÍ£×bÕU£_©’çG¿RSü}…Eë|p8æJA8ÔªA¸È×þõb7ÁÞ3ǹ¢äÖòkBpÝÅE;ërùÝÞ.0…ô´¤ó0ÛÔàuZó‡&wÑyûU¨Ov˜_Çt¡Q[dÝN-Wv鯷;­RÝî²²sÇ
+»¸Ýƒc™…áæÑå¶–he%‹û~¯\îz¯{fç¥Å>çeø‘—bW)ÎÓ ]Wþ>|qêXÔgu™Í¸–ût1;Õ}•n]ùLStoá—Ïá0/§sý‰ÈÔ³NÖ¶(Vi±¨Wê㦜Ð^9hÎÓÅë5]È•;8þAü‡ƒMý>*¬½·gž<ë¾½L;=e…gN*ÎL×)–r·È]VËk·¬µgE']‡zagK_ì'Æâý•ãuƒ…¯·ºS(ù“þÔñ^îR#Åú¶ºq¬}eoê×àT)gÊ×YLúUÌû†¼×½xú(xv)ë—Æg®^r"«]œÝ ƒbiÚžº«ìhã–Ïàælev˜^…)Нôêø•YÏŸƒç</=ž9û6Ÿ9v×[–ì®9.þ‡oüÅd½
+Ç̵S'Ù¤ ¸Ê"+Gû.ŽÞv÷–%­—[Ñ­!v¯X ìÌ‘afþaØ&Ü/M¸a»_ü ߇åý¹¼šæ¨GD^¹ùfJm#høW¨õ_ëèŠËuiJJÓ¦ÛbÎw..õ›ÏQG'xáè­˜BS„”üeÌØù¶c”ìûܮؽ°ÕúAl]÷­a{3±,‡Ø™£¹õ0‘\?kL¯„bdÕº¯/^¹X÷c
+Ò ûfhwÿ`}‡Iwa]:·oJq‡Ïb^ˆ¼©Ê#pÖÏ/ÏÄCÈ¡èOœ¥[ og·~®
+ÇýmŸ¿ô‘W*2l÷iëý®`ÅøD³¬Õ!oeÚxÙ,<Ⱥ‰ôŽ13ª‰‘]ÍÖú²‚Ütbo£ZÕ˜‰“®µQõT¼UKtå´8»Êi¸u”Ss^P¹ãÁ™£Hù6„ØÓ êlÅzy‚
+Aç´Æ¼ñeñ,–˧ƒ³o’‹B‹ŒóÜ›Ø}9ÚZïéäbÙÜ=ÅÌqgM'¯±ÆljJF©YÎMŠúòt¨è¤ýii[˜jLÑž«‡l|Rù‚”³\å©wräÎÇjËêH¾ÊêØTRL\Yí ÒT5*|ñƒ|½ßý¹÷[Æ¡éÖ˜ù/·6ZÂû~Z„iý}A*œçöã㎬x
+ÇÄi+µÍƒQÚ†O¯-½üº:c‚V#YMc¶f^=Ôj¾jóðQÎÃCO‰l|*ß&Æ^îºãô<‘Œ4hõóâçNDbÒ{ļ $1/ä bâ¹ÿ`šù sû\zäÓî6¡‹åTÎëçC¯R$?Ö½Y¥]ú{6æsËÞQ‰‰×ßÜçz a;m;d¯K€zØ4³ª ŽåüÜIJ´À,Eö…¢|'ܪ¬Uk‘ôRcÉ¢[ Xå—˜oIaŠI¦à.6éw°òpÏ/¨©À/\^ÀêhÌEÍî±sd§1…àN‡\i‘¡aç·¼VUwV>_›óá%Öƒj³¯Õí±®‡+墬JýMž”ï ˽œEÈÚ¥-H¯öT“†Ê¥ 9\GžÜ ¹ÚP˜V&K;@7~Öpž
+7”ï³Â\ÖùúNF½«dåO@Ê HV,”ZDxK¦çº)£QQÈÀ¯ëϨO”‚9WYÍÏ잦P¶¾Oæ$0¦yy,˜æc˜ævd3'hj3ÇÛÐúb˜ÿ¼¶‘ö´jì­où
+¦Ýº±Ìä{þæjÁççV_ÇÂì×+ ä’íIoîÔ•2ùÌD,숕0ãÄ£P|WžB6Ža¾ Ír<±}pܦN¨\•³
+}jlÃ^D,cc¦UtŒ”½Àt'0Eê±ÛúT¿Ñ™þ T?Ü“Tß/ZÔcè§hLZYÍþb:\7G3¬\^%Ó˲;Êi&qƶª‹™•[­¾Åfò£ %ë}í‰È¶Õæ—ÃÆ'‰þ”«v–ŽQç„=`Ü›m,”åýÅœ‰™ÈH“Aß4¢Dw^…õ´Új
+š˜²¹ [üA tôie<Ô™•wz
+yÏW‚ú’¹Š§ eçŒôê8·êáL7߯d´Á~ÁùónYèõ!¨@J ¨Iq–b´Ô{¤ðÑGP{3ŸbÛ
+€Z+€Jº@µW¿é¦TÇp
+_Tz&Ö-U§Tèi°bMKYZ;ŽÎlÕnwÁ²G¶Uô×Tü\Î|ϲGh>Dï¾ÙC,ëBÓÏ¢ 6z³èe;I1ý½ƒ›>ƒ  ?æ%E÷˜L¤ðäç"`ˆR0df•b– ÕÔ
+è,E~÷ž“ŒA
+ªÇ<&BH·¨ d™9?>¹âduËa¦¸´Ü]¥G{+ÈýMúŸÃÜ;ÅKrœ–¸Žêôâ#ã©
+×®Ò.5+:~BMâ‹
+ëèRh×<Rè×ð­9ªl~^%á®R×àÐÙe‚P£¨òyèªåÈÚúþm"õýnQ|{Ïå^ôž+1ÿ¿pºŒz—UsŠõqe</ ¬.ÂvÖ§95ÍÕµ%7À¦©Þ†»ÆgèÐØ¿ÕÑyÔðu#[ è]Ù6b±Âœc58D;àï¿ZbïZ.KïkÍ¿%Häk01ðž»bZó{&×>”Ò©îSÊç²8ÙŒ¢Û`kîüóÙ¸ÞåCº~¦e¸¹qYÿû±Œgã ÌìlfÑR ôÝËàPÏúÖ²æ®ÓZ®´\¦×…¢ª«à˜mÊ{ö.ËÛ3ê÷Ø'é½b\ð†6¯z`«Z¥Q­T,Á¯jPœãf1›™õÜÅ63u VÜ;ëcûíÐÖƒ,Ô'º^àÜ]-ÊÎwy1ŠÈľÊgåaÃtÊ»¯¨Ü›×êZëÖ=¤ÃdR{T«×ú.h%ñ´ÜÑÑA¹ýF“²b£3ïÍB/#¢çtЖÞÅã¢ÅlîR覿1—MTgSØZ=;¸…}ˆV
+Ü…iåOmg˜¥Ý)]¯¶š³1«_Kæû)–Íxˆ-L»œË™éIÿP^\¼àíêþÛ©mˆt•æÇV­-±[ùž£·ÞçdLJ5é—œP‹JÈÚ³üÔ%ñ`ëT£Áµ°¿OÞ¾xDógy)ÊñöM» {¶õ,ÖJÖ ›ÔÌOëÜ5™Ik~ꕆ›=@º2xÝ£–Eß7GÚj0´ÀÂÄ?X YI[5sâl*åçƒQaDò'¿Þß í¬¯_™¶¸P?c7å{Îvzo9ÌrÝùA
+!yIò‘öZØ·%u°5I»[Ï[%c™n;klJ›Éñ.™°øÒÉr ÌÕ}…‘žó'‰¶&ñFu«µö:3*—
+Ê1Ï •&]@a¿KkKE˜-¥‰E%|¬…IÅ»Lz‹³ç7‡»’PÝSÄ ÍK)+5VÎî2š–.ÝÈ^Ó¾kŦ­5+}Ë×Çæˆ­Mx.I¦ü2Š„‚苪žûAt_­szî9T´Š9µ4:÷òÔz5×P9Æ(Í0Y)¢Å¿äö¸KI ²¤ÞbÓ“ôòHzÐþ —´ÿ`t¢Û£.ÝË•B$føŠxjCi¯týìV1uYgAb?¤K×z½
+¡.ïª ?³£èq±¾¸:¡TöÚÝ4º´ø¨{þ©õ A©ÜÈ•RÔ1íGòµt¬ÉÊ%דúŠ»øA$ýq¸‹±)çD¬ aì”"aì߸XþB×ù/å¬ÄíÓðR¬/ÇO®ü¸2%Ïž^$—öãl&– ;faC4Œlc\ÑýÏ)úA´*¼ï«ûú{¢6 e«œ무‚æK‘¨1,wê3\Võ7'=>´*õ+/~àJYŒwÇŽñÔLp¢ÚE@åKý@ãq“oq«ŒþàVP–çq‰H{å÷ÇI„ÆÝÇ R¶S«Î笿hànÉÓ1Sn +¾çbë}Ù?ÕµjШ¨z©®œÞ®"•ljÜÙn²ÚCRÿ½JÆÒâ§\Mk1!û”hßN’0ÑÞ¦à¼EŸÛ~“÷ õˆ[ÕåGž†»…eî‡Ë<³w–9lX¶†ž¸?¨>7ìÀÎúÍ©ç«ü( ½‰…€¿þ䯞lNtë+UokLЯ)g] ä[™¬HÏ®I&fÄÏ¢1ób¼¡ëþ,L:ï§àº<,`˜Cðž_æøkÜêú.r”F×ÙÚв¬ußÿ L8)@ŒàÞ%:Z†UZâ3tŠKŸ/}–r1Û[©v'ý߬Üf2Uò”Å»p^tÖç<˜õdm;W:J¤;UY“ð@2ƒ/æÃOó&—OGÀ2ᨱà½CëÀ­…Ε .³7G>»sš­£ŽÌ½†Í4…k•¾t¸>-?›kê>$
+`1Ì4À"~½`G"SÌ^ˆÌ´Èj+à|‡]©Ð‰®˜…¢³‡Îp«½r}Á âé˜/ËæÜ߆Ô`ªU‰)q,ãëÐ/aÇGÆA•xãþ ð{rª@ãæ¸!×8ÌÌ:›n¦¤¾G`9Ì.AÙ²N€Ìô>€„2ES$LÕRl§€xTŸ€x" Ž\ˆS¾\*EŽD31œ"§ó×§£ß çÙ¸‚'# Ž.õ
+’&1…:^™¬E+ªòH þH§„g@ì_T)Æ@¾2£zú‰Øèò«Áf³PŠ& (¶StC@åÔ 4¨Ì^ämÔä†
+6ôg
+Ä/¥Û¢;›.9öxѽ¹1Hû ROÚxÕÍ…X벨#êÀù<d¼ãÒÔ&®¤85uC:€†ôÐd{hé²t^{¦b€.äÄ]ÐÓNqÚÚÓ?è•ü¯Òk䇀&9@UЙŽ¨k£T¢F¤“y#ÁÂZ³œ&ìoiÔ¬x:è­éÇx?%¶šà5²ÛÃd‹j#CúAÓ¤Ù›ì¯ú{J¯ýÖ÷¬g²Í>`¸õ0X
+ä,…
+ ÐvŸ#bPü̈YÑ”maçšvÎ|×…‚ï¢ùÕ{Mç½ÁE°w4Ð~`Ûßq–uä3%«ñ[æÅ<ŒÌ6­r÷Ú0•ë
+¬`¼‚YÉèWî{£ß™bFN[¿Òï©ù _i÷{Ý—ÞÌVÏƵ²bËÒãô]1â±eÑ^­«E˜îûÅô´ß, ËxTHÜúÃ|%³XØ»Åeo×ZÄÍ:Þè·Õ Õ´%9%Âl/*¬©–ºr®»é¹l\£ÿâF®ßêãÔa§ÃŽðÉÎÐ9ÿg±²YÊ,­ö!³´U4–þW&åݾ‘Äÿ6ŸýâSop)gÒƒyq’ŸGÎJy5ó›w©’g¡T1Ï„)Ï®Su«•‚;æ5Ï Mõ]üÀ¹G£²Ïé }5Þ£ÑËØû´‘šp}ÜÀ8¾0JvÖ.XYîW2åâ%ÌP„±Ôª•á]Ý?9J­GgGÌÔZÊ'$–+ÿ¸G‰[À¼ÿw¶WL©OžtÚ»°Í£k ò³ÔÚV|é™!ÑqL­bÚ¹G|Û¼Z7L¯²ëùé{¥#Eò-®˜kìOf¥Tá 5íQÚ¶°‰ÇÉ»|èêÁ“\U¸7%ȧŠü±®rh­q9{¦ô< RÔ̧¤ç±ƒHÏENdiB•à¢Ç]1Ü¥ÊY=>5µu"4ï~¡žëƒ}ù6†3=o@§gNŸxlIGv‚ŸÅßrY Šý MÕ§Úv;Þh~x:©‡×õ®6–TJË:®Û<«(~O“Ã×É‘3©!õ¡öXÎë$ZË"¦Úø…©ªM…©FX4XLWõ_‰Fê¤ÜêZe¡Z«µA)c{‡·c«=x…¹\Ž©é}§¤£bgÝí*—ñŽ“¼¶“¶žÆuuõèà]UŒ‡ ?°riæ—ŠrÍoåû¸s‘»ùå[Î"7Xê7qZÊIº,Ž:US´µ“/Lê@ÀÈÑ_l%˜'¹u†[Ÿn#n}^¾x’€Ò¿R2tý8n3…QÜ\‘ZÖ§8ÃN»<?ZMÌìdÑ1ì1\Ë.>åR¦âŸmm×oêqãšÊåàÕhTå˜r¶²¤7ó™Jýµ–r‘y’RFõ!Ú£ %•-!öÏ/ƒÅ²Á—.Õ2·Q÷]®Š”vìÞ½B,¿))ÌÉoD,¿k=Y~:‡ØÚ}õ+á&Íë3òÆ–>ú*”©ÁÖºöüQ“ul¶÷3UZ/i|nÿÀªÔ± ù¡ß59»æiÀp%)Õ3êâ¸tÅ<> 3o>ŠÊs'`êÊ/{Ú›/ë>ÊmVŽ«zŸ,»ßË.+Ój§VŒ¬¶?t'"EZˉñxŒÖLöFwZ~оžBˆ¾.©ß_çkev”އ×Ë©ú­mâO6ò¥f'Ká3_xÜU®›Ð–{.¡K¦²ÐÄñôn ³å±(ƒtUÀ1àËS#î÷yªàM¸-®¸êépâØ
+}×ú-:ºÔË8Ž©~
+,)c£)Põž¤uèÂdºqçˆBJ1ðE>¬â%”cëÒàŠÑ[Bw¸ŒÑ3ãðcÞcõÁÖÌöWúaxÚ邨³z³2,²þߪÂ}ÙÐV®èØ´ÅÓf3Ë3ªÆ(—“Lgá«Nõëo›çw‘µÉ*iÛvû&a¸Ö'¦³ÑŒÀðÅ_\ÒW¼ÔΦ°õ£Fa•Þ%ƒîRFy{ÒGNˆ~Bšó'H¥S ‰Êiþ9žÃÊãÑ§Ž¿Çz@ <1}›@8¬â·Ø:µh¢÷þ€ïhè
+îž°9n[Ó™v‰Ô¨7<“Éѧ¥…{'‡/ ½<N.{.¶)/}¬J=Ú³#è¾k/Ðz¶{Dù÷ñ…´, CätEþáëô^„;E¿k~H?Ös,­³å¸ pFËô£ë|`uçþŠÏ9šæ:‹š˜¾©-3»üh *»} 7Á DTâé÷Ëf®hÉ ³c8>ûäX(v×DŒ5qB6‡œ3ri½Ì"÷ëuø–‡p˜:`mB®amÛ¾§#n™Në'†‡>­® ™ÛW35áýe
+  ˜+‡L
+©’ãX¾ëV§&ú† ™xW”ËM)T4 6_×ÁE/ü¥v<ô‡J·¶oK¶ÃÖRõKìI¾äé^ÎΑãÞ)¾Z*V}³r?e8œôttyZi£ áñ~`ÈŠä&”6нÔ f)ÔÞì€;O=
+Xiùu²róX¥¢
+p«Qp#kšÈl¥ÀTÎÖš2ªä¦ÈS¨ºô‰‰(iMju†^ÂÕ¤¼T4Ðx2“þRd„Ë?V`÷¼|ã¹û…¸üK>«ÞàlS_Oo—°«ò@L? 2¥
+Ùx;";<‘óh bš â^dD%€p Ü:ç$ÂÁXÖ+²¦U52¯ì]ïÆ•NX«¸ÜÁC^^ÇO·s[ý)*©'©Â©ßþD'ð6ÉqHÌ·IŽÃ?÷ç5â% Ú9 s d?è
+¬îÓ@f³BVT*yj˜/ùŒF„
+„‹äè…ä€ô€J§åíONJ²Xè ø¯åÉ~ïÊ_çm‚KÓþ“œð/x”€*àm ÎÆS·WW ¾à¸/E ñóÐô-4#¾íµÒe4£€VA\uÀw­Xd+=è\ jÞŽBÞ[ O»à„ýòä_¦•¤'ù?™»¿èöoðî¿‚þn¿nÛÞ&y ¯Ë dkí4Èö2jÜñ° }Á[ c0tœ¨]dw@—ðøG™©þãZMl¡"ò¿M¬«É¢^I2B¯‘¬–c)mþ1Ú –þO@ÂZùµÕgÀ~ qP|@^ºrÏïÐÆ?A±ÊèJ…Æ*Ɉ=7RGôtvVîI^êýã­\œüÀI6l‚C“u§~'ñÿï8ÓdNRí/ÿ&#$±û0ÁËñHZÚÜ'$v¸÷˵|Þß/ÕûìõŸÙyê, æÈ)€æÄñæ¬Ø£÷@‡ˆ<g÷Cö–ßMħ·kH°ñ€0YûðòµjõÌò\Y“È$f`¼*.ø‡þ×$þŸÿ ˜ôîÄmN­™î¿ÔkùDÛ6DKÿD4ëû!] vùS6Üâ!ÖÛPÑ{°fGûѪïgË
+±f˜Wþ+uµþ™Ÿúu‹ôëêù ¦Vña­8.7Ç+}ÊÎ¥==-¦7TM®;ÑM%œ[¦ºéz¡³*ôªím¹Õ~µÇj¯HÕaóÖ)OšÚ³¼h<£ú¦a€Ö±>´;·:4›€Zü¥‰/]³’ï2e»ºŠ§ê‰…E•ºÖè¿ÖÕ‰Ÿqª‰Iy[<]š3ódVÛû›w覞ç6»öέs=skÞñÕÔî9ønDÙÙÐßm¡nAMµæ ÝŸ—*–ï’íbuUm–«Û¯U¶n³â‹‹®wh¯‡žð8ÎËçˆ>–`½K¡Ý'K͸‘ëWܨT\¹Ug‹ŸíÕø“ÿ$ât*wÐç‡þìã¯ôA÷
+ ­  X5CÙžÔ‡ÆfPCf‰¯ªóªG×7•m­w®°§ñÓ;´Pù®Ðr9S¥[ÿÌ•2©sÜWºÏñSs#É? $_–d¯môFÁa˽rX.W‚/~­"yO»*y/3­ç©÷öš§>co‡ 6‘øÇˆ[5šYä¯ÖR×ÙR­6¬ìàzº”WWËOPñ9«ê5$¥^–ŸDP
+ 4t#;=p Xœ‡Å̶­ŒSaâ¹÷²sßμÖL;ø©†æW­.•÷ä…`oõjû™—iDÉj˜n<†1Ï“ÉÄ
+ìÙT°!š =!› +…q.ÃÏXìô¯„Õ
+œ@å¨ßy0A3ú<ê0W[UxKŽJxøææðQ¹8Jóù"´¸
+y-;óêÕÿüðòžGö¶ƒmö‰Í­CÄo¬†‘=šçQöjváe*p+»ÍÆD.ƒox㹆³†Á¨E}Xo¶tK3ºø·ì¤CÐYÇêY”‡·Y4[‹ïÊ,Zh¿’?™|d…˸§h tçRÙ×7Ó’žŠ:…™5­:d .ä+ã±eïæùœ½ͼÍÙ¬c‰:R2ƒ•P7• æºéú0—™öfFŸ¯ ã´?þÀñMô¸é#‹zz* Ùi­ÊfÑSOÍ,ZïB†LçZZ¥0šj .ÞÔÝjG«µÊªªòÚx£òÿü+ƒŽŠêmZ!µbœÊ{uRD %>/ùê çY­¨—7;6®çµ¹š{ðólîþ>XÆZŒœs¨è£ã§¥Û-ª—Þ¨a¶01YÌ)o3K´sÎ¥ÁSÛ‡´Æ„¥î3š¬òýº­´ÈküF–¯~v"w¸õEz´r¤ÔS_êÙùm,ã›ÔS{WéÑžÝ"³5Ê4Ÿ%']õÉþÁ–©‘CÝ_-+ÈÀå\¤š¶a.WY}¼µd=Ï¥yÝ~)™ìLâŒ/µ…Lq*Ú¦á7µªuã1Ív¤îGÏ…*ÀŸ­ršSg¥Õ(~äë¥Êš:d¥'‚’î+â`½é‹+í…ñá„
+ˆ´(
+ˆyX
+ù¡~IäÆgóÒ-¾T¥a±hÊ ¢ì®ˆjç¡ÝXN›¹—x*i·²óI˜Í¬J¨”)_v|†R§¢Ví­3ê¿XªàÝ‹ÊYåjŠÜãZòM7zrˆ7&²¶®~`)¢'{I¿âP' Ñü8´0ñ†š€ì>.ï
+Å.Ÿ[ÎS«iŽzÚG£Áœ£^·çe¡s"ßÞ‹µùgÊ™¶uÖÆp`)ÛZéƒR|ÄòÓÊÐM+£qöERÅ
+›U«¦Ê·iM—3|7/=OkO2Ìc]iЇ;q(Z5k.LNîVpZá…_@ñߢ·Æ ™£Ë‘Ãúwªó³\w°f!ň#Ø`ı2erb×ù‰9J‡s[,ö¿’?%0=mò5Ûë›Y®ÐЧ½V)SÉÌMõØ¡5åR_HŠäX9ÓMQRô&E ˜Œ`­­”Ó~¡(8¯ZU@ûÝ&¿(íz|‰|ybK¯¸
+§9úÕx±µÜeŽã—À4Ǧ/‹m‹V)mAuýå‹Êò=Ê
+»!ÕmxGê>÷ånui«roãUœ.k»9³ú¹”Ä׳¥¡éjµ~d*—gZ“»AS”ú5†G¥%)L;3Vˆ‡2¿˜¿už$S6·¾Je®Ò±ë£Õ:lmÞ°¼»žÿÀ̉xí©JÜè«Vi5šrT/•ÎQÙqЈ_1ÐŒL•ZObLåbLwñöÄ™G²±ŽÍ³Þ"ž…%T]Yíp5UËTîlQm¥f|W~Q²”›DÎ]ËwHžl”)®ri‰ìÞî¨lžåX~v*0-á]a¤ Ù¤¯mµKwìÚ„Öàé†êJ'˜9hW2õXëDÞ}«xœŒóú[. [ƽ޽ï{|VG‰4`éÌ”{È员¾Mzõtl­iõ,RT:y×”ÞÃ&汬È/«K†«Š&ÁÖmcN{gâç@܋ѡ™¡5«jQOz\¢z»}Òkï69øÒæ$dµD~ºxás<…ãÅs3ƒ­”W£zÞ­žÏg”mO8”í¼C´z[ìÑ-µÞc”o«ôüγ(™ {Ë×Kì2gfÖÝ,%ljj«ÿ,ʽHÉIPzª Å2/r›Ã€aÎ KÐ·ì £žå3Eé·@3O™´0Ö ¡¥î“rÙ#yÐÄçᬋ»™Ç÷ˆáo~‹yåÊÝng(ê×9œº%¤ˆDT8¨û,4–DäFDD»{¤‘éï}X¤‹À…«-Wž #ßuì­Ü¤sQ:'"…n|¿ðå-&ÆMW¡o=Ф¢Ö'-'À§±ø"5ã&l¢bköüç€ÑM¶ŠîrÅ6ZKõ†È1¿\"M» ⢜†/¥«·©R1}÷ï½t—o~àt&~9¤3ër=Ý%íMúžk`¥Ù>TäÂ+”ÏÌ%1}ëø¸&à[~vVE Ѧ€/kç³o‘ *U–zá8EZ“ N }Å*šB ñ£Œû‘ã%#"Ͷ£ÅYˆdŸËðAp{®G°ê¶æé±?¦»@o¶Î@¹ÆÃ„RR3HÙ>X鱟NÁüÇKÁÂrš²]r§º/GòuØÛ7#"7 ³,Øa¾¥ÏsXQ8mò^¹–aÑC¢ÃG™#GDè+"°JGAQ¾bðµM0°ÓBú1•t¯hÐ{å:ÐÀ +IoƒÔ¸öüÀ©¼ ¯À,¨^vß"
+á“Á*W@ù§‚Ã×KO÷( ™Ë#—BÂ>ð~SŠe­|€bqj±Ì{
+€ÂPB˜”~°åBE@Eñù£æ­ ž ÑÜ×<½êM±ŒâO|2¥X>C@]ö/@]— ¶ë/í¡f¨ÞPKÄ„(ò“Ýì>GMæÐ…4^¾#¾z¥ZL&©N§[ÄvØFÚƒN.ýöMBÀ'®¦¨€Út@³ˆ è¼ðMƒÐò€·Ê_ž¼ÙÔaÀȹ>`Ìî 0Aú
+‘ÇèáÎ׽ݞ²×¾Ã©oÐs0H«îðß<ù[Í/JN\À‰ø×û;xü¥In‚‚§’ô†â~=¯&)ªå€Œ¢•XÂ>1ù¤÷-þ×óØÒº?Rg„
+ø/ãooß-c¶þm¹MÖ)ë×V ‹Ñå×4Èê²nu²ÍÒd[5d‡ád×á÷¼d7ãüÿ!w‰51Ú&Ù‰u·b©Vc9uÿY‚,1ÚϧŤc±ãOX]þ©O»òcˆu3¨ÜÍÝ'Ô¢ðÍ"ð×Þ o.ê×òõÒ½TŸ¯É¹‘"v§
+Ôó¨-hpˆHÞYßC ŽÛÎUTÞ”?icÍNQw%,3ÁRÞn·?ð"ä·|‚’Z› ä¤¼‡&ÑÿUèc˜¾ïH¹ _ËG ¹°ž>´’r:•ÜáYdì=äï ÛâµåþÀë­!—V›­.ÏE¨¶”WPkRŸÎ<ânÑìÓ|Žgæ ZNó/h?qSø}¼Ê[Èhë6ø»
+CáðX ÎÍ2¯¤¯=5( \ ]òsîP‡`ÿožü» Õw&âÝM˜p´ÞfîÃU#µž.åùt½ãó5˜ÝcÝÏdž-Cã•™GG[G#FìB¦‡Â†â‹‹ý[WûÚ…Šûý(R§÷é镞ù©¶»Nº3
+ç…ë>ÄWzªã1°×C“d¤²_ëªüîÎ&ú׺z’Õi\—]GÔ'7™k´:¦†¯x/ÃÖÏ ÎåEq o~ÿVC;‘~Ýõzæs1ì:`4 çöhâ³þ¦_T‡öÖëžÛ>=}‡Úô§ Ò
+ä+ݼua¹©½³v#2ÛµúgòÕ-¬qûùæ®ÎÍ$cµ­[ÝÄ·êÏëTP]µ¶^]O.¸W˜w1A½¡2~…sÓütVN&ÝÞ–D¬Ín 68T}%Ñ’/|3Tî—Žê®Wÿ ÝZ}hù­:4õÚƒy‘?/ûcߥ¢euåOöUêx~T|™@¼CèžðšÛåóÀì”Ë~–ØÖÿåZUà~¡KùãÍÌO¾ÉƳº3ÚZ&¡µU~~ „ÝeÙx†ÙY}˜cVµIÞÖ„?ûsW}ú.QNWW•*^ÙÖ‹t…=yïÔO¸W3ås·•+ËïV¾t J¥ 4«ýÀnälÚ®Á£â°¬nŠÐ¶ù(Lj)²€œJº3ÄŽãªnÊ™wXù¿$1)Çç·Æö nõ‘°Ú†pjÈLhW=jX÷M·õ—Ï£S–ŸFTºEþ´”•ûMŽ®O®Åaqò,Zø.U˜x;¤àТ€ìë¸üLgòž"Zö¶k•m?ÖaðœZ +6ÏÓ-jžgÜlîni¥~:–5öWðEž
+kcWO›Ae; ëå@KÚ'k?q»R„o·àÏ™W5Çå°v~Õ$£¼'©{ÛQ_‡ªfo­C”;X £tµDÐ|™ÝJ™
+<Ær·Ù‰Íeˆ”j<×Ù¼a0íºþ9¤Çº%zgº¢„ÝØŠÝÕ},Ýç¯XÊô•@åTßñ5–£¦ëJÍzîkÿª¦5ÆqÜk]Ï{õ›doƒ¹b³÷yÖjdãëö<XÌÀ¼UL% 5rajç2Q™%ÏõÏÖÜèC?î cõ–4‡àÎ:ÒÈÌC\̸™ª­­úû†æ™öL£¡ûCÝN²º}úªï–wêvÞÚ'ÒÏRÛº‚ÕÍ'«©Y©—j…®R¶÷lË´$ï©™jÖs÷ò]$Ü¢¢ñœ/à Š>¬¬uÝbîy:¤KYGLW³È…nfæ#£›qmw¨­ö¹æÕ‡[Õ¡@ål‡R³HUD—*ËÁºÞ“•*¶—îì‰ü¥ð®K]l>—ºüm-ÝëÐ&ÃÖÉÆ@0ÎÞ)¨ŽŠym$~ÚÓÓ‘ôqp:l´Éìt]£³ÓJ“Ï:Š˜Yè5C¬º¦V¡»ŽFß¿3RÕ]÷RWkzº£WD_izÊT‘hw#·»ÁYV³ÛÔ¦ )k¹ªØßËbJÐ#at>X­¬Mkìø‹X‚•`‡áªSÅ>h­˜YïKOŒŠÇÉÎ*½«[rcàƒ÷6›½1³\÷mÓãq­Š=­²|cÁ¬Z›¦å„ÁªÒ<©¦"µ
+E¹}+x²6ÒÃYt¥z‰ïVz)då(¦Bçù ö«ñ³Ü]ⱉQà–Î!äÈ¥¹e«4„±U5Wc«˜±›1çÈ =Z~®nÉæÎ5¯ç~ž=µk¹È.æudd2ñÕCѦ•³|Å”VØIÿÀñH­É×~‹;R‹–ã(éÈX߇øî4SE1u…+¸Ã7…|¾Ðãgóʘ/º£5·\/\¹£¦Ùͳͳ¬ñ²™úØiÑg伤[‹!LËåMܿЭUBŸñÅ”ŠÇY3J‰©Šp6Eä9Œ¬óÍöÛo–༌Æ1 A <†’Cg‰IÏf1 ðÄFÁ¢ÙÀq24VÈtI@*u/îëÖÌãõË­ôSó@ªÍnmrÀúLaÉêÍ#Ó®€>·yš–Ýhƒ|Ž‚yü
+ùNÍã1 Ùß< dΧªÄè(´»Yà³Ôx…cùë[¢&‚‘¥¥‚V¶ª‡ì¹Å©üR[M,¤?®PÆ=ÑJtW¹%÷(äâÂj·.}º{Zm1‰¯1E-^T)Å3œPèôHž)„ÝÝ')¦9Ð^´Z½Ý©îóö"ß%&s›4F¦:MØOQ àÈÒð™Xx‘˜—°eåPÇÊ,ÒÅÈgqŽVÁ Ù>ÐŒÔóu P² ·Jý!,CÊ–ÓžËp¾ ·ÔÞá/û‰wÙ¼î2#ÌÍl8jg—BÇKÀ·œ]­UÑ>1<¿d[$Ç´!„9eín©¸¸ùá±Ô%¼<x±>…q\XãX9Ø1e§et s:ZYʃ*rÎ!R¿°SDh×pë±°é|úf\‹im\èA½çýõÞ¨ õ>P+­Õ[㸯ìì“r¶Ñ¿æ÷Æg–˽ÚíÌ&¨z¿àÛLw¡H¾9nÓ(“l]…PFz‡¥gð7 ‰Û'>‡½VþXOÔ§ír褑­±LyDÄîÚ ŸW„žQ†:h¥o»C<*¯ [èy®>!C9S©a/o¦ Ï5
+ëq,—¸L<Ås±”Ô/ƒMM-€Ch%/üºÖ
+H¨O
+©y³üO&Çå?…$(¹Ðìüº€sþ¿M°i 9~Qÿ$ º7 …²ËÂ^ë
+÷ •SÓø*_ åËV«5ΣÝ;íéüxsӷórT÷CÑi$Q‰uõOìˆÝIÔo°é‡þ?mbþ'#ö™£ì/?†H±uϯÍÞ ¯ ÃËVA¦çCæ¹8©ùê"Ñöðt£ã¼ÿTüóª»÷XïÍ*+AkbðU•øe@ÚúB;ÔÊsý
+F3óùB§“œ£&¬6)®÷îßÉÒSÉLþ¯â5ܾ“¬8™ˆ~¥‹ûù vß§Ö§
+äáŒ:†é“´7·ee³RÏÊšF™å¹àê‹[Ù0ÚÖpæú‘+ΆRÜL4ÔŸ"Ñš¸€ë=˜¶Eg;<xû<8×hÿj_{Ã(Ò¯¤¼Ô¶Ù‹²ù;“ÿ—Õ~Wñº®Øþ!Á¡I°éšígk‹[ñÔ˜?½M{ö©mú3ót%?ðï/£m¾wÜú}p®_ý[Ý
+sTʤ<Ìò.éHƒ+]_,ZD¨&•(Wp˜øòœ×Ÿ5¿°½üªÝZå©ÇãiûF‘±¹ãZ‡‰±¹´Aÿ•8™ÆŸDJôCÖL'9 ­õñå9>MÚ+ËnÁ,µPüL³^ÑÂT¿0)«ÍXj½‚CõÇÎÜ-üØ_çWÍÅ!O]{ÛÙ=l_{€ï^¢l5rižÇ oy;“»ÍÛ…\è¾ZÆscL¨z¾púç´ õÏyýˆåþ1 ‰Œüt–ññá”T6Ï/…z!_´ÐA6óÏ«”œ_Õ¢½ ¶šÍÞWºuè,«‘}-áƒWÍóPhšJZèänS½ŸË`ÚÈx.ós#*W7†AuŽúÐïÝt‹Û:AxÖ‘!‹Ü:VÆÍ<âweÜV¦ÚjH<5ÏNg4/oNcYe5>¨Ü
+féMu-hÝRfô
+hcÚ;á&[bAaL%ÕGsa¾KÏyŸ5"w$èŸu_Ö‡•Eü]t‹¹Ø:t
+8‹œfhÆ•ÇD¿-m^%Í˼5uÛªoby•ƒW9LÔª"¢Ùæ,Ÿ¥®”ºcé¶Y¬¥Ð)Ã`1j.DÑ3Eás;ô„a·u,}© –Ñ bYDñÃõØŽGèá“êiⱑ¯¦®Ž©îš>fïlkˆXÛ`ûtò?WÕÝ$÷QwN>­r%RWVD !äó“eå`®KÒÐ5)¼TM)ÓîÄ—:«ˆ}øÑ9ÓF„:¬uóÀOÕ+àÑHe¹…±¶`®”²ÚÑynÙuØuáí³!Ûe×õB¯Ù}ø—
+?, 3oí[-·j¦ìÊYŒy¿QåVSòµ”zÊíËò&·××·Ü®ÜRÒ}¶‚¤,¾Š;qñu~ÒbNyò"x Š0šÉ†`óŽ
+ß!ݼcÝ$Nù.k9_©˜ZE3uÄÉÈZufSJk;Cd-Ç|$]]>ÄA±xsí,æVù‡˜²òoaœö .x(?ÃBšÇÊ!Ï-å­Æ•n·GNY—­b²ÿ³Ì²ÜaêTgDŸü͆–N؃êÈU’RïpŽìe›Mâ=$Vñ@áˆA•òˆ÷¸Ü"õøªaÇîÖýÛž}UÏøÔ‘\bûU…CÊ!×CÄO5ÆE¨Ø{ˆÂžùÙävà‹ÜöÈcÇç[¶ 7GÞ@Š­fÒ(Ë¢"Íì–ÈÔK~†>m&Ýê¬J´¬}T§ÏFä3•_zuv%>œŒfsœÅÇ7¾öãHf1Çfï Í>Û2ŽˆxÏ7„fus-lŠ6ì&nj}Ò:Úvt…†IÉŠ°0©oÞüÜ+Þ¹U:sT~`·)wòÕàÌðÎèýÓgq‘¦ð"©›ýb©0¾r)m®èä³Ñ(’ú%ò‰¡º óIÍð‰[=ãÈúŒ`.kªèª±¯ Y™þ´½×bëFº­ûzˆBÎ æœÁœs3A‚äÍ~ú](Éž^nŸuºwÛ¾–¦$èÇ_$€ú4jåÕŠ -jyº,ú.½MnE2Û\AÏö¾Èdoø,9+œÉnÚµ'¯§ª&¡®®³·²=Är­è]¤ÓÌ=JZ`íÅv\?
+þÔº ½J6àƒcêÃuŠÇ¦÷ù4n26GHbš³Ø1ZsÛL¡½1\D>Ñåå 
+¨Lê ¨,Á@ñ’€rZ=@Åêp\
+h<ÅêA¥¡\†€:î@¹,”mPëí }‰ô¦qr&¹QõäŒ÷öf‘ßà;4{K—úŠ»fìÿ‡Û—‰doÌæ
+ÎtÕ[)m]ôÈv"w ìLîŠ]½ñ¼ºþ|
+o
+e>lYÛCÙ|
+J]èÇE'²1¹%!(ôN¹)³ sDìqf0ZÈÁjzi
+VÓaxrȀߊê¿Õ
+&ò½ÐÂ{ ]ÂI+
+ÄŽQT¯]ÓþÙÆíþÊ»EÔ¹mÇnéÇhƒWÈœ'„n[ž±€u?»Àš{`Õ=XùKX¼J)¦šÛ Yn°dtÿ:EIÊ &>F?†\À(½•ô+#á—Û¶Ú ÿúöxû§¼u‹RÐîdÈh‹bm×ý=pýì÷Nö§Wö­´ö,C»–ƒwq'»v¬-ÿÕú}d´ýS6ʈmÕ¡Ü¿%Ú6Pv>ŠGx~²Oâ™\?™Çœ¿óªúTüüéiÜ·æÜ¼(ç•7y(•¯ÚÔhý¼39åþé8ÑTáy¼ªûmrÑ@8t¢ïì÷ÖS¡1-•ÿÓþSh :”† :p‡
+Þ…ç›±¹'^À+~þprïBkÞ¹žìöàrM”Çg?“š†œ¸8N$°:ÌÕÛÖËûÓý^ÕÛS&wÛ\ Å÷Ú¯´˜UИiˉuL.æ±Dg¾J§>ˆÕ~‘ˆÖ"Pûm ÓM[×þaý+ÖÏïÏ´1Ñ«Ö{sü¾ž†ôì~Š­}p‚—,ÞËÃGïí)Eós½×ÑýB]ÅN}#L<¸Õ#‹Ì3mÏW±tbÎNÒ™Y™È§5&]›œJ•ÎøZëLG~s±ÝÇsð%ÄA
+Ï6ûYF¾ (XTÚu
+áPdE¬Å „û‹«‹ï´<‹kxíL-ñ¦—]dî§0r`[›³ÃOwVÆØÉä”?ÍÇײ·ùõ9ü¥­ÞqðéÕ.ƒä«|ïg±ø£·ÊÄß=v•&ºe.Çv¼J]ù";òqj·µë9ßjG”vÓôÖMÄ w˜iÕÜ8õ#B‹phE~ÙÄDQœ)ÚÅk'¤2B¿RWÉÇëSã!Õ[%W|H{p_ë± ÖîxÅ Ñ>U¯™ÖÕõ
+-óæUš½È¶îƒmËÆN½Ægr5RÄiVŸçüMÚ|޵‚(?«Ûz–ª
+—•Q©YB®|ê/Geísgœý"‘eµ0,óÉï•ü!=â•BïÛº2<ä¬E¬¶ÕV§+wh3°ý fÝÀg̾>ÏH'ø;(¼VàZtu[iñ¯Q’õ‹,»VÞ,]û™h©íTE\Í{ÉA©ÌgBœ¾ ò“³Êã^î–›×îLŽºäœìªf ‘ŸeŸž)ÇV*Z¿Vò¯ý|>ƒ`7ìu ©uHkñi£_Ý–¶nÙ5ˆR©¹UŠþàá¢8S8yG…`j/
+Ãtv›ÿ, Ç|Š-\sórÖÏQ‡T]Á‡–lAí„+ù;|¦l.ÔŸ•üH…tt¬WR:^ë'¯ ÜKZL KøÛh¸ˆºC«ö¿å÷•ü³Ò§¯ („fÔÌ«-];‡B!Ž¿y|ݲ³«ªle¶®Ë”u þ–Œp‹æÒ^'S†2h¦åç ›: –£”ö™M“íÔl™´ˆÍ6á/.Ç„Í\®ñ`‹Á‹GY#bŸCŒMê+†_.gÞN·¬õÚ9ÔsDØ«áµb¯Æäù‹„ÿSï¿½Ñ £"Z›ËòÉHF¸ö”Ô©¯ÉëèÌ&üÙ‚OØÔZ‰«Αø°H'âq^ÉÆ&U¥KÉV5†ŸânèPÕㇺ†öªãÎìBd²‰n«c´ QXd¤–®)…ü¤­ÓªÕ±Ü¢êYºÀ‹–.º}K—ð­åV„„æÑ8—ÚG)G ç‹L+©¶šx`k6×,–§/g^o¾êÒÅíUk@ÙsÌþFBZ´l?#QáÅ"Þ˜OGœÉY§¹Z¶Ü¬Ó°tºØ5¯ëþÄ´øÅÚð=p ªµ4¦ç…¢á°éqÓhŸÇì®}) å5Öã6=AÒºÁ®*¶áãÇ4•˧ëq6špLNŽ7‡¶‹Lÿ-ûÒ#âËK¤ܬӴûø"-7=xY:|ð6¯Ë %àM‹õ%ÃßáºÑ«°=8²1}ØÐÒz\KµÏµÑÐ&íe_KE°¥:DÎj6¶$TDLe5½U”B¦·T8ú$+³‡ó}…c±–RÈómô*ëR§Ùne‹Íg&Ù9¬¬Xz8l>÷$#jÖx™w°¼›)s6ídô®øÙ°ÍëE<ïªkׇW‚·ö¹Ð„–2 ZÃ}Qø"ÕyOWÔ¬²TêU‹+«Ñ$§p˜W“·sª'—³ù•ä­7w©VŠq’"ÜâéÐéˆnÃõE×ÇD·)—‘ÔŒ\F'ºž>F©øØŒëö&æ¾`^3»Üýi8ËüUOLð£6õW{-zZšž4¢éžÕ_»ªt&{WÖ²ûR
+§®pîš‘wñ‡$W0B—8C©SvZRN½ªØÔ·]Q¿³‹/RèDKWÁ
+> ÿuc¼í“ Þ~€#÷j:÷Ì Ük镪R¬5É“íu5eÜÌ8Êg@<Ù¼¯þ>v}mVfÏê2;÷TfTØ*EXRœé»ZW­ƒÂSÖY®”«,éñ§TïÄ?’)’â…ìóbs¹VE#ÿˆ
+‘"»Ì÷Ÿn‡w×7†¯KÏ6½Ò–,]f‰5÷LnÞW˜ÜÁÎ~‘Ì’(Ê-Âe7õJ9ñ|â1‡ÕJDK^hc°ÇÞ:Îæïj¾v=)¥bm/W3ÜZ: ©ÁNRý³ÛHj{·/Öá(ýÝE¸çA tY"[ƒþ"ù•–xçÑ4¹±3Šs‰÷­ÀÎR\“%Ù)“gV'zSÖIº$žljßÈÔ©ªnxTÕH«Ô¾Ed¡Ð¹Rä 2Õ’ZŒÔ“c •‘l™ó ­O¤û¾ÆV¢vSJëùQ–«öNÒò·•Øšôç¢iUfÂ}‘˜ Q&½æŸ›ÜŽ”*G>&tnܸ7
+¸¤½Ç9ìõdÙL“YJ€—Ó|­’aXeW§K-zDíý|º àL§dg!¹Jj©ç†Ô2¼D6º‡ ”s¶ð¾Ozi­ú.ÄææÐŽÊǦlF_]J›ç—/8Knrï%mÅmEs²X
+=¢>çǘð±æ{ôErc›Ãé7³fç4¿g3kõÄREçάŒú‡a»-’ÞF7"]
+Þ&åUã›UJÞ?†¤«Ø{R;
+¤ë÷* µô:q…rÉ·s€J3@3d1 ˆg§>½×Í/2NäcÙÈ9,cä`"ßò!— ~˜7§ø‘xÛqS¦¾b³¾4gŠÔ}J‹·ÖˆR¥Ò˜l.[ ¢ói¬ñ˜Ù`ïzr%.±#˜™ÉÈàÝp^IÎ= Yƒ6gIÖ ô§€\Ùw@®i£SãÍ
+Rr€ŸVÕToºÎºÌf¬ÛòféÓæQP¹Ï‡Î3#Ôð*À9í^aÀâ°c¥ÜŠ)Ú䜮¸—Ußͦd‹ìΈn¬µÄcViƒ%s±- øÏP¦v€笔9û
+Òô#æE+ÿ¤gC´"»cYŒn¶4¢¯L¹á”ûà|â±>ßñDµyýUÔTvG°®Óù ÁÀ¿Í:¡‰w$΀uðå^ʵ2ØJ¬e³|Æ
+9/¤{u
+¨”›ʹºÊø¨¥B uæl§¤”R ”SôYm8(æcž=ë§OˆÚ¢’ehw9dþC¿7üî»ývºþÊ»EnÛ0åÖÐÉ0¡“FÔ‘¨Í€! û¾ôű ôüá’ÞøÃºÙ)§™Ö¦ªUcŽ W ã{[¿™¦OÁ¶á¦pÛþ™Ú¢Ì„nQ!?{¥ý
+»EÌ4LIø6¹†œ4WA¤1X€Hœ¹ˆ¾‡³æ{%¬ž=V.N‹€yÝ%Ç9Aȶ©^eçâ¹TÑý­cßþdÔ'Ô°1ßõ R~eÛ"ÇïŸ,·ˆÚ†nÛˆ:/}gÛ~mÛWs&ä¬'pœ) -•
+«áwpmXÂ¡ÃØõþAı[¦&玼kûíSeò†r#Û®L ­¶N¨Ížõ2Ý`
+ge^©)¯|ê¶¥ëðÀ”,̶‹½Ì©Q6ÌûÓ¦”™ó½~ÿw&Š¢TW4‡+`߬ÁÐ uþ–m)èV…>®xuj%¶…ŸÎÂðÕ®_ÖüÆ«d¾
+ ØKd‰ï¨†™²ÿ¬Ê¡µmæñ]ÏÉÍ+ÛLŽ:ú•lA—{?›PE± #c5\YŸ¨7Ò
+äUý^¿oñS:áï;Úi 5 †‡²VÑ"þB0QSùIvÏãëK67/>‹¹¬€U¡­å¥Ù‚’ž|‘™m3½È”Ê6íu‹^º­ŸS§aýžrãýWò:ãÉvúÈ%üå[Kô
+N"ì:•ø°*NãqyuMš%–2Ÿs´jß™÷ëO'ëäH¹¾÷On‡rPyÈ·še~hX­É?8tLHÉvRÔ“®G dãPzÙ„?Ÿ—6=©Æ‡Å‰ûì‡íؤ
+ŸžS²7‰á'édõ`÷E:Ô]¸Ø«næm³A›‰n‡-'J…¨ˆ“£ˆ7ßù‘ZžNGj…ä.¢pIï— {í …ÂŽ•¢éš•ý6¬Æ‡‹MÊ»÷ã·“UÇ8”#ãP— ïPWR±WmÉ´ –aG·}5-;N&*¼Ó…ˆ7®T#
+^q­Ó¼ß³Üìnjé Øší’r5-¡CþÐ^}X4l- ¯0zp‹zp?·ôÀ·gH ÛÌÎÑ«4êbZ~Øb¦¬«Ø“¼EËÑå9â LjªDB-7Ý  ¬
+ç¿H³ÿH¦ÅQšáïËèÁÇp=8š)}ØÈô¸ÖªkŸë¦§M:äBßÎI6¸š§-•ƈš²švÊj‘q”ÕÒè¨4™èªÙt±‡ZYôŸ‹|í(9’èóÁYXoZ˽•Nf{ñÙþe¸2l¡¼ÒÏÝéÃZïe~ÑãÊêÅi“æ h)ãEj¸O°ê¼'JjÖ¶t•zem¥(§B‹MJòv´år66—Ezy‘¼M’•j¥wBRÄu_Rà¿JŠb¡dªR­V­UË­ O=Œlª™Úš±é°Ã¡Û°eè›»ÑoÈG=!L¶Zš¶á¼R#
+¯©:ßçj6²^¨Ôs·VVƒÝÊã âà¢p€öåíT|É")a’·ŒÐR-Ÿ%…íâi{Hˆn…+‰ºTèíÆj)XzìÉû·@á{Ý0C•ïõ;¾7à,(n–÷ŸÃ\¹©LZÙbñ’F&eN؈ZsÞFÿYºê˜yòÔÜ(¾VŠÇ\ÞG¹«Oäò§2“Årz.yc.ÕêÒBRTcóg¨ÇÝ’su!w®^ý%´kcB°ì§ÈûoÉä{ãbŠ{á£
+7\ÐC0î‘L8ŠM¥¶16•^ØT–¡Y‚ž:ð£]¬ØïZŒ”é&ã‰9­„þdçM!ž¬'®òYͽÜÂw‰¥,5FSIm'FbóÀ ÄæB‹Íº:õ79nÍ÷\èðÏ%ÿ¨Þ7|_Á¼ý’®ÜkäÜ(Q¹6à8°_$›VøKœ+&gì íÇôºv&è¢êÚ4·ÕšÔ.¨]†×¡8ÑB¢ñª¥Z@‹ÅÈáNŠ4Ìa Òˇ–>'¥d¤¶Ò±±ž‹—vq"Üë$¼W
+‘ó±Ç?Ë>ß¿|¿óNf<áÞÔlÎ%
+ã‡éû=;½ÝÏlºK>X2jbÌ"ȳ Shô†{&h¾ª–©½ÜPR“>GcJ’u?n“j-p¿HRmH(w…T݈“'ËÓJ²+¶ÃÜú¢Õª5p}<hù*KÏrµªlÄ˶3î‡È˜®/}>†u;Ü8SírØ0Úá07¯ozrȦw±1Kv½˜Ü3¿f˜Aë@oò›;]bŸj_¡hª*%UJ:ŽãdC»—HõæôˆKg·#Zц߇ï&†5<Šonx”hˆxäqŒd7N¢˜ˆ©«¨-HEÁ쮓J6ÎÜ•Òhx+°ÍT}Æ 3â>جÇ%£ù6›‰Ë-–¼Q-&où-†y]zÓÿ èR Sû >§ªem ¥t¦dqú– µ%—OÎĉÖd–Ã}káÑ|n‰ñÂb•¾>”]ãuæ’IœãqÃÌTM2OÂë|TÎGxä®@Ø[®ÞËžx=ÖB´Ÿ˜ò±16â’ÝA͸f+äü7¶Å°VТKým“ò˜U›ªn¼>%·®#²áŸ¦dãA­I­¯ˆ«“¼mfp¿´eñ¨hØPÕXì2¨‰ENAò±x
+ø–NÕ¬'øíõŠª‘)7á/Cv.·zÌŠ“a…Vx8Á
+m‹’7ó6é&·}â:Ÿ‰vvzz-Ú[ས¿Ãm“:cÞüÄâv–
+ˆvdˆýDål¢ºm"G¾áÜ£€ˆI©DÐ)el†iÛ{Vƒ3øßb{hy|ð­ñfçÝãˆY¹¥½…hÊ[\›!OV=XãÆë7ø¼€?jó6J”`
+ŸL
+k¼UÀr° Œl^Ùæ(
+Ø‘l©Ð„,
+øžÝ˜Æxaàp”qB+"ç¯ÝU‹gÀ¤.*ꧤ_<ùºý¶ÿn¾-=¤ßˆ©fu
+ù „S¾eÖšYaŸ°…±Ê¡´K
+ÜÊå“Ô­”´‰ÄðózFí³£4`XÒïnÛ¿â¶(àqÒ?¹mQF2ÚV;‘NŠ€­ã×AdÙš€H^#"z•
+÷I´º·°”­ÌÍ<
+ÌÉq,-¢Â¨•Hžø˜Ç(Ïb™o÷6rK#kò7Ø>Ëçï’¾I6Úª-ÿ˜Óðã¶E »¿m¿cm!ÛÛqø½û˜C÷7
+ÆÓ"Ó%Ú&ë‹Diˆ‰ŽÛüŒ›û'üU§£1ê)jJ%?øtR'«½qitË̦ÕñJ›^Gö–ö«,§­ks¹l™÷åö·Àû£ñ™~ðFŠŠÊaÇ
+ãdÚóaÚÀú
+q¶©ä'±ÇO¥;Ê@„ÁÐ/2¥3€CLÑZ†€Êkú­ÊâÖÕp%Y»½J¥6œª¢M¨àG…üÈmP^†éò>ÿY–Où[¾}ïD•ÝO¸²>Ü‹ª ŽØÌ¶5•2‚¿3Ó^ŸH¦å·UN¹Év?¥ò1y]œ˜d;,&->ûާŽñ#&Š2MQ´îŸñ‡Y«_$y9joZ™mÃÓ2eíKûꤽ•‚bæ¡”ªiùYrSn¬ÔI^'ù~²jLþ¢:OôrÝM<Øt½ø°´¹ÅãÂó›Ô)*–R3ª3oÍ’¡wÔ2šõ¸íìÕ`ÍÚ…DlhsØûŠÖïÿŠ3EöZT#ê*–4WSÚëÅ%z™"Vs$ûì$:6©D(9å‹„ÿ0µb)idÇðã:édµEÆ¡®»‚]°v›}ÜÜ(|LíFË1qñ&ú2RK•Öi1|Znžæ,m;æug6Ívåí™–âDÑú}ÿô–F¯U[|‘ècôD}ÌO²›Bɨ¼oºjI÷è¶{¼D…`pŠxÃñÊ%ˆÔâ÷O¤–Àñˆ7ዉÖi–Q-Jæu•šíB#þ–]#cô*ã’aK—¦>l¼z\¬´ÏmtÓ&]‰ÑRÑ]\Ã_nWÃ߃@OÕ”f œ Œõy:ßC#ºˆX­“UÓTr¨²;Ób&+£W*/ [(,¡ôvzàMŽP¶¡1ôzׇµ Ðã*´I „ÆPšÑp_ÕyÏÖÔ¬]ˆ*«Q-©«’Âáx[ÞÎ3s¹œ=^¿HYdª¼,²‰œä픹äíOÉóºu$²(̨•|'¯ë#ˆÅ;*FW„iõž]h÷0Rçz_ÍFr=•zVFPú3(a•_Y(ç½Rˆ=N
+ÀM.§^Y$éä-URªå¼¤°uM<mŽèV@^Ô¥TK¸½¥Ðvó`‚.X&ÙàýGCôS°"«¼ÐîœB§}Ì‘)>f|D5›_–~k]ôQr¾Õˆ7W
+ÏúP._ù®,&/-É››R-;ëH
+½ìC9Ž <§âiý^ˆn [‹ºÀì…«§ž…vÍñ¿HÁRŠÞ?h¾×¼(¼mJø•7ìݺ\Ü)ïÙÏG¡ØÉ$H²“¿d'óŽåç
+#ã
+VWƒ’‘›¼°-Þ? ¾ç
+=ÞÖå!ÜÌ)7ìÄ–\<’ß±Ÿg÷ÂN›€MÅ>$K€¨Ì̧ƒ“͆¦:“/’^­í½Úp](ã.]¨´ºP•LzµX¹ìëžJFÔ‚f¯i…ûCO´‚ƒ²Á¸¥,å;±y ûB¤Øjñý}ÒåÞ[çFCÊå¬ïrñkHH8PÞ69òÚì”8õX"·@ù̘K­˜ìÖ<0t9§×âÐ…ƒÏÓ\^³¨;ÈSåÔ§ÄôâH‰Ñ:Gzg+Kz—Ù…ô®]™µ¥šå±ð: ¢­l–¸ñLîY›ìå}ä±/)g$t^û.ïÌ2M.±¢êìôãUY25¯1‹Ç°Î,Ósj¡´› ýîuèõxا‹Éù˜æqoAíæáß’¨Ý¼R•\õCIÌ–#[Á"ëåVžT%¦Oœ—£#Ñ,¤YÂÀˆ¡ûæ†ÐM苌œ>ùxlìÄHoaú{Pº©Ìº¶C<¡d~` :°c‰@sÙôÕ«2KkXa˜z¹BoÔr.nR5š/Åë4ŸŒ7¨=oR•yªCIÙÂ
+]R jߊÃM»FIžëR@•TƒW•¸ 5¢U$š„ÉÓü¾x·jϰgP؆N®àN0žéÀ)×8-× <a¡ï °Ï(°›·ØýÊ
+à•øÊüðªÂCYÅ
+˜ šxóìÔ‘?´¡<z€(ˆ(… \ï2ØoÀ<‘Ñ2S€(íá ·Üñ
+|ƒ¤ å’䱨äI¿
+ýÉñþhÍ3eV«î€.Wä¥8^“t7—èàµn¿ôn
+¬l]†»Ág"ü÷TP|o¨b°
+¨]¤¨éa¨Y~½ç€ÊUÛ€Ší1@Ö·gÙ´†ŒLßÒ¹Å>¾3NBìÍ.p8‘ã
+Ý!U{
+]²©""LZØ[)Ã{%˜ñJ{Åo*ávÕœôu¾Ž
+~óºå[€.¿û€ŽJxèV¼. éÏXŸ®ß’Riû„d˜ìSp¸ø‰ýõÞ—˜9u˜6~˜wd‚÷±qv«á³ðt‹½Þÿ€Êò –”YþpRb¼ yrêyLǸæÖù
+yGÂ÷þwfÃÞ¿ý­¿,·¿¸-JIø•+‹8©«š?‰¶ñWXyô9ûº²2öžu€¶>€–>§ÃÝû³SÙ+¤ŸÃåor8`ß-BL5Uƒ
+AY¿¿¢¶–þdÛþŠ”Eœ4Üþ ÅÇF#›)ˆï[)8|—]T`u›e`Ññ-07¶
+‚ç'‹…ÆÆ--Œþ"lê½ê ãl­›”íú•Ä*®¼:agç§ÖZjâœ7º‰v$ßD¹^ÿþ
+YÄP›ìߣZf,wL=v¹9K×´å|¬‘þây»s+³·v`«{ÕÍ+MvÜÂ9ïs‰ôfð2{xN…SÔõÏm³¹¿œžÙOØrå^L±iI>[_ä[ïVA¬<|ä¥éµðÀ+»©€ðÑ$|1þñÿ¿úüW]þ"ÿî>ÿU—¿È¿»ÏÕå/òïîó¯.Ã.r$®gϰ™n½)üvx–‰æ®Ø&ÕámBI^a çÞþ-ì-`ò;Ÿg©uÞàpÿâ&×z…É\Oè'&K1âhgÉhŸ€Ü»RØè¸Z:¼*ÚÊõÆ_¤A˜ã“¶¨°Õ6¼0L\§º~zñBÁá“ÓœWÉD¼ú)/
+v‰îVÖÕ<·'÷¶hÓS_êŽÙ¾;0ëÑ+ìÓL¯ÊŒTØló\F<¼Î®Úø+¹>-=w+&ZÓ°E§=ýyŒvÄÖÒ£°O^{ê÷lV}{Ñ™×õªDêàz0
+‚ ‚à„" ¥÷ÿ•ýæ={Ÿ}’|Ý;É?:¿~X‹Vu©H ;ý#œóßFAßt…W%Øú‡¹&¯ÚVªï•™ãš’H(ÚÒHÏeÍ
+ëy帿Žòóë0Ýùé7ÑU€‚Ý„¢÷žy)'ôOÖØ›ç,©õО-¶W£×èÛ›éj­¯kצ¼êO!‚ðÎ,ÎͽbÈÌÖJqö»ñ¡ù‘ý©ðCç
+O²û¸Þ)3”5ã‡Æø:ÕÃYl.Vm³µLm»‘+Âk)F]‰FWžæÜ›0fÁ’dâƒñ=D^úàSYa íB¸ß‚b‘ë݃RÕ´¾šñ¼<¹T¸ “ñìmtHiœ/
+RëÉÍ–ézžZ°ÍÌ€ Û©£îê-ÚZoÚÿ䇗 hqúÕ@!7{"k B¹×ýt‹N]K"=:½îÀ°|Ûh;ÂY®¦ S$?}ž»óåÞjÌ”IX‹ SÒO—'´Ãüv
+ÝÍß(‹àÜzæQ§0°³{š´c‹ËwqZeÁ>±¨ ÑÂÆ÷™'“™t
+¨uz 7± øeÀ{ùß'@¡¼_ÜW9Á¹TâÛƒ3«—Òf—ÍL4\6t¹}”öb†2o Ÿ+{ì€yzLöŒ{tAœyã[.áDSñ(YhyC•݆ñÝV¯/¿  $Ï‘¦Ë=*0SÕÌû›-9’X«…Üd…ª¥_=À¶N\sz¸v¾ŸÜëÕ¡žDÙ¦ºrê0û¹=I'2Ÿ?òÙŸü@ØÎïØ5t»¦BU‘Séõ‹¹ç—³¸Zâä’Rg“›<1Å÷kf%~Ô‘©¸.ËCâ1SI«ÜÑÈÌÓ‰É9a(µÞŸýßã©O«y(t5fÞÔäöaçCǞϵ½BÈÐÉÊkâ±Fj$7É"”+·Hkí}! ô´£GvOfÅý¬ïòÛçªÚâä¯ã_È·˜.ȧÍ9È—Š6À• (T±÷êhÁvO ðêï76Kß¶˜¿vÝiS=®G×­3lvô=omá%¶ÛjÞ2õ{Ûi
+ãêN´g¶¶™…Ç¢Óëf(žë“Mœ¾iÈS-û½B²æ@ï3п‚â\®ÞW‰wKòš[Þ‹ö!¾?;vôs!¤CŠáíåG•P4w“~´Â5ûœø¢ßx¾‡òÃK?ÈùÞ7Ñù
+
+WŠš”JÈ£yoô­¥çD—öy•õÎûa.wµÜÝìºmÐuOïåø»†Èµ¸2å[qi‰äa]¹Tô.$ÍÙù÷à›fë
+§Í&{
+eÌZyw9žÝŒ“^¬EÊ
+ Ù?5cüŠmÐA4¶®¸{°êNÌ»òñ磛ç"‚üüú?
+"È_ÇßD‡wàˆE+û<(¶ŽÜÃË›÷ðáX 14Ä+ÛM\N…»¹óX;<)§NÆ’Dfû^°åe-«ë9߻ȳue
+çü³õ$zú=ø»f~»ÌqX
+¶² åŒ ³nºwr™¹dÛÔ«°½<Ïrqj¶ØäDÐq±6G©å$Ž %âXø=xÏ”Â+=—° þÝßЕÛH¯MÜÕ²iWuNÏÜØ0,Rªu²¿ÚšDcq©ŸÊ¬¼`0†ð„,=Qö©ñœË'GÕ„ž®«¥¹ÁéD¿Žÿ%
+[+Ú#·ÄˆbškÄøŒN£Óœ«¼&ì=ÿ¸2Y=© `öΓԨыØö±ßØÊ‹Ô{¬TššOÜ:çS/·ðmΤÚÛÁ5#ª\T<Ê2zº¯r§Òƒ C)dNi^ô'\'ôÇÕóÔ§tÚ¾šv0ŒW˜$˜ L ý"È?N|
+Ömv=^¤íŠÁl;X$=µ¤EË’QÞÔ.5kqré4œY½Öœv§¦41|C½ºªJÅ´1Lf±-Iõ¬’È uˆIë´xå0`ªÊù÷ èQùž3}(9X‰#å«–TÁ‡Ž ”vÉ×¼" †NR¹‰Ç¾ª#¹)÷¨¸Þ’Ö›™}jFÐù(78²¡0˜Cq€_C¹ÏU/*D¨ÿ«£ÿçì'`·­Û¶ž3!®Ì×q.·J–a°FsºâÎÊ7üÇ™Škb#6ÊL=usPª[ì»|½Ò/ÅzÏ‹­žÐ,v{UPìw¥2ìŠÝ"õuDï˜ò¨
+Y§j 5‚|¼üKü-žùR j2Ør&X†2n¼7ÚfîþË3Ø Ç7ÕËùêЇ]ì}†ðLOï„úF¹z;!žåÂC:Šaëðþ‘†Ë[Ôì¸@É“qS?X‹ýèÒ7Q›|; EWõ(lC5(ºÆÍÇõleî Õ¢|£×Ñ..U¼c^Ó·ó¥u°mR¡§Zøä,Ÿù̾TŸíU4˜ØïwàMžæÜx«Ï¹îVûÀ§¯}h~Ì“ó*앺°‰Û>ÌyÕ|TçF&ì$dÊO¡ý­·C²_¸Y‹°áéZM:o°(º_W™p·Ú ï–^dÌE¾Ííæ×wakºb°µ•¤} ‚üuü%üW”€‰_·t€0žWr
+ÖÓ±tmB ½êÈÝ ª–”;T-•¡)aÆp_Â{uô
+OL-È ¨—û:"È_Ç /Í›p
+’ŸÂE>ñÑ6ê.OãâÞYÄf»zÆ>n‰4—RÙ{ —úN pgžž¯e,ɘ•gœž.3Ññb2yj×k¦Ü‹T_ÙŒ)bƒÑb… ²9æ—
+rž„]TNDnºƒ©1¸Fþ TnõàW
+î–:&ºÛô®6îôt{Àm¡a+*f¤œ'óÕ}˜fV cèäª6ñæ%bô=šŠëôl˜´ú™Ù—E‚Îc
+ž ï•Hm¸µú:ý’žzyù1"ÈϯàÏŸëf·B}’ÐúJc'm ãz·ó}{fu»šÃø ”‹ß©¸vJ’µÃ [V³â¸#æòÃnŸ«Ž‰~)S=OœNzBsÊôª`:ïŠÝ11]þ
+"ˆUµ2¦ž6Ñ“,gÍŽH…Ä:FÙÙ(zf'BÁå(µSÉŒ­m³»ë—ü±Ûì½ÈL´+væHç¡MR|®#x¬ÓAøBûeòå¶J­j¾¬ë¿€úd ºÚ(áMa½tÝÁIžxr1x!º}Ïú@=ò‹n=\ö)8suûÓ+ur.“¥§ËU]—…ÂúМ*ʾSª«ŽÚOoøÌ·møg;™»†æ>~ˆòóëŸý„fùeÑOhˆýªè'4ÿû=ñ|gÿ&þÿ—ýß“øÍò‡ÿ¡ùÎþMü§ÚËï‰gùÉÿɽüÆÄhF?“x,Fç¾i®^Ì{…ĉ9°õÓ
+À›Ã8È_¦ÍàŽQÔ-!÷Ù ]­ðçù´
+R܃ýVÞG©úÆIÄϺM6ÖΪLö» Ñ»˜8¦ž ž/Û€õËÿ
+ÉfÄ~l´},¼Q1l)`|3³CÕ½p®uP9ÇžÀ¶n.Ôî^W’[%[5[™¸S{}ŸŽÄOÌó²;¯©Â@-ËÒ/¯þò½³ßÃ7ÑœE7<Œç} E}Æó*Ì?6Ú>*Ψl&ÑÉ-}jîb—<ì›{˜>îÛÆ:u6µ„ç˜
+­o57@¶¢ˆNÌU[)íÄ«é {®“X}äãåDaS²× X(ï\PÈí  ð”¹¶÷MƒxÐ:èjçúÞ²ìš7M!¶zÌg´M®UÛ¬åº,Š×Õf©Ê/U ?vÚ/&§”Áá]E„دþ%
+K¤‡’îeÈnGD‰Ä}T Ûb›@ðÞäçx¯ŽþàÀ.ì•ÚPt¹U@1õzñQˆ_.Ή<õìîœrݧÒVIÏ*M¹avK»MU¸›¹Çgª?ÉÒŽÔOE·ñ¼éž|ÅÛ BçÉ9=æúöüXêO2^ý_`àU
+n.:—uûÅPOîAãí>®fbjÝdÃMä`mb(,¢ƒ1ÛMô§½tªw2Ü'
+dXoø
++ð^HclûdR_‡DF&t‰¿úÌ9ï¹éù—
+
+V8ó°Ô}ôn/µä :öaYFý•ÓnŒ„ÒN]Ì_˨2ËÌê[z>§¬ñªéì¨'Óq†Ú+<ÛP;‘éâÂ%Æ“¡7Øu€\Ø/Ü|лL½è`Äþçø³
+ ÇÕ ¨Q…¢BîöM´Ó…¢eŠî €ÕµPHy]ÍFžöL¯Ã¤É^²u”sÑ«%œqƒ–Nó]Y9•º5óÈ3Ø>‚| óö­pçÛqØ
+·×ê½C•ˆ _Ç7Q¢_„=ùÞo™Ö
+–#^ò}iç.Í¥YAŸ‚Þ™;‚šÜ…°í+h§¸ZχæZªU¬_¨0è§Vüö„-ø¡Ô侎ošÌ¨°*þ±ßrÞÚ‡³E'…>(ý¾Š?žA×_–½IŸo{šÛ§ÉÜš/os£¥ðsu˜è³
+Ö˜Î×¥“½"Î/_YOZÆW_Œ/š°È)n‘³³0b/_ÂEIÏ^é½ßr
+×ñ±5lÏϛժ—?'D\›ì
+71:Xq¬fJîh}%–”Øj­†Ë„ìÂú…GSî
+~š‘R£Ó%–ƒ :àsŽäŠ(±Ëî’žIÏtnC‰‰æóÓ ØD¿Ž?€úÍ$Ì>W†'O<¯í@¡»K‚bK%ïÉK÷Ëëye³Óò©³ˆwm|ëGãÉ”û›ýjÞ[×&pÀí…Ï.?‘]®”©õÙúE!˜M²5šöE–6á¨B§^±=ÌäÙÏ"‚üu EkÈDóéÍàÓ‹
+
+H?*¶LjA–oãœ÷¸T¦ÃÒ!er K ô¶žÌ\Úòµ\j‰]üÒ²“M‹ã¦VköÃ6'º]ø[( N&&ÂŒ§(ŽóÁSOÃØüë
+¶‘Ýü´É¶‰wŵ-Ré¤Ôùƒ=ÌZM¥©ÕÄAg<ïϤ¹š¸™Œ½‹9tÑAq}ÛŠb%NÃAMqÉazp%Æñ¼? ~Ð?• _˜Ñ^™’]Ae¿‡òÃK€í¼Š¿7†*³v95êÛ› ©%z‡¬ãœŽ.gq£´hœ/ Öhu µ®³‰7gV#¹ù’©8éd˜ì4 2SOYšÞÛ¦§úgqéöçMÅï]‹{¯r˜¾º«$ÿ:
+.YºÍ_vg¢¥9ÌKP¢Ô.Ù&­,AföÑ1žn³Á¬xãû.û¥`¯ô<ñbôª`ouå+ñn3v¹tz|Ç'Ò=XB²déf¾zRÄTº/ž)%K–æ½`)OÔFl,^&ÕŽöä(ƒyÐ,à×tºÏUÓy(…”¡T¬Þ;Ï6TŠõá/H;2‘›t:É"Û~Y­e»Ÿ­K?Dùá%ëJ‰k=UM­å`1Ú‹TàTyo„õY£ñìÓîä1ÉÂp˜ÜJôà8˳=¡n
+]±mª‡z¶;ÄùÜ~mýÛ{ vø|´û™Ø«w’‰–AgÒ-Cs­ä Ǜۨ4GÅvãëГÁL^²O8î¯&.à…Ì2Ë&e"C—Ž4¥¶ÇYÂÛX¿t+º§Wk¿ «ÓŽïT²•<ˆ“æÙfÆU…†Ãbƒ.[J½9Zý¸<šõYý¸ƒxëø#~‰ 5WNú_ȵ›>Ⱦ¦ÈÒ‹Èɳ1@g½-À±Àj«&<º.
+¿ØE—Ïä¨lö™EÚž*
+wÛÇÊ1§wT߯¯Í—
+»ã=\“ ü6"äe±Ï*§˜1ôv—Ý(4ú‹Ísƒo¨Ô.r1ÁÎWbÜb‘ŒÍ”Z*þþ¶¬Ã85Áú‡Ìˆã'yªÖ$jÃf— ve‡ ÿÉWAŒþ
+çÉýsaè÷‹ãî£W!rÏîRÎ {G;­:Zü@~‹f}MLæܬFëžÔà ì“18‰V=ðÞ8æj­˜`‚ÉÒ\>S7£C©]n4LZ5†Ìì žSë~ZŸ««ç‰àЫ^n—n0¾ù݆~õãï´å{æ0bÿsüsøýræéÀÉŒµYÔÖ°••å\n*RáZá«{Zeæb;eL{ð¸K©#&Íe¶‡f±÷›–ú.ßm@³z¯Wõa7Pºt·ëÎ;}¸ìÈ«´_k´Õ‘˼ìîòY|¬Žî.ÎNÉ(Ûö0»R§dj³V_MÀ±·±ÛpfuÎ]:>&£ö£É“Û½08κR¿äç^õ‘?vÅNåÖyhÐé Õxûe¶Óm•¢òí¸C”"HË 'õ‰ÍÛ­äiM4-Ö¦›£âuþ%l% Ì6G a¤ÍäÎ/gѲhœ†…YF«æáˆ\½”óTïµ(ÎøYësåy«+¶Ñ^§“ˆÚýÔ‹m‘9o%q¹9Â_Z3sIZ gt¥ä6P¿á׫ö½>kÑ:þ˜%k®,¢ácuôï'¾Å愤()Vsk_>C'VþX, ·aÒPnüü »4¶_F!Þ"³·Ts”¿` º´/GúQØÕëx¸k×\i߯•ž»aÕÛìÇU¡wbªµøe^ Œ_I Ušé”
+Q³¾8Vf{Úd‚}23?²Â® r#¤—Ò …#¬›Ÿhä6rÕùdúdß½X6Ô@f÷<PF¥î GÓáyYcÃ9R‚k» :¥G[x‘tèƒíµClw—ÞÎX3n~ŸÐ|ÏùQôšä—E?¡A~Yôš0b6ñš°~ù5Qå
+0gL}Â¹Õ ÿþ6yô^÷¡-…>ðÚ
+üIø­Äv|‹V‹³«1Ú,.ÒX¹ö桸¹£gž§ pOEƺ;Ýjq·K—MÕÌS„d\kþ{ˆ ß;ûM´…mÃ[¢
+ÙÛz¿V‹g#åw¶Î/æ¡7 )Ú}ׄ3XµG¤¸ AÝfŸ½ÀxÒ‰ȵ÷,ìÜ÷ÅÚ/±w·ò€'µùc^ÞÚáòUyÀö¢,Äœ·Í€ÆÅéÁ™æ¼§ÒH£õzÙÉKoºË¬Ð…yÌx¢‰â¦î›šºY*« 1_,” =žÉ“p<ý!"È?N€ì5„²0¢k‡Í'NìúÔA~®}l\®œ ÷êt¹ Ú5ä~CVdî²gå“§%û¼mÙÃÙ¶¦•xo{ÉŒøŒ lj¤‹ÝÕäF(ª½ývõb¼ŒncsÉU|Z*çüñg²¯¨ûMóý
+Z5üL§®ÖRµd½·!—1J±Ï™ìxYâº=VV³
+ñ9ìÇVMŽ¢>‹o¢jáùŸëX¯1ùvQ‡¡L}ìnÞoI°:ó±åY—óÝukZöÉÏ%šµw#-gºÇ9f¬Î®õRÛâf¸~–åýv^_³h©#-R8%Þºm8N”Œé²ã¸“et Ú
+’»áy9ó˜r¿ðƒüuÿ¯€Bó7@¦)¯Ý°¥UØÏ±CrŸ$Z³ÇúC"–£[ƒCVGbQÝíBw~tlê¼*“ï÷ø¯q¡\ù>ó­Ç¤v6¢ñ»ùLd^Ò3£ýºŽ‹G—)‹Lïˆyâ±_àG¹e§û§øú–ƒê»mäÅ
+ËXçì1ÓÏLè<Õ@ñmñ²j–eøÜ¸ŸZe·!÷[ßlÒ­Ó<+“ž/¾¯“z\ÇŸN#vþ,‘ïÚ{8i%Ùo†jõëÊaèA= A=Ý–Gå6ôÖ;”y š°iò(­´ã‹N¬÷Ã7§0F¾Í.Ká„óÌÔ›!3ËA¯·ÍªÒëï\úãéÇ·½Ÿ<mó °çóIò’ÔE<_ÔÇàá"qÁFÁâ?TïãÕ[½Ï-”3‰!ÁÒÝ¿ðƒü­ào€&|7'eaêÑ hϦÉtjCTfÃ{-ÛÄÏŽ‹°‡Éè6Þv¤²±¾t k™<ñÍŒ°<WÓ¢ûƒ(ëÁØ“5m/ªþó8˜­ûÐY
+–µ ©˜—Ãh×qeÿã†AHϯ¼?ìE]¥ÐHq“Oëè¯l©:“Ô<cF·9Ã`žù§
+-pƒF¯µÕwÏ{¥ãµ‰‡xþSìUS^‹¯ªÛXYþqohÈš|t²2¦vDEŒ¦lcÙ¬èž?*pßorU~gÁpku9Ùô‹É8—¨žO£çñ­í)ó¨MÜçï–?õЖT»Q-*üTšJ;ÓjA½×¸­˜á¿â»:ú?
+~²ëY½ØïªŸ¾^?¼.”¬à£d˜Ì´Dð„ý›/Þ*®þºá,›Qò´+VËmo\j¶| cZÔë5•&6lã’Ÿ¢1ùi£òi,êMwS7º’WodS-Ún‚Ú¢Žkmü™úS¬*¤ßžo›]zZ_bµï?ˆ¤‘îv°¡m§'å‡{ lc›O»CåÒò'¹{óld£†Ö¾€úcuM×éZ[ô¹Z½ªÖðP®²¹k½ò9†te#¿¹J·ˆ•Ì•]­ G˲Pí
+@´Ó.ÀïäàÓf Ƀ\ãȃœ-,
+ý™ßõ±ÿK¡ óÿ´ØŸ ÅÏ
+ý~<Ö†£×ÕfµWyWZDÓGÞ›íýö±ê5w÷m#<^%·¸‡O®ýU>µðSÊeöƒüºü€X ÐR
+u/ÑÛßÄem¨RÅ”²‹¥Re@•{"Ô:YC­ë
+}ô³€B»ßŒ`fòM]öA¡âÀðI+³qPÀ§Ñë<qÔœ×O½o'‹@/µÈWÙ7åg¢^iïU“š72-z7–~;é+²Îæ×bœ±IÇOu5V#‹¢eÁ<½EþðK(ìMò 6Mõ»
+ruõ
+
+å(t€ÏA±ùºÅÆ£T?n>Hl.]Ÿ]mq„¡Äz_Ì)®—ÊžÕ?û•ï`Gsòj~×Çæóùm¶æ3Oƒ‡ã­Ž·å씬©yMvžÌäŠe»“k‰å&ê¾Ûž”Ånëð• í9z>
+•ü7ßRøþš0Ûšñ³×¾†ü5û Š‹ ta>Qç0ž5‡[ÚÞŒ7{±©YÕvd,˜Çen Æ»…vUo–:ƒsÄxµ›m¥í’ôG"ÞżwJ†*‰ÃR¸¦‡—û‹˜×k Q:ðÓWPè |mS
+ó…»<¯ôô€ûù3VÙ‚ŒÙÙø\ß ýµ8ç½®¬¨š2i\+“ñæÎN¥áÀž‹J™´¾ßä*:Žð¿÷ŒŸ½Œdex·Y©ö6¹˜ë}RqK`ÈôŸ
+Џqaj%pÌyÍì¾QëÂÙÝJæF¹/¯VÛØ%sï¶ÌèUjNªŸ[•ýξ9ªØ$7H8½/¸éŒÜ#wÚo‹«Œ„]§® ¯lkªÆJßIú#TÛô¦LÌé5iMèÕIQ~_—ßP³M
+a¿ç£‡Z´†L²pûÒ8ö| {òJéêVÂ+ÆÕ“C¡³`¯ 6±ÃóÙ¦K¼U&s,/i¡@oiô:½´‡•mÜ&žÍBËŸÍÚ¿ðƒüuÿO8
+UIÝ.W¢´ùáÈ|½cm>ò@Z ^Õ áMù<(¶ÅhªpƒÌæ"ôüQgÔ½io•«Ç‡y§õ98?Ëfw>³éîLæpzÑî(IÓè%C´‡e¢Ü&‚V»%Õ{| F‰ró¼8ÍšÅwÚ…¨ù€]cEö~Í(Ûëší^ÒøDœ.¨×Kí%\Jº¤ÌÐj®²‚'¼q^)NòPZf-Îo0]Ìbi\íaQ“Z¾fh-êi̛登icÏkÜÌÇ¥Qù$áRØÔ§nð,^‹vÃbm1°Z>ûØì·™¶Ùò”–q|`í ¸øªâ1Îidý€{=‰$OßxC¿ByhÀd¼ó«=,hï–T‘‘¦ÒèçMêu³RodšÚBh2µ6Æ÷ª–È«,©ª•Íx6«t‹Ûu%s{ze¡–ºBäá»:ú?
+þ‚9–ªÆ‘šU&L¯VS®7•ç¬:á¤{›j§´)‡ÍÚÄÅ^6•º¸iT@Í­EN™»ðXe‰
+Çô ƒ?
+ý™?Èo„G.Àž(t?N‚Æ+€X.€¸æ Ðxó„•"Á¶;€Hi*Ôzs
+Vبçà¼ÿ°†«uÐ-ô×wôS[ý
+oüþŽõ¯þE±ûøªtßõd5¥ïûi[ù
+c Ïßö3€|gš€Ô‹ãDVˆÍ«©óù¥™‹$2ò4Î÷ïü3>]ëOúæ°Õs>ls YlÞ±íárzÚŽÏž÷Ëý¶#ê^ªtÿ?È_÷
+Cë™^ì÷A¿…Ünþ‹{]n=ãs
+_Aæhr:Ñ•üw¨Ú~›Ô¹=^+»B…™9!vÿZ*ÞtnŒÄ׈Ýîü
+/¿ ŠW(tAû°qv“ï ¬eqƒo¢í&™T–È<Ô‘'jEõ{±‘êcæ û˜ÕP<…+sà£Ben¯Û¬¹Þ^gëUîEnÍKÍ9,5ot[4š•hÞ®423Ë0ʳTöÃÝ Aëîmè‚AUt¬þ/øÎ/Å+¬øsÿȾç[þÊ`¿'°nb}3 ÂM½˜ ü£W¹>„K÷è¨õþ^–^ÒÖ¸íÆögíiëÝó5³NRy¹ H}µhV w–ÞÁ°) ÎÈúÓbï¹éž3˜iÎ5šÚQØÔ5óÊ
+j0ÛEó6 ý•Lfᘼ|fqižÌ{ê,ÓõuÈäçÚ¸z\OÊ»éN­mfw¥¹ÝÆñ„¯(Íë½>ŽÏÊ8bÅ/
+¿ zñú×®´Õ PÈ%õÍ·¬ƒÂ2%•ÝrnÞµcp¼§ãkÓhþ^ÚUvg»å¬“7»Q>oÞã­°hßÃY¶ò‘tâ=œhEïnÀaA4•V·ìÈï7~úf¨fš/ÉqO”œÉ‹U™ëÚ™k\Š_~_BANßžþsÆzþ@}ÓBI)n¢ùÜêåý}’ a,v^
+zP,†«‹eNüÃy˜‹¶qrFm\óÖ´KU¬ùªÃZÐÒn|†U[Bž§¯bOÂÝXåÓX¹ú¬?ÛQaé3çD ¥ÃGˆÙ°ÑoVÃRVKçÿ
+¬Í‰¼û!&Ýáâcrþ»åsù‘“pÒãÚìæˆw£;x„¥?
+Ÿ{g$9KáÃg«Kö­Ë¾ÔsÏtÏ‘é§LNƒN5ïLØFù´brdÚÝ凅)Í´‘ ÏÌwKš™›³¿ãù[ wØäï£÷õa—÷G\0÷ûJ8?íè¨óvrdL¯×Áxù¹ŸW3)¿ßiÑpwQÜÌ9”NIê-Öi鯭M®·›”J<½`ís“ÚŠëÔ’©ÈĶªÓïÝÒ¦9ây¥ÓO9Ûvœn‡N;g™î Cá
+gëìJ©ó%¯ñç _E’„2†©4ÕÈ•räBn”ÈvAjý ¿.¿ÀòÞ K к˜¼¾,¬dN®k
+†úÜ`¯>Õn›
+]D,g¡ä а8„j5$•iL‘ÝÊ #Ô£îì9¥Ù#餦ßõdxùW
+à€TMNPèhûËîòM´u¡ÐÑxœ¨\ìDËÓøòDdôs/`òÒ0¨šïV¶Í=•>°Û ÿà—[ج½’7™O ŽÉ«!v˜Y£Ð\›coì-'öä¶x ·ô¢®ÍÛóÖqݘY…yeÆÄ»’±aÌüïñ™D1´Aîðº}ÓB¿¿%åÓÈ;5®)»uØIZÇ`Ô?ÇW=bG[Tò{ImV¶j¥á˜#Ù8)»»Æxß’À|´¼£”º0¤³1_ô½õ̪»ã³øDF·ŠFæó’t¡K”t,_/N½Kú=`j€è½¡ÐOö(²Ï ¥¿ß»W_M`šO´Jy÷âÚ Ï¦©gûä›;º»wâ¢ÛÎ'Õ“¼«Ã¤F›×’ÞYÖŠa¾lâŒ=°#ÛÆæ:VÜépܾj~rA´ciQÖ¤û¦ å§9¹ÔüÜ?áùëà,….p•üý{p$ñýð9\™´1âdþ ŽÄöú8+÷£›ÅÁ¾àï‘m³h’ЄJaM—óF]‹ðT¡ç´oq†”`ѱd?ÒŽ³Ùtr¨–z—l_ªouÚË!Úyµ†vs
+ñ{üWæ¹ì
+Š2Û"óñòPÞÊoj—ŸcP?ÈxùĈ1=Ëa¿•z3ØÙ;.ìCÃ'È“Zè ûЍÝè‰[“Û}Y'[¾öçpòض·\>Ø_
+ÐC]Îê19gçÅ‚!4”ÊTJ?“[NdÔ:ƒ÷Ʀrd.­k’Ó;Ù²¯Ræó’£.Iˆ#!9ý ú¯øA~]
+YD /º(lw0àmûÁ‘ÍH×ÙÛŸñŠìzU¯ru>¯G?eêÍUvþ.9˜±DÔT
+rù‰fM+Jt-´äÏÂü~-Ë%{ýt‘Uqdsæð\8E)SÈ‹£¨™ýLõ)ò{ÀÁs2¹õl(ÞüþFböʆ]±Âý wusëŸßÑP=È/rµ}ÁÙÒ¼ýÓº-Fñ"©ŸY¯6A¦§†…O¦óKNYòÍ¢l×wU ë0mQ²P~XÊ?¤Aµ’™÷ £·T‡;b ­Üà¶/ÃfXÿ€@׋ÿ¬ŠQv’À~žÉÇÚl?±S†¾ÕnçèåîC/ØŸnº¿Ø­e¾5«V8ï6ª
+œßˆÉ.шñä×å_rõ²¨æð
+¥MúmÃÝqüùß÷5z—Ù¦ÓƒêF.[}óÕ\NçBg²Ö‹m¯&Uù<F©ùý›þsX»ä“>}rÒ='Åà<ÎdŠÜ1z4;êô̳Óy¢2!Ö³™ÙÙÚ \eï“r±Sš¨ßäõ{2X>u¾ß&]guÜbÅõþÊìÝB«\gмmò“­Pi° ÞhÊúÅ«Ï'Ëzà »¦7:ƒÉyبø÷>[f_==¾»>?A¹¢ÏØG¿Ø`¢Q«÷ƒÐ‰2šÐV)tiN´Ål›tBØu¦E¬*¿ —w'dlŸ8ÏšîÕJÛØ2%`8بë¬o]&¶Z瓽ë–1Šâu0I²m¼[aËÑÕh:ÃæiôntéáœôìÊ«<èÜã
+´+ëmoÜeZ¾ÆBO©E=»R³³ZCc†V£ò± ‡Òõoµh›€Z›ÈaÕÄÊUKÚ°U¶@)‡íïa ‚Q6Câ^˜yõNþÑf³¸0ö6ﺤ‘š5d^{Sbäº{›è6”Gì˜MÏ;ÒèQ¹µü 6•F44I×k¯EΛªµ±t¥j‰H«ò9øJ·ØünÝïëe¡jÛ?HÉ›]üÒ°•}@ˆé‘Øäï‡-9ÛBkìÆø˜(s£úu;dVï–©W´óqrú[åk±€Ÿ6n‹ò¼ntÒV­œ6U–p¶•n~~(»ª~.£~/ ëzTôóO‘´ tüBþæD|C»ÔcßQ†81¨é¬þ†äoó^úi†É òAô‘a»™ÏC$©v+A£Æ²)µL£‡¸Ô¢îój£’TêµE7‚v:XL%sÖº%oÚîýyiP8[ÙQ¡”J¼Öõؾ 
+üe’‹Ñk“K.)/Ç–± ±ÑK!‘‰oÜ]¨Ž‚iá÷
+½ÂYk®Ÿ
+Р }ØÂ7ÕŸ2@¥üB°Ú]¡êÛóû.Íj þe‘èeDˆ±ì¢úÓg%špÊ(Ô•F?¬—ò½´bÊ Ú€^" `òè
+M_ú
+xÕÍB¡}EƒÆ,®`z…Bï"
+Tü›N­*Ñ­Ÿê‡•RŸ ̃Ïl;¿»yzóŒÚø* ÁàšÇ¿ KA‚uî~W{k€ïÓ'@Û ¶Íò×ç¿Ø ®Ù„tÄÀÎ
+û¯
+Píµ“ºU~5o{¥@_šð\¶@! …©' ‰DªõQâÊì¼
+­lÛôzvt'ÕGæ2e #@¤ägœ^ýÙ67Ú#ß¿½*%Ñ ª/Å —™ýúl×6ݸû›•YMÖöf Ÿ|2«~Ì6¬Q­š'£T2Ç ¬ð¯
+}äC@Þ²9@=&QÚÄ8Z˜‹Å3[Î{w)Ç}Ácǯe؉Á½|×*l5.Wu¢áªe›WšÛ¬ ¤¿îa'yµ«tëP<¬‹œâ³071³˜™6–•’\Yè+±0Ò< ÑÿwÀ>dɰiŽf —élÉ1´bƒT˜°qUK¤0etõ$k/çª]‘ËqûÏmÁîZydÝÙÃmÀÊùM—ŽËkÔ7-¿Æ°æØLzˉÉ ]fóЧ¶óV~Ï[³Cyf!§ÂŒÙ”±©D½ñ_—ÀºÞà7}rœêÒԠй@<WìD/Û@ϧ1¹]‹ÛÕ™¦æ‡ƒ÷h_½â¼ºsµ÷¶;–š^o—o8Z®r‹e*vRZV‰E}-»ì,UûÆG¨ªF¦Ó6õÞé|Õ]‰ÈëBƒ¡tôÅÓ!ÝB ˜ìêþö!ó»'1·¶€Œ]
+—å>5¬^„Lÿ)1¥ès­^JÔ$~C7·?XìnHŸ Ó©¾‘äß}#$Á_²Å‰
+mh½¹“w{¼ëýÛ¥ù¶wGOR¨hOW÷èv;HU­ñ‰âÍ糘ŽjäbžoRKU´6r+4öcœ½Ÿ¤\‰„
+D½>Iú챎öÒýRÛQ¥&—™LÅî±”³¸g¥yÞ{#üæ”úð€ÿàû7}7”ru^ÓÝèrÑ¥¬~êO“Õq¼E6û¨'ùÎG¥-ù‹ÜŠ.ïX#½9 sŸ8©êÝó5¹ƒà«11æ¶âqáÄð(ŸLö³ÙaÌ÷bá²³R®snVëâJÚa§¸îÈ™ñ»›[¦ñ®¤¼ÓÝc@! ©·ÇÍÒ>¸<ù䤄éáÒbà˜¼ÓóUݱݕ³ ;ZW«Üø8½šæUdn:ßÎ$ÙR–ê8³:/Ä RZ ÄzÛãåC¯ûî0WÝq©Ÿê”Õ®†ÝZT½a«‘žX­EׄöÒ®F ²=õÊx;ü4þ@ú#Y½þ[È‹ÇÜí2vŸ‡ñxÛë {^VëZáé™kÅ}/ÄÔ'?«š%FI‘5Ú‹ÐUGb¸ð¦C#_2ú6åZ=¼Íºœd¾ýNˆ£ö|m}ZºîPìrÖ™Oº"0–ÿ°ëüü°-Î̳Kn“c_£5õ~°? lsEªÛRßËffý]]Ìô·XÅêYEÓ’–Ïã[3¶fg·úx6o9¥ó¸WÆâcÅ+èõ‡lÅõùruƋœٽŒÅMgVéxíú¥pƒ¥€9õaSM1P›M·s“šƒœé4è,Ýty2Ï |½Ä R᯸ ccpâÍAg ~{‹jvÌ’¶¹Z…Ðß×3zNž‡+ÿ05†ÖSv¤Tztw(‘>廙ϤÙéãZ,𹹬tUP×;ô–Ü´’ÖóÈ~8±È0bp­œoŸ"ÓðØÓ¨1ÊŽ¶QªŸnH÷sákH>^ûÿÇùÙÚtÅbÀn9)¡×ŠÎUÍÖ¦T_ÙžÕ7´¡v²’?kq$]íD`뻓fŽ?iqõãJo§Ý¹;†Ð6Å‘ÒZOF&»™
+[Æ]ŒÎL³_M*{ÀãqªR¿¼8¾®ìb­^ŽQ½ÜibôÕWKôu«–ÿ
+¸ºý ¹ýÌÏ•õ¥GŒ÷e]ÐF_R_“F]Þi¹Í¸˜öŽ¢>f‚a:>Ü{R¯8UK°ŽnTëCpåBµoöûL¶Í›ÒFÐgLÛ4ŠªgäzE c:jä1šÞ*µxòµæ½«ÕšBPM&Kb‡ÿNRŽ+V¾q§Œ÷€'µ\ø"U¶¥’cïùjJZ‘Yþ`Ãy²úÁº|1¸yØ^Úm¤xg!ŒP8 Í\dfÅç¬PŸ² šŽ¬u»;ëA!ru%>ÍÊçLî*¶,Ü*\é.;óTbÚ.÷ëÝå÷ÿ÷ëêæ¯°¼Å4Ù2…k9Á•uAG¾ocB± Ÿ¾x¹Pu験Ûí´0cû$¡7=å²nÈôÚ©WÀøðÝÓ›æÏ5goÕ6 ßÝ×>$hÂâ+½|I¨÷jEÏœw‹¹÷q\8Û£ sü®PBÍ î°D ÿ+̧òý&Q“ÚDYwŸÑ¤È¥†í „õFÙ3ÝæL£ÝN­i÷Êæ¨]°iSçèzj8¨&»Ší™”9RJû™7/ežYÌ%öº ·m'nœc.ÚÙÁ7›=¿(cP2 JEr¥Ž²]qúÙOôQ³Ÿ;>û+´c•ý(Éy F…aòØÝþ“ fãt;YÒl?c¡ÍÜ5‡~ûØËÁj'•Ï1O–ûÅ Uªn±˜{)•|h7ªù*Z«çê™ CÖ&“3Æg?ák˜åª¯QÆÑÓ³ žPKBh7\ÜÛ|¿pç×O|ÔKcxO?ÿ$¿`Ô¢ P}!
+@ïHãÿ‹z2¨Ÿ«Åÿdúëá7Òû'n›1 Ð!~„è=
+í\…¡¸­
+€Ð·m@ô1¥ pà>¾{àóFà’Ò8½†oš»NŽ¢&D{°§&;å ^GÌ?}%_ƒy©ûƒ=nF~ð  ¢w×§Ïn´ÜlØÛ×ÿ
+€‰.úÎK
+L˜û·C¼<ÚT{܈{dÌqê&Dãõ.ÆëeŽ00¡ ¢ýd'Ë) 2®r¼qMJzÜ1‰¸QCŒNý´pqL9KþFÛ.H¸ÕE¢© ŠÞ. 5è¿ôÎ{s&éyK ìµ£ÔnãäÕO‡ãõýøñê
+‘#;/¤-R•ùLÊÈÄUÚÑ^¿F˜ô¹ÿ “ ¿w f}(ö…nœkü´…Ûí¸½„çäö8½}óýä¾íªÛªEùv{9s}ì#Íͧ³‘Ø×ÔP½/Úo&Ësz?FRK_Ú-'wQÊ7a»“L›ª¹icX¾ä&Ãà´8 þuôsÄ*)°Šþ
+~Ä¢b” vÏzî~ f·¢ášŸ%ÔÝr¥QçJ'µ”½çz…UˆOãeÍø¿ÙŠs
+‰'êuRÊϺ©OÚRy=r¦;GÊîºGá̯CõúIó3Jôõk½ÖoÎÎb?Ö¼A¤×_Ê/<‡»AOü›ühìÅ´!j;g~^˜Xõß+yzÂ7ÓÅqŒ9ë]ý-œ¶Ü
+ ºR‹xü|—ÿËÿ @¦·“G«}0[G‰®q»z*à¶›¡ÎÙR]¬+ó£f®Ò˃޲ñ,8t0µé… ¿ÝÓ#ªA¶„ëyÌiLüÁúí.ón¹2çÿ³êæ¸$½ÂkëžÞì³@æX#p8ÖÛV]6£Ö=ŸíjŒÿ
+@®oã ä-o ¾ '¾‹È%»µ½\Ó:ˆYÑ“°k¦(d¬Õ¬5/å´³Ê`÷dâ°MbWxE!ʦêƒåAl÷ÓIºÇëÛQן÷¦µTY¶kÜ®Õs!ËÞ›Æj¬Š?“^ey&½.8ŒÅ¦Ì[áÞ,ëö“¿âþì{~[I×…pÙØ~¬¸f:ýòò±j× $(±Z!_êÍf7WÙNÞ…ö2¨ÏnÒeòx ‹‚´ÏÏr¯ï܉,t»¥?lß‘¥Ò2ªÁ®¶ªËØûeØtNÇO“(f }â{pÝ7‰²7‰ŒónöÙ=ø?Ø~¨pMy{óš“d²MÖª…åÂ;äõž¥”æ2×Õ¥¾á¤zŸŸÍFªt Þ0!ƒgëõQþx
+¢ôé¹ì*µÇõ¹®5é¼W]:£gµÕ¿“•OyÚ©XQoU±¼Û¹bm¨Çðƒý›ß"Dž\MG5Tç'ËôL r)™»ªoImgßÃ÷Zm÷|)ñÅ€–»tœÌÛíôÊd¡¿a²A~ß8k©s=4ž­¯¯Z¼ Ò5¿Áv©¶É[é{ùT³‚„…~¹_LKû…i•„fê\Ê&ýwÑ[Ý‹ÅQÇ–Š£nÑü+ÖE¯†©íì3S•ÑZå)ÞFûç6@Îõ˜—ó«zW£™f»º°l?«t›žÂÅ8-ÑÑê¦Ô ~;¯®†ºQ±GªUv”¡SÆo’WÊÆ½ 8b&qAîLÓùpëäòU<iä¢Ck˜ÓÄåüÏÕ÷–ŠÏ|ñ@þ
+£½Á,Pü—ÌuбxËOïý^r¿ug»ÍŠ+àl?ãgš£R/[Ÿ6‘\-¶½bu50þœ«eüZeJÙÙ)æ^€+?×A¾ŠÄœ68)T|¼hdr>­Èvé¹Ërt*È8FdúŒIfpp§‰½Uâ äÑoøÁþ$óñÅÊv¿‹×KûÚï…—kG;ïŽlÏJßš£¢Ö+¯fTcð^±Eù^ÆÃj\ô©¤ ³§w¾ŠÌ>¹:1D(†jád»XÌdíiŽÊ8 Ž–ü…T ¡…7qÏÎqxmJ˜<æX)³³Ñ©ôòÑéˆÑj^Mÿ
+}l†
+¨:@¯ßª¼.ˆû  Ç’áÁîãUÓ q©CI·já™S…ø²¹Xé]É8s/™¿ =ªùOÀ»¡À£ox9^ü5¼@œS
+a–ckd²­‹ëôÏÁ™û·«hœËÙ[®Àëh=jȱûXdÎDAýæ8u pá½ßïÛÉo€-nB™fþ{¦
+¡9Ô81@¶Tö@VÞ<AÖ-“‰ôn4ž•é®ûh¶º“¨Bãê†íMH^îþåB/¢óôЧøõA¼Ä;g½6êÝÛŠ>8ô¾sÀåPÚohû;é¬_ܳB·Tž Î5£¦œém œª%ÆÛ{·óø °Ím@ lÓOe&Û%È:â ùTHnL=«“[ùGTØ“oýÒ~
+€Ì¥€b/n2Y2ç;׫íÃÒ­·õS/ûx€–wµ]ÙØjZe¶Þ©.}ÉzÒÕÙ®<Ô6bj4ï¦:#¹Ž®žoK ·¯ƒ¬µý`b4‚dœ¬rÔ¸ZvÆÉ¬&Æ%>ÉõÍðòÅqòä[û=ñÿËÃIˆû_]„B»”(åî>±¶¹ ÑÖº,üÄô6ãìËM÷ºsÿc÷'©ìê¬”Ëæ|?¬ë­ÌŠÕ¶¥<7Ï46ÃéxÔ«%ðž}÷ôN¶æäÙž;ãÕK:ìHŠGhÞ!¤þ’f$Ѧ;õÆ
+~þâðÅþ7@cu¡ 5EØ5FüX½¾§¶ôƒM¡:ñÓ‹úôàËOËÕS³qôd}jm°eµsËéɦX] ]¹9Ï’÷ötRô”Û -É )OXAÒÇ©WÅ’\ ?ˆ% '©ŽãœWN›Œöƒ‰‡k~/ ÇÿâOn÷à
+ôÎT%懹Ÿ^:DE¹gÇIr½µÆ>éðEQ¥£'O<`4}p{›þ#ù½×}•ô–z©Ð[ô¤¯{üf0Oò§AÔ¬ïQ±äþQC”5º… ã'ªéeÒ¯Ö>Xùßï+éÓ‘ÛÚ#kbyrw½ŒZŽo´$ô© Åô¬`ȤòÀ.¥ ¸Ôjc>}d¤CµÛóƒd8œÚxP§¸y/ÙW×ügS÷xd'Äœ39®OF"o›åÍÖks¯Rè¾—DÀý $|rªmáø ²ô íìèÃx¯7Ýk5·¶†4láXVW2Ñ¡·Óß/úÅC4;—ßo•îT1h•ïhA±õ»$‰áœ,×Öè=ß7g‚ØëàÕ)nˆ%GìªÇ.i·wÉvÆØ~ةۊ#n‹#ç*§º÷7|çÊ&ÎE…•)û•#v+»Ú+[ÛÚ9§b“b¿¼ºî5Î4UYÕÑ#±™îâYÕœåCn7‰ô¿ˆ„”ÓƒœPYô*ý˜4ú-Æîð[pÃjUùÁºT@š‚—ߵ˗vÔº)×Lk^ñmµx[w
+ñÂëøóÓ¡ãKîî7À®Ùê……]­ê9Ûnq_ãåí'§m2MäV°³~ÿÿ­Ü›:f<ŹßtMõQÁvr§Ó ÇDä=Å‹TNïÂ.3`–­BÏŽ“OPÓõöý^Gæ«ãö´U7Zu´î²Æ@‹˜ÄKŸË·%”ÃÖÒ˜Íþ‹Ýox<BëÜ”·¥Ã˜D
+ÎËÒò¶äj)cýnšG”Ëó¢ÎsjÜ((rW¶Ö£c˜=Šê@» u†H~°¾ÕX"=Lk’ܱŒ—»…ɾپ‘ÓnëMG¬éÉ fíj“΄·&?jM~167~yŤӛ#ÓéœöL§æº¿!ô±ZåóðÏíjŠ“ßàÕ{fFÜX¯ßß­x:>­Wz¤Ò6‡µ‰‹¨ƒÑhKÏÅ*n
+-'ÞÆQ{ø9Ò =Ÿê½ŒHn¹qð<wnyóÞ^Ÿ+7 ¶3k–~B³ÍÁ´9hˆ‹á¼>Þ-·õ‚‰h径k—‡;¢§´¢CÞÓŠ,ù’VúÂî7ÉJD9칚]Ÿ+,b2³ÍGOPcZMúÛb"Þ8&;¹<àòr»'Ðm“;Ä´í®Ë¶1:í[íò&øÁX.ãÌ‘&™žç£žE×eaÇÑáé%ÓÕfêi«Å¥VËhõIÝÙªáØzÕ“mÕà‡¿Â]Ò’T6ýƒ™Æl´Ã)ýRu§ûï_^$Ö”þ}€Äk´'ª5î—:Gê9l³ÃƒÌn.3Xc«Iõ;»FAì\êŠ?ˆéY"¥köœª¾øe­Ê¾bU êVé¦ÌmyË?Ãr¯hfËØu*”±ÃgQÆ$z Ñt~ƒ=lŒ‘%}]$ "z?Õǹuïw««°8¢AowøœJ­ÉNÜ|ÒßÙøM‹íycžÉš¢Ô‘šZ¯0¦™u­™¤Ýj›ÄüŠ=ÎÜÊŽšIÊxTÁJÙg«X±óV1÷~Œ
+2W6
+%t}úîKMåÃ}¾•Ÿ
++›}ÿ³&ÿÁ2bÖÉ¢ÝêÃ(ÞÆû¥
+ÚÉï ì©£9Ö.Š1‚Å>ÙÌ]¥|=Ô»ºžÊÖ«‰›´*ŸcĕنŽÞÌ€k˜¢§Oåb.Ñ´B)%/óS^Ûæ´¡q¢bïSLÄÉ$käJzd»Úœf?÷­“µu>Øâ¿AG[¯ÇtŽÐPáP¸À¹A¹§^¶QöÚ†˜Û0C¼ylœ§ËˆŽ–­¸Æ Ÿgås8¿ËÎdƒ”„ê/æ^l¶Pü¨|ÅŠ¹:ñªP Ðäjrc³öÔç²Èý!fð×kJíÂúýxÂGü%ÁG=–Äó¸Icçý“ÇÎRø Ó[®ràjÿ,4Òk?¾2Ç6SËn§ªÓZyµ«®ú„]ANW·$T8‹sOvŸ­Œ—Óz3ÅË3Ù.L‚,Wé^3ýzåNìÍÊ ÷¬ìu¡ü³ÃX‰àJh•ê7mbôÓÆÔ‘Ó°¨u*yôŽ©•¾½C¯?øÁþÍÿ@²R Ã- k´ØéAÌFðGWÈl2k_ÒÙ
+DL$Nðu¿„ôêþÊn§·§×®_„ÿâû7Û\¯
+Ðm¹ñO[_ûÂËßýÉ™½ °~˜×}ldä
+=:
+pÕI<
+¢@Üì€◗ÿàû“
+ \S©¡ÐG6• !–V¿šÏš†–Sbþ¾kêS¨±f½. ¸¯G“ÝUì—Z¶z¼ð:dß²+®.y‡nÕª›eÿÈü`6ty‹_Ñâ:“Oäùö´å˜ö,S!V;cFÙ~ï½~ÈS 3½ï-Ìîr±0mv»0=~½xµÌ?0Àû.ýçÛ„6ûOø.»õçßM—V|¿?ìÛŽø¬µ:²Oƒ¸?ˆœus¯½ø½5–eÌþ»‚ÕÛ6k«£wf–¾ip¦šk Æ äe}qÚÂÜÝ-mm_¡_©¥žà6ç[,:sí;3sï¡:ÇD}=ÇÖþjÞ+cËy¥Œß
+vI•
+q1©UkµV/”ß° o²áÜgUafOÝ^¾î³ \ÿQg±Üù~>{c¯7Øa>7±qWu'x‡–Ôr+a–öÓ±DÆÁõï1oúºáæûú¹2êEÍzÑs׿ïOµZ½jó²œUµ°¯¬ZTeE^»?X¥µÍËŸfiZþIõ7l׫uz8æSÿ,ƒÛ<kB›©ÿB&,»ü1³t:¡zm3S{·Úq7Ë¢ëV¹yèÑMXcãj…¯+ŠDÏZÅiíîu×°o¶Ü*[–‚ÊÚ²’òÆñˆ2?(Ò%÷6–2ôC/ÑÙ¹xÄÌbQœ¢\Qls“¢XmÃ\¦J§—±Â·ùèM
+{× Fçr€iÆá)K²ÛúÉ1YÞ÷ïL&ù¤þé“©!R¢çZ\¯5a«Ú’νJ×8Kå-ˆ¦¥=¥ìä³)æÖÙÓVÓÍG>ìix¾ÆÕ\4ë rZç¡çêˆr¡â˸@Å&èPF%/ü³9~ß4rÐ
+”ejzúW!çKC»µ|ô¬)¸è¢.†­µšÏƒ?•ÏE+È9E–öÓK¾è-•Ây¹©çC{Á䫨Éåê™Å2¤L®dWÏ~®O'ËÑÕkÆ1féLŸMU2ø{Ô#öV¢}w¨Ú» !ð-ô7hûK/P^Uí4’âëÿ4NèžÍ~†íE#_+5½¹Ñ+ŸCj[vÆ÷CI¨l¼b Å·xÍW‘Îã£âõ$“Ó;É~‚Û'ËUb,Ó¯‡$±7£"îY¸\;»ÝVÊXK4ô¨ï®Õ@§c,‡V ƒ&‡áoP«l—‘‡\ö ¤·Ü¤+mX»ÅéuyÛ‘+ëÁ.v‚Ú¢8¢·j>\g9­G/(CÀu²Í,r3–<VD0>ê0.vÞÖ˜ÜãÎèT`®Htâž?X:øTj¥±Dª]_WSíF¾±š§ÚÍœ 1¿B<Þ¿¤é„i¯’HE­
+Òüø{ªy;œAê{XRj¯P µº6@j]íAÈcˆ½R³xRó¢ 1ô!vÒ²HÙ»*Hm¨.Äxq˜CÜu:ã_ÀRþùí/H
+ Ùn Ãù7x4
+ %ÎÈÕøþ%²~
+XÑ Hß\FïØ:_ºŽyVCÿ °*­ÀÒºþüÝЈ+œô½.Oø2ç~Ϲ<«¥^Ô»žýžªÑ'ªy
+tî‹à.D¦}Pnày2nòisÝH‡ýu—p@\!±~§)P—S_lžKþztº¯Nš÷<=×GðrÍãz·ÑéìxvØDâô~°ó
+uÉôíñ÷˜`¼´K¸’z–2ÈóA‡&µvlî†ä1:Ü­ƒNpœì„‹ßÖ•sИ,üYÔ\ý`§GžvN ¥uòÌc÷v|ç&ïcGÞS~^éî†4Ù‰×îúíÂPÖÌ-<–ªèªüÅä?
+ñ~®©È
+ÕWÕª}†=«^ÃGV]l)æ£FÌÝÿ‹½vˆ${ÞUì.Ó ¾ÍŠßvƒ1ÿë®…Êm5»JA¥Z»-Q߉‰çlÊ]l»jàópic…—·ðbíI˜³Óê„}æÉù…ŠÚóŠqV]¹Êætó0f–÷p§ Z9OÍ13…êzû»::¡¤i71‡SsÅ)Èþ¿
+…ºSv\Ö2Ùą́C¸_çú WVãjù³SK÷<š×Ø»Ò]#‘Ü[$™!F ƒ|}׈Ҏ+¹‹"èJôÓ¥×K_Åš¨`пˆ—ÉJò[c¾têÐ…ˆÆíK%PG1|„sådWH¶ñúNI±ïÈÉËã‡7dAûò|ª°“~…â4Ë5µI‘X›ãB‚ñ˜jæ7ëQÇ
+LäÂáz¾ÊJ%¥>ɉZI« “œÖåg™Ù‹„ÈæÚõÊEú¹ÂÏxŸHäùDÿ æ>ˆôÎÊnöü¨Ûé~SŒãM«ÑªÏ¿–™Îâ¾!±ëÛïÞk1ך®®7‘»2Ö«“©jÆ[IK†/û^c7+¯Ë pòbÐåcaòneùÙ¹ÏÍ/d»¿ØwÄ>\\˜½å¥ôá–^ê»0Gs…ßålìFsoþó›Kó•ë¶ÊfŽÉ†Iyu¢u#l¾–^ty¸ùs+´'ÊŒ“­ù2‹Íúì¹näŸ<3¾ö.5jôõL™¯qîà !I…WG±¦ó!ÿ*U".ξk½‹=òn°«À³x20˜½‹Y<mØÍ¶Øéqܨûƒöü“Ûù/âÁ¡R $‡ÐÆë½ŸäÈw–ÐÓÝ •›Sí>/Vç0ºÎú2‰TÒ«F©ïêŒA ªÈX`L
+Ѱ¿ëÉ‹—/LCuÇ·•æ…ëŠÄ³¼)»œrÌaLñŠsÝò“™Ð¡™ºåˆÈwÏÓ¸Í š98ã?t~ñ kv~tWõ +Û÷êåÔAvÏ/Oç’Bë=§›ëý09g_/½6ÊäÔ˜X×GÂ02þx¥ƒ¨‰žñoÊq¸Åƒ_õûãʱ'ÈØÍ'—¸{17Yúê`4Nןh¯3£úãN3væ'ŸëÌ:ç]7ºô¤Åvþ‹[V¢G(ÍÅÿ$ew™Çsÿ67KîyÆ©ñN·í•özwCP_º=Rx–,åî»%…c»/¼´Ú§OwC$ÇêmQ~ɧù-3šca·FFoºQ~¢*ÿ©¶ér‰¥\]Ó(–Ìo)¶H`”;¼·¨W¤Û´Zþï¡ý‹ Â¥ÈnÏÞ+KoFv[<lz&+SÇÉÙÔ7Z{}ô¡=ÛËxý\¶AV|lÌŠÐɰmnÉfù>ælöØR'?(£ÞÛvW¿TVtÓø~.O…žíî
+6sŽeéýz—ó[©XRògÐZ–ˆ§‹¹¡@—ê E¨{†ú/œê¢p5Ù){šT8k§ÂÕëj0öáï­i‹¬Þq‹3¹5œ½ÅY^;ä,Áv?hÝ.Tš[®G5Ø*ºò<TÊÚÖ,z†Ös¨U/ã¢1…ìÂ+Ó;(Á{çãZ³”§§“Kûì,çe;¯œ¿‰œ»«6s,ƒÿÃú÷ÜLs£óäüÜîUFKÖƒ.¸|O£,¶Z—'ø<Sš£`/ÔÛb¯Æ.çn5ìv•Ü}y*£å­¤vÖÑ÷“üIqÊ›HÁŒñ||2Kà*ihÔs½ºÝÆù–Ëa[û©ay¨ºÈÀh6+sUpl ç]s€ž÷Ùà™¢ç‚üÂ÷ƒºØ@ëa™!=¾7gçlU²¦ óWóQCÏ™|­;¸3å=¥³eŒ ΊSŽ,©±È;Jq™ëU_+œoœ·ØÖ:±\r²2s¢g?NÑ
+†a}P/#ÓQ¯ ¿Bo
+¿.U Šo…ÔmT×€I
+u©ì0ÿÛF @ù ¸—ï!ÎV&çlc<ål1›åj­sê4ØÂÍQœÞžvœz­CœÊ³'à“‰S…)æ­8í™8å2" ­ô)à°øAã”/îæ+NµZ`€'øñqAøÿ%„9 Ñ>dA˜‡2sÚ¨$0A5¿+½‘É'Ð<'XZ$PM;Åizy'Db€Y5NG*NŸhº8Ìãô…€\qà¯}§WL L@%Z¿U”¸'¶†à^2¹Òà/ïÕ•~’r…$ð>F$WƤF8å@ wŸN`ï3Hà>9M`Tñ(8 ´%’r´|éÇú÷^&yЖ
+g dä\
+qºåðU†Ùøø«ÜÃbêNQYþFw‹£«ë¥£+Û»Ë?hè_¶Ã/ƒÓé "3 FªØx’9>‰- Ú€£˜ H~’dV]/ɨÖ9ÉTÂø½õ×htpÖ¥×y¶n¾ÊÁŽùAŸzí">îòeôh\®ÆýæAwê
+4èE.‘^<M†íêqÆWZ‡h×ì:p]Ü/@‘ØCN{ºëC¼»]÷ÓVÈæ>›üQ®®‰÷Š^ibØ÷¯÷ãO<þA}²7m¡~‘dfF&A»O,A“Vá»SL°Ló»¼ì ßËk×{¦ÝzUØs[Û½”ǵÝñ…­‚Mñ¯=|$ÒjŸ7¾îpi38?Éõ©òî¬A#í4ÿ=ôo:ñÉAË[> ËÐ{c%Üë,M×-D¬ ›ÇŽ û×öÂçvÍ…?ºü$¥ ÂÜáøwwº:)%‘i~wÛ²ñ€°Äg+?çn™ó”,'Ÿš®§ aï²÷åzs”ózô"«ñM|c£KÓ Þû~®z4q¦\W{.hùä—É‚ÙÙìÚÁ÷ïÄ6ÇåùéònÏOo¼=mÐæ|óò®ÿ"Aë'!VyþZ—A„õV‚ç¼ÞëV¸vn>~j%%mç·íÑüc°e[_]™§íO¿Û}ñä¦÷âÍE.H¼à’vÄç%gÊ—Š]”®Ô|tLúV˜ÅFVMêÚ¦q<\f¯²ŒÏ,,Kά6ךµ1–üf|³vE¬þ"Aõw6Éò-,ÁÇÒû¸Šê·„—îðÚ'Âa;›?¾#¢°Û8ËÆfTcú+rq’—‰5Ü¥örâ­ü„?ÚĨyýAç•.Yú …Í©öÉÍ^ûOeÖΠí)ý9Ãc[c£‡ìV“• A“•Ý.OVÁª1Y½µ hª_Ê¿
+ 1óÜ­¿ØG—ñ2{1vƒ=Fbáöˆƒu}®½– ŒÍº¼V¢:íÌk Æ›Ôf"OS¢ªþâiMD
+Ô³a~¸)6BÙ³¦ÝËwurÉ}F³}SÞQ•?X›‘™¥Êp‘ÞÉp“†e戗e·Þ)ÉG!¾~Šf)“`Æ¥xÅ}ãu²p'õe&.[ù½<­¯ÓþÑŸK7/óHaç8Ýó Ro˜ÖqÇN½))Ù]0ÒÔx2k–6ž§žj÷ÝÈTbV‘ìRsdèw—Ä]¨WÀèb€;g€ïné@ìøÄ`³~Ãl©Pr
+Yø(œU$ªÂx68mÙç±[”‚ôxœ6Õœv\µÚŸí.ï/‹ÁÆýØUÏ™Qø¨~דíAÇÀÔ§Ëæx0®äkšzŸß§#«áØŠƒI¾ìÁÝÃЛ·ÁæQÿ r¥~A*愆8’ׂX.õ±L cñ\‹RÑð ix;¤át†_~пçIöÌ ÷Å/ãcñqÈTê—-ñ^W³Ki·t îÊÅ—ìÁ¦ôË2:s¡eɦՖ~\ìÙq%äDu–‰•QÛptº¦œ+î:ÌÆØn{@¡T¼$±X>À¸P[£5~Š÷û|cŸ˜|ã‘{ñw'Í :?,
+áÙ.|Éý"ÁŠüÖßï“U]]w;:¯o¹Éôbä³õИ[:£qv7oðñͤ¹+<E߃¤pÉqÕÓêìXä”ä³ÈK•W‡ëAs6ÞÑ÷S —µ¨^vgA߯_ütµGÁ¼æTî'Héwù¹ñƒö»‚wï'-ã¬g®ÀE •ç› .÷‹î
+Wüé¿é‹¶Jÿz^™sX"…`»‹Þ|ZôV¦CZ—éª;ýL†÷6ïö•ïz²Þh*Ÿõ•9D‡ø¤®
+4/én.\£…ÏÏ åÀÍÕÁ½¿ŒàÞ’{™mÐa׃ή‡êµ×¯ÉÙÞr|Î÷¡wšë3Tÿ ¸—ïáu=3ÁÈá^û5Ö Ö·Úü´ü|ð½+ÆæÆÖ…Ð5EÅ™®ÂúarxW^ãðõAÕ˜
+’·×ý~W=a4VJH‹!LÜïVtôD×<ê4ÇÞ¶îf³Ýž•d*)gŽmë Áí¨(ç;MVÅ¿dñƒ£í0ÚíD°2§ó‹Ç³Õ³}ís[saYž1.–F‘'Mñã™,ÚöjpÎØT‰ðÂ$œ§)¬ÄÁ÷k³·Ñæ]vØPfTaFÝê©`Ðä¹ßi-Ñ}»³©¾(Æ£2-?c“­þ¦:hõ·ÍC«ï¼ÒÖòÚËQpEÆ¿`¿8µ;÷ÇF{?‚¥×. B߬(Ím¦Â³¾ß"az1Ä©Õ8(â -!*æv¼s-?¸ÞƒzÛÃgea^aÔá„êÖ1†¥›0)uÚÙê¸Ý…J6ÕCÉ]‹K[·æ¶gdš¹õ³Þ8ˆŠÔóò¡1|f??hãPPs_ðÆ–²¿Øc×îuEÉ‹À•VøyþØÝ¬ŸX¾¤G{”žÞÆPŠ#¡Cæy­Cj_˜u½­ŸØRO2Ua†€2ö,*…Îë×ÚŸÓiÃY²O¡ùªÜÂÑÒ´9û«†"k!y¹œ`²Z+Uê×ûN¬OfÛ]}±Ÿú„òrõIÙÂꓬžýŦD ¯\fp%´¯Kó`2çp=9g+žßKÎ/GºØâ§ÃþF<òÝ{çèW»£Ñf²ö:ñ0si§j>j#“BeÍR¡%Æ-²9|”:s«%!ÝÑÈš§-~Ðzc½<Õ¬”Ô¨ƒRª:%ˆ«:åâ®jÇ›O•Ö_x•–B¬’Bú ¿›Y^Dø>[Cyg }Þ׬ùÜ‘wÒ~*šÈ]å²YLúAïô;PÞf[ùÅ5S[k*-Øm\ìÖ±¡‰óˆ¼öI·B}f£dmîáÝê'ÓTA™U–ûݦ‚ùgy-óeÁîöÊ‚3Ú”ñüû]Ú™90¯, J1
+y;O7reÜË1ï@ýAs°Óñp_ºÞqôÄàØÆÊ0˜Ø~-0±º‰0ÜQ ¾²¿˜ùÖk«ëRÅe^Œ#âhÆeOS­û–j}tYò¶º·«^¶ÆÉ¥Ã\+ã’Ìï¢^b:ËÇ3d‘§ßÙU®—Ëîñ•\ ðlDf«Ã¦Ùƒ+cY™ÝVÑó*¦Ñ
+Þøn] Üï³V¯™:qÃ{ t©Þ6‘é$æ/H“ä>¿˜”ãîjÔo Òmcš¯ôîcýP(zòdƒ¿w*+ # Õí€j¢¶ªù.1¹^pž¼JØÖ¼ÈÙƒ³V³´ÔPTí/g™p³v2õÜÎGšÄn·«»ï"è%‚ºÍ¸´ nKV¾»V}ÀéuÛbá»`Ë
+]š™…ñ2@ ’Ìs-Äkê¢ErÐ7žºõ±ï¯çius$Ìø®^ß¾‡+Ñþ„YÏ˹ù´p~¬êùܘ6Oa[ïžj K8NáÕèðâ^óµ£»td~vˆs/n³Ë°²‘J«Ò&XÅõa¬æ×rÏýåýs
+ Ì£€}(A2ÿÏNÖlûB2V“ŸÍ[Šåš¡H[KéÓæf}-ïÝ3Ùõç{+èz3½kE
+®«Ë2x¯ªè ñ'ô_>¦wH”6½È¦8¯º»èãûÅRTâE&}ƒs„^€;8qÊ~Aí]´G~‘df ˆ±þý"Æð 'Y^($ÙÕº©^ºwá;D ;5žZiï•Ðæv[Ü÷6Ef4\!z1ßè¥ÖÒì¼|ïmÜ÷îG=.³;<>|H(wÅÁy”í=œ¡ì×ç§ìynîfiÏ ÊI 3oj.cí,üúÅ·¾< F}$ü>X½ïÃwçºyßzæðJÓ×ë‘P´Ïô|tsDÄò*|æ¨åë°êyNGºì¹>^p»¼åˆ³‡kïƒÇv~:ìNó2qyXcáúýN®›ûÄÌF­ÌL¡Óµ>KuJgÚ{㥱ánºà xØpß2~~‘  Ó
+;³×Þ̨3~™ÒÛÏÃð`45ª˜›pKê[ÈtÑ×VzN­?Ç~ æ/ãƒzHÇ+ü|‰‘ ÒädËÏ=ÁÆ×à8N­Ñzu`ŽýÕ¾¿ÁýrµwþS=<7ׄë$*8…ò‚œkýmÕï0÷ƒÎ"Þ—¦ cjS¨ÌMžÄ¹¾Omô­Mõ\*FãbÄ#šÚ±JjhÜ»ªÞ³Mµ^Ò®jʧj«'ª~âÿðþE‚:³èû׫Š\×¥ô|žìóÛ 1Þÿ‹ÍÉç+cTó½Í¯ÂÄ>}wôÞúxÉl›Æôƒ icé×ú“õ8•ô|×ôü:4Ç„»õ´ŠåíTýºs±Ñô´Š•¨å”ö¤Ù’2TåœäDžÅr2ÝÅJ;Þ_^¿ø“ç¯ÐnEá€ÏG›Ú­w›
+ òõåñ‹$K%émY A¾œÉ}}¿Ïö®lÇ·N9ÓëM{Ú"Ïb³ùµžøfT»œ§ÞMyVCôbàaZØãKªñ©4FÔèB)´´de¶?“†­h±Ù7¥½6X‰§¡ü BÀk±PÝòümØgxƒèºB b¯ÂX:EB°}>… ü<~ñOÑʹàhÛíA,,Wlì,»Á}¶Ø yÕ®„ó±ÕBç ÷gLFÙÒºëòãöÑ®9=«6;DA‰O—ŠìI³ÆpÕêÓƒmä¤QÅ3ŽBˆÀ ž<ó ¦q¦oæ8Š/Ð}ÐŒÏ9J‹îÕ2¢/”o,7÷_Dåwÿj÷ËÕ—›:|zþœä/“ngŽ2ITk¶l)3fZ2õ‡ÜêÇøh×À~«­LQä—{Û1ÄúLýäéj["ŠV¬`·P‡O:߈÷׆nû¾CÁ^ê°X¯í[=Ä_~¿–i{饇ڳ߭q¾3Ý~ñˆàq zÛË>;S֠߯[{oÉp g±ozæü6:ª3·ˆÆ†?Mô“øãêÑ=©­ûPä#£å:Ø…‚¤ˆ¥ª¨5ÞMaR9uùYÁ¹yÖÖú Èœ÷¼·½aWéÌ’X¬™"Ìö=&ÉÌã,hGŸìš¨=¾\qï^ÍûÉÖ[Y$W~´³=ÕsŽSÆ¥™Eu'#cóöõ‘O´{]´A}¡ÆìNq/ÅPÎÂç÷`Ø R) qá6ü”ø'4¸7ãÓýE<âzˇ¨°ë€›ÞT\1…ŒváÑ—]£5¥:¦/'bßUçG÷¸{ݘŽ€{ùsøËuí³·ƒ_w›Ê_-]ØX.öGd17^•éŒ}·]%³¬ÖÌÝ#gO•^#õ‡ÛÉù4(†Ó‡¨—Ú¡qÀ³<eÝŠý”´ÁÌ¢”GT/›çzl¥‡Ý“WžtËnIW?ÃsÇ`7Ÿös].·Ÿ›Â¨ý´“mÇ ¼Gçfî_Â_J‡»íl¶]ͬÏÚ㨑Æ0åùÁ1ýa’ñ‘Õ$„²ìH¼Œ­xup¨Ž¤•õ½ðPÚ!?Ïåß\wõ„û½Å>×èq™Í×Å&C”h¦[É5$ºž©;MœvÛm„;RïÇ?(ÕÅåVz€‡­Ô‹6Tš>(§nݨX_]q2ÝÞis¡ûÛågrZ/†ÚrÞl¨öt]šOôJâG0¡2ƒSyבªË£(’ÛÌÔÊ·‚-9OŽýþùñèáƒeÂ8#ÔºÃb·Ú¹JÒT§ÓjÖ¸v§ZS)¦AÛ­~EÜ7דMÔÑÄ"Tjb—dÝPý˜Þšë–uýÃú÷|¿Ò‡Õln¼,çnl]{y&4É[“ãÑÕ7ÇdüÓcÊF뜗»¢õ•~Ö˜Ù½œµØ0gR;wCJ{Ò÷ºv^-oǵR,m6šR-³­l£!7E•3ɱ%OÏç‹YÃ"YîY®U6d)ÞÈÒ]¿’£šó›-±™ì—îâ³sÎGvmµÏúbŠ—.&Ã×§¦pÓT’ô
+Äp½á“d “m0gz®| Ñ¾èDzi×Nf¥ Ý ªÕ»—ásÍ]§Vmåvƒ°X‘¬,FF]nÖµGÔ¦‰ ÆÉµæ^ákÍ)†½ÄöV2«[õ9×_¬ZrïJåÍn>Ãc†’uG¯eúÓ»ŽêPøEiÞe;›ì¼{Aù¬JRrîý ­VÖZbuä4÷†½m^û 1jß"2XÄpýæÇùzËÕkæ€îUß'C­Òeпº“VXaÉ
+1䨱[ -Zæ»ïžÞÞ~ªõh3›Tv¯—É+´ËU¯“+ãÌ-OŒ¨g…(%¥6Qƃb°‹ŒâØh/ΩXcÓ¨p'zÙ‚qYÕò/:ÛË›©aä©ciŸsJó$G‡ýÆЧÉÆSò~ÂÓböŽ»þýú kî¦kcoÖ]Íêÿ,Í ï¶%÷ö÷5O[”ßimo«Y?^
+U˜(dKCÒÏîu.›þìƒÍÏa²›KF‚˜ë†ššƒœ£…÷¡ÇÏœê'lmN# ‹hv·«×²ƒÉ–Ï qŽžO€žˆSU‚9–$}ƒ–êñ-A¯ç/~Ðéf¢ûãF4·ÿYœoCµw¨Emù¶%}îúl™”*\¦•#Ê éÉ[ÍOšƒÇ9®ÚÁ6Û*›Ý?J|v8ëÈètt´™Í3amígj^túA‘)šÂVÉÀTz©Cñ‘¡.zžCÝì8„褑ƒè)AtZß¶àðþËø>½,!û1E³‰Œû¹mE¦çH•krYªö6ó )‹óN¡X Çh®+)& Æ';<pEôìm¨Ú :]pid:uaKåz3¡x¨KŠ2`3…º | P΀äu›Z ê¶*Üÿ,º·?;T¹<Ôíà,Ô¥ ÿ£Ìª:—È{cҗʽ¹šÔ<ÕtÍ>õ¥ƒ6(š’á½ÏÎøá-«1ž©cii< ŠCû^¥ÞW©« €ºjR}Û
+ÌÊ/ât»½}÷ÁÆçÛ¿8+
+i–ásÓxÔ©
+Ó‡¿7™ªv³ü‰wíê[˜iEiÀ;(v‘'ô|$Lä,_F0˜W.ÕVúb¼±—Á‡A‚TÍ[‚ˆpš 4KœI&ðáÄÄýè9ˆöˆ©¾”ÅØ|V)Ñ}unsoµ¸óm>æn×ä>3ñëbjÁW(rð°ßñˆ0ó É@`cú²[—¥K['Í:OxÿÚ¿ËwxßÑÝt·x “3cãÜjD¿Hà‚ €f¾õïóJ2%öû-„v!A‚ þέÇígXAº÷ºâotO•¯ð‡×CôÅÛžð«KáÙÞŸ‡8ŸjPýq4Z•øð´IøÐJ;¹½Ý*{:³èì\!ü [ªú±ãëá0LV§³÷^•+òË®½Çb´¯ rÙ„I†
+¨/Iõ„÷}>vf‰^4¯}oO™wn@W‹¯™[ï})AäØ"Œüž)RÕíz¥6Ò;ì­å4®Ôh«ù×êÖôëØï/§çÃÖ‹°íÉkÎw×9½>nW­äÞU"ò 8¼5ôliwºÌäû1—ŸÁuN˜›àËeNø§à/I&sI9 N ZoÝ¢¾÷rïÇ;ÎWTƒv§[¿°ß¬Û}»áÀÐåÔ—r«ªW¯,ŸU¨å½ Öýd¢ CËÑ¢ß_ÎÚ^;v_¯ìAóxšµË}NÜ’ÔÒšÕ‚U‰ÔœÐÝÙìqw³V«x¾Ñ\ðåüå4¯Óã_’Lï¢} A4ªîƒ«'¼ 8Qæçš['ÃÛ[{Ö¢ÜÍ¡\<®ÆQü\šçh-Ý´ à Ûª8bóÓ°‡Õ=? o¼ð›¡U]øcÓ€÷óÙ“:ù³–óW~|Æ÷›[El× ˜!‡“þ鱜 ™ +õ¢oxþ¨oTùð‡ôïy’ñžgÓÌý––¦ë@®ìÇWz2Në{ätW7rKŸú÷H^yΪ}q‘'9Rí†ÌÏ·BV,›÷6Ýœ½
+9z£qJKè+ w‰L øÌ'hš]ëb³xïçl<v¶íä5mÔÇm­ÜZµrÿqúrø²ÓÊJ²ÿË?IyÒná•K»«Ó4,YûeGÔA­d·Ê†˜µþ£ñy 2r´ÛÙrª…ÖµJ¾Í©ŸÂ³v÷›v[^ÅèådrÂgº}ÛérãÛ–ÇE«=ÑF×ÞB Iu§ÖFÎ}d„QFy‘<'+ŠE Åj2Åê »/Û/›¿ü)—sìYM–‡Eï4ÛîÎYm­-yáíÈy‹É`°¤þÔVÆßšhèq‚;Á2Ð(62v™`r>¯çù¤<&˜KC«PËŽªëKnt?®å¬gJ;wñeGÉ\†iÐý {µ]eˆ<<m˜Åå°7¸í†½É{ûeýeõ‡ôÏ!A%áqK•Ãî|eÖÞ¾¯¹³MÉ9«þsLHÞG°‹MìQæ­YFñ¹˜Ùw;ÓäËD#½P ?ã¶FµÊSË«õ°_5dC™£d÷•¹*‹öZãa¦Ó²¼«ì¥²{IÒºS¹üPÊëˆ+Iñx+ ²ææËê‹ÿ—XÌ Ïëj<Ý£mÇÙáÆpºº=ui{è+]Ž©2öÉs{–aŒ†³wbXS8¦—±”=ŒOÃÃUÓõ¥’f3µ$U:½g^f:ûÊpéÔ`míûì(Rþ˜¢RA7ÂeÂÜ pþj-„T¨f,[ДòFÐfµpy°«/þ_^ÁÞÂí]ÞGÜÞœ¬†á›<òu':
+fæ5¦gq;'LÙSm2éb|Æ[­Ö–Nß]ЧÊ]±Åè-»½<ì߆™Á¦²c_I©0»b‰ãDAÛƒ6>÷DŸ7ÛŠ®5h±\˨˜Üìs^sÏÎËçIYòä&÷¿øAŸ ž>ÄœXïû—Û|=Þ匥CKª›MÖ’­Š7Æ´˜¸5íå·½ÉÖÕñyîXZí%û#«ß+ôæÈHsþf«J2ÈiD*bµ¼X.«Â˜+·PÞèyî9kŒûo›sû»yê}f9¨çößžË=¦½ÏyºêÓEÏïÛÃÃò‹÷—{<)ŸÎ×E´Úá7×ZMó©áy^sŽ…™d=š6(cS/”«iӣǂhu£gŒ¬kÅU ôº®¼Åi Uû7I.’oQÅr° C1Æï3Á½èW«7ï½^Z½+=vZ´YÿÂØlu“0›Ôh²ÙîÂøAYnÑ]±+Lõ{o-¿x¹y³øxœ‡Š·9#cËo·BÃ壦fkœ60çšÁLv
+Qk¨¥>ö?š«y]Ž‘=äÓ¹?ÈÏ…ý*F'®0:yäŸ)¡‡lLe
+ýnÓ#{½ªÃ°|c5d¶êÝdr·ò®+7Œw·QµnñÍŒ»3¿drÏæ\F"{ÿ‹+&®@Ë€g¥ÅZ¯pÖWÆ¢€çµ9©÷3xÈ1ã
+fDíðÌá5Ÿ¶.ã© ¦ŽÕ¯eôÁd•T5ó Dwòlr«òµH9Dø"Ò]\$èÆ«…Î4‹sSÃBwî`ùôû®(*Ó§ew$ûn-ŸM:H9µÍ^­Òº.¬™lÆr“¨¥c$=ª[Ä30â) ¡î³ Bíb¡Áì_~eºðö½üÜ}!í1ÈÇâ¡a£»®jˉӓ®ôŒà³‰ßdi »›o´’»½ºõ\è®b»ÓDGtû’{¨mk‰Ø­{‰ðZ£Í`Õ|Ö¼c“46Odvyå‘ Ún5z³÷×£wbÕŹ׋ýn¦^䡟âqM¡#¯¦´gÿÆ—NÃh‚ªÞ̉F7wxáaq+)²IX~þ©cÌÁm4ˆw±œGU¨ýþöôVÅ;Â.2ÏFØhåüiØhäÍQ_1±.gzF½´ ¦µþXÖ4¤}ªU_ãOuÀÜjU$ËÑSØ•v‘Ê×kñ]¾n1¾ì`‘[î.£Y¹;óå»™ý1m‹é?Íóšâè~ªë
+҈϶®L»$•ój¨jŽrí®¥×o5m3¼–oënRz$ÝwišÅ)ì[Åw³
+®b`3T‹³÷Ñ)2óú¢˜ÑF§Â¢ö†
+½§Õ*À+DÎ/Íë</1þ3/±w./>zv^Œfó¼8 ÃÿÃeyfí¤x< ë¡r(žTpEž/žÕ›’¤´¤‰jùG©•\ÞP#ª4ÕÃ|x)c~öX̤ûK^îäßy™áòðfgÖ' ÖÆ ®’ž;Ö“Ü`]‰sˆ=J²g.eÛ†®û™[«0Ì8×Õz`r§C"âú Ћ bF†Ø*üšRþOOºðî´%V7: IC8ŠÖ¤Ö"®M¸ïÈWåÂMï%Š­^~àBÞœa­'rÇÎ Ÿkî¼jÖnŒ·¬µ gßÄ!ò8f©‘'ÖìVyî=
+E-ý#ý¤&ÏT^¦ŸXÑ(}ß ëÙ}k°˜œ¤o8†ÓשÐL_G¥/3TÒ—Ur
+¾fîýÜ¿5ùÞü2ºõ6ç'’6rÙ¿ŸÞRïvòKØõ?0›úåo v®^Ðn[ÎâéZð‚ÜÓî É *Йô›•ô£lºé§ Rõl!«>–Ûtø(å³ã¤ZÎÎïz¿´ºÕÝm8Ä/×Ë•z\»(õ¾ÜßO?]pÒ©žŸïz¦¸ªpÊ6íÉa•]¬öj¼<ï®tÜÓÖ~ÛòJ»Í•úlÿÅ+S9­^™Î>¬¯ “€²Ã+ÓçO&»ú®E
+pÄít-0Wg’“.cÒÏiRvNŸWazâ˜Úâ”Ëë£ÀÓ‡ÃjÅ]岜‘Û †£êöô¹á›Ì ÖãëxÓo³
+ŠÎö^åTn½Œªh¼öµÕ²0È-ÿxe”D6KA q²xe‹Ô Ÿ~»„S>;‡’~šËÞ:¢•¿Y=‹JûX,½‰ý6:²;C]J?ðÖ,õÍí¼¶6Xç8]OÜK¿Òû*žÍàCœÉÔï«OAËhe×–¢®ïÜpGh´X4û³8°ŽJt†L8¿·:‹ñÊ\ªñ·ž\Ì‚ÅFøÊFöî·:šòoÿvÍãe:ÛOy¨wÛïæÅ÷¶ÛYÁëçÉ«ÄþÈi­S‡XÁW‡[¦©¤£G•÷jêìÆ[Ç›pÑ|À\,¸ø¥[Э
+Ù¹;XµüçEêÏüp:e ëÈ[ÄþÂËWTÿ—érÇÎþxe›å+kTƒWö!-Ÿ2Hwïæ–›ßÍ‘¶<blzÐrß®›Mâ¥Qü9&ÇUþ±zD?ʆÂ*.LÆBWºóÄ3è9~ë󾇪O=k6oãYÖÙ.<þqÛzùiû1•>Vq²éµ{ãýгÇÏgážæ×ùìšÜfæ¶sóé¿xe'xô㣟ù„(­c¼]ë‡Ú×ö‡l¢¬_2¡¯ÄR{Õ.¹åÂÂ_Ç
+lnïa*­ß ®·Î´ÎÌRñ>lZâ0ÀVâÌgŽxkpž÷É.sùŒé
+vÏ7¯ìv°ys4z˜ó¸†¬CÖV²=3N¦ ­°_¬^­´²rýnŠÍ´<Œçki¨ô{î°‚Ó/ccûGÿâ•«Ùñý¼šgx?ïºÛ_³;SX•´ˆŒvë:º°å5§¨¥òÏxcéL׉L5v/'ü0ºo[‰ó\æßöÛäl&¹•¬rCÌe瀚EýÌ •ÓK1v•œiÔuÔãý iå2ºu?1º]õ-½³?ºz'¹?ù6ÎûßÁ¼>1Ú?Vr´¡‹ymU.×záé"‹nºhù©ÕõVb£7­î[ƒqëžuGÉ0 ¯¶YÛìÚ9Z|(ßLiF¤?ðp}DzÆîT)v ûL¿™žÞ-)zßÕ&cíy¸ÅÕ"žêû†`ª\ûª¿cG_lÕ¿KÖóT¬­w¶T˜í®Ã£õÇ‚*‘‘{õ‚Ç ‚Ï! ox«pÕœìïsÒ½•çòˆz–=?ÈSKPÑ…Y.6Ãô8çö6h]g©îÔ¦Ù~"NË}|½hiTè“ê¼°“Ô,ó4`puJ^ˆ®²´î7eéÊ)²ô¹Ú²\J-y%çÍ/Ã?’fãžóÝÇlóÊéÃUyå|ëÉ7Ÿyo|&¶JÉájî¨t×´ æbv/‡èf̼œ¡–f2š„ƒö°¿ÑQ9ö ±yÓ<¿þRça)§f_Õª"tZ¨¼rùž´ñô¤µ×ž¸wëqÀ¿+â@xˆâÀ0mq0›âþ‚|ì»1þ¸[§StÜ‘·Џn²<Ù çã>ÝU“ƸӀJÎ;whÚp÷@›ŠÉÃzcb ¬~o¤$ÌL¨Ñ¯ËZåîK+QcqW
+æ’ËýyAÒŽû–x(%´ˆô«š`.Ù1k­·¼sÓaÞ¹=~”­ ùK
+ÁºGíðŒõ9›<OÓ1þ;p¹æLºaœ<+ÕÇR Ò>FI }¼£ÈÎȵLC’|ÃÛPÞºdÆã—MØ£’\‘ù™ +ã'›×£<S’c„VƒEíómjÈÎ÷Óè§JëF÷­.ÕЊ*
+è’0˜•gmÒi×E'Hª¤Y_ ­Ç¥Òé^$Ãë~Dk«U„ÑÑCùÉ5äz³G¨rÁel°Ñ~0bâ³<c*aI×óØûT³Œ^I«Gˆ‘$VðÇ|‚ãdî&c3 cLé
+&ÄÛ¤…1_ÂflFÇf§Š½lø±ÉÔ.ãðÚN­yŽ£éá÷]òpìâé;bF7ÔU -¾E[Úùçcÿì½ß¸—­!MŽ7š•lAdÔݧwgÁ¦Ž;tB^NH@vËÈšÀáò ŸJèó9¬ˆe" EùâAì.¹Ú¨+Õöû¸[\n]1µÀ±ß]6.ÚõÕÚ¶Ý }®¬™"¶‡“ÎiÔwë…`6úsB÷¢TÝ,ïiû —Oˆ+[.ÈFS‚
+½¿¿P0b§Ó‹¥Ú‚B¢xÎ Æ^a„§·Â û<sKŒk¨€–ݕ廥‹ÕéhݳÐÞOÚN{@žvíÆçZo7¶:ßnK­­ßÖ¿¨üÀ‘Y Üy¯:Ó£X.Õ'5k#H¼¶4B\¦!µýRŽ‹ÄÕn–ã9Fw£‹9b0 ü 8ÎH3 6‘ŠÁµ­‰ŠÆyÒ×ç [©A ëÔ›¹[ÛP› Û÷‘V9pÍ‘Ý6›Xw¿nbhZEKCÓµ†<† yHS屨ŽgjÎsÇIãf:B«¨ Ï•¯C“.nðwÅé7žÀle}z’x=:`¼Ñ9¡’=ºëD‡»êxÝèVÅ"ÑÑ×¾ÓP½=ÜvëŠLf­®-›îývBR¢öA(«Wmø÷ˆj°cÔhd‰Ó²¾x'帾ظ½:Ï­µ:.
+“cær¾=3¶]Éeº¬R‡Ü¤¸‰>1_6Ö€õ ÂWÃ6„Ÿ™>„_ÊK¿`ˆ€pÂoSp¿Ûnú‡UwD[÷ORäcßÂ7 šv‘%zçÔsM¨ >kÚU=•GÊf[X@å%¼ž¢\C…™î«@D‡ŒÆ"˜øjáv¿ þ¹W‡ÈÒÇþ!²í® ²S@$óD Rݪ©™ ˆñ/ˆt)"ÇD†AôÇ`zß •ý{£LïÜcŽñ“$¸\Úî˜Á¬ˆp/ë]=\ý[iÊfOßzr[ÛÁÕey“¹îGKˆ˜1àq
+“õ!'ˆÛ †‰•Èh[„È'Ú†(Øc ŠÃJ¹—
+X‚_‹,`ò[é•ÚQê•Ì ùOeöùÑV¯ç¼æ¥ÿé§ éûsžg“ô#ô%Âvš®3a3Wa=!¿õdˆp
+`nRïŸò²´Lπㄗ‡
+ìꘋ»ËcîL­‘ÕZþñ‚òµ) 
+kæ(©&}C”Úvæ
+û,£-—³žîVÍùb8êl{X?Î]§žøÓIå={¿ðŒíVK³LB5=ž`Av1]ÎTvZÊ åÉz±6'Õüs6Ö•ÎvÜ¨ì ·E(øÈžT'©tÇÝEšƒëõ‹òÇ+óÚL_Yâã_‰nS?ÆuJÜ™/”ØŒ—Fû^§ì»±âwn'Ü÷u.¸Îu}N4uwöIÔÀ -|5]¹øaZ:u/­M¥ãýˆËŽGºèš-¡1º:ZwÔ= h'1VàI/‰m{h#°éÜúnÍ¥aë¶²·Œ`
+XQ5 0¬|‘ÿÅoŒwS{8çÜê©ï:‚Ç­™iŒ­Dá^_–õVqqÎmªóIv…Í‚ÎJð
+«Õ`²í‡îØ(}g⮕ӢÑMl+œ"”Û{ obØâVÝÊáj
+ÞÆ¨?,óèØØæW;£f+ƒAR¢H¨Ê_$ý,°‚~Hÿ‹ï» ^'Å™}ØÁ¶¶öõ6³\GL7âj5¸×ÆYÿ•´ ^t/#“]¹L#"îhÎpžÈ ì´ÝÖ³3 ðB—ƒµ)ö£ã0^÷a¥Œ>·ÎÿÀƒCto š%˜Ð-®.ôo‘höGâ-Ò× «MF¦‘½“¨‘ýDøÂéý‘ìWÜä’Éöíß²wœ—sLxbáN0†ºUß÷éÌìIP™Ô¯ý’k‹£®óÜ[¬í[†bñžaJmƪõ¦gè%8µâzÐŒàƒÞYgî}7›ykO¡QÒÈY·©ú•V™ÅNW…úLéo‰ÛßÚå%Ç _z_Øÿùå~nÓÓSѪ˜Ê
+ÅeõP ¿tüù$­xr‚&`Þ™Žäs¨nXa%ƒ›Ò)ëÚQ1â\Å-u1Ò»‚=í»³áB›.ÔX}§ÒAei=Q#'£ÀÏUM–¨”ÖAK–Ôž?ÁYywq§!îÎNܽ ½/ìæÛ+ƒWþÃ.x›qÈkBx.ZÔü%/;3ár­NÐr»I|³? ãm m·<Ü,§cÀÙô õæù~⪖Ϊ†FÝKŽÊ¦YO [i äGÙ,³giÓj¼`©ª%Q? :©¹â…–S²ù+ºÜò¼®òN^¤ø«q_˜/ôWR|MvöFÕW²Œò‹›ªP~0r;^%»ªŽÛGçãÐ
+v
+´äf|áÈA+ì}6·ËŒP¦°Ê)tl†­tÂ,ÛÃ.`G1…ËüBüqTg¢ ‹vÈï/”:=Õªëº;¹fó™\D~ÓŸ6Òݬ.°ÕE?û¬Ö¡²¤_MqX}á‚]83¼[9H½'kÜ[LŽ ‚ »€ã…©ÃžèI+ÓbÚ~zª?ßJT½'xä1ϨºÐml°÷©~è_ð?ö'¾èü¾ã<Õ˜ŸIn†ž˜9ý-*[%¿žé?“l¬.m)¯7Ä^Ò'ÂCú,Øá¤Î'…í¥™ Å}òÈ-9–6vX³œ*#¦ø1æ´ÊͶÔnßÉ£‡Éæ£Ò".‹'º¥ò˜è–)Ñ…’&a3<ñ'.3ý±;Só÷ý޹?žðàÕRƒßÌØ™PøoQy8pß-ëícY+¾|ÑDÎkÁéBW> ÉL/Å
+÷Ù‰-Žkô V(÷XfÕe¦´ìétuÃ8Ô ÇÌÈ“¨ÄÄ•í_ˆn¸ÌþÀø¸˜A°”%YÌ«>Fu¸Þ1Þ ˜—o˜
+Úô ÷ewrXß}+z{ܰÙmC[LÝœ4Ô[×^6E´n¸còšuC²+ìDÒl¾Üfóø4Ñë85öQÌ-i,ËìDð‚|Ñ»Ëôît6ÐÓïTùʦ­{Ì£u‚üïç÷[<L4¯ùÉ iÇò¶iûB¹iÛ'¼i«¬i³!úÇÂ}šƒYŒ¼ú¿Mßζ{¦„„îF e£©°0–«w¶œbÒñ§uZóËá­åÝ+Zן.H˜K]y×mv¶
+ëÔLžmÚ®ÒFF ³e>^óFÃqa7düœ/:ƒ5fШߠºA…r*„XýóòÐúçì€ öwóË|~Õ†Ócå6p)¬®Xç Gü¯¢r¯—KÚ;|¶_]e¹rÛÆÃ ZrµnÙ¥Ò­y׺Ÿ¦[ÓŠMì0G§qÇ2¥|ã=§ú Ÿ³ÝF&ÚEõÞ's®-9)W“r÷Nu-‰JU-#QUÍ ¥ª²›cÕ
+op ýc¦L8kìš#ÝÉûGyxºç92
+Èa›wŸ¥û³SWǶGT²Ü°uEqãã>Fu¡|˜Õó»Üª¶r¹sMÆÇ¯Z)=ä«›Y¡QÕ«Ö*žPÙïäš]¯|:ÛÛ²i?¥k2D~à’3©ˆ%”¼%´+–Ph€“‘€“>ý1mu{ÄÁŒnÕ„©< K!«ì£®0Uõ*»92±äVÈõmsÅF½–§5¥Ø?pù|mÚ%̨L‹Ï&'k_$íø^x£™LÁÏ1ÕMÐBF¼õ
+™¡få­k˜ïݤ{v³Ux‰û=X|‰,^F98ú ]iŽ6(ñËü»qérdÚe-Ó70¼!jE¿F‰î±ØæäN¾B9‡B‹h(iŸwÏ=ÂIX«Ì
+Ó²-)NáƒìŒ<_عçOsZ{䪷ç&»w›×ì
+uÀЇ°gÕ
+çmˆ21D ¢7DMúmˆZrˆZ5Ö€+Q±Ý¨µh
+?ýËUä§—îzU
+àó¸J-þ/ÒOÉ2Ó÷•ÑÁØfäô•,øôAÈbìÕKqš¦*¹ú§^ɦ^N¬=Ù5ÓNVH»o:Eú^u+ ƒ½éi]¹žèº~5!̺\{äøÒ]’³s"õ—g|cíN^-º?à |d¯#â¢7埬½ô~X»  wZ5ø‡nlþòß
+-Õ׿{‹`l3¹ â®1Nú:~ú2¶Û”¶×Gž‰ŒKÐmЫÂ×v)[>'á¾v&´sò¬qüÜ?Ì‘#²Â1Wèö‚Òîe½5Úifköo£îrs†Ýë¦mâµõZ2ñ‡õÕkyï¯fÃKÅ´ÖÚ¿H?Ç-ˆvi¨ ÚJ/ýH
+øYË
+Z6kF(H㦤 ˶jZ¢»ƒ"yÄÒ#ݼۻþ­1½õQsiã[ZÑ0ÓQ½t*«tÌÌ• Zû(\#€sëKSÉcóÿn~¹›µÈ<Æý¡²ö £Ð™#¸NZô3Iö=]×DZAdÖ£Qÿ~¶_ŸçÛê‰IÑ,‚YѰ‚$˜Qï$Ì ‰ŸÝ2÷j4ZÚ゜_#[«©úº&‘Ê`Ùƒ «%wåò²ˆŽ[rá™å%ErÇR¥l=¥J‡¬‹Û{ùÒø×äÝîy^ŒáOL„-wÝô?Ʀø­'ŸNïññGX0\Ø®»1Å ~VB!38ZZE¿èr«7L{ö Z}‘žÊ\ÛŠ²¨!6ÚŽ,^h_ZW¹•T˜gq‡œ3bÝ.×#‘ÁY)4Ç“¿Hð °.ó9Óà/f±ñ½Uûã2«2í‹„üR,HVÓæ,ô½âdŒ_nwom¶Uš™Å×iièúé¨_n gIòPŒØtó¨©~thªÙâ WòÙˆ’%!æ¥õ,R¤j&6D?¸ÂqõZ­LaÏÛ<ñìÝWV©çÊ/¼‡wÆ'îù` \š[6¾Ô¿Ô~ùVGÁæœ{õû.pQãBbso+!^\—ÆgÌû8¯BÿdJû½g4t9л.c}Q?Îࣄd¿ äí~M.•¶TµHTÔï$%œp†Z&£ýÀ¼}­8@¼> Ö\Úz%5êäYÿiuXv…¨lV©î
+ÅÜC.gáŒT+dŠ"R~׳÷lñ×åçÑr†í¹·È¥KxÀÑ¥æ˜k½%›=Œ.Lh¼sLþJ¶è•s–`ZÆ61-ã^†–‰|•^•ÿ¡üËA‡}}%—sì ëø,šâ͉ËåÑäÚ‡,QÇ΃¶+L5ï¸J”íû²kI»WqÆopŒ™·Ì;O¿Ü{P ¤G8~—ó ŠsžåüÆ„ÉÅa
+$¼ e;ÐeÖ)”A¨šhñä¡$/ÈC™ýP59©~©|))ýòïºhK]jÚƒ¿VOÌ[—º­±]¡ËŽÏ)³âUÎ}²œ+‘Ú1¥­OD‡CÁ’;þ¶½ÇQÊôÈ–^äfš^gCj³½¦M2‘íö˜ÂÞUÀˆ)ÈÒ¦¶ÖýûÅ“ðŽ4Þ‹8[‹*a±$Gtrå˜qU_„Õ[Ÿaóò—Ò—â/›éë*GÈdËÎ3í;>Ý9­¶ë.¸ª½8ˆÙa£]¿~ëɹóXÚåºp©/Füèêû½Éc½æÞRæÊ1›ú›]Ôºy6¨#®ñ&½®R]° Õ?Ky¬èC²©O=Â:]6ø½Ó|â˜;,cO¦±Éë03ñÉûúÄ&W¡„=[ƒ?Š¿¬³;OZÜÖ3Ö‡ƒ 1FùöÈkÐ5K.2¹S{_I<O…K`÷{Ï er¾Ñ³ _ŠØ|vzd¤ÁîFl}N²´j—ÊÔnŸoP ¤Ô%‡Vƒ&.÷®ˆßO>Ž·Y›ŽÜú~Þï¨oRE4 íÈîba9ÝÅØ¹£™QF3‡vñKektñ—•¼Ò$]LƒõÖÇ%1vÈR×ád´><dë9ï7¹zyøü[ ÙHtFΜlz#äçtõLm©AG¿‘'×}“&*Wo_%PèÚÆÇ½„ÀÒ姇ÑPœûè¼×¡Ù¥u…ÒîÚYõJp§tt±Né¤XíÂ]:%?W
+•]ÂË•!éåãk:*ãçâ.·Ôú¶t­ö®%{?z—ºƒK©˜4ëí"î+l!eÏFÁËq‹%`2(™Õü¼Üòì‚Üäý=œÍûa§”÷ÇõÿÅØ¥ÍÉ« ÑD*%d°¡²¡]j<³Úä½>ˆérvèn*ðÕd·Ê¢^­5×r 5S¶
+Q4…(¥Z¬™o¬
+‘/r»c{b-ƒQw9‹`Ò¾\nÕí»¶mÙçÒcç¬ó ¥sºm-þ[Oþ é·žü×mû[ºý+ÿSºý]#B{@wøÖmÑ*DÝÆHð­V´ý]7êü³2»þçÛÇô`‡þ’¾Â§ž¾„…’¦V•MS¦Ä(ù[Qd|+MÙz˜]Ò”+f
+ì~àßMúF3júJ–Bú¨`DéyR FVù§q™ŒñýŸ:øI©(Àžóâ–}fÕƒøà÷g-Y!·a"] QR²ËÞ]MÊßµÔªãöꦿÐÝõÄp×kk1x_œü¶réÆZï<®ìÍS:ÈÎ
+«»  Û=;joáߘ…³¾*±´îî΃xŒ&+¯ÃmVTR†—sBh.³³…–ID3ø7ßµ [u!}¯}D«~¿.Ï&À
+ÙÃâÀ<Z}ÛœáÊcÅ€üB­
+[¨ò¨…ó²PÑ5iR7{Rfµðkj%³­qï%.† &êüÖ ïåV,–Ô§¨‹úóül’Øõ½@^´z<5å
+m<< ÕKôà{ô=axô¹¾PEØk@3,“k¨ÌP Lú9¹L象Ÿhà°ÏQSÓL/<kPÓ&w¡U7þƒqãöÅb#¯„àií(¿ldQ÷6£KN¶ÚË¥óKǯÝÚI¶¸ÖwuÐT;%¥½âÄÕe(‹ezkí4áÅê„ÇqÅéî‘#‡Î½ñá›e&Û<3Êžª ?‡(zR e:#ÕÓ ) }rQÈï@R[{²àH,Y´Çd¡S:‘•¸ýBséës”“étBàø –«ô—ZìáW&6.»Ñ"¶åwvY[B“âM¸óZÓÓkŒªô{¬ë­†,Ë=CæñBwŒÐJ~WŒ„–ºýEçeJm®pr±» dE[D«ßâ»[´Á;žúÆ>Oáˆ@Žp?q„€n8R®_ü€ßÍl@iÂX'ŒwáÞ„óNêˆeL”|çc÷V}œÝÕ/§š'…Þ¥) —Dz·ÇÉY¢ƒfÙ å²âP¡ÛŸPuv¾¤òÉõ@.ÇÐ,eª¯ÎÍL)Õ|Å»1‡EÛ«ŽYÑE/Q¸Fi²’ ñmƒ!qür‘ø0> ñüpEâáêòß&úѾh§Geü¦ìú•@z'Ë·Ç&–¤óËvïU¾¬øµÒ)nszZ£ådB‘MP牭éÖ‰6~ìÕ+èâ‡im†÷”ÞK'žÖ×·, 7‡Úpeà&ƒ Íi~^s6<X+x$°:õ=´ªÔfNU¡´}UÉö/¿˜Rƒ_Lò™‚8´œ‘0¨eÞŒµ=æîà‰Z ê•hgssgªØ-î1¯ù©N¶ˆƒÙržA]-›æ0([ tdÔR{ˆ@M†ð+>ÎàÉÜÂÏ\àlš[Ugë]±ªe²²8(rEï¬òúBÏË-gs/í"\Ú%f¿´[a»Ò®¯]ÿ Y»|ñú•$#¹÷¹[³µå†1±ómO¦¯B£³Z$ªŸ³5 §œÇemji¸¥¬Dä6xB,¶Ò°œZ¥DlåÍõm–;*WÕVX:ägûRwy»ý€%´‰g‹ÖN¯‰ÎŽ/\Q¦] Ç«
+õ2Ýf#¢
+o/‘n~;¯þïÛäÚròä¨t§¢ƒ¦ðÒÄ=“FH}mgö_ ‡¯
+~Öædé!­IôVxúžï*b¬Ÿ%.ö{Í[ëÖë\÷lϺv’¡{‰ÄñøbÛùù¢œ¶gj \N^‰ÉDI{KEBD7a ŽÒÕ³Ãûâü÷ú‹äÛµäé±é9÷j£Qý#/×òé¾ÓyyÛ»‡¥Ù<^»Ãmƒo×Î}ñ¼Dü>w¾H›â™
+÷ðɃöx”4÷ô »= H>f­H;¨×¸½_2ïþ®õ¾xÛnîn"]:oðÍ»²vÒl`›-õ.)+öBÉÿ¼*&ŸÔ,š<óV!I¢
+F»”ßNÂõý5˜µãÖ  2úl<²Z{? ê·áâb¤÷¾·ÉY¯¢y°‹Ø>90cŸàÞFrè¢Ûq3nÛ^¥h÷÷…§¾´ÞãÙɒ˯·vÙŠYçnro¹ÞN{%Z¼v!÷èVyôþEgÿ‚Òù»‰7OS8çub{îóËÙÀ½‡´¾öí`2„ØÞXx„½Q½ê¸ÞfÌ]”ó÷¢¹‰í;¾mÎ?Aý1p­ôåŽYr”cÌ9‰&4(¥WroÅpF¯4ª÷»í¢7îV7—M§¯ôáäÛi2È´ÉrÆ3þ¡uO¦·ÖÐ\ÿ×ÓN…8‡¯ÞÝ8¯\„{»èn<¹ÃÁ8ÒqÏÛ«­ÁÉ=õ¦töÇ£ÓÜšnƒ9”âÞê ½ze»í9\ì1é`6O¶ÏT“o“^O1Ï¡a0ã“Óe¡ióÕ•÷Í º|5kgMk±™Œôjî¼n€Gòª/Ìò僳®ã™Óç‘¶—vY,o•Üôʽã ÐÈíGþkêFrh:®ÚSúkZ`
+†§GJK¤ãvSÖóƒF¸Á}ygWz‰—š1Q/õCV½×»¡¨'ÈT¢éW•k9¢º›•å;*ud®ïޤ§TØIbØ~K€rKÀ”Gµ)~Ù§ˆìÿ ívѳÊ,V«YeÊœìWTöL{xpF7´œlw{­³bp‹€iNs†Ø¨§Ù±^òº=­ …Ãú¡¹O3¥:º‹Vj¹ì•«ŸúšÜå»[ÊÈ\‚¤çÒÀ¥ZÉ$`›´j
+B¹µ\4Y‹Z­ð ™5&²WGXÍ»›ÎN,ä‚ýßW»Í]™§çeÞÞuÊÀ[¹Ñ¦ä<\';8ÚhÏÍVŸsÚ·úLiN™Ð›ä•ÖÚõœ\ÇH¨¥¦h_øÄPaÄúLN+é{©¦wÏRŸµéå
+Ö@¨þ€âüÁ1bAèÖ…ÕûÒÊ
+5çwðúÆ·£ÌÃý¶ÉÃ6ºJ!nÑ>7v_¬Çæ€ ±D*O¤uîVòGT6ÍE™UcÔ·•ô®, KXýˆL)µðŠ Î5yT\u¤—qr¤Úáî×fØsV`%.hô(ê¹ú],Ì¡U˜…òúIò**óðÁéq&ÿžqØ»saÏÓJ™µU¬û¦›ë‚µëІ=‡èöVŠ£3J¤Êãz¥z'°ûtžÜðl­ŠöªK°¨ÛÊvTEë4O¨J•4\éeJš8Œ$eîZ³¦ò=S\L†i>&³ã‰°ÞŸ–B«ìùݺò›~qÇXÉs¦7ÆØ tY{-v˜¸L÷€D ÛÛèDx´é„† kÝ7Ð8­¿X†0>e·øÇo¼áÉyXt_é\Ì­c®;tD KŸRêøl‹ÉÂðQ­…Ù5^Ë×o¼Xl>T¡Ã¡âÕ¾óÔ|÷C.j®¶œU¾ŸÙKHX%A&î+i>ư·!G'Т…§<¤Þ«ø@-'OÉÕQ“’amJÊtý_0ÃÕ|·Îà+#ãIç]7Þ6XÞzǪ½mó":Ò+¢*÷üÇS¥Ú£âgnD¨fLšïª‰‹¢s‹³ú Å^neufÊŒ¥ÕÞš¹ç§Ã5VwúYzei±S.SA¤0”l­t2¼•\²žNs$T³Äª jÄò]N+ñt£­Hoþ¡y;㓟T†Ÿ.пéeúzyzæÎÈ J¾Ö°ºüŒžŒØÌºEu+Žð‹;(Iso·êÌYtîññ§ÇÂkL×€òœ¶Êžš¶ûW*w¿I ?Iè
+SD“i×ñíè`ãmQ^ãpša‡}^ÁáÑÇ«wrùxñÅŒFv„¿²/UÏÎÐÅ4‡‘ä~Ϊ¨ÃGÇ=·ö­<\Ô)PjI3âÉ pwRI+ÏK‰~U—0]ë]h:# 2¥l“å–I.ª³!©Î3²`‚¢y)GDÙQ¼}7ó8<<ã? Ö{c2z6†}Ô†«K”<.ŸÈÍZŠÈ­é‘[í¶@ÉI&ýEÿ±ø"—FÄ×GÛeUºl«÷AþëWÜ+ꡱ1«S•=šÝš¾+Õ¸ ´KgdZ’½<©K\‰XïÑê<QA#ïL²m‘p‹²ª™ûÑûÇzÁ
+w”²©,S.‚°Þ­^|&‡°ú–÷j
+ê¸U¥YYDâÄÒo¹ïý€×x»ð³Œ°ØaW0ÀºçjÄ@5W«”+óŒËW õÕ.¯ ^Pn¬«·rÉ
+iÖÇ3íw³ÙgOu¦~ ¥mÚÜ €êôtŸa ÛTPF¼‚p¦Ø/VÓ”®Ìc²¢ ïµJA(éåÕ[ì¥ð‡å²±K;8¿/µê£›ËB±wͧse¸ÍB?ýéOЭÖC åõ ¤\™H:þ¢ï óad´ØÁ¸œÐý2\E»7Ž/3At<Ÿ=ýZK XkÀ
+mºE¶ü—€Q­ûá,s‚Ê­9–+š·G¡ˆ{¸páªà@}©@­æ (nÞMÈ« #ˆÝË|âE—¼ÏÙ¼ð
+pðàЋ!(=jG0ë—Ë`¶O÷@i…àä•_ý€àäô^~áÑ[´çÉž1M<‡u„VjVz™·êÓATÛøýçy”KCÑÁ®ð±Ï QÞâ×r‘X—sùçr‚ÓÁ¤*Ìsæ>Års¿gä4qåä
+™Ü4»šÉûl9ï'™íªTÉÀC
+9{_œ¬‘öþƒJØë&O.›†Ôà 4¸j.…\ú{dcþ€‰vW·YóŒõcetÛàÒòÚ¹³û >AÎg*¬ÜOì*ÿŠü2”=¾;¥ÂQŠJ•CH”ÐC~P"wþÎì*“§¸E»º±
+P{íTªÎ*îó{®¼ÓšÁÖB§#ÿ"àBþÿɳ¥Ч?1怿Ñ6*q±«A×έ†\p_ãN±Ò¬G~Qk¥C­¿_RewWñÏ£-2=N7øb‘æck§¸X¯bc}X±‡õyécëxñ¶w¯…_ÀyÈ%åy~’ÇÃbXág¤7§ûæm<Eöý[`¡ç|`a™R€ŸnÅ/&—þ®ðůL›$ô9ûi´wuÞb}÷¼©Éã||7Ýø·Àv/T¡ >ÏÁ«¸PK¹¦ß”8ÉóÙjóüè`„úûЛmäƒ=«ÌOÃi·pšQë:ðýs;q0èüŽcMÆlZÝù>7ÄFï  Œäìr2ÊΩhjú}X/ñ0¿Æoÿ¼²ö˜ŠFñzÅ×é¾àOÇ)Ä.ySô7Qm<^y¥Z¸x›Ÿ¾ñ¹zÇ®aq¿¦ûz>DÍ|úýwEdBEYbìYÆOYÁyeô!‘ô¦ºÃPœa~®½FÁ^º{¹Õ\á=Ø&Ø
+aeæMΜicÕϧNO§sîØ+æ‹nK ÏÝ
+ÎAδRoGðnÜÆ}pm\„Çêöû¸üâ-üEµÂêE¸·yÙ3¶SRçÇãçïùÙ7$õ&ƹ»ÅKàLÆHÏ‹{¾ŸÃ+(`K3¤È½™÷˜sOò÷]£læ;ûŽ]î ÑkGþo°n\všiÐÈxÜòüç¶ÅÕŒWóYᘦ8¬ Á«µhÈk}ñ6ä‰4ÿ‡ónà[»0É-tS¸M/$·O…ɨpLL÷ðÜ*Οýd0–\Gd")£WRU§[µ:“N÷j/ÚÖ£³1®ºƒâ"_Zwcñlq‡M¶ùeËMñ]Á›@ 
+ ¥:iêóÞÙÖ5œ uè}¸i«iÑʈaÖ·Ïü¢Þö“ùX—ð‹H™”°µÐ•²a—.Þ&I¥÷Ëh¹ÎL=dsé;®;TûM"z+ËûÕGƒiŸ6]ŸÞývëQX:-¾µ÷›ãý%lf¸÷¦¡Œß‡(U®º"‰¶†è¬Ö<uËõõöí-«ÇºV|ª Í&*v&#ålߊ
+•;}V…2‚Öì¿ÓC%¿E—u²’ýŠÊcùýÜ«»Ûl@—v_.)joÓÂcQªL¸B[üéã§ÔÌÌžõ†Š¢]}ѧ½p}­¥6Âúni¬ëHÉØ«Ç­wQq8H”óã™W(±Ë1ÐàewºiJ ‚xÒ(m$a,ekoI©×‚*ä§ ¦µ W ~Àïæ‹­ý +ó-xÉÃfûúzpÏùch?_˹‚™z'Z&d+¡æÅf¶¬TõE¢ÚhŠõ=Ü×ëˆ9ì¨æ{å(y;RœÜ}&ÇÚ3Í-e¯X:H‰Cß$Ÿn¼%á>/×þNÕä¢\C#è‹uYŠùÇñ%,ý›,,WsOXŽO¡Q[_}°NÖ¯rØKsÕIÍ ã_QÙ%fáØŸQÛk›ŒÞ¦§²™}* ¶ÑÏ…z—>#ªÅ?å²)Ër|(7dúRr®9’H)# Èög5¹>\×rÐ6ÃÕó.Öí$,ã>&4¼»Äo bzöyc½™ñU§óàŒ)p¶6H1ÿÒó¿Xñ±<#e/3VóÁýë*ጶ‹…µ}¯kùfëù)½qÃ@õ”3AÅYë%ÙÛ·ÒYòŸ6'µ‰R“g³F-§ž{b=z8"Ô/øÂŠdæB3ÓÛó[uxåÛùs†¯®°*×#û<‡Æ@‡=yÝ€ík•+K†%K™~ŠÚøŸ%fô~7K š•§Ì¼›ñõ†sw/Ò4²ßíÅÒÜ Þ°CÉd«9Ã}ZƒY Rè•‘ÆlÖ¦Í|µ–Ûᔨ!‚(¬,IJW½Å·½Ë·•µÇÃà-½Æ¸C¯9Œ`"ötí¿ØþôQfny’aËi‹aúô˜~Ñ™ÆIï “>
+þ#zxÌ ¿X”ËR `FvÔºZ—ÎNOvö1[õö+ßÿÛ­tºá0ê¹BÒxw~Ö@"ÌŠ&,
+ea‰òí bxXÉJ\ïXÑ9Ìd;ìo9lÿ=ILp\07<0Ì‘ÐsT ùˤ^E£&Ê=Íú()o©ÌÉB©ÌZï¤8ލÌdíQµ×yøExDØâD²åì°«t“ןûõãqÛCûáÄðçµ®^6Å ^…îByÁ¾xxCç8“6ªéy9’µù¥ÀÜ&{•q¬Á°UÄ¢iH†iÕ/ð»5™¼n”$ 9;LpR5Ë*™Ç‡¡OѡϪeBqƒÐ=×ûƒ®é‹ýÈûggŸ*ëY1÷«~¹ùÕj
+»]—,§'fÊ+f=:ˬì·áŠX´f gÙ„µÃ'ÀÄZbX›ÃèQ,3´àYõ®/T
+†<úTYÈ@R‚wð réÑ9?ÒF÷¼PX¼óB1[
+ÀÖ#9Å« `ÅKqZØV¾ØH?2óÒ߆êÀæå€ _0€Y¥>€5üEŠh`âfû•{TP"[ËÑ­ôj´;{K«VãÄFÌ’ÖvÅnC×.¬æ\¹Œäz¼¦08{ŠÙ ã‚
+È×£ÿ§øüwÿÓwûŒ
+jÏ[¨Êé¹n*dóÜ“o•Ì@fº™‘ô@±P>ÛûÚ«6L÷R›”_¹Zm¯`âŒ[É¢ò",1M~VˆJ‚ÎÌ4AgASÐáWïVo”ÊÌ¢Cö6žÀ~X½½Bç>Ý~ÎZµºû½Ä^á^lÌý8=bÍý9®Ããã[ÅfH!£–]2OI·"H#Uª×^K©Y“Jj·–ÙšŽ8¬'¢Ê<–Âb §sk£ýŠ¥s…_߯°«·‡ê!·Çºy®s‘ÒZ,ݬ{°™u¿X†µbf:€’Ø×äÓÞ=#·¹ýâ“¡¹º®Ú>*ñ-±u/ëÅQó®âdë,ßé.%@=S{ƒõBM2-Dœ]<JTeš' Ë\¤¦×û%õŠÉoŠ‚Ç·.öŒÛÓ‹× wYt
+ldeX¿j-æ2‚É›qêqq4½ûAçx©½(ÁðååmÔ<Ò‘ß%“×ÒάãQo§Ì;íÁJ›¹ðT©ïßÍDöäX{i±—Ms>1
+Á×N¿†·èUÛÍï&ÖÑц»+ôxIbÔ%äüˆ8Ä^vzÕ$7Õj¢%CbtóDlm|ñ]žñëR=àÂíµ%™Ã×\Â;W·QŠÜ¦â%²VoÙ<ؘöÓŠÌÓÌš(òØoM˜ ^‘šOl_™å±ÎþXÁÞÿ¡ë=×TÕ¶®í#¨ƒ$g$Š¢(f (æœÅ€áü?¤æÚsíý>ߟ»(§SF!¡vµÑz*®ú¸Ïë\ó].°¡åUÙuî±ò›3÷†2êå…ÐÏqG¡ƒ<U¥M䲤㓟Žq(Ðö*Ñwu í{úÉj‡{«|bÎI´Ä7 ˜~ô¦5­æ^/X5ËÔˆP¯†ɹêõ»àü½àNÓõškÕÞ'6<#w¶ãÉ€ÍHÆ<R–ñ§» £RýJKeÚôÊ ¤í„ß¹#•›­à„œcœDÖË2™®¸S2]dÚÎ[da:tÈB¯[$g·‰÷ºÍ.hçÏç¨Y+ña-Q^áaQw\j%šã/h¥: CáwFó„雵%ýšÔ¶tiŸipÜ=©Ió Qy‘"(øjðä¼ÛÔÈb~W 1k«e1 HqµÅ··ÌgÔ9‡^fkŒ ÆÇw,¬Ñy›Øa+p†ÈœJ¡ð8´«‡]ÈsFð§!LÈkå3v¿>à{w×úõ·WS6ÑH¨•è Û³%e#ÂŒÄ6Ð’pihKá‚ïö¯ñgÞ{êÁŽcHš95‹ pÛM_Q/- ʽ…«ÚëfiTUy$zd0Ü@¨Ê/ TÅ¢ÚÓß¿ð¥)u¬ñ*Ý¢noP{ äݧ…Câþ¦ÿãV‚逗¬€pá6s§ÇurKlì^þ4IÕ1–{ô>½_Q¿Î~âÛj.cÌ!·W‘ -;ˆ)÷ÚðçþžÁã~õ
+ç „øA ÙØ3¡4’ö 4úœ¡‚F‰Ð¬¾(ÂÐlý‹?9ó!Öh=/©j|RDårY8Ù×|’]¬Vâ–çË$ðrg†ñI®VÊ ;‡ÆòAûÃÙ I¡•%œ/•60Bøgh¾ÝDP±ú‚ ŒåÈT¹•“R¤X Ú «€QÕAŒË0Z /i1^-Ààûø5ò€I9ù½ oMçI:
+¶èKŸ¨+¥© ìõ.a³ù±!PhK±Šç¢Xëöv°2IÊé(
+°A滂› ü `‡P°žÝŒq›¶] ÛáIÀV`«¾XÛ‰_ÓÚ*`y²#ßNð+*·¡.UuoéRm8ŠòŸ¢¹3÷Ul¬­“?Å*~ßáü¥RËgšÂ[ B_%=Brã"öìð`|< Ü â­\*F@ĈÉFŒAð0Ö‹Ñ_áž1ÎàžÉÊzîeM
+„"N
+úo‹b#Æy„eáã!®Ä0²ê@3k t"ÕkµÏ"þ(û£vkÈ.®ùÝåâM3Ó °V£N¨+ƒü:ƒí„!ßóÎ$WÅ–‚Žå> —¤ZbE†8¶Q nÜô?P@ o"
+4_É­O cÜï@,y -›eJã¸ÙÊÀàÛ]?_E‰]l¬nñ^i¢¨Ê–S§F!ÿÇšüÔ¯n›¸mÕû¿ÓþJ·‰ï6‘nµ×7ÛVŸ{
+…Ñ»t =u¾öq½¿ÚK§€Ü«ÇOí”=Z§°p˜‰ÏJ8ðƒì6&l÷yc»99w4«­;̨˜ ê7rà›1°°^¾–¨£ÿ#‚&ˆÆÀ¨»"oîy"^ÙÞÄ…`ë\äɹñƒœžÎ¶{üT¶ÃÃŒïOöKÙ[ìÜW~½­§”ÝædKǰ”Âu‡P¢Õ½ª|VêÑB—àP‹O§-- ~ϘÛï‹;[æò£¾ _Ó
+îÑ“}¥Ì$øA&u:KO ’ü5¯þ÷ÿ‘C‹ç‹<N]OÆjìx"»s#ÛÖ>n#ÌÍÌêîŠÆòYçò‹G8 ¨ܹýõÙÒñ›áЛVL©=›Ôqn=nÑÜq6´ûH¾T¡¡/OèàÙ'²ñƒÁ8gŸ9xð@ófôkbý~à'7{ÝÜ“w¼´ß½Å7A·´ v”¬;¨>X>«üla…Èfn?ÈËÌy3Ï)5CSv¥Ç-¥FæÃ ïÍ4TÃç×¿Šš¿þÕ±—И­÷ °Üñ—E{äãëöú«ŽRë°[ç>PçÔn+V¦Üm‡ýí²ÝÑçËxë°ú‹èSÙ¼o4º O/ª½ÚUÜGgÝAÅ¥q€òs´§ÔÄ,[¸ØÅ3?x¶‰ñwq5°^â¡_HÑiÓw+ç^…`RÝ}•A»uF!;§¦Îu„0+·;™²éÝû]ÇSß«f+°^Aó3׶Ͷü4fßlªÃê5УÜýA’ îelž:ëŽ~ÙòÒx™ °ælµ†
+“zx–G¡‹pÁ³µVÔVýeαz›b­Ü­Sv½sª×âCà9ƒ¶|s'ž¯º‹Ö3p7­ÀlãÙëgÒ›9¤5fN
+Ök+ÝMÔVÌWcÞNz¹€àµªÇð?HµÑc”ÊÕ­ŠTÊ;nw›¯¸JµÚ,÷£n¯ô6ºA)»™ÌJ©Êuçä ,.îS,†UXç®ZÁEr{× ÇvM !›MôüqѬã'rò#ñ*'¢òD”ÇÁT²Ú~õ@;ë"R<ÓãM´SzÔvµøÍO°Ð½5±W9ËéÒû`0%‹·%g:ªé
+5sÅÅlàqnQ+¸Þ¢õƒ(éÖ·k½÷Äf Ê6ß\UoyÁ»¡¹öròl^°|ŒëY¾ ?²Ï *gŸÛv!T]û/ÖÓô6ÛÕë»$Z·¿A…N÷ò&ÊÞ{}t1µCÖ˜ötïã÷®ô‘í3KÛaq)ÕÞ…ÝA Ô2ˆÏVq#Úì5Tó­üÇÊ…‹O!×)Që¾e›–ßuú–¦ŒæÙ`q;fÍ’þ1Ç×)cæzLö1a{åótçh.`>‚ý!•O°Ên›·©P6wCh.MýzIèu4*]iÙ›Yg‰x_ÊŒx•&Åõ¶Hˆ÷µ½–»ü?]óbåùþArˆN[ƒ£,m,òÙW¥¤fÍCÉÌ‚F§`æ.¾k£°mÌsøÐHì¾j,îzÙP ÄwºV­J5­Úã¶£‰ŒÆÂ|¼µÏýÅϯú=ömz7X¾™YOœP~@µIè7«ßâØòhÞñSmoÑšç8³²úEó¶ ·,¸×?f¾ÐGM$½¢ŒbyËyÍè«;‘ÕI‹gâÚö•‹‡²ûSþ,ÔÆÝ¸¨¼¿A”KÑÊ(m>ª(R8Y(2xP™Û´n%P¤[ësªƒ_GÁ‰Ø÷·.9ï* Þ÷à\¯ÚÇ\Å?–¾©2
+l¡wÈ)anšÍÏ33?=¬âézÒ×â+>:7h;[Ik54O©ÇR‘W›„§¨üu›S.ÝWYiÛZ[‘Q’鵞nj*€üL=y°ïų$ÙhèÙtqÙ´¬l´2ÿÂŒƒÑ[’©ìîØ¢£ÐA +ƒFwßwØl‰‘RùpØ<f?ql8jc¬»fy®Õòå­z\4âªOm6ç‘r ×@iwnéÌíQ™Þ‚3ª“Õå§Ð/ʃð܉HŸ¼¿•Æ,õ–¬çŒ“ ¡Zg.2 4ƒÄ8k⬚Ί³<0L®Ü=ž‹óñëлe{äP曥½štŠåɧ.éè çRûlÎôDÛ‹R 6©±ÌJ‘ÇÒ!ãÃJ(?‹z$˜›’Ë—>í#'s˜*å`%>Ç$èЯ‰³ÆÕ Fn-¢Ÿ}$,·%F ˜“%êbÀo^Ç¿ywЦ@ð+#ÁøáíoI£¾žœFVmHÆ=)vª½yªTFÂ@)ð­úƒXÃ^;4Jz¦ŠtägîëõP~î§SÙäÖkésŽNRNzß$â?âlf¡b¡Þ¦Å4·„å;•J#¥,î°ËoixÁW;w0
+5’ƒ•},®ÔHqÔ»úVÐúÓ‚Eà?|a}¾´Ç;d×Üæìe­«7÷VÅúãd傉Θ%Šk~Mhòh r‡w¹&¾ì±—Êfȶéç‚•ŽøŽ¹ Œéµ;0=¨ì)Ú` ‰zÕ,5Œª”e"r:)nI»†¤H”íKÄ✩Îȉ¯JbZ ±Øô2$ŠOÒ6ÊoîIƺ^Ûð”?6g]íŸJ£Ä\8>¶5Éy£r¦`å-/RAÈÏxvÊv™Þ£Õ¥—œO£Î„úX³ÕB¡ðHYsèFA 3¾Ž“öeÆKë®%X.ø<¾Ud…Saÿ…í»±ÑÊIŸ‚Íc_'«óªŒí‹†ŒSs'“„iw^³ëÝÃlýòƒÔ/ w[•*“’+;ö­‰;ÙÒ´ó B2Ynv‰qyÉ^UÙ§_ê…:Ü!–g¸O”<qJÙÒߦü3^™n"œ.
+ã¼J&}––q¥”nÝ©fZœMç赨<Ð.vaÐÌÆ+"}¦°@3SG33HF;wô¾xì|:©"x¶(´x«Ý±ÚÞÅ“§voøùAˆ—M×idTù¤ed¸yü |ö¶ŒXC²ØÖZØá:÷Òç¨ÛI{ƒy–Œó ½xà ÑîÖÿ Jõ€!KHÿT±½uªÃï¬=G©è
+gý§tÒ‚&ïë
+R‚Îh3G[KÕ;Ûâ³2‰ëøøYI;ëükÊ÷Ímyéª}=¯K«<Ds×ÒøCO(ïLTŽÎ"}Õ:ò²A†+Ã~ªtÑû€ö± Fv£¿týptƒOÅèÒ€nÂzŒVåt ÆXœ]vp@»ŒhëD>¾lieŸ´ŠÇ¯±P¶†Wx“›1¿Ñ.¦µ¯ZÌ…G_—–7ƒø˜.ð—e²‘—"Éa1þ[0iãnPýÎaÍ Á®?æ2œÄØ.
+d_Ðê2º¾õ<
+ ³Î€ [­ g®A†Ï¤AF ò #b!‘T +^×j:W<R/½7y3øp½ä5½Ò§¶ _‹¯íz²bŽPS|Máÿ Q‘k]ån›XnüpHdܯÝõ~{o¥i@E¢5Pký¨3ê¶¿>ØÇ~
+; á hšË#Ûd´_õGfyÈøGŸÑ$1ï`ƒtÓEaœíi_ùãNTÛozÿݶ‰ñW‰â‹ø7å6QpÿG¼ýÍ»m¬€Î§o@ï÷a Gñ À@>.0ÔË…ù¡Œ¦ï#¨…ÀR80¼‘ö?ë÷$Úâÿ,âOÂl“¶^çèŸnY‰Ñ6‰€³ÿ0k?dß‹û8ú”ÐåY[|AUãú°îç6÷Ô}):¹;Þ#ƒ›ûôI
+B‚ĺšhµÛzZ¯®ÃêÚ‰‡¤aÿ¨Ÿô0šÈË3‰²MܶI[¯$axyÐõÓ7$‚Êñ°woö¾äRÒ÷Rêm#‘ÙÕ#íë^•ÊW6PêáÉ,wCaÒ^:Èpy¾;‡ÓYÝJÐ)`AöøiÕ½äo@lâ¬M„ÑdiÒšêÿUBñ69Æ{%úäïù*AÕZÅ;w¼)Œ¯û ¾O:¾»„t:ßíg|·<«ËÏó
+ÊÜù¼G'gÍŸÅÙÁ‹…F9SX¾¯åBi3žˆÉ&mok–jo{5žàWT†îðdÒZ¯Û?È(~É߬²jWS¤MÂg+EtÙ×èL{[9óƒÐ½Z‹wùQß eí|"KÃì“/éSv&uÖtà3›/Î=µTÄRv½P¶ÝNDG#»ÚºÄçXþp#¯ùF¯‰ä.èMÌy«²““\nbu4lõà“b)‡ý¿°¾_Ñݬ*®&#½ÐëôWa©Üõj’ÝD?³wmŸªî+mØ™•ûÓS\õ•²¥ÙÊÉw÷§bq°¿Öø Èn:mW#‚²™ÈçFNÉó㜑kÓ;'‚ŠÕã—ž¥„Ð(;ÈY»¬¯_æ¨)Ц%,,3u-ûÆtÚùv]Ãö5)ÙN°œ÷»‰y“À–Ü®ÏÂ…J;Z*ÙâB\~ A%SR¥1y&Ý/ß6Ý, –yû\»M_­Û{Z=? YjÁ#~ì`å1Y£<‘ÌÑm«›VïeÓWºbØ«RO_’ã¹îÜÙ«ŽÏ–¸VÁ(U£.7O£®D¤î'[.ÆYø‹f»Qô)OxÍîu;˜VýA<kËX ·÷ªÒ¥ •³yþT\±÷‰Í^½œ<Ô{–ºË޲ÚX™Ÿ {0s šÐöð2
+Ì1e Ç7¦—LŒÑ IÚfR4âs´[T÷ÇsS­f<ãSÎ 8*­+boŸÉ\µZ=Óņ×L/31<þ/f5­²Ži}ú}²ú]h´
+UÅ®³ÏÏ7
+¡‹8‹&s±`3ÍeöÝv0Qû«Z ßHg.c½´H­tÂaZeÇÝ)¾ÔúÃI©\¿)g}Ê(öV2×
+feº™r5“‰'òcaîd]Ø|¤×Õä¥áu¥áL9IÃCÅÈò?ˆ4œ+\‚Dýfóì¼wÕ~{(çšÍ2¢k¢eÊŸû+nå–oç7óìð\ôŒô¦×Ô¶ ÛÓè‹ÛWrs¢œgƒ•"¥GûÌuµ 3Ýr×üEfaùq7Y/ÔéžêÒð‚•¥l»Ü•RÖk-楧ˆ4^Œ°0fް0/;a‘]`1ÞlaÜD¾iûÁ
+“æ= X7™™­$±á©*$ª–áúœ(Ô+ü=ç#áÊ´³Õ®V=X5å’zÖ©Žv2Ý3È‘ÄNe½'¬¤·í¥Ú¼JÙåô-¥ZwTœJ#Ú)W‘Ý®(8Ò°Wwþƒðëqç]wNñ]°¹]Ô\s»§ƒò”ºcyŠ]0¼[;²£JïûÛ6¿è*Ucè!õB»~4²åÊ %%,½¢l–"Ë4ökù5|UàåjFyΪ_Õø i”ÇGg1 D[ØÏD$¼¯…E=
+NÞx8ROñëã†äÝ$ñ”iç¸=XÔ¹ÚN™plï|eOZ™`…5l±Â†³Â‡ØÑfØøv—cOfIRªûŒ¥¬:Å·­Ò­ x±â¾wjÖ)¿®l¾ÛW^Ù\xÚê´Ú(òE¨H£kÅíA¦*,>ZKpÆ¥¸Bƒ+#Þû žjì÷ÜžÿÄUWûÈŽ—Ò쩲äÙƒš¬ÕªL8ˆ†L§äéû.…Ò~onК6ýÊ÷È éûQ¦˜ cÑCø
+>?cVíÑ@77é–ÿ§sÛÔÉÿ ÅÝrLz›ó+zÔê|œÑÚF]DœÃo:À婨sû>×娷°§QnƶÜΖ©Ù™ ïÏ'Óés“)ÖYúm4Úe—zez}jˆð‡„2k˜¥¢JN¤BŸœÈµ7Ü'I™”¢Ìч °ÜëØk×¥Mr¬i“:ûƒê}Óo–gµZ¡ÈŽÉú`.d¬»ìE½¨¹<õÚB)¹W_•âï¥u^U™«´m1]ô™ÌŒžÐ‚¶¢ûéÊ‘Öγ5”)* Ñf¥ 9©ŸŠdž7ºÄ¼½ÞÅœ Ad|µx´ñÕ
+}XzLEmFsOÆõyxœº½¿õ
+FmÑàÊÁ°ªWíÒ
+m:Q¦3–õî¡յ窱ÑkijM{|õX*3]¯ëкš-Q¯g¹F ƒ~‡Ê–C*E¼æädGïÈ|¯ûÄüYÅ`…X™ñ¸>ÈãdíÔÂvš»Äªoä…1£©nÒFú˜Ÿ]1fZÀ±ê­ô‹>[|]:'í[¥°¹ª{Ùͤ’Íl»%â9rí®œÓ¿iÀï¡×ã¿&óÒ[ÑÙ¯œ`Ô«´AzEÒ&H‡XT17¾‹JM;æ{øzÐãn\‰áäçqÄjùôc™"’>'lºÙJ›iÁôh˜bçh{ºŽ~T.Äw‚ÞEª!w¼¢r‰ÃÑv þ¢Ž®a{B*Çæv3ÚÖº·ýÌ;ÃëZ>’;–¹Z¿õ:¨½åÜŽØ ´v1÷0hRЮ[Ä+ð­Ï÷ÙõÔIŸÜ]5Ý¢"/-°>6”)ÚáË›Û+*Èr—iÄÏtDcr5øy 'pÐªÝ Ï ¢ qo]„>ô6éy€'è>rà?+“~}ÇÊúPôî˲ÓxÅfðnåF~'oÔ¨7§ècû#®žé#çy—é×sÒ&Êì°œ>6š™Õ
+H?í–bLˆvŽ|øÕÆÆðPÖ×°y÷.? üÃʽ‚GÅLªÍ‹©ô\ëƒ2¶9
+¯[Ë
+ù„¾nßDPêiWc´[1=@¢1 Îü*†w‰@]òtŒƒ¨°P‹ñ™
+¼ÒúŸ1$»ÿ
+ÈÿJoH,· þ+½á«à&Âé_÷}jÅ0 ,n[ ¢#
+ÔÖ»
+¼éÇ:A[~÷Oc¼'zòÙ‡Ôé~)€ö:Ì/¾bè·¿Øl©vŠ“}>ó5¯rQ‡{CŸÏŒãNÌcÛögYâØG§Dè—PâáãKîÝ«<ÔÝWùt·ÎèTçÔÌò!,«íx6÷|}Xi=G×^Ë„¹ÅW-&´éhYUný5¬þ˺Š×VçÓ³€Ž·ì©,}hƃƒ¥IÖéá½v䟮Ë÷g†­ø%ØÈö¨µPèÖ)µÒiqrói‡žÚõî=¶ïùª:n=uÞ2¼jŽóò®™CìKcæ8º©ƒz‰ðÚ¦q«ûöÓ¬Ö3Vµrê¯GáSÜN^Wb¬7ƒ ‹ñƒ\2Å÷nË=€¿˜tzî”~æŒáÃJуOg÷î£#ëÕݗp¦¼{§‹µ½J7ÇVUlÌ
+eµ®ªÙz‰¬ÚµM­ZªQ§jµºoyÍ*{õ:•–âõŠÅ—|ÇØ®Ë÷ñã\ÖâU2qs>›IÆÉ1°Sœ5ýa± ©"zcÅjæ/˜$¯–#µØ™²ZŠ­¿Gt‘[ #v §¸æWïÝøÀ¤ø}}éD§Z…>Þªuþø®´ä=ì†þ wåWÈ”ýl(”žÓ0SŠŸÅºóY>-'G|Š´£ªÅ+· Ë–ðk{eW”ã5¿(8_7{â’;MÉr®U w¹VýäD¦þb#gZ³Ù&uF~ëîÐûÄ螇i«Y4§ÔãX/çÓj½ܰW ʾ™›–‚¼¹rÆŽ¶w °Ï+‹…LåYXνTàûi{s™S6mmùüz(y®ð2s­ íäÄv¡iu¡ñ0û(<·Ù>ï¾Ì×èΘÃ\Ó6³Le¦Ðb*Æœü‹åØå¦?Ȥõ¾uƒ<³¬ôÃn¶Ý§ ®9;¥¡:5Üž+íœ;)ëµBÇÉëvae;ö¶^Úô«¿È7²“]î¼Û^r»½[×Óõm)Ù’R$‘Õm†5ßl1¾ó›ÙV5k¦Ä•k仝޾@s ½¸Üßt¬œ#´òí“Ó\XZiäü4rA¦5òÈcÉÖ¼ô|LGýÙ´Û_³ZWÚy¹ßØZ’ Ъ8Ý^Ë&ïÌã#¶¸¦=»1Õœ·T­n4he#gÝÏê›ãÄÑ—¥™jÜvÆTHüGŠôENúèì¤ulÞ¦5—_Ȧrj-£ÔT^”¦CA˜Ã™Ë5§þ ™ö´6Ê´÷åWŒ)òÓÃu5¦V½ž_ëõöó»
+ºcâ‘L’%€Ú¶˜k­=–ô¥0O]ŸBaÖà„t£ÕÒÍ^(¤Š·>)¡p¡±jx³þ¦Vëw3E¿åÁf¿T?Pm£â4¦„Nï˹;+ªÀƒøCEE«œƒœÒ
+ˆ\¦3ùÀ®¬9é†ô´ž„¥‘2¥™8ƒ­˜›ï/"ÜD_Â\0ÓBá3øU.4ù2ã4xâx›rÛÀ»sL
+¡8†"âJ‰chùc¸jeŸJ026yŸVÛAG»t¼VzÛ-×Î~Ót‡‚É9¥î;•=÷ýÐôÈÀǺÊKõ¬ÜϬLiÈ{Y Œ†E1M*"<;´~¡˜>t…ôñ«6·àËVéÀ“ã¶ûÄU"Í1z ³‡ZaùJ{Ä\>d¼hG0R0tiî™6Oc½·ûa.z s™êÂç–Ía{8
+:ñS¬ÜVjR¡g¹àSŠÛ‰ÄÎÞ9 ͹‰N—fæÖ«XR¶ÑÕ…ù^2Œã-¾ìIEž”òU®ÚË79ÆîúS»NÙ#—Þ°W.dù¡ï´ü"¦M™
+}óg%º§ËZÅ.'*Ú41jÐ-¨A¯³¡¢»úI@«”ñ
+ýü¢ç•š£vÊ
+º¿Ñ=ma—óaK.2¶‚åôÔ 4Óº8ÓØÏ»ýƒÈOµc‹È¥ªñä2R¹Ú×ït¬Fy¶ÉbeæÒ"ëLÛÌvT‡ŒLVômŸè^Ï|RÑÓKSƒåK¢Œr¡@~¤OŽî½ Y„˜ao‹˜Üò!¡ÒýEZ+èMŽ^è{PƲËn÷Z{65ó,³mTžé]±Œâ­ÐT‹¤5ÜMoFY/­TðßxŽSYpÞ}=£Ê„ÈTgdgjÑwüèÐjåX‰ç/Oñ¨AÄ÷)cQž‘g¸#ÇÂëNZW&¡É„'f6'ÐSÐÁ—ž¼ÅKÒ)…ÐÔÀ ø1ÇKùå _v7¿èWÛúªMÚÓÖâ¾Ô›Ñ9>Ç*÷.—œ¸6·C´J[©Ýí©ïô`§Ü@q(M
+h¯¬ç&Ó©îTê962” :ù94mrܹdŽ_7HèõˆÙ˜Ÿ…\qC¤éù/5n
+Gî¶
+èÂz7Â4|‰»ôö&GX_Ã<Àèg|]f÷^a0]7:>OZÌ•1™X‘WØbÜ`lë.¦<äð*œÆ‚çRF~6à2‰:‘ÕF¸Œ¶[¾ŒÜ®éuÇÙwQmª?Q«–í l”Ž;èC’wÈê¹(©éIÙCÝËÔv
+}¢x.‘3J}:vª ÜTÞ·Àr€À…EŒÁ" Ç € ¥bŒGü¯Ô8]¡¸5€?__þ¼¾Ûùl#lÖð÷©¦ZÂÆµ'…©³—ûýüÓP+æ:Zij¯'ÒÂÛG<hl鑨‰ò>ÝH_¦ˆô÷–§H(›Â„Ž HÚü¢Uˆ±ùvÿ!´ÃÇXï
+ˆ}‰QM{Ž´ŠB «&dìËˉ¿,ŠZcƒa´òM®4´LNDI¡ÎOæÙ.îI'bÇXóE·Qu­ÀÖlZ
+uãÁ¿ò]@^Zo@†i‚kîíù+ —¨äöµó¯ xhc¾Y•f®ª½M"!Å)ÀÿPÅ´rĹ 5Cû¤èù°ï%E e@Ok¥Ó: {;/Æ­û-XŸÅèîb¼€ndÑ[ÐM³ãéºÔ;
+hÙpo¼©¦Nè ˆ.a›5G`ׯv
+à‚Süª7=Î=Ê€ËÍ=ÀåéÉo¸[DyëÙ§Ã{¬ä&3O8—óE&Ÿ~fè0ð>0Ưo ÀCèø3{BÚ¿nÛøÇ¿‘º\3‚¶I¡a@h,úS  ɱ €°,€0L§cÜs@G@èLž@èÎu ÔN ŒS™:ÈÐb›¹ShÌáZ/+‡tƒ™f9">‰$täó8´ŽìÔÙx¿{Š>@̱Ðéö/þ†9üGΕð ÷Mo0 H­¾ ¤ÝŠ1›éâD@Н
+P˜÷ÆûxýZW¿Ëù“Eü‰úÿ£„&òò·aZ–µ“‚Ô%øzz—Çù­.6·Ð;ܨÎèz­=FQxÒGŸPáKcy¶¡Ï~zßžå}ædìBó8fŸ…#äᵃ}3ü½ól.wÔäòÜîí‹ñ
+ûMAøA’ dx¿+ù¿½¨þÏ"ý°×+üŽ×'ìu/M”+Û;ç/0)žN£vüTªÞaÆg{û¥, vm÷ÙT|Ķì佨´÷f:ïÃZަ•Ï`Ïå³)ÀK#´¨Å8Ó”ç3}”›£c²=+!ðmºqúô¯©ö›6hµ‰k5YÓÿ?æÚäW=)äþìC‹ÓqLÆ·R´%G;ªÿülNÖ#½–Wsr¥îæüÒ8 å…uíjó™ÒÍÎÑAמ9Ï™nr^eJͽƤŽyí »žãµÂúc—tË‚oCõ®@ñò¾Ý²rµøi-ìÿ6Êò—.~M"W;§.½ý×
+ŒKÔ8Û?
+B
+UÞÔj|™;5?L®©Þ©]³<åUË•®ýV±¤C-ßí:Zñ=ŸÆÅAé>*Œd]@ÆÍ™ÕG¸C\ñtÞ×Û•<ýr›M&¹$Ø.û]^¶p{RØÖü¼’¬Îˆ{ñ­òb 7ÎÃ(Þ57©AÃŨ²ôµQ9ˆ©/^Þ«Òµ»Ù–tp:»²¹ÅÍ»h7¤0öoDÝޘęÕ`Ñ!N°š_F¼•§oª“ §bïûQË– Í$ècVÅ»h&ö‰tFg©N‚ç%£s9ØzìÓг~¥ÞÖ6´×˜X6¦‡;±qå£ä¸„YÂ>‹;+WéwËSQ8t»v/,Úx9* ›LÛqùÂ0ïË™iž¾”V¹@+ïí}·t²C«~Ïž†õw6ÊÐÌu:¥2:±¬Ç
+1,‹• æ V­ÿ ¦-ÞÆÆxèœ '÷$ ŒÛõ9v_ês¡þü‹‰ ŒV?ž£å¶{=ÛMiºSê@Žð`Ye.e±xŸ—:ò UÌù½²CVnns6 vXûÔ•ÙÓ[©eU?ÛÊÄÛ|ÏzÞØê¾ x­9¤§'wc¢¿!ë¤>÷ò’NnZ­¬ß+ó* ÒÛ%8¦¦Œ©‡»Whm®ÖÈý‹D=òìcìØ¸;l^+­vÈ7Ú§uºV¨ÄT…?ªO÷ï«bþµóë¾\²ëgÓδƒŒ•á¬9ì…®‰¤¢ŠQ¨ ú|?ˆõR¸è”õžiåá;™'klž>¤«3ñ–Â
+÷kµÙ(
+õ¬ºÖ«ÔcÀú—¬•\ùÝaû²qVRÜËIP¦’éLÞ¦‰ô0U/í†immÎ-¬òé¼-¥…ɳª6D8RåÓQ.‘>RZª»ï“h/w
+Ë»l*õ#[²êÒGS%9úb¾¸˜‰¨Tz ÓKZŠc§/Ì`âø}c¼ŽÛ´¸¯Eqêì†h{mVöRÌyM*^—]¾: ³mÞ¨ZÐÊêÞê,§k¾!©Š€$•¸Òjຢ)²-wÆW6REzc͆”­÷Ú$oGâè
+–b~¬ŸDÔi¡x8ÐÑ·MÞ‡fžÞæÇܦúºq¹“¹ w¸ ´<p›ÞæÉ],Ë/Ú17Ø„nåPª¹…c¦"I©ô×Ñ‚7Hsôxüµ×sŸÈ§÷÷‰¨Äü‹—ÍõHÜH‘ pjˆXÛ":¼…Y©.*4b_Hæ0ü2lOy¬¶ÜfÍ<¸ RÆ9^{¦Ù°çx¬äAæL·NLtóxFí4›Lôp¶ÌY ÷_tó½i§ú§Fh7¿ê¿.öoC4;Îf _»uß̬Q½ÚÔ׌XTÏ­"yÁŠŽ›e·,òÉš
+ïײψžÍmÞý ÖUŽ/§Z앇lx¯¯Y©³º0M’BuU—è[‹,Òú#îü Ôs®¨žÑT/Ê4¨ç™ßPÏ¿¥ Rßu\jÔmFÛe£ÏÎå`»xåývy¯º“`K;|”{d»ÇÒœÉí¶¶Ûšžrw.Á—)ïrÍmÓÇË÷÷<[‡N
++ßs®Šy¦)d=F}µêôm±ëÒmŸÑ†Z:RÏÛ:Eõ¦†@¥ˆaŽží˜Ì5[bŠbŠ]ª$R˜/ÈÜlÉáÞ¶ýV¿—Ü‘oí}3,„Ï "¤à#ÂpqýÌæËÈ‚â{m’Ô¼—>U‹ùM’–ˆÖ,'‚9_C’iÆ1Kßž±H·{36J×,õ¢Ñ"Õ»šU*×âd¥œÝ„áÔŽÌ«/bªìh¢—²>…#ÜãNm§.­2î)å¾hÌ7~<lã­Öï7^ø¦U[dàZå8K^¿ÂYEO€…¼<XÀ™œEï~£œ:ÓÑvZ—®›¼ .±Â›¢;¼CP}Y¥¨ÌÅæ©Ô(TÉQ~`‘ytãÈ­ÓºÅLcˆ/FÓîì§ù‰­_‚…qÙaˆîakñƒ µ £µ}ÖC÷|zŠqNuýEë0ë ™crc¦)²^Q|Ã÷ ¼iiC’s­è‚YSI;ê;ˆœª·í½%Ãá¸Äo6Fš‰E”¦ YÂITÃ’Š˜>– ,FÄ—@×poØ8]”°MúQÇ*¤溕ºw/W4¤²8*^îrŠë5$2ø9¢â·¢’DQÁeˆD™Ì9õJ›æEj ë£þ#9+«• •c»üñUÕ~”Ò*†­›^˺+ÕØ±] —6eî—Mºû¦£l§ß+Û ¶Ø7ãY&îwz k~•ÄA9½ßÉù‚DCm†¨åÑQ+
+Çû~ÖërzfnÈÈš„ÔCZ話kXo¥7Ñ}Õ‡3åÝök+öŸj3üu‡ G·­œÀ˜ËpýÒ"(Læ–
+Dà=aÝÜlóYÎPÃ¥)à«‘E ‡hK ÒvB!MaÈ&¸)ˆúf,ø6°
+pÛNîF¼Œ¡çŽ›A1€Ài¥riQLÁ÷µ
+V¦pðühxê)‚M7tf`-–ß¶‚É™@iö|°m7~}ÀçEœ±Á°*šL‚Òºî酪x±3N&'r5GpVÆjšM"-æ@Â|Hÿ PŸ®2PfÓ“ T°ÒS#Χ>ãƒiÖ‰
+Ýä‹»! ¬Ì!Aùò'X¹]\–Mt0þuï1¶‘3³û‚¹ÈRºcŽaiÁI^®¿&?=Ü"¢ì–]ô¼hXð³»”!xjË€oE@ÆgmXO Ú€¬ú¥³5¢— ¶Jðº2¬€¬ã& Kà = H[‚™£³É{!µÌÜùH!5Žßp ?»ßÏ}À*x5²è<tôˆIJ~ëÃBíLž˜7·ž’^#jarÙGLºŸƒò2d
+2æe*ˆ&ôç•¿/ú‘UÛ_³ïG'ýïà†?¼Oï_÷k|…’C«z³O×4qÔÁåÔÕˆê¥î‚4Lzs îòy îd“C Í”cý õmd&í‰M‡³Ñv•”x4‹ÂÙO³T•½Óÿ+ø«ÛþuÛ~3¾ø*¸_óíßÖi¥Ûÿ´*ÓSøèõÕ èýöí•Æ[@?C-`€û ÔëÓ+íY†ºß#¡ÿ[`üºm¿ñ/Õšz}“¾±…Õ¯ÑöOàÀú¨£óqS/æ†V^ÂuÆãêµxÌËRì‹wɸçVÎ\·BÐîO¹íò¨Ìøû!&oÊA[/ë“a¿šè¯uõÓzê+†þç÷ÿ¯¼ü#؉¥;ärµëŒ¥ã‹w‚úg.¾NNµçcyŒRûÍášßÚlÙ?ÜÇso®n`7`oȪ=“Yßnܦx‡Õ5ÝZ«
+Êö•KrG„ü4õ7mà×¼Š.ÄË÷Iþÿ²×ž7R&Ù;Ùüx7  ùz©ZÓå> /úÜϵÕù:}×¶ïÉXÁã™Ñ'ÆD¿Ÿ—=nðÃ
+m/2›£Òu0\ºÉüèZÉ%¹8ð3lÚN2…qÍiÐsñéÌZ„ä¸*ÿbÖ┵¯¬êÓÁËôäÓAé>¹ÞFûÜ¥¹qÝm#†³ÐLf;Õqîµ
+ˆåt[ÞT[Gߨ]}ñÚxyÊ3€Jq&ÀJz*¦Ýn>æ‹ïY$î@+B«¤úpØqÞ™Õf¥Ä!N§0ï§ÑnnÓ1¹ÀœÞìý l i™ÙÓ¬7ÌF%ÿ•`…gUjˆ}1oWäÚ¨<šåzF¿$Ç DÄ£zx½…Ooò\1®,½GËå ©Ä=å¡¶ÝÇì…Û¨0ö³º9¯—?ïâ.y_Ÿ&ŽÏw.ÐqÄÞ÷pÂ32—= %«¢I9»M×z¬¶ Ë☱ùÞwÏæ .‘&t¹:I%>ŽïscÜ®ÞÿbR„û•~khÛí&+MÑ{áë>¾W ‡êª¼=å{žÚ‚Ë®§Ý‚Cí\‡Øw¼<}n9î^©Ûâ»ØÊžµ^V…k£$j³ŒŽwÖV×kí,‹ž^ÌA°xš6ÿHÆÐ@i½*ŠîꕜN<ïuÍÏf&®éͤɧƒâ J®¹LàmÓ<™ÝŒ¸F±Òtô|k›bÓ?HCÞôo×*$g‹@ôŠ#â8¥ÌÚÎñö598}WÏÄŽdZÝc›ï5S0mŽ÷qÈW GRz¶Ûº›¶û:q¦šo†«ôf98¦ÿúúAÔý'Ô°*Ê™x•håÅò-ýÚÈñc„Éú|ëËú«;NpZ}1P-è`KµÐ”9J‡½”ÕyÊÕƒk+ÄÐYÂÛZNØ7ìÌÔÄZ+Ép¦Aw—²¬­ÔŒ¡1íL6½5J…4¿¨úêÁk×Ôº²•s¼í)MLåÛ‚ÙÈmϹJÏk–zmXRx‡K¨)"Le)Lî
+,ºŸg¬„œ&°çÂó½^†âò+4î!eÖ¦YT¨“-ânó\ÄÆÜ$χF6&[Ž•ºZ²^Úe˜t5žRj½×b”Kª%(é|G•ïèÔ”pûA¤—t/I½ H) oˆ#4Ós³úL˜’ÃPXƒ'¿Ðr_zLn]rj\…zL9v3ì®},pì}ÒOpžp L»Ã ]kÕ Ò­¿I6Sõƨôƒ”• /e¥ù½@9ÚÜ>/n­ŒÕ컆‹éjz÷Æ ¥%›¨ôª]Q)s:âH½²bîIÂÔ„ÒB‘3¾Ìø…_)óÓopëö±ÏULjÁ±ïÒùakþaEÖP˜Fgë1Šåè+%ÞéÖ&›¥¯j¦Ë(Q{Ì(~}Ô)rhØl¯ ;ØÄ¢êÇ&F¹ùÕêépX~õµ×þc®õ 5òY:Ùco …DRþRSübáâ<M»4·N¦ÛÇG*»{.3l­w-°¢Ç̉.ÆLã:0JÙÓ1î¥hmq©®Z.Ræë’ïÎòL܇A¾ç—ÖBYxzøEÛ«=ÂèڗʿἣiÞ“`˜âlxùkí²É©Ú7 ÷c UëQFi3iN‚Qâ½—øâ¸ÖéÅÖ‡+vŸ'Å(k>¹‹Ñ׊ž¦c-´ií1ó¨ÇjPÝ’9 ,y´¡
+HÞ$g¿HÚÍq„óP—pöÙ%…ÓÛ‚Ï3&“£u[9…+ ücN’ˆ•%-ÄVá´“ûÝ µ_¾£†µáƒyÏËçgÑ* —¶XXÒX®É¿®˜ñ§ú&¤"õ*ŸŠrVkBiï%sK¶î)/Z_wÊšêrH¨oŽ21QK$áÜš<Q€o:MÉ>¯¹œ|bl5xO±r¹rÁXöM Û}ËB«=#B…ÌsƒÔÇ[Ê\ˆV‡­¤ÿ~iIM´UÆj­ZžJåô¼’ýãnHûŽS«
+àlN0"¼D€si#A»’B¿žÀ%x
+A› ¼q‚ËP¨
+KÉ€|¶K€|9£ž|â’Á/ß* Gé! cøÈ ÿ†K|ZÌý¦‡ãaφºl„QÓPm‹¦Ež›½˜÷ÕØ¾ðîcÊùÂï)¥Ü”˜ôti|ñÑ`kãd¹vúÇì¢n± h+_O0ê:ƒ/Ôo€VRD‚vòªZ4ó^
+D<­à’+ P= »?ñ¿OåøG¼ Z·?¾Û¿K¿é Óǯ÷_š.Ã!ßÞ6zi@j–K@j‘½›â˜R»’¬ëT—@ê– ÕH^g—³ áÞ<ä2Ó4|~´D¹Þó™"®Yx«ºå|xÁþm
+ " Âʨ,€š>j@ÕæÉ:~üIiV…™T±_ªt¸[Uw»ÖRNg(‰Œ±ù^"”s¨!ŽSæþHÉ ÀUÛ¿Á ³ï¯~›ºY_÷ó*Üÿo¿¡ _Ëí%½zj~ºÒän»ÐËù9Ð[y èq¡ôª>zù?$Ð?nÛ¿m½ÐìÿÈF§sœÛþ9¾]g\úu-îóðe)åñK2! Ï›´ÇŸ¹NE9ÕÞýx²ö1‚ÞáZ¸ÔÚ’Jæ–û.SÚìÂÞŽÕ¹óuÖ~ñm”õû$ÿ'rõ«7þ?Û¯¼ñ÷w.Roc"Ì\‹Û x¡nåT{ªõ䥤ø£Loß%±Ñî]†§»ì^nÇ"ØlfÊy¿^ê«Óšîmï+n4/Côˆ-N¥7¿V(m®í Å™yž´¦ÙGü˜Œ3ª”ÀI‰éAüîù•CÿÓêßͼàøq%*«ã™kÖoÇÆ;ýØ?
+4´{{¹Eûژ° a[YD¸®Ïcš·f]³§ï:V˜f/pi
+µÈÊ2É?Èp<³˜Ö˜ò½‘èÓá¦èn{?¸Äí
+í+§¤ Õq¥ûèŒß ¯³ö_ÖÕÏ3ýÿÊøHÇ“1š}Ûdý kïzˆ—â¨ÐžÇ$Û›™;|2…¯å¸ø\ïF~jqèô1Éλ*wá¾²‹±Þµ6 zÚiÀu»jWìš·¥ÖɾÆV{œ]:mt|­Ä.NÇ­¥—Ÿþ -z3:5¹#MD¡*¿ñŸÎXõk¿:ùµ®Úd¿óÛÌëc¯ý>Éÿ?äP[ù
+¹s$«ÏF¤ÔRõkÜ!êÚsʇÝÌݨ ¼Xƒæ…¨êPÛy0 †xà
+c¯²llНŒk_×êâï{¼Ÿ_jõÕ“¿}¼¦PÝ×¾rè_ëjíψÖÒ‰èf@¹|rŽÒˆ$Û¨ÇiË«šoË« l+¨A=¬Ž v³Š.ìÎÇ;JÛƒÊ2°'úà®Êü}kööÅg2ƒ‰2–VŠóqÉ}Ì•‘Ûõ<ê³ú/ëªÍŒï¿öÚqñ~“ú§Òïš§Ëûk]ý¨Ö+k×PNàvõá©f§ª×ªƒ–_KzpÅgóxyS+2eîTüPÍËÞ©×¼èÓ¯æÚ/Ú%*Ü®ãù®…uêÅAiÚ)Œ+ëI= {Ç•óÉ59¿ endstream endobj 6 0 obj [5 0 R] endobj 30 0 obj <</CreationDate(D:20090126140616-08'00')/Creator(Adobe Illustrator CS4)/ModDate(D:20090126140616-08'00')/Producer(Adobe PDF library 9.00)/Title(octocat)>> endobj xref 0 31 0000000000 65535 f
+0000000016 00000 n
+0000000144 00000 n
+0000070708 00000 n
+0000000000 00000 f
+0000074503 00000 n
+0001040413 00000 n
+0000070759 00000 n
+0000071118 00000 n
+0000464670 00000 n
+0000074802 00000 n
+0000074689 00000 n
+0000073576 00000 n
+0000073942 00000 n
+0000073990 00000 n
+0000074573 00000 n
+0000074604 00000 n
+0000074837 00000 n
+0000464743 00000 n
+0000465095 00000 n
+0000466088 00000 n
+0000479721 00000 n
+0000515709 00000 n
+0000581297 00000 n
+0000646885 00000 n
+0000712473 00000 n
+0000778061 00000 n
+0000843649 00000 n
+0000909237 00000 n
+0000974825 00000 n
+0001040436 00000 n
+trailer <</Size 31/Root 1 0 R/Info 30 0 R/ID[<1BB8998A062240C383A0BEFB33838946><595845B968BF4D138D7253F410796FA6>]>> startxref 1040607 %%EOF \ No newline at end of file
diff --git a/samples/binary/octocat.png b/samples/binary/octocat.png
new file mode 100644
index 0000000..a9ec5a2
--- /dev/null
+++ b/samples/binary/octocat.png
Binary files differ
diff --git a/samples/binary/octocat.psd b/samples/binary/octocat.psd
new file mode 100644
index 0000000..910bd90
--- /dev/null
+++ b/samples/binary/octocat.psd
Binary files differ
diff --git a/samples/binary/zip b/samples/binary/zip
new file mode 100644
index 0000000..d3b48df
--- /dev/null
+++ b/samples/binary/zip
Binary files differ
diff --git a/samples/c/blob.c b/samples/c/blob.c
new file mode 100644
index 0000000..ae320bd
--- /dev/null
+++ b/samples/c/blob.c
@@ -0,0 +1,25 @@
+#include "cache.h"
+#include "blob.h"
+
+const char *blob_type = "blob";
+
+struct blob *lookup_blob(const unsigned char *sha1)
+{
+ struct object *obj = lookup_object(sha1);
+ if (!obj)
+ return create_object(sha1, OBJ_BLOB, alloc_blob_node());
+ if (!obj->type)
+ obj->type = OBJ_BLOB;
+ if (obj->type != OBJ_BLOB) {
+ error("Object %s is a %s, not a blob",
+ sha1_to_hex(sha1), typename(obj->type));
+ return NULL;
+ }
+ return (struct blob *) obj;
+}
+
+int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
+{
+ item->object.parsed = 1;
+ return 0;
+}
diff --git a/samples/c/blob.h b/samples/c/blob.h
new file mode 100644
index 0000000..59b394e
--- /dev/null
+++ b/samples/c/blob.h
@@ -0,0 +1,25 @@
+#ifndef BLOB_H
+#define BLOB_H
+
+#include "object.h"
+
+extern const char *blob_type;
+
+struct blob {
+ struct object object;
+};
+
+struct blob *lookup_blob(const unsigned char *sha1);
+
+int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
+
+/**
+ * Blobs do not contain references to other objects and do not have
+ * structured data that needs parsing. However, code may use the
+ * "parsed" bit in the struct object for a blob to determine whether
+ * its content has been found to actually be available, so
+ * parse_blob_buffer() is used (by object.c) to flag that the object
+ * has been read successfully from the database.
+ **/
+
+#endif /* BLOB_H */
diff --git a/samples/c/cache.c b/samples/c/cache.c
new file mode 100644
index 0000000..f8d8940
--- /dev/null
+++ b/samples/c/cache.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "repository.h"
+#include "commit.h"
+#include "thread-utils.h"
+#include "util.h"
+#include "cache.h"
+
+int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
+{
+ if (size < 8)
+ size = 8;
+ size = git__size_t_powerof2(size);
+
+ cache->size_mask = size - 1;
+ cache->lru_count = 0;
+ cache->free_obj = free_ptr;
+
+ git_mutex_init(&cache->lock);
+
+ cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
+ GITERR_CHECK_ALLOC(cache->nodes);
+
+ memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
+ return 0;
+}
+
+void git_cache_free(git_cache *cache)
+{
+ size_t i;
+
+ for (i = 0; i < (cache->size_mask + 1); ++i) {
+ if (cache->nodes[i] != NULL)
+ git_cached_obj_decref(cache->nodes[i], cache->free_obj);
+ }
+
+ git__free(cache->nodes);
+}
+
+void *git_cache_get(git_cache *cache, const git_oid *oid)
+{
+ uint32_t hash;
+ git_cached_obj *node = NULL, *result = NULL;
+
+ memcpy(&hash, oid->id, sizeof(hash));
+
+ git_mutex_lock(&cache->lock);
+ {
+ node = cache->nodes[hash & cache->size_mask];
+
+ if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {
+ git_cached_obj_incref(node);
+ result = node;
+ }
+ }
+ git_mutex_unlock(&cache->lock);
+
+ return result;
+}
+
+void *git_cache_try_store(git_cache *cache, void *_entry)
+{
+ git_cached_obj *entry = _entry;
+ uint32_t hash;
+
+ memcpy(&hash, &entry->oid, sizeof(uint32_t));
+
+ /* increase the refcount on this object, because
+ * the cache now owns it */
+ git_cached_obj_incref(entry);
+
+ git_mutex_lock(&cache->lock);
+ {
+ git_cached_obj *node = cache->nodes[hash & cache->size_mask];
+
+ if (node == NULL) {
+ cache->nodes[hash & cache->size_mask] = entry;
+ } else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
+ git_cached_obj_decref(entry, cache->free_obj);
+ entry = node;
+ } else {
+ git_cached_obj_decref(node, cache->free_obj);
+ cache->nodes[hash & cache->size_mask] = entry;
+ }
+ }
+ git_mutex_unlock(&cache->lock);
+
+ /* increase the refcount again, because we are
+ * returning it to the user */
+ git_cached_obj_incref(entry);
+
+ return entry;
+}
diff --git a/samples/c/commit.c b/samples/c/commit.c
new file mode 100644
index 0000000..8248a99
--- /dev/null
+++ b/samples/c/commit.c
@@ -0,0 +1,1228 @@
+#include "cache.h"
+#include "tag.h"
+#include "commit.h"
+#include "pkt-line.h"
+#include "utf8.h"
+#include "diff.h"
+#include "revision.h"
+#include "notes.h"
+#include "gpg-interface.h"
+#include "mergesort.h"
+
+int save_commit_buffer = 1;
+
+const char *commit_type = "commit";
+
+static struct commit *check_commit(struct object *obj,
+ const unsigned char *sha1,
+ int quiet)
+{
+ if (obj->type != OBJ_COMMIT) {
+ if (!quiet)
+ error("Object %s is a %s, not a commit",
+ sha1_to_hex(sha1), typename(obj->type));
+ return NULL;
+ }
+ return (struct commit *) obj;
+}
+
+struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
+ int quiet)
+{
+ struct object *obj = deref_tag(parse_object(sha1), NULL, 0);
+
+ if (!obj)
+ return NULL;
+ return check_commit(obj, sha1, quiet);
+}
+
+struct commit *lookup_commit_reference(const unsigned char *sha1)
+{
+ return lookup_commit_reference_gently(sha1, 0);
+}
+
+struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name)
+{
+ struct commit *c = lookup_commit_reference(sha1);
+ if (!c)
+ die(_("could not parse %s"), ref_name);
+ if (hashcmp(sha1, c->object.sha1)) {
+ warning(_("%s %s is not a commit!"),
+ ref_name, sha1_to_hex(sha1));
+ }
+ return c;
+}
+
+struct commit *lookup_commit(const unsigned char *sha1)
+{
+ struct object *obj = lookup_object(sha1);
+ if (!obj)
+ return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
+ if (!obj->type)
+ obj->type = OBJ_COMMIT;
+ return check_commit(obj, sha1, 0);
+}
+
+struct commit *lookup_commit_reference_by_name(const char *name)
+{
+ unsigned char sha1[20];
+ struct commit *commit;
+
+ if (get_sha1(name, sha1))
+ return NULL;
+ commit = lookup_commit_reference(sha1);
+ if (!commit || parse_commit(commit))
+ return NULL;
+ return commit;
+}
+
+static unsigned long parse_commit_date(const char *buf, const char *tail)
+{
+ const char *dateptr;
+
+ if (buf + 6 >= tail)
+ return 0;
+ if (memcmp(buf, "author", 6))
+ return 0;
+ while (buf < tail && *buf++ != '\n')
+ /* nada */;
+ if (buf + 9 >= tail)
+ return 0;
+ if (memcmp(buf, "committer", 9))
+ return 0;
+ while (buf < tail && *buf++ != '>')
+ /* nada */;
+ if (buf >= tail)
+ return 0;
+ dateptr = buf;
+ while (buf < tail && *buf++ != '\n')
+ /* nada */;
+ if (buf >= tail)
+ return 0;
+ /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
+ return strtoul(dateptr, NULL, 10);
+}
+
+static struct commit_graft **commit_graft;
+static int commit_graft_alloc, commit_graft_nr;
+
+static int commit_graft_pos(const unsigned char *sha1)
+{
+ int lo, hi;
+ lo = 0;
+ hi = commit_graft_nr;
+ while (lo < hi) {
+ int mi = (lo + hi) / 2;
+ struct commit_graft *graft = commit_graft[mi];
+ int cmp = hashcmp(sha1, graft->sha1);
+ if (!cmp)
+ return mi;
+ if (cmp < 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+ return -lo - 1;
+}
+
+int register_commit_graft(struct commit_graft *graft, int ignore_dups)
+{
+ int pos = commit_graft_pos(graft->sha1);
+
+ if (0 <= pos) {
+ if (ignore_dups)
+ free(graft);
+ else {
+ free(commit_graft[pos]);
+ commit_graft[pos] = graft;
+ }
+ return 1;
+ }
+ pos = -pos - 1;
+ if (commit_graft_alloc <= ++commit_graft_nr) {
+ commit_graft_alloc = alloc_nr(commit_graft_alloc);
+ commit_graft = xrealloc(commit_graft,
+ sizeof(*commit_graft) *
+ commit_graft_alloc);
+ }
+ if (pos < commit_graft_nr)
+ memmove(commit_graft + pos + 1,
+ commit_graft + pos,
+ (commit_graft_nr - pos - 1) *
+ sizeof(*commit_graft));
+ commit_graft[pos] = graft;
+ return 0;
+}
+
+struct commit_graft *read_graft_line(char *buf, int len)
+{
+ /* The format is just "Commit Parent1 Parent2 ...\n" */
+ int i;
+ struct commit_graft *graft = NULL;
+
+ while (len && isspace(buf[len-1]))
+ buf[--len] = '\0';
+ if (buf[0] == '#' || buf[0] == '\0')
+ return NULL;
+ if ((len + 1) % 41)
+ goto bad_graft_data;
+ i = (len + 1) / 41 - 1;
+ graft = xmalloc(sizeof(*graft) + 20 * i);
+ graft->nr_parent = i;
+ if (get_sha1_hex(buf, graft->sha1))
+ goto bad_graft_data;
+ for (i = 40; i < len; i += 41) {
+ if (buf[i] != ' ')
+ goto bad_graft_data;
+ if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
+ goto bad_graft_data;
+ }
+ return graft;
+
+bad_graft_data:
+ error("bad graft data: %s", buf);
+ free(graft);
+ return NULL;
+}
+
+static int read_graft_file(const char *graft_file)
+{
+ FILE *fp = fopen(graft_file, "r");
+ char buf[1024];
+ if (!fp)
+ return -1;
+ while (fgets(buf, sizeof(buf), fp)) {
+ /* The format is just "Commit Parent1 Parent2 ...\n" */
+ int len = strlen(buf);
+ struct commit_graft *graft = read_graft_line(buf, len);
+ if (!graft)
+ continue;
+ if (register_commit_graft(graft, 1))
+ error("duplicate graft data: %s", buf);
+ }
+ fclose(fp);
+ return 0;
+}
+
+static void prepare_commit_graft(void)
+{
+ static int commit_graft_prepared;
+ char *graft_file;
+
+ if (commit_graft_prepared)
+ return;
+ graft_file = get_graft_file();
+ read_graft_file(graft_file);
+ /* make sure shallows are read */
+ is_repository_shallow();
+ commit_graft_prepared = 1;
+}
+
+struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
+{
+ int pos;
+ prepare_commit_graft();
+ pos = commit_graft_pos(sha1);
+ if (pos < 0)
+ return NULL;
+ return commit_graft[pos];
+}
+
+int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
+{
+ int i, ret;
+ for (i = ret = 0; i < commit_graft_nr && !ret; i++)
+ ret = fn(commit_graft[i], cb_data);
+ return ret;
+}
+
+int unregister_shallow(const unsigned char *sha1)
+{
+ int pos = commit_graft_pos(sha1);
+ if (pos < 0)
+ return -1;
+ if (pos + 1 < commit_graft_nr)
+ memmove(commit_graft + pos, commit_graft + pos + 1,
+ sizeof(struct commit_graft *)
+ * (commit_graft_nr - pos - 1));
+ commit_graft_nr--;
+ return 0;
+}
+
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
+{
+ const char *tail = buffer;
+ const char *bufptr = buffer;
+ unsigned char parent[20];
+ struct commit_list **pptr;
+ struct commit_graft *graft;
+
+ if (item->object.parsed)
+ return 0;
+ item->object.parsed = 1;
+ tail += size;
+ if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n')
+ return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
+ if (get_sha1_hex(bufptr + 5, parent) < 0)
+ return error("bad tree pointer in commit %s",
+ sha1_to_hex(item->object.sha1));
+ item->tree = lookup_tree(parent);
+ bufptr += 46; /* "tree " + "hex sha1" + "\n" */
+ pptr = &item->parents;
+
+ graft = lookup_commit_graft(item->object.sha1);
+ while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) {
+ struct commit *new_parent;
+
+ if (tail <= bufptr + 48 ||
+ get_sha1_hex(bufptr + 7, parent) ||
+ bufptr[47] != '\n')
+ return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
+ bufptr += 48;
+ /*
+ * The clone is shallow if nr_parent < 0, and we must
+ * not traverse its real parents even when we unhide them.
+ */
+ if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
+ continue;
+ new_parent = lookup_commit(parent);
+ if (new_parent)
+ pptr = &commit_list_insert(new_parent, pptr)->next;
+ }
+ if (graft) {
+ int i;
+ struct commit *new_parent;
+ for (i = 0; i < graft->nr_parent; i++) {
+ new_parent = lookup_commit(graft->parent[i]);
+ if (!new_parent)
+ continue;
+ pptr = &commit_list_insert(new_parent, pptr)->next;
+ }
+ }
+ item->date = parse_commit_date(bufptr, tail);
+
+ return 0;
+}
+
+int parse_commit(struct commit *item)
+{
+ enum object_type type;
+ void *buffer;
+ unsigned long size;
+ int ret;
+
+ if (!item)
+ return -1;
+ if (item->object.parsed)
+ return 0;
+ buffer = read_sha1_file(item->object.sha1, &type, &size);
+ if (!buffer)
+ return error("Could not read %s",
+ sha1_to_hex(item->object.sha1));
+ if (type != OBJ_COMMIT) {
+ free(buffer);
+ return error("Object %s not a commit",
+ sha1_to_hex(item->object.sha1));
+ }
+ ret = parse_commit_buffer(item, buffer, size);
+ if (save_commit_buffer && !ret) {
+ item->buffer = buffer;
+ return 0;
+ }
+ free(buffer);
+ return ret;
+}
+
+int find_commit_subject(const char *commit_buffer, const char **subject)
+{
+ const char *eol;
+ const char *p = commit_buffer;
+
+ while (*p && (*p != '\n' || p[1] != '\n'))
+ p++;
+ if (*p) {
+ p += 2;
+ for (eol = p; *eol && *eol != '\n'; eol++)
+ ; /* do nothing */
+ } else
+ eol = p;
+
+ *subject = p;
+
+ return eol - p;
+}
+
+struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
+{
+ struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
+ new_list->item = item;
+ new_list->next = *list_p;
+ *list_p = new_list;
+ return new_list;
+}
+
+unsigned commit_list_count(const struct commit_list *l)
+{
+ unsigned c = 0;
+ for (; l; l = l->next )
+ c++;
+ return c;
+}
+
+void free_commit_list(struct commit_list *list)
+{
+ while (list) {
+ struct commit_list *temp = list;
+ list = temp->next;
+ free(temp);
+ }
+}
+
+struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list)
+{
+ struct commit_list **pp = list;
+ struct commit_list *p;
+ while ((p = *pp) != NULL) {
+ if (p->item->date < item->date) {
+ break;
+ }
+ pp = &p->next;
+ }
+ return commit_list_insert(item, pp);
+}
+
+static int commit_list_compare_by_date(const void *a, const void *b)
+{
+ unsigned long a_date = ((const struct commit_list *)a)->item->date;
+ unsigned long b_date = ((const struct commit_list *)b)->item->date;
+ if (a_date < b_date)
+ return 1;
+ if (a_date > b_date)
+ return -1;
+ return 0;
+}
+
+static void *commit_list_get_next(const void *a)
+{
+ return ((const struct commit_list *)a)->next;
+}
+
+static void commit_list_set_next(void *a, void *next)
+{
+ ((struct commit_list *)a)->next = next;
+}
+
+void commit_list_sort_by_date(struct commit_list **list)
+{
+ *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
+ commit_list_compare_by_date);
+}
+
+struct commit *pop_most_recent_commit(struct commit_list **list,
+ unsigned int mark)
+{
+ struct commit *ret = (*list)->item;
+ struct commit_list *parents = ret->parents;
+ struct commit_list *old = *list;
+
+ *list = (*list)->next;
+ free(old);
+
+ while (parents) {
+ struct commit *commit = parents->item;
+ if (!parse_commit(commit) && !(commit->object.flags & mark)) {
+ commit->object.flags |= mark;
+ commit_list_insert_by_date(commit, list);
+ }
+ parents = parents->next;
+ }
+ return ret;
+}
+
+static void clear_commit_marks_1(struct commit_list **plist,
+ struct commit *commit, unsigned int mark)
+{
+ while (commit) {
+ struct commit_list *parents;
+
+ if (!(mark & commit->object.flags))
+ return;
+
+ commit->object.flags &= ~mark;
+
+ parents = commit->parents;
+ if (!parents)
+ return;
+
+ while ((parents = parents->next))
+ commit_list_insert(parents->item, plist);
+
+ commit = commit->parents->item;
+ }
+}
+
+void clear_commit_marks(struct commit *commit, unsigned int mark)
+{
+ struct commit_list *list = NULL;
+ commit_list_insert(commit, &list);
+ while (list)
+ clear_commit_marks_1(&list, pop_commit(&list), mark);
+}
+
+void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
+{
+ struct object *object;
+ struct commit *commit;
+ unsigned int i;
+
+ for (i = 0; i < a->nr; i++) {
+ object = a->objects[i].item;
+ commit = lookup_commit_reference_gently(object->sha1, 1);
+ if (commit)
+ clear_commit_marks(commit, mark);
+ }
+}
+
+struct commit *pop_commit(struct commit_list **stack)
+{
+ struct commit_list *top = *stack;
+ struct commit *item = top ? top->item : NULL;
+
+ if (top) {
+ *stack = top->next;
+ free(top);
+ }
+ return item;
+}
+
+/*
+ * Performs an in-place topological sort on the list supplied.
+ */
+void sort_in_topological_order(struct commit_list ** list, int lifo)
+{
+ struct commit_list *next, *orig = *list;
+ struct commit_list *work, **insert;
+ struct commit_list **pptr;
+
+ if (!orig)
+ return;
+ *list = NULL;
+
+ /* Mark them and clear the indegree */
+ for (next = orig; next; next = next->next) {
+ struct commit *commit = next->item;
+ commit->indegree = 1;
+ }
+
+ /* update the indegree */
+ for (next = orig; next; next = next->next) {
+ struct commit_list * parents = next->item->parents;
+ while (parents) {
+ struct commit *parent = parents->item;
+
+ if (parent->indegree)
+ parent->indegree++;
+ parents = parents->next;
+ }
+ }
+
+ /*
+ * find the tips
+ *
+ * tips are nodes not reachable from any other node in the list
+ *
+ * the tips serve as a starting set for the work queue.
+ */
+ work = NULL;
+ insert = &work;
+ for (next = orig; next; next = next->next) {
+ struct commit *commit = next->item;
+
+ if (commit->indegree == 1)
+ insert = &commit_list_insert(commit, insert)->next;
+ }
+
+ /* process the list in topological order */
+ if (!lifo)
+ commit_list_sort_by_date(&work);
+
+ pptr = list;
+ *list = NULL;
+ while (work) {
+ struct commit *commit;
+ struct commit_list *parents, *work_item;
+
+ work_item = work;
+ work = work_item->next;
+ work_item->next = NULL;
+
+ commit = work_item->item;
+ for (parents = commit->parents; parents ; parents = parents->next) {
+ struct commit *parent = parents->item;
+
+ if (!parent->indegree)
+ continue;
+
+ /*
+ * parents are only enqueued for emission
+ * when all their children have been emitted thereby
+ * guaranteeing topological order.
+ */
+ if (--parent->indegree == 1) {
+ if (!lifo)
+ commit_list_insert_by_date(parent, &work);
+ else
+ commit_list_insert(parent, &work);
+ }
+ }
+ /*
+ * work_item is a commit all of whose children
+ * have already been emitted. we can emit it now.
+ */
+ commit->indegree = 0;
+ *pptr = work_item;
+ pptr = &work_item->next;
+ }
+}
+
+/* merge-base stuff */
+
+/* bits #0..15 in revision.h */
+#define PARENT1 (1u<<16)
+#define PARENT2 (1u<<17)
+#define STALE (1u<<18)
+#define RESULT (1u<<19)
+
+static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+
+static struct commit *interesting(struct commit_list *list)
+{
+ while (list) {
+ struct commit *commit = list->item;
+ list = list->next;
+ if (commit->object.flags & STALE)
+ continue;
+ return commit;
+ }
+ return NULL;
+}
+
+static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
+{
+ struct commit_list *list = NULL;
+ struct commit_list *result = NULL;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (one == twos[i])
+ /*
+ * We do not mark this even with RESULT so we do not
+ * have to clean it up.
+ */
+ return commit_list_insert(one, &result);
+ }
+
+ if (parse_commit(one))
+ return NULL;
+ for (i = 0; i < n; i++) {
+ if (parse_commit(twos[i]))
+ return NULL;
+ }
+
+ one->object.flags |= PARENT1;
+ commit_list_insert_by_date(one, &list);
+ for (i = 0; i < n; i++) {
+ twos[i]->object.flags |= PARENT2;
+ commit_list_insert_by_date(twos[i], &list);
+ }
+
+ while (interesting(list)) {
+ struct commit *commit;
+ struct commit_list *parents;
+ struct commit_list *next;
+ int flags;
+
+ commit = list->item;
+ next = list->next;
+ free(list);
+ list = next;
+
+ flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
+ if (flags == (PARENT1 | PARENT2)) {
+ if (!(commit->object.flags & RESULT)) {
+ commit->object.flags |= RESULT;
+ commit_list_insert_by_date(commit, &result);
+ }
+ /* Mark parents of a found merge stale */
+ flags |= STALE;
+ }
+ parents = commit->parents;
+ while (parents) {
+ struct commit *p = parents->item;
+ parents = parents->next;
+ if ((p->object.flags & flags) == flags)
+ continue;
+ if (parse_commit(p))
+ return NULL;
+ p->object.flags |= flags;
+ commit_list_insert_by_date(p, &list);
+ }
+ }
+
+ /* Clean up the result to remove stale ones */
+ free_commit_list(list);
+ list = result; result = NULL;
+ while (list) {
+ struct commit_list *next = list->next;
+ if (!(list->item->object.flags & STALE))
+ commit_list_insert_by_date(list->item, &result);
+ free(list);
+ list = next;
+ }
+ return result;
+}
+
+struct commit_list *get_octopus_merge_bases(struct commit_list *in)
+{
+ struct commit_list *i, *j, *k, *ret = NULL;
+ struct commit_list **pptr = &ret;
+
+ for (i = in; i; i = i->next) {
+ if (!ret)
+ pptr = &commit_list_insert(i->item, pptr)->next;
+ else {
+ struct commit_list *new = NULL, *end = NULL;
+
+ for (j = ret; j; j = j->next) {
+ struct commit_list *bases;
+ bases = get_merge_bases(i->item, j->item, 1);
+ if (!new)
+ new = bases;
+ else
+ end->next = bases;
+ for (k = bases; k; k = k->next)
+ end = k;
+ }
+ ret = new;
+ }
+ }
+ return ret;
+}
+
+struct commit_list *get_merge_bases_many(struct commit *one,
+ int n,
+ struct commit **twos,
+ int cleanup)
+{
+ struct commit_list *list;
+ struct commit **rslt;
+ struct commit_list *result;
+ int cnt, i, j;
+
+ result = merge_bases_many(one, n, twos);
+ for (i = 0; i < n; i++) {
+ if (one == twos[i])
+ return result;
+ }
+ if (!result || !result->next) {
+ if (cleanup) {
+ clear_commit_marks(one, all_flags);
+ for (i = 0; i < n; i++)
+ clear_commit_marks(twos[i], all_flags);
+ }
+ return result;
+ }
+
+ /* There are more than one */
+ cnt = 0;
+ list = result;
+ while (list) {
+ list = list->next;
+ cnt++;
+ }
+ rslt = xcalloc(cnt, sizeof(*rslt));
+ for (list = result, i = 0; list; list = list->next)
+ rslt[i++] = list->item;
+ free_commit_list(result);
+
+ clear_commit_marks(one, all_flags);
+ for (i = 0; i < n; i++)
+ clear_commit_marks(twos[i], all_flags);
+ for (i = 0; i < cnt - 1; i++) {
+ for (j = i+1; j < cnt; j++) {
+ if (!rslt[i] || !rslt[j])
+ continue;
+ result = merge_bases_many(rslt[i], 1, &rslt[j]);
+ clear_commit_marks(rslt[i], all_flags);
+ clear_commit_marks(rslt[j], all_flags);
+ for (list = result; list; list = list->next) {
+ if (rslt[i] == list->item)
+ rslt[i] = NULL;
+ if (rslt[j] == list->item)
+ rslt[j] = NULL;
+ }
+ }
+ }
+
+ /* Surviving ones in rslt[] are the independent results */
+ result = NULL;
+ for (i = 0; i < cnt; i++) {
+ if (rslt[i])
+ commit_list_insert_by_date(rslt[i], &result);
+ }
+ free(rslt);
+ return result;
+}
+
+struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
+ int cleanup)
+{
+ return get_merge_bases_many(one, 1, &two, cleanup);
+}
+
+int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
+{
+ if (!with_commit)
+ return 1;
+ while (with_commit) {
+ struct commit *other;
+
+ other = with_commit->item;
+ with_commit = with_commit->next;
+ if (in_merge_bases(other, &commit, 1))
+ return 1;
+ }
+ return 0;
+}
+
+int in_merge_bases(struct commit *commit, struct commit **reference, int num)
+{
+ struct commit_list *bases, *b;
+ int ret = 0;
+
+ if (num == 1)
+ bases = get_merge_bases(commit, *reference, 1);
+ else
+ die("not yet");
+ for (b = bases; b; b = b->next) {
+ if (!hashcmp(commit->object.sha1, b->item->object.sha1)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ free_commit_list(bases);
+ return ret;
+}
+
+struct commit_list *reduce_heads(struct commit_list *heads)
+{
+ struct commit_list *p;
+ struct commit_list *result = NULL, **tail = &result;
+ struct commit **other;
+ size_t num_head, num_other;
+
+ if (!heads)
+ return NULL;
+
+ /* Avoid unnecessary reallocations */
+ for (p = heads, num_head = 0; p; p = p->next)
+ num_head++;
+ other = xcalloc(sizeof(*other), num_head);
+
+ /* For each commit, see if it can be reached by others */
+ for (p = heads; p; p = p->next) {
+ struct commit_list *q, *base;
+
+ /* Do we already have this in the result? */
+ for (q = result; q; q = q->next)
+ if (p->item == q->item)
+ break;
+ if (q)
+ continue;
+
+ num_other = 0;
+ for (q = heads; q; q = q->next) {
+ if (p->item == q->item)
+ continue;
+ other[num_other++] = q->item;
+ }
+ if (num_other)
+ base = get_merge_bases_many(p->item, num_other, other, 1);
+ else
+ base = NULL;
+ /*
+ * If p->item does not have anything common with other
+ * commits, there won't be any merge base. If it is
+ * reachable from some of the others, p->item will be
+ * the merge base. If its history is connected with
+ * others, but p->item is not reachable by others, we
+ * will get something other than p->item back.
+ */
+ if (!base || (base->item != p->item))
+ tail = &(commit_list_insert(p->item, tail)->next);
+ free_commit_list(base);
+ }
+ free(other);
+ return result;
+}
+
+static const char gpg_sig_header[] = "gpgsig";
+static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
+
+static int do_sign_commit(struct strbuf *buf, const char *keyid)
+{
+ struct strbuf sig = STRBUF_INIT;
+ int inspos, copypos;
+
+ /* find the end of the header */
+ inspos = strstr(buf->buf, "\n\n") - buf->buf + 1;
+
+ if (!keyid || !*keyid)
+ keyid = get_signing_key();
+ if (sign_buffer(buf, &sig, keyid)) {
+ strbuf_release(&sig);
+ return -1;
+ }
+
+ for (copypos = 0; sig.buf[copypos]; ) {
+ const char *bol = sig.buf + copypos;
+ const char *eol = strchrnul(bol, '\n');
+ int len = (eol - bol) + !!*eol;
+
+ if (!copypos) {
+ strbuf_insert(buf, inspos, gpg_sig_header, gpg_sig_header_len);
+ inspos += gpg_sig_header_len;
+ }
+ strbuf_insert(buf, inspos++, " ", 1);
+ strbuf_insert(buf, inspos, bol, len);
+ inspos += len;
+ copypos += len;
+ }
+ strbuf_release(&sig);
+ return 0;
+}
+
+int parse_signed_commit(const unsigned char *sha1,
+ struct strbuf *payload, struct strbuf *signature)
+{
+ unsigned long size;
+ enum object_type type;
+ char *buffer = read_sha1_file(sha1, &type, &size);
+ int in_signature, saw_signature = -1;
+ char *line, *tail;
+
+ if (!buffer || type != OBJ_COMMIT)
+ goto cleanup;
+
+ line = buffer;
+ tail = buffer + size;
+ in_signature = 0;
+ saw_signature = 0;
+ while (line < tail) {
+ const char *sig = NULL;
+ char *next = memchr(line, '\n', tail - line);
+
+ next = next ? next + 1 : tail;
+ if (in_signature && line[0] == ' ')
+ sig = line + 1;
+ else if (!prefixcmp(line, gpg_sig_header) &&
+ line[gpg_sig_header_len] == ' ')
+ sig = line + gpg_sig_header_len + 1;
+ if (sig) {
+ strbuf_add(signature, sig, next - sig);
+ saw_signature = 1;
+ in_signature = 1;
+ } else {
+ if (*line == '\n')
+ /* dump the whole remainder of the buffer */
+ next = tail;
+ strbuf_add(payload, line, next - line);
+ in_signature = 0;
+ }
+ line = next;
+ }
+ cleanup:
+ free(buffer);
+ return saw_signature;
+}
+
+static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
+{
+ struct merge_remote_desc *desc;
+ struct commit_extra_header *mergetag;
+ char *buf;
+ unsigned long size, len;
+ enum object_type type;
+
+ desc = merge_remote_util(parent);
+ if (!desc || !desc->obj)
+ return;
+ buf = read_sha1_file(desc->obj->sha1, &type, &size);
+ if (!buf || type != OBJ_TAG)
+ goto free_return;
+ len = parse_signature(buf, size);
+ if (size == len)
+ goto free_return;
+ /*
+ * We could verify this signature and either omit the tag when
+ * it does not validate, but the integrator may not have the
+ * public key of the signer of the tag he is merging, while a
+ * later auditor may have it while auditing, so let's not run
+ * verify-signed-buffer here for now...
+ *
+ * if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
+ * warn("warning: signed tag unverified.");
+ */
+ mergetag = xcalloc(1, sizeof(*mergetag));
+ mergetag->key = xstrdup("mergetag");
+ mergetag->value = buf;
+ mergetag->len = size;
+
+ **tail = mergetag;
+ *tail = &mergetag->next;
+ return;
+
+free_return:
+ free(buf);
+}
+
+void append_merge_tag_headers(struct commit_list *parents,
+ struct commit_extra_header ***tail)
+{
+ while (parents) {
+ struct commit *parent = parents->item;
+ handle_signed_tag(parent, tail);
+ parents = parents->next;
+ }
+}
+
+static void add_extra_header(struct strbuf *buffer,
+ struct commit_extra_header *extra)
+{
+ strbuf_addstr(buffer, extra->key);
+ if (extra->len)
+ strbuf_add_lines(buffer, " ", extra->value, extra->len);
+ else
+ strbuf_addch(buffer, '\n');
+}
+
+struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
+ const char **exclude)
+{
+ struct commit_extra_header *extra = NULL;
+ unsigned long size;
+ enum object_type type;
+ char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
+ if (buffer && type == OBJ_COMMIT)
+ extra = read_commit_extra_header_lines(buffer, size, exclude);
+ free(buffer);
+ return extra;
+}
+
+static inline int standard_header_field(const char *field, size_t len)
+{
+ return ((len == 4 && !memcmp(field, "tree ", 5)) ||
+ (len == 6 && !memcmp(field, "parent ", 7)) ||
+ (len == 6 && !memcmp(field, "author ", 7)) ||
+ (len == 9 && !memcmp(field, "committer ", 10)) ||
+ (len == 8 && !memcmp(field, "encoding ", 9)));
+}
+
+static int excluded_header_field(const char *field, size_t len, const char **exclude)
+{
+ if (!exclude)
+ return 0;
+
+ while (*exclude) {
+ size_t xlen = strlen(*exclude);
+ if (len == xlen &&
+ !memcmp(field, *exclude, xlen) && field[xlen] == ' ')
+ return 1;
+ exclude++;
+ }
+ return 0;
+}
+
+struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size,
+ const char **exclude)
+{
+ struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
+ const char *line, *next, *eof, *eob;
+ struct strbuf buf = STRBUF_INIT;
+
+ for (line = buffer, eob = line + size;
+ line < eob && *line != '\n';
+ line = next) {
+ next = memchr(line, '\n', eob - line);
+ next = next ? next + 1 : eob;
+ if (*line == ' ') {
+ /* continuation */
+ if (it)
+ strbuf_add(&buf, line + 1, next - (line + 1));
+ continue;
+ }
+ if (it)
+ it->value = strbuf_detach(&buf, &it->len);
+ strbuf_reset(&buf);
+ it = NULL;
+
+ eof = strchr(line, ' ');
+ if (next <= eof)
+ eof = next;
+
+ if (standard_header_field(line, eof - line) ||
+ excluded_header_field(line, eof - line, exclude))
+ continue;
+
+ it = xcalloc(1, sizeof(*it));
+ it->key = xmemdupz(line, eof-line);
+ *tail = it;
+ tail = &it->next;
+ if (eof + 1 < next)
+ strbuf_add(&buf, eof + 1, next - (eof + 1));
+ }
+ if (it)
+ it->value = strbuf_detach(&buf, &it->len);
+ return extra;
+}
+
+void free_commit_extra_headers(struct commit_extra_header *extra)
+{
+ while (extra) {
+ struct commit_extra_header *next = extra->next;
+ free(extra->key);
+ free(extra->value);
+ free(extra);
+ extra = next;
+ }
+}
+
+int commit_tree(const struct strbuf *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author, const char *sign_commit)
+{
+ struct commit_extra_header *extra = NULL, **tail = &extra;
+ int result;
+
+ append_merge_tag_headers(parents, &tail);
+ result = commit_tree_extended(msg, tree, parents, ret,
+ author, sign_commit, extra);
+ free_commit_extra_headers(extra);
+ return result;
+}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
+int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author, const char *sign_commit,
+ struct commit_extra_header *extra)
+{
+ int result;
+ int encoding_is_utf8;
+ struct strbuf buffer;
+
+ assert_sha1_type(tree, OBJ_TREE);
+
+ if (memchr(msg->buf, '\0', msg->len))
+ return error("a NUL byte in commit log message not allowed.");
+
+ /* Not having i18n.commitencoding is the same as having utf-8 */
+ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+ strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
+ strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
+
+ /*
+ * NOTE! This ordering means that the same exact tree merged with a
+ * different order of parents will be a _different_ changeset even
+ * if everything else stays the same.
+ */
+ while (parents) {
+ struct commit_list *next = parents->next;
+ struct commit *parent = parents->item;
+
+ strbuf_addf(&buffer, "parent %s\n",
+ sha1_to_hex(parent->object.sha1));
+ free(parents);
+ parents = next;
+ }
+
+ /* Person/date information */
+ if (!author)
+ author = git_author_info(IDENT_STRICT);
+ strbuf_addf(&buffer, "author %s\n", author);
+ strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
+ if (!encoding_is_utf8)
+ strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+
+ while (extra) {
+ add_extra_header(&buffer, extra);
+ extra = extra->next;
+ }
+ strbuf_addch(&buffer, '\n');
+
+ /* And add the comment */
+ strbuf_addbuf(&buffer, msg);
+
+ /* And check the encoding */
+ if (encoding_is_utf8 && !is_utf8(buffer.buf))
+ fprintf(stderr, commit_utf8_warn);
+
+ if (sign_commit && do_sign_commit(&buffer, sign_commit))
+ return -1;
+
+ result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+ strbuf_release(&buffer);
+ return result;
+}
+
+struct commit *get_merge_parent(const char *name)
+{
+ struct object *obj;
+ struct commit *commit;
+ unsigned char sha1[20];
+ if (get_sha1(name, sha1))
+ return NULL;
+ obj = parse_object(sha1);
+ commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+ if (commit && !commit->util) {
+ struct merge_remote_desc *desc;
+ desc = xmalloc(sizeof(*desc));
+ desc->obj = obj;
+ desc->name = strdup(name);
+ commit->util = desc;
+ }
+ return commit;
+}
+
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ * struct commit_list *list;
+ * struct commit_list **next = &list;
+ *
+ * next = commit_list_append(c1, next);
+ * next = commit_list_append(c2, next);
+ * assert(commit_list_count(list) == 2);
+ * return list;
+ */
+struct commit_list **commit_list_append(struct commit *commit,
+ struct commit_list **next)
+{
+ struct commit_list *new = xmalloc(sizeof(struct commit_list));
+ new->item = commit;
+ *next = new;
+ new->next = NULL;
+ return &new->next;
+}
diff --git a/samples/c/commit.h b/samples/c/commit.h
new file mode 100644
index 0000000..d617fa3
--- /dev/null
+++ b/samples/c/commit.h
@@ -0,0 +1,226 @@
+#ifndef COMMIT_H
+#define COMMIT_H
+
+#include "object.h"
+#include "tree.h"
+#include "strbuf.h"
+#include "decorate.h"
+
+struct commit_list {
+ struct commit *item;
+ struct commit_list *next;
+};
+
+struct commit {
+ struct object object;
+ void *util;
+ unsigned int indegree;
+ unsigned long date;
+ struct commit_list *parents;
+ struct tree *tree;
+ char *buffer;
+};
+
+extern int save_commit_buffer;
+extern const char *commit_type;
+
+/* While we can decorate any object with a name, it's only used for commits.. */
+extern struct decoration name_decoration;
+struct name_decoration {
+ struct name_decoration *next;
+ int type;
+ char name[1];
+};
+
+struct commit *lookup_commit(const unsigned char *sha1);
+struct commit *lookup_commit_reference(const unsigned char *sha1);
+struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
+ int quiet);
+struct commit *lookup_commit_reference_by_name(const char *name);
+
+/*
+ * Look up object named by "sha1", dereference tag as necessary,
+ * get a commit and return it. If "sha1" does not dereference to
+ * a commit, use ref_name to report an error and die.
+ */
+struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name);
+
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
+int parse_commit(struct commit *item);
+
+/* Find beginning and length of commit subject. */
+int find_commit_subject(const char *commit_buffer, const char **subject);
+
+struct commit_list *commit_list_insert(struct commit *item,
+ struct commit_list **list);
+struct commit_list **commit_list_append(struct commit *commit,
+ struct commit_list **next);
+unsigned commit_list_count(const struct commit_list *l);
+struct commit_list *commit_list_insert_by_date(struct commit *item,
+ struct commit_list **list);
+void commit_list_sort_by_date(struct commit_list **list);
+
+void free_commit_list(struct commit_list *list);
+
+/* Commit formats */
+enum cmit_fmt {
+ CMIT_FMT_RAW,
+ CMIT_FMT_MEDIUM,
+ CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM,
+ CMIT_FMT_SHORT,
+ CMIT_FMT_FULL,
+ CMIT_FMT_FULLER,
+ CMIT_FMT_ONELINE,
+ CMIT_FMT_EMAIL,
+ CMIT_FMT_USERFORMAT,
+
+ CMIT_FMT_UNSPECIFIED
+};
+
+struct pretty_print_context {
+ enum cmit_fmt fmt;
+ int abbrev;
+ const char *subject;
+ const char *after_subject;
+ int preserve_subject;
+ enum date_mode date_mode;
+ unsigned date_mode_explicit:1;
+ int need_8bit_cte;
+ int show_notes;
+ struct reflog_walk_info *reflog_info;
+ const char *output_encoding;
+};
+
+struct userformat_want {
+ unsigned notes:1;
+};
+
+extern int has_non_ascii(const char *text);
+struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+extern char *logmsg_reencode(const struct commit *commit,
+ const char *output_encoding);
+extern char *reencode_commit_message(const struct commit *commit,
+ const char **encoding_p);
+extern void get_commit_format(const char *arg, struct rev_info *);
+extern const char *format_subject(struct strbuf *sb, const char *msg,
+ const char *line_separator);
+extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
+extern void format_commit_message(const struct commit *commit,
+ const char *format, struct strbuf *sb,
+ const struct pretty_print_context *context);
+extern void pretty_print_commit(const struct pretty_print_context *pp,
+ const struct commit *commit,
+ struct strbuf *sb);
+extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+ struct strbuf *sb);
+void pp_user_info(const struct pretty_print_context *pp,
+ const char *what, struct strbuf *sb,
+ const char *line, const char *encoding);
+void pp_title_line(const struct pretty_print_context *pp,
+ const char **msg_p,
+ struct strbuf *sb,
+ const char *encoding,
+ int need_8bit_cte);
+void pp_remainder(const struct pretty_print_context *pp,
+ const char **msg_p,
+ struct strbuf *sb,
+ int indent);
+
+
+/** Removes the first commit from a list sorted by date, and adds all
+ * of its parents.
+ **/
+struct commit *pop_most_recent_commit(struct commit_list **list,
+ unsigned int mark);
+
+struct commit *pop_commit(struct commit_list **stack);
+
+void clear_commit_marks(struct commit *commit, unsigned int mark);
+void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
+
+/*
+ * Performs an in-place topological sort of list supplied.
+ *
+ * invariant of resulting list is:
+ * a reachable from b => ord(b) < ord(a)
+ * in addition, when lifo == 0, commits on parallel tracks are
+ * sorted in the dates order.
+ */
+void sort_in_topological_order(struct commit_list ** list, int lifo);
+
+struct commit_graft {
+ unsigned char sha1[20];
+ int nr_parent; /* < 0 if shallow commit */
+ unsigned char parent[FLEX_ARRAY][20]; /* more */
+};
+typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
+
+struct commit_graft *read_graft_line(char *buf, int len);
+int register_commit_graft(struct commit_graft *, int);
+struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
+
+extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
+extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
+extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
+
+extern int register_shallow(const unsigned char *sha1);
+extern int unregister_shallow(const unsigned char *sha1);
+extern int for_each_commit_graft(each_commit_graft_fn, void *);
+extern int is_repository_shallow(void);
+extern struct commit_list *get_shallow_commits(struct object_array *heads,
+ int depth, int shallow_flag, int not_shallow_flag);
+
+int is_descendant_of(struct commit *, struct commit_list *);
+int in_merge_bases(struct commit *, struct commit **, int);
+
+extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
+extern int run_add_interactive(const char *revision, const char *patch_mode,
+ const char **pathspec);
+
+static inline int single_parent(struct commit *commit)
+{
+ return commit->parents && !commit->parents->next;
+}
+
+struct commit_list *reduce_heads(struct commit_list *heads);
+
+struct commit_extra_header {
+ struct commit_extra_header *next;
+ char *key;
+ char *value;
+ size_t len;
+};
+
+extern void append_merge_tag_headers(struct commit_list *parents,
+ struct commit_extra_header ***tail);
+
+extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author, const char *sign_commit);
+
+extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author, const char *sign_commit,
+ struct commit_extra_header *);
+
+extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
+extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
+
+extern void free_commit_extra_headers(struct commit_extra_header *extra);
+
+struct merge_remote_desc {
+ struct object *obj; /* the named object, could be a tag */
+ const char *name;
+};
+#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
+
+/*
+ * Given "name" from the command line to merge, find the commit object
+ * and return it, while storing merge_remote_desc in its ->util field,
+ * to allow callers to tell if we are told to merge a tag.
+ */
+struct commit *get_merge_parent(const char *name);
+
+extern int parse_signed_commit(const unsigned char *sha1,
+ struct strbuf *message, struct strbuf *signature);
+#endif /* COMMIT_H */
diff --git a/samples/c/cpu.c b/samples/c/cpu.c
new file mode 100644
index 0000000..a4eb522
--- /dev/null
+++ b/samples/c/cpu.c
@@ -0,0 +1,725 @@
+/* CPU control.
+ * (C) 2001, 2002, 2003, 2004 Rusty Russell
+ *
+ * This code is licenced under the GPL.
+ */
+#include <linux/proc_fs.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/cpu.h>
+#include <linux/oom.h>
+#include <linux/rcupdate.h>
+#include <linux/export.h>
+#include <linux/bug.h>
+#include <linux/kthread.h>
+#include <linux/stop_machine.h>
+#include <linux/mutex.h>
+#include <linux/gfp.h>
+#include <linux/suspend.h>
+
+#include "smpboot.h"
+
+#ifdef CONFIG_SMP
+/* Serializes the updates to cpu_online_mask, cpu_present_mask */
+static DEFINE_MUTEX(cpu_add_remove_lock);
+
+/*
+ * The following two API's must be used when attempting
+ * to serialize the updates to cpu_online_mask, cpu_present_mask.
+ */
+void cpu_maps_update_begin(void)
+{
+ mutex_lock(&cpu_add_remove_lock);
+}
+
+void cpu_maps_update_done(void)
+{
+ mutex_unlock(&cpu_add_remove_lock);
+}
+
+static RAW_NOTIFIER_HEAD(cpu_chain);
+
+/* If set, cpu_up and cpu_down will return -EBUSY and do nothing.
+ * Should always be manipulated under cpu_add_remove_lock
+ */
+static int cpu_hotplug_disabled;
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+static struct {
+ struct task_struct *active_writer;
+ struct mutex lock; /* Synchronizes accesses to refcount, */
+ /*
+ * Also blocks the new readers during
+ * an ongoing cpu hotplug operation.
+ */
+ int refcount;
+} cpu_hotplug = {
+ .active_writer = NULL,
+ .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),
+ .refcount = 0,
+};
+
+void get_online_cpus(void)
+{
+ might_sleep();
+ if (cpu_hotplug.active_writer == current)
+ return;
+ mutex_lock(&cpu_hotplug.lock);
+ cpu_hotplug.refcount++;
+ mutex_unlock(&cpu_hotplug.lock);
+
+}
+EXPORT_SYMBOL_GPL(get_online_cpus);
+
+void put_online_cpus(void)
+{
+ if (cpu_hotplug.active_writer == current)
+ return;
+ mutex_lock(&cpu_hotplug.lock);
+ if (!--cpu_hotplug.refcount && unlikely(cpu_hotplug.active_writer))
+ wake_up_process(cpu_hotplug.active_writer);
+ mutex_unlock(&cpu_hotplug.lock);
+
+}
+EXPORT_SYMBOL_GPL(put_online_cpus);
+
+/*
+ * This ensures that the hotplug operation can begin only when the
+ * refcount goes to zero.
+ *
+ * Note that during a cpu-hotplug operation, the new readers, if any,
+ * will be blocked by the cpu_hotplug.lock
+ *
+ * Since cpu_hotplug_begin() is always called after invoking
+ * cpu_maps_update_begin(), we can be sure that only one writer is active.
+ *
+ * Note that theoretically, there is a possibility of a livelock:
+ * - Refcount goes to zero, last reader wakes up the sleeping
+ * writer.
+ * - Last reader unlocks the cpu_hotplug.lock.
+ * - A new reader arrives at this moment, bumps up the refcount.
+ * - The writer acquires the cpu_hotplug.lock finds the refcount
+ * non zero and goes to sleep again.
+ *
+ * However, this is very difficult to achieve in practice since
+ * get_online_cpus() not an api which is called all that often.
+ *
+ */
+static void cpu_hotplug_begin(void)
+{
+ cpu_hotplug.active_writer = current;
+
+ for (;;) {
+ mutex_lock(&cpu_hotplug.lock);
+ if (likely(!cpu_hotplug.refcount))
+ break;
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ mutex_unlock(&cpu_hotplug.lock);
+ schedule();
+ }
+}
+
+static void cpu_hotplug_done(void)
+{
+ cpu_hotplug.active_writer = NULL;
+ mutex_unlock(&cpu_hotplug.lock);
+}
+
+#else /* #if CONFIG_HOTPLUG_CPU */
+static void cpu_hotplug_begin(void) {}
+static void cpu_hotplug_done(void) {}
+#endif /* #else #if CONFIG_HOTPLUG_CPU */
+
+/* Need to know about CPUs going up/down? */
+int __ref register_cpu_notifier(struct notifier_block *nb)
+{
+ int ret;
+ cpu_maps_update_begin();
+ ret = raw_notifier_chain_register(&cpu_chain, nb);
+ cpu_maps_update_done();
+ return ret;
+}
+
+static int __cpu_notify(unsigned long val, void *v, int nr_to_call,
+ int *nr_calls)
+{
+ int ret;
+
+ ret = __raw_notifier_call_chain(&cpu_chain, val, v, nr_to_call,
+ nr_calls);
+
+ return notifier_to_errno(ret);
+}
+
+static int cpu_notify(unsigned long val, void *v)
+{
+ return __cpu_notify(val, v, -1, NULL);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+static void cpu_notify_nofail(unsigned long val, void *v)
+{
+ BUG_ON(cpu_notify(val, v));
+}
+EXPORT_SYMBOL(register_cpu_notifier);
+
+void __ref unregister_cpu_notifier(struct notifier_block *nb)
+{
+ cpu_maps_update_begin();
+ raw_notifier_chain_unregister(&cpu_chain, nb);
+ cpu_maps_update_done();
+}
+EXPORT_SYMBOL(unregister_cpu_notifier);
+
+/**
+ * clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
+ * @cpu: a CPU id
+ *
+ * This function walks all processes, finds a valid mm struct for each one and
+ * then clears a corresponding bit in mm's cpumask. While this all sounds
+ * trivial, there are various non-obvious corner cases, which this function
+ * tries to solve in a safe manner.
+ *
+ * Also note that the function uses a somewhat relaxed locking scheme, so it may
+ * be called only for an already offlined CPU.
+ */
+void clear_tasks_mm_cpumask(int cpu)
+{
+ struct task_struct *p;
+
+ /*
+ * This function is called after the cpu is taken down and marked
+ * offline, so its not like new tasks will ever get this cpu set in
+ * their mm mask. -- Peter Zijlstra
+ * Thus, we may use rcu_read_lock() here, instead of grabbing
+ * full-fledged tasklist_lock.
+ */
+ WARN_ON(cpu_online(cpu));
+ rcu_read_lock();
+ for_each_process(p) {
+ struct task_struct *t;
+
+ /*
+ * Main thread might exit, but other threads may still have
+ * a valid mm. Find one.
+ */
+ t = find_lock_task_mm(p);
+ if (!t)
+ continue;
+ cpumask_clear_cpu(cpu, mm_cpumask(t->mm));
+ task_unlock(t);
+ }
+ rcu_read_unlock();
+}
+
+static inline void check_for_tasks(int cpu)
+{
+ struct task_struct *p;
+
+ write_lock_irq(&tasklist_lock);
+ for_each_process(p) {
+ if (task_cpu(p) == cpu && p->state == TASK_RUNNING &&
+ (p->utime || p->stime))
+ printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d "
+ "(state = %ld, flags = %x)\n",
+ p->comm, task_pid_nr(p), cpu,
+ p->state, p->flags);
+ }
+ write_unlock_irq(&tasklist_lock);
+}
+
+struct take_cpu_down_param {
+ unsigned long mod;
+ void *hcpu;
+};
+
+/* Take this CPU down. */
+static int __ref take_cpu_down(void *_param)
+{
+ struct take_cpu_down_param *param = _param;
+ int err;
+
+ /* Ensure this CPU doesn't handle any more interrupts. */
+ err = __cpu_disable();
+ if (err < 0)
+ return err;
+
+ cpu_notify(CPU_DYING | param->mod, param->hcpu);
+ return 0;
+}
+
+/* Requires cpu_add_remove_lock to be held */
+static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
+{
+ int err, nr_calls = 0;
+ void *hcpu = (void *)(long)cpu;
+ unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
+ struct take_cpu_down_param tcd_param = {
+ .mod = mod,
+ .hcpu = hcpu,
+ };
+
+ if (num_online_cpus() == 1)
+ return -EBUSY;
+
+ if (!cpu_online(cpu))
+ return -EINVAL;
+
+ cpu_hotplug_begin();
+
+ err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls);
+ if (err) {
+ nr_calls--;
+ __cpu_notify(CPU_DOWN_FAILED | mod, hcpu, nr_calls, NULL);
+ printk("%s: attempt to take down CPU %u failed\n",
+ __func__, cpu);
+ goto out_release;
+ }
+
+ err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
+ if (err) {
+ /* CPU didn't die: tell everyone. Can't complain. */
+ cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu);
+
+ goto out_release;
+ }
+ BUG_ON(cpu_online(cpu));
+
+ /*
+ * The migration_call() CPU_DYING callback will have removed all
+ * runnable tasks from the cpu, there's only the idle task left now
+ * that the migration thread is done doing the stop_machine thing.
+ *
+ * Wait for the stop thread to go away.
+ */
+ while (!idle_cpu(cpu))
+ cpu_relax();
+
+ /* This actually kills the CPU. */
+ __cpu_die(cpu);
+
+ /* CPU is completely dead: tell everyone. Too late to complain. */
+ cpu_notify_nofail(CPU_DEAD | mod, hcpu);
+
+ check_for_tasks(cpu);
+
+out_release:
+ cpu_hotplug_done();
+ if (!err)
+ cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu);
+ return err;
+}
+
+int __ref cpu_down(unsigned int cpu)
+{
+ int err;
+
+ cpu_maps_update_begin();
+
+ if (cpu_hotplug_disabled) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ err = _cpu_down(cpu, 0);
+
+out:
+ cpu_maps_update_done();
+ return err;
+}
+EXPORT_SYMBOL(cpu_down);
+#endif /*CONFIG_HOTPLUG_CPU*/
+
+/* Requires cpu_add_remove_lock to be held */
+static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
+{
+ int ret, nr_calls = 0;
+ void *hcpu = (void *)(long)cpu;
+ unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
+ struct task_struct *idle;
+
+ if (cpu_online(cpu) || !cpu_present(cpu))
+ return -EINVAL;
+
+ cpu_hotplug_begin();
+
+ idle = idle_thread_get(cpu);
+ if (IS_ERR(idle)) {
+ ret = PTR_ERR(idle);
+ goto out;
+ }
+
+ ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);
+ if (ret) {
+ nr_calls--;
+ printk(KERN_WARNING "%s: attempt to bring up CPU %u failed\n",
+ __func__, cpu);
+ goto out_notify;
+ }
+
+ /* Arch-specific enabling code. */
+ ret = __cpu_up(cpu, idle);
+ if (ret != 0)
+ goto out_notify;
+ BUG_ON(!cpu_online(cpu));
+
+ /* Now call notifier in preparation. */
+ cpu_notify(CPU_ONLINE | mod, hcpu);
+
+out_notify:
+ if (ret != 0)
+ __cpu_notify(CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL);
+out:
+ cpu_hotplug_done();
+
+ return ret;
+}
+
+int __cpuinit cpu_up(unsigned int cpu)
+{
+ int err = 0;
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+ int nid;
+ pg_data_t *pgdat;
+#endif
+
+ if (!cpu_possible(cpu)) {
+ printk(KERN_ERR "can't online cpu %d because it is not "
+ "configured as may-hotadd at boot time\n", cpu);
+#if defined(CONFIG_IA64)
+ printk(KERN_ERR "please check additional_cpus= boot "
+ "parameter\n");
+#endif
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+ nid = cpu_to_node(cpu);
+ if (!node_online(nid)) {
+ err = mem_online_node(nid);
+ if (err)
+ return err;
+ }
+
+ pgdat = NODE_DATA(nid);
+ if (!pgdat) {
+ printk(KERN_ERR
+ "Can't online cpu %d due to NULL pgdat\n", cpu);
+ return -ENOMEM;
+ }
+
+ if (pgdat->node_zonelists->_zonerefs->zone == NULL) {
+ mutex_lock(&zonelists_mutex);
+ build_all_zonelists(NULL);
+ mutex_unlock(&zonelists_mutex);
+ }
+#endif
+
+ cpu_maps_update_begin();
+
+ if (cpu_hotplug_disabled) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ err = _cpu_up(cpu, 0);
+
+out:
+ cpu_maps_update_done();
+ return err;
+}
+EXPORT_SYMBOL_GPL(cpu_up);
+
+#ifdef CONFIG_PM_SLEEP_SMP
+static cpumask_var_t frozen_cpus;
+
+void __weak arch_disable_nonboot_cpus_begin(void)
+{
+}
+
+void __weak arch_disable_nonboot_cpus_end(void)
+{
+}
+
+int disable_nonboot_cpus(void)
+{
+ int cpu, first_cpu, error = 0;
+
+ cpu_maps_update_begin();
+ first_cpu = cpumask_first(cpu_online_mask);
+ /*
+ * We take down all of the non-boot CPUs in one shot to avoid races
+ * with the userspace trying to use the CPU hotplug at the same time
+ */
+ cpumask_clear(frozen_cpus);
+ arch_disable_nonboot_cpus_begin();
+
+ printk("Disabling non-boot CPUs ...\n");
+ for_each_online_cpu(cpu) {
+ if (cpu == first_cpu)
+ continue;
+ error = _cpu_down(cpu, 1);
+ if (!error)
+ cpumask_set_cpu(cpu, frozen_cpus);
+ else {
+ printk(KERN_ERR "Error taking CPU%d down: %d\n",
+ cpu, error);
+ break;
+ }
+ }
+
+ arch_disable_nonboot_cpus_end();
+
+ if (!error) {
+ BUG_ON(num_online_cpus() > 1);
+ /* Make sure the CPUs won't be enabled by someone else */
+ cpu_hotplug_disabled = 1;
+ } else {
+ printk(KERN_ERR "Non-boot CPUs are not disabled\n");
+ }
+ cpu_maps_update_done();
+ return error;
+}
+
+void __weak arch_enable_nonboot_cpus_begin(void)
+{
+}
+
+void __weak arch_enable_nonboot_cpus_end(void)
+{
+}
+
+void __ref enable_nonboot_cpus(void)
+{
+ int cpu, error;
+
+ /* Allow everyone to use the CPU hotplug again */
+ cpu_maps_update_begin();
+ cpu_hotplug_disabled = 0;
+ if (cpumask_empty(frozen_cpus))
+ goto out;
+
+ printk(KERN_INFO "Enabling non-boot CPUs ...\n");
+
+ arch_enable_nonboot_cpus_begin();
+
+ for_each_cpu(cpu, frozen_cpus) {
+ error = _cpu_up(cpu, 1);
+ if (!error) {
+ printk(KERN_INFO "CPU%d is up\n", cpu);
+ continue;
+ }
+ printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error);
+ }
+
+ arch_enable_nonboot_cpus_end();
+
+ cpumask_clear(frozen_cpus);
+out:
+ cpu_maps_update_done();
+}
+
+static int __init alloc_frozen_cpus(void)
+{
+ if (!alloc_cpumask_var(&frozen_cpus, GFP_KERNEL|__GFP_ZERO))
+ return -ENOMEM;
+ return 0;
+}
+core_initcall(alloc_frozen_cpus);
+
+/*
+ * Prevent regular CPU hotplug from racing with the freezer, by disabling CPU
+ * hotplug when tasks are about to be frozen. Also, don't allow the freezer
+ * to continue until any currently running CPU hotplug operation gets
+ * completed.
+ * To modify the 'cpu_hotplug_disabled' flag, we need to acquire the
+ * 'cpu_add_remove_lock'. And this same lock is also taken by the regular
+ * CPU hotplug path and released only after it is complete. Thus, we
+ * (and hence the freezer) will block here until any currently running CPU
+ * hotplug operation gets completed.
+ */
+void cpu_hotplug_disable_before_freeze(void)
+{
+ cpu_maps_update_begin();
+ cpu_hotplug_disabled = 1;
+ cpu_maps_update_done();
+}
+
+
+/*
+ * When tasks have been thawed, re-enable regular CPU hotplug (which had been
+ * disabled while beginning to freeze tasks).
+ */
+void cpu_hotplug_enable_after_thaw(void)
+{
+ cpu_maps_update_begin();
+ cpu_hotplug_disabled = 0;
+ cpu_maps_update_done();
+}
+
+/*
+ * When callbacks for CPU hotplug notifications are being executed, we must
+ * ensure that the state of the system with respect to the tasks being frozen
+ * or not, as reported by the notification, remains unchanged *throughout the
+ * duration* of the execution of the callbacks.
+ * Hence we need to prevent the freezer from racing with regular CPU hotplug.
+ *
+ * This synchronization is implemented by mutually excluding regular CPU
+ * hotplug and Suspend/Hibernate call paths by hooking onto the Suspend/
+ * Hibernate notifications.
+ */
+static int
+cpu_hotplug_pm_callback(struct notifier_block *nb,
+ unsigned long action, void *ptr)
+{
+ switch (action) {
+
+ case PM_SUSPEND_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+ cpu_hotplug_disable_before_freeze();
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ cpu_hotplug_enable_after_thaw();
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static int __init cpu_hotplug_pm_sync_init(void)
+{
+ pm_notifier(cpu_hotplug_pm_callback, 0);
+ return 0;
+}
+core_initcall(cpu_hotplug_pm_sync_init);
+
+#endif /* CONFIG_PM_SLEEP_SMP */
+
+/**
+ * notify_cpu_starting(cpu) - call the CPU_STARTING notifiers
+ * @cpu: cpu that just started
+ *
+ * This function calls the cpu_chain notifiers with CPU_STARTING.
+ * It must be called by the arch code on the new cpu, before the new cpu
+ * enables interrupts and before the "boot" cpu returns from __cpu_up().
+ */
+void __cpuinit notify_cpu_starting(unsigned int cpu)
+{
+ unsigned long val = CPU_STARTING;
+
+#ifdef CONFIG_PM_SLEEP_SMP
+ if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus))
+ val = CPU_STARTING_FROZEN;
+#endif /* CONFIG_PM_SLEEP_SMP */
+ cpu_notify(val, (void *)(long)cpu);
+}
+
+#endif /* CONFIG_SMP */
+
+/*
+ * cpu_bit_bitmap[] is a special, "compressed" data structure that
+ * represents all NR_CPUS bits binary values of 1<<nr.
+ *
+ * It is used by cpumask_of() to get a constant address to a CPU
+ * mask value that has a single bit set only.
+ */
+
+/* cpu_bit_bitmap[0] is empty - so we can back into it */
+#define MASK_DECLARE_1(x) [x+1][0] = (1UL << (x))
+#define MASK_DECLARE_2(x) MASK_DECLARE_1(x), MASK_DECLARE_1(x+1)
+#define MASK_DECLARE_4(x) MASK_DECLARE_2(x), MASK_DECLARE_2(x+2)
+#define MASK_DECLARE_8(x) MASK_DECLARE_4(x), MASK_DECLARE_4(x+4)
+
+const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = {
+
+ MASK_DECLARE_8(0), MASK_DECLARE_8(8),
+ MASK_DECLARE_8(16), MASK_DECLARE_8(24),
+#if BITS_PER_LONG > 32
+ MASK_DECLARE_8(32), MASK_DECLARE_8(40),
+ MASK_DECLARE_8(48), MASK_DECLARE_8(56),
+#endif
+};
+EXPORT_SYMBOL_GPL(cpu_bit_bitmap);
+
+const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
+EXPORT_SYMBOL(cpu_all_bits);
+
+#ifdef CONFIG_INIT_ALL_POSSIBLE
+static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly
+ = CPU_BITS_ALL;
+#else
+static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
+#endif
+const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
+EXPORT_SYMBOL(cpu_possible_mask);
+
+static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
+EXPORT_SYMBOL(cpu_online_mask);
+
+static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
+EXPORT_SYMBOL(cpu_present_mask);
+
+static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
+EXPORT_SYMBOL(cpu_active_mask);
+
+void set_cpu_possible(unsigned int cpu, bool possible)
+{
+ if (possible)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_possible_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_possible_bits));
+}
+
+void set_cpu_present(unsigned int cpu, bool present)
+{
+ if (present)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_present_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_present_bits));
+}
+
+void set_cpu_online(unsigned int cpu, bool online)
+{
+ if (online)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
+}
+
+void set_cpu_active(unsigned int cpu, bool active)
+{
+ if (active)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits));
+}
+
+void init_cpu_present(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_present_bits), src);
+}
+
+void init_cpu_possible(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_possible_bits), src);
+}
+
+void init_cpu_online(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_online_bits), src);
+}
diff --git a/samples/c/diff.c b/samples/c/diff.c
new file mode 100644
index 0000000..90baa95
--- /dev/null
+++ b/samples/c/diff.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "git2/diff.h"
+#include "diff.h"
+#include "fileops.h"
+#include "config.h"
+#include "attr_file.h"
+
+static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
+{
+ git_buf prefix = GIT_BUF_INIT;
+ const char *scan;
+
+ if (git_buf_common_prefix(&prefix, pathspec) < 0)
+ return NULL;
+
+ /* diff prefix will only be leading non-wildcards */
+ for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
+ git_buf_truncate(&prefix, scan - prefix.ptr);
+
+ if (prefix.size > 0)
+ return git_buf_detach(&prefix);
+
+ git_buf_free(&prefix);
+ return NULL;
+}
+
+static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
+{
+ const char *str;
+
+ if (pathspec == NULL || pathspec->count == 0)
+ return false;
+ if (pathspec->count > 1)
+ return true;
+
+ str = pathspec->strings[0];
+ if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
+ return false;
+ return true;
+}
+
+static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
+{
+ unsigned int i;
+ git_attr_fnmatch *match;
+
+ if (!diff->pathspec.length)
+ return true;
+
+ git_vector_foreach(&diff->pathspec, i, match) {
+ int result = p_fnmatch(match->pattern, path, 0);
+
+ /* if we didn't match, look for exact dirname prefix match */
+ if (result == FNM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+ strncmp(path, match->pattern, match->length) == 0 &&
+ path[match->length] == '/')
+ result = 0;
+
+ if (result == 0)
+ return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+ }
+
+ return false;
+}
+
+static git_diff_delta *diff_delta__alloc(
+ git_diff_list *diff,
+ git_delta_t status,
+ const char *path)
+{
+ git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ delta->old_file.path = git_pool_strdup(&diff->pool, path);
+ if (delta->old_file.path == NULL) {
+ git__free(delta);
+ return NULL;
+ }
+
+ delta->new_file.path = delta->old_file.path;
+
+ if (diff->opts.flags & GIT_DIFF_REVERSE) {
+ switch (status) {
+ case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
+ case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
+ default: break; /* leave other status values alone */
+ }
+ }
+ delta->status = status;
+
+ return delta;
+}
+
+static git_diff_delta *diff_delta__dup(
+ const git_diff_delta *d, git_pool *pool)
+{
+ git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ memcpy(delta, d, sizeof(git_diff_delta));
+
+ delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
+ if (delta->old_file.path == NULL)
+ goto fail;
+
+ if (d->new_file.path != d->old_file.path) {
+ delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
+ if (delta->new_file.path == NULL)
+ goto fail;
+ } else {
+ delta->new_file.path = delta->old_file.path;
+ }
+
+ return delta;
+
+fail:
+ git__free(delta);
+ return NULL;
+}
+
+static git_diff_delta *diff_delta__merge_like_cgit(
+ const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
+{
+ git_diff_delta *dup = diff_delta__dup(a, pool);
+ if (!dup)
+ return NULL;
+
+ if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
+ return dup;
+
+ git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
+
+ dup->new_file.mode = b->new_file.mode;
+ dup->new_file.size = b->new_file.size;
+ dup->new_file.flags = b->new_file.flags;
+
+ /* Emulate C git for merging two diffs (a la 'git diff <sha>').
+ *
+ * When C git does a diff between the work dir and a tree, it actually
+ * diffs with the index but uses the workdir contents. This emulates
+ * those choices so we can emulate the type of diff.
+ */
+ if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
+ if (dup->status == GIT_DELTA_DELETED)
+ /* preserve pending delete info */;
+ else if (b->status == GIT_DELTA_UNTRACKED ||
+ b->status == GIT_DELTA_IGNORED)
+ dup->status = b->status;
+ else
+ dup->status = GIT_DELTA_UNMODIFIED;
+ }
+ else if (dup->status == GIT_DELTA_UNMODIFIED ||
+ b->status == GIT_DELTA_DELETED)
+ dup->status = b->status;
+
+ return dup;
+}
+
+static int diff_delta__from_one(
+ git_diff_list *diff,
+ git_delta_t status,
+ const git_index_entry *entry)
+{
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return 0;
+
+ if (status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return 0;
+
+ if (!diff_path_matches_pathspec(diff, entry->path))
+ return 0;
+
+ delta = diff_delta__alloc(diff, status, entry->path);
+ GITERR_CHECK_ALLOC(delta);
+
+ /* This fn is just for single-sided diffs */
+ assert(status != GIT_DELTA_MODIFIED);
+
+ if (delta->status == GIT_DELTA_DELETED) {
+ delta->old_file.mode = entry->mode;
+ delta->old_file.size = entry->file_size;
+ git_oid_cpy(&delta->old_file.oid, &entry->oid);
+ } else /* ADDED, IGNORED, UNTRACKED */ {
+ delta->new_file.mode = entry->mode;
+ delta->new_file.size = entry->file_size;
+ git_oid_cpy(&delta->new_file.oid, &entry->oid);
+ }
+
+ delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ if (git_vector_insert(&diff->deltas, delta) < 0) {
+ git__free(delta);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int diff_delta__from_two(
+ git_diff_list *diff,
+ git_delta_t status,
+ const git_index_entry *old_entry,
+ const git_index_entry *new_entry,
+ git_oid *new_oid)
+{
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return 0;
+
+ if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
+ const git_index_entry *temp = old_entry;
+ old_entry = new_entry;
+ new_entry = temp;
+ }
+
+ delta = diff_delta__alloc(diff, status, old_entry->path);
+ GITERR_CHECK_ALLOC(delta);
+
+ delta->old_file.mode = old_entry->mode;
+ git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
+ delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ delta->new_file.mode = new_entry->mode;
+ git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
+ if (new_oid || !git_oid_iszero(&new_entry->oid))
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ if (git_vector_insert(&diff->deltas, delta) < 0) {
+ git__free(delta);
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
+{
+ size_t len = strlen(prefix);
+
+ /* append '/' at end if needed */
+ if (len > 0 && prefix[len - 1] != '/')
+ return git_pool_strcat(pool, prefix, "/");
+ else
+ return git_pool_strndup(pool, prefix, len + 1);
+}
+
+static int diff_delta__cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(da->old_file.path, db->old_file.path);
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+static int config_bool(git_config *cfg, const char *name, int defvalue)
+{
+ int val = defvalue;
+
+ if (git_config_get_bool(&val, cfg, name) < 0)
+ giterr_clear();
+
+ return val;
+}
+
+static git_diff_list *git_diff_list_alloc(
+ git_repository *repo, const git_diff_options *opts)
+{
+ git_config *cfg;
+ size_t i;
+ git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
+ if (diff == NULL)
+ return NULL;
+
+ diff->repo = repo;
+
+ if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
+ git_pool_init(&diff->pool, 1, 0) < 0)
+ goto fail;
+
+ /* load config values that affect diff behavior */
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ goto fail;
+ if (config_bool(cfg, "core.symlinks", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
+ if (config_bool(cfg, "core.ignorestat", 0))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
+ if (config_bool(cfg, "core.filemode", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
+ if (config_bool(cfg, "core.trustctime", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
+ /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
+
+ if (opts == NULL)
+ return diff;
+
+ memcpy(&diff->opts, opts, sizeof(git_diff_options));
+ memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
+
+ diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
+ opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
+ diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
+ opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
+
+ if (!diff->opts.old_prefix || !diff->opts.new_prefix)
+ goto fail;
+
+ if (diff->opts.flags & GIT_DIFF_REVERSE) {
+ char *swap = diff->opts.old_prefix;
+ diff->opts.old_prefix = diff->opts.new_prefix;
+ diff->opts.new_prefix = swap;
+ }
+
+ /* only copy pathspec if it is "interesting" so we can test
+ * diff->pathspec.length > 0 to know if it is worth calling
+ * fnmatch as we iterate.
+ */
+ if (!diff_pathspec_is_interesting(&opts->pathspec))
+ return diff;
+
+ if (git_vector_init(
+ &diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
+ goto fail;
+
+ for (i = 0; i < opts->pathspec.count; ++i) {
+ int ret;
+ const char *pattern = opts->pathspec.strings[i];
+ git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
+ if (!match)
+ goto fail;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+ ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
+ if (ret == GIT_ENOTFOUND) {
+ git__free(match);
+ continue;
+ } else if (ret < 0)
+ goto fail;
+
+ if (git_vector_insert(&diff->pathspec, match) < 0)
+ goto fail;
+ }
+
+ return diff;
+
+fail:
+ git_diff_list_free(diff);
+ return NULL;
+}
+
+void git_diff_list_free(git_diff_list *diff)
+{
+ git_diff_delta *delta;
+ git_attr_fnmatch *match;
+ unsigned int i;
+
+ if (!diff)
+ return;
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ git__free(delta);
+ diff->deltas.contents[i] = NULL;
+ }
+ git_vector_free(&diff->deltas);
+
+ git_vector_foreach(&diff->pathspec, i, match) {
+ git__free(match);
+ diff->pathspec.contents[i] = NULL;
+ }
+ git_vector_free(&diff->pathspec);
+
+ git_pool_clear(&diff->pool);
+ git__free(diff);
+}
+
+static int oid_for_workdir_item(
+ git_repository *repo,
+ const git_index_entry *item,
+ git_oid *oid)
+{
+ int result;
+ git_buf full_path = GIT_BUF_INIT;
+
+ if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
+ return -1;
+
+ /* calculate OID for file if possible*/
+ if (S_ISLNK(item->mode))
+ result = git_odb__hashlink(oid, full_path.ptr);
+ else if (!git__is_sizet(item->file_size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ result = -1;
+ } else {
+ int fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0)
+ result = fd;
+ else {
+ result = git_odb__hashfd(
+ oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
+ p_close(fd);
+ }
+ }
+
+ git_buf_free(&full_path);
+
+ return result;
+}
+
+#define EXEC_BIT_MASK 0000111
+
+static int maybe_modified(
+ git_iterator *old_iter,
+ const git_index_entry *oitem,
+ git_iterator *new_iter,
+ const git_index_entry *nitem,
+ git_diff_list *diff)
+{
+ git_oid noid, *use_noid = NULL;
+ git_delta_t status = GIT_DELTA_MODIFIED;
+ unsigned int omode = oitem->mode;
+ unsigned int nmode = nitem->mode;
+
+ GIT_UNUSED(old_iter);
+
+ if (!diff_path_matches_pathspec(diff, oitem->path))
+ return 0;
+
+ /* on platforms with no symlinks, promote plain files to symlinks */
+ if (S_ISLNK(omode) && S_ISREG(nmode) &&
+ !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
+ nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
+
+ /* on platforms with no execmode, clear exec bit from comparisons */
+ if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
+ omode = omode & ~EXEC_BIT_MASK;
+ nmode = nmode & ~EXEC_BIT_MASK;
+ }
+
+ /* support "assume unchanged" (badly, b/c we still stat everything) */
+ if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
+ status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
+ GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
+
+ /* support "skip worktree" index bit */
+ else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if basic type of file changed, then split into delete and add */
+ else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+ diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
+ return -1;
+ return 0;
+ }
+
+ /* if oids and modes match, then file is unmodified */
+ else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if we have a workdir item with an unknown oid, check deeper */
+ else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
+ /* TODO: add check against index file st_mtime to avoid racy-git */
+
+ /* if they files look exactly alike, then we'll assume the same */
+ if (oitem->file_size == nitem->file_size &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
+ (oitem->ctime.seconds == nitem->ctime.seconds)) &&
+ oitem->mtime.seconds == nitem->mtime.seconds &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
+ (oitem->dev == nitem->dev)) &&
+ oitem->ino == nitem->ino &&
+ oitem->uid == nitem->uid &&
+ oitem->gid == nitem->gid)
+ status = GIT_DELTA_UNMODIFIED;
+
+ else if (S_ISGITLINK(nmode)) {
+ git_submodule *sub;
+
+ if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+ else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
+ return -1;
+ else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
+ status = GIT_DELTA_UNMODIFIED;
+ else {
+ /* TODO: support other GIT_SUBMODULE_IGNORE values */
+ status = GIT_DELTA_UNMODIFIED;
+ }
+ }
+
+ /* TODO: check git attributes so we will not have to read the file
+ * in if it is marked binary.
+ */
+
+ else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
+ return -1;
+
+ else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* store calculated oid so we don't have to recalc later */
+ use_noid = &noid;
+ }
+
+ return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
+}
+
+static int diff_from_iterators(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_iterator *old_iter,
+ git_iterator *new_iter,
+ git_diff_list **diff_ptr)
+{
+ const git_index_entry *oitem, *nitem;
+ git_buf ignore_prefix = GIT_BUF_INIT;
+ git_diff_list *diff = git_diff_list_alloc(repo, opts);
+ if (!diff)
+ goto fail;
+
+ diff->old_src = old_iter->type;
+ diff->new_src = new_iter->type;
+
+ if (git_iterator_current(old_iter, &oitem) < 0 ||
+ git_iterator_current(new_iter, &nitem) < 0)
+ goto fail;
+
+ /* run iterators building diffs */
+ while (oitem || nitem) {
+
+ /* create DELETED records for old items not matched in new */
+ if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0)
+ goto fail;
+ }
+
+ /* create ADDED, TRACKED, or IGNORED records for new items not
+ * matched in old (and/or descend into directories as needed)
+ */
+ else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
+ git_delta_t delta_type = GIT_DELTA_UNTRACKED;
+
+ /* check if contained in ignored parent directory */
+ if (git_buf_len(&ignore_prefix) &&
+ git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
+ delta_type = GIT_DELTA_IGNORED;
+
+ if (S_ISDIR(nitem->mode)) {
+ /* recurse into directory only if there are tracked items in
+ * it or if the user requested the contents of untracked
+ * directories and it is not under an ignored directory.
+ */
+ if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||
+ (delta_type == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
+ {
+ /* if this directory is ignored, remember it as the
+ * "ignore_prefix" for processing contained items
+ */
+ if (delta_type == GIT_DELTA_UNTRACKED &&
+ git_iterator_current_is_ignored(new_iter))
+ git_buf_sets(&ignore_prefix, nitem->path);
+
+ if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
+ goto fail;
+
+ continue;
+ }
+ }
+
+ /* In core git, the next two "else if" clauses are effectively
+ * reversed -- i.e. when an untracked file contained in an
+ * ignored directory is individually ignored, it shows up as an
+ * ignored file in the diff list, even though other untracked
+ * files in the same directory are skipped completely.
+ *
+ * To me, this is odd. If the directory is ignored and the file
+ * is untracked, we should skip it consistently, regardless of
+ * whether it happens to match a pattern in the ignore file.
+ *
+ * To match the core git behavior, just reverse the following
+ * two "else if" cases so that individual file ignores are
+ * checked before container directory exclusions are used to
+ * skip the file.
+ */
+ else if (delta_type == GIT_DELTA_IGNORED) {
+ if (git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ continue; /* ignored parent directory, so skip completely */
+ }
+
+ else if (git_iterator_current_is_ignored(new_iter))
+ delta_type = GIT_DELTA_IGNORED;
+
+ else if (new_iter->type != GIT_ITERATOR_WORKDIR)
+ delta_type = GIT_DELTA_ADDED;
+
+ if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ }
+
+ /* otherwise item paths match, so create MODIFIED record
+ * (or ADDED and DELETED pair if type changed)
+ */
+ else {
+ assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
+
+ if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ }
+ }
+
+ git_iterator_free(old_iter);
+ git_iterator_free(new_iter);
+ git_buf_free(&ignore_prefix);
+
+ *diff_ptr = diff;
+ return 0;
+
+fail:
+ git_iterator_free(old_iter);
+ git_iterator_free(new_iter);
+ git_buf_free(&ignore_prefix);
+
+ git_diff_list_free(diff);
+ *diff_ptr = NULL;
+ return -1;
+}
+
+
+int git_diff_tree_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_tree *new_tree,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && old_tree && new_tree && diff);
+
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_index_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_tree *old_tree,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && diff);
+
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_workdir_to_index(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && diff);
+
+ if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
+ git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+
+int git_diff_workdir_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_tree *old_tree,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && old_tree && diff);
+
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_merge(
+ git_diff_list *onto,
+ const git_diff_list *from)
+{
+ int error = 0;
+ git_pool onto_pool;
+ git_vector onto_new;
+ git_diff_delta *delta;
+ unsigned int i, j;
+
+ assert(onto && from);
+
+ if (!from->deltas.length)
+ return 0;
+
+ if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
+ git_pool_init(&onto_pool, 1, 0) < 0)
+ return -1;
+
+ for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
+ git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
+ const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
+ int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
+
+ if (cmp < 0) {
+ delta = diff_delta__dup(o, &onto_pool);
+ i++;
+ } else if (cmp > 0) {
+ delta = diff_delta__dup(f, &onto_pool);
+ j++;
+ } else {
+ delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
+ i++;
+ j++;
+ }
+
+ if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
+ break;
+ }
+
+ if (!error) {
+ git_vector_swap(&onto->deltas, &onto_new);
+ git_pool_swap(&onto->pool, &onto_pool);
+ onto->new_src = from->new_src;
+ }
+
+ git_vector_foreach(&onto_new, i, delta)
+ git__free(delta);
+ git_vector_free(&onto_new);
+ git_pool_clear(&onto_pool);
+
+ return error;
+}
+
diff --git a/samples/c/git.c b/samples/c/git.c
new file mode 100644
index 0000000..d232de9
--- /dev/null
+++ b/samples/c/git.c
@@ -0,0 +1,609 @@
+#include "builtin.h"
+#include "cache.h"
+#include "exec_cmd.h"
+#include "help.h"
+#include "quote.h"
+#include "run-command.h"
+
+const char git_usage_string[] =
+ "git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
+ " [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
+ " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+ " [-c name=value] [--help]\n"
+ " <command> [<args>]";
+
+const char git_more_info_string[] =
+ N_("See 'git help <command>' for more information on a specific command.");
+
+static struct startup_info git_startup_info;
+static int use_pager = -1;
+struct pager_config {
+ const char *cmd;
+ int want;
+ char *value;
+};
+
+static int pager_command_config(const char *var, const char *value, void *data)
+{
+ struct pager_config *c = data;
+ if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
+ int b = git_config_maybe_bool(var, value);
+ if (b >= 0)
+ c->want = b;
+ else {
+ c->want = 1;
+ c->value = xstrdup(value);
+ }
+ }
+ return 0;
+}
+
+/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
+int check_pager_config(const char *cmd)
+{
+ struct pager_config c;
+ c.cmd = cmd;
+ c.want = -1;
+ c.value = NULL;
+ git_config(pager_command_config, &c);
+ if (c.value)
+ pager_program = c.value;
+ return c.want;
+}
+
+static void commit_pager_choice(void) {
+ switch (use_pager) {
+ case 0:
+ setenv("GIT_PAGER", "cat", 1);
+ break;
+ case 1:
+ setup_pager();
+ break;
+ default:
+ break;
+ }
+}
+
+static int handle_options(const char ***argv, int *argc, int *envchanged)
+{
+ const char **orig_argv = *argv;
+
+ while (*argc > 0) {
+ const char *cmd = (*argv)[0];
+ if (cmd[0] != '-')
+ break;
+
+ /*
+ * For legacy reasons, the "version" and "help"
+ * commands can be written with "--" prepended
+ * to make them look like flags.
+ */
+ if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
+ break;
+
+ /*
+ * Check remaining flags.
+ */
+ if (!prefixcmp(cmd, "--exec-path")) {
+ cmd += 11;
+ if (*cmd == '=')
+ git_set_argv_exec_path(cmd + 1);
+ else {
+ puts(git_exec_path());
+ exit(0);
+ }
+ } else if (!strcmp(cmd, "--html-path")) {
+ puts(system_path(GIT_HTML_PATH));
+ exit(0);
+ } else if (!strcmp(cmd, "--man-path")) {
+ puts(system_path(GIT_MAN_PATH));
+ exit(0);
+ } else if (!strcmp(cmd, "--info-path")) {
+ puts(system_path(GIT_INFO_PATH));
+ exit(0);
+ } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+ use_pager = 1;
+ } else if (!strcmp(cmd, "--no-pager")) {
+ use_pager = 0;
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--no-replace-objects")) {
+ read_replace_refs = 0;
+ setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--git-dir")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --git-dir.\n" );
+ usage(git_usage_string);
+ }
+ setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, "--git-dir=")) {
+ setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--namespace")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No namespace given for --namespace.\n" );
+ usage(git_usage_string);
+ }
+ setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, "--namespace=")) {
+ setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--work-tree")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --work-tree.\n" );
+ usage(git_usage_string);
+ }
+ setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, "--work-tree=")) {
+ setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--bare")) {
+ static char git_dir[PATH_MAX+1];
+ is_bare_repository_cfg = 1;
+ setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "-c")) {
+ if (*argc < 2) {
+ fprintf(stderr, "-c expects a configuration string\n" );
+ usage(git_usage_string);
+ }
+ git_config_push_parameter((*argv)[1]);
+ (*argv)++;
+ (*argc)--;
+ } else {
+ fprintf(stderr, "Unknown option: %s\n", cmd);
+ usage(git_usage_string);
+ }
+
+ (*argv)++;
+ (*argc)--;
+ }
+ return (*argv) - orig_argv;
+}
+
+static int handle_alias(int *argcp, const char ***argv)
+{
+ int envchanged = 0, ret = 0, saved_errno = errno;
+ const char *subdir;
+ int count, option_count;
+ const char **new_argv;
+ const char *alias_command;
+ char *alias_string;
+ int unused_nongit;
+
+ subdir = setup_git_directory_gently(&unused_nongit);
+
+ alias_command = (*argv)[0];
+ alias_string = alias_lookup(alias_command);
+ if (alias_string) {
+ if (alias_string[0] == '!') {
+ const char **alias_argv;
+ int argc = *argcp, i;
+
+ commit_pager_choice();
+
+ /* build alias_argv */
+ alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
+ alias_argv[0] = alias_string + 1;
+ for (i = 1; i < argc; ++i)
+ alias_argv[i] = (*argv)[i];
+ alias_argv[argc] = NULL;
+
+ ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+ if (ret >= 0) /* normal exit */
+ exit(ret);
+
+ die_errno("While expanding alias '%s': '%s'",
+ alias_command, alias_string + 1);
+ }
+ count = split_cmdline(alias_string, &new_argv);
+ if (count < 0)
+ die("Bad alias.%s string: %s", alias_command,
+ split_cmdline_strerror(count));
+ option_count = handle_options(&new_argv, &count, &envchanged);
+ if (envchanged)
+ die("alias '%s' changes environment variables\n"
+ "You can use '!git' in the alias to do this.",
+ alias_command);
+ memmove(new_argv - option_count, new_argv,
+ count * sizeof(char *));
+ new_argv -= option_count;
+
+ if (count < 1)
+ die("empty alias for %s", alias_command);
+
+ if (!strcmp(alias_command, new_argv[0]))
+ die("recursive alias: %s", alias_command);
+
+ trace_argv_printf(new_argv,
+ "trace: alias expansion: %s =>",
+ alias_command);
+
+ new_argv = xrealloc(new_argv, sizeof(char *) *
+ (count + *argcp));
+ /* insert after command name */
+ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
+
+ *argv = new_argv;
+ *argcp += count - 1;
+
+ ret = 1;
+ }
+
+ if (subdir && chdir(subdir))
+ die_errno("Cannot change to '%s'", subdir);
+
+ errno = saved_errno;
+
+ return ret;
+}
+
+const char git_version_string[] = GIT_VERSION;
+
+#define RUN_SETUP (1<<0)
+#define RUN_SETUP_GENTLY (1<<1)
+#define USE_PAGER (1<<2)
+/*
+ * require working tree to be present -- anything uses this needs
+ * RUN_SETUP for reading from the configuration file.
+ */
+#define NEED_WORK_TREE (1<<3)
+
+struct cmd_struct {
+ const char *cmd;
+ int (*fn)(int, const char **, const char *);
+ int option;
+};
+
+static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
+{
+ int status, help;
+ struct stat st;
+ const char *prefix;
+
+ prefix = NULL;
+ help = argc == 2 && !strcmp(argv[1], "-h");
+ if (!help) {
+ if (p->option & RUN_SETUP)
+ prefix = setup_git_directory();
+ if (p->option & RUN_SETUP_GENTLY) {
+ int nongit_ok;
+ prefix = setup_git_directory_gently(&nongit_ok);
+ }
+
+ if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
+ use_pager = check_pager_config(p->cmd);
+ if (use_pager == -1 && p->option & USE_PAGER)
+ use_pager = 1;
+
+ if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
+ startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
+ trace_repo_setup(prefix);
+ }
+ commit_pager_choice();
+
+ if (!help && p->option & NEED_WORK_TREE)
+ setup_work_tree();
+
+ trace_argv_printf(argv, "trace: built-in: git");
+
+ status = p->fn(argc, argv, prefix);
+ if (status)
+ return status;
+
+ /* Somebody closed stdout? */
+ if (fstat(fileno(stdout), &st))
+ return 0;
+ /* Ignore write errors for pipes and sockets.. */
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
+ return 0;
+
+ /* Check for ENOSPC and EIO errors.. */
+ if (fflush(stdout))
+ die_errno("write failure on standard output");
+ if (ferror(stdout))
+ die("unknown write failure on standard output");
+ if (fclose(stdout))
+ die_errno("close failed on standard output");
+ return 0;
+}
+
+static void handle_internal_command(int argc, const char **argv)
+{
+ const char *cmd = argv[0];
+ static struct cmd_struct commands[] = {
+ { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+ { "annotate", cmd_annotate, RUN_SETUP },
+ { "apply", cmd_apply, RUN_SETUP_GENTLY },
+ { "archive", cmd_archive },
+ { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
+ { "blame", cmd_blame, RUN_SETUP },
+ { "branch", cmd_branch, RUN_SETUP },
+ { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
+ { "cat-file", cmd_cat_file, RUN_SETUP },
+ { "check-attr", cmd_check_attr, RUN_SETUP },
+ { "check-ref-format", cmd_check_ref_format },
+ { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
+ { "checkout-index", cmd_checkout_index,
+ RUN_SETUP | NEED_WORK_TREE},
+ { "cherry", cmd_cherry, RUN_SETUP },
+ { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+ { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
+ { "clone", cmd_clone },
+ { "column", cmd_column, RUN_SETUP_GENTLY },
+ { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
+ { "commit-tree", cmd_commit_tree, RUN_SETUP },
+ { "config", cmd_config, RUN_SETUP_GENTLY },
+ { "count-objects", cmd_count_objects, RUN_SETUP },
+ { "describe", cmd_describe, RUN_SETUP },
+ { "diff", cmd_diff },
+ { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
+ { "diff-index", cmd_diff_index, RUN_SETUP },
+ { "diff-tree", cmd_diff_tree, RUN_SETUP },
+ { "fast-export", cmd_fast_export, RUN_SETUP },
+ { "fetch", cmd_fetch, RUN_SETUP },
+ { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
+ { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
+ { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
+ { "format-patch", cmd_format_patch, RUN_SETUP },
+ { "fsck", cmd_fsck, RUN_SETUP },
+ { "fsck-objects", cmd_fsck, RUN_SETUP },
+ { "gc", cmd_gc, RUN_SETUP },
+ { "get-tar-commit-id", cmd_get_tar_commit_id },
+ { "grep", cmd_grep, RUN_SETUP_GENTLY },
+ { "hash-object", cmd_hash_object },
+ { "help", cmd_help },
+ { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
+ { "init", cmd_init_db },
+ { "init-db", cmd_init_db },
+ { "log", cmd_log, RUN_SETUP },
+ { "ls-files", cmd_ls_files, RUN_SETUP },
+ { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
+ { "ls-tree", cmd_ls_tree, RUN_SETUP },
+ { "mailinfo", cmd_mailinfo },
+ { "mailsplit", cmd_mailsplit },
+ { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-base", cmd_merge_base, RUN_SETUP },
+ { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
+ { "merge-index", cmd_merge_index, RUN_SETUP },
+ { "merge-ours", cmd_merge_ours, RUN_SETUP },
+ { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+ { "merge-tree", cmd_merge_tree, RUN_SETUP },
+ { "mktag", cmd_mktag, RUN_SETUP },
+ { "mktree", cmd_mktree, RUN_SETUP },
+ { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
+ { "name-rev", cmd_name_rev, RUN_SETUP },
+ { "notes", cmd_notes, RUN_SETUP },
+ { "pack-objects", cmd_pack_objects, RUN_SETUP },
+ { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
+ { "pack-refs", cmd_pack_refs, RUN_SETUP },
+ { "patch-id", cmd_patch_id },
+ { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
+ { "pickaxe", cmd_blame, RUN_SETUP },
+ { "prune", cmd_prune, RUN_SETUP },
+ { "prune-packed", cmd_prune_packed, RUN_SETUP },
+ { "push", cmd_push, RUN_SETUP },
+ { "read-tree", cmd_read_tree, RUN_SETUP },
+ { "receive-pack", cmd_receive_pack },
+ { "reflog", cmd_reflog, RUN_SETUP },
+ { "remote", cmd_remote, RUN_SETUP },
+ { "remote-ext", cmd_remote_ext },
+ { "remote-fd", cmd_remote_fd },
+ { "replace", cmd_replace, RUN_SETUP },
+ { "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
+ { "rerere", cmd_rerere, RUN_SETUP },
+ { "reset", cmd_reset, RUN_SETUP },
+ { "rev-list", cmd_rev_list, RUN_SETUP },
+ { "rev-parse", cmd_rev_parse },
+ { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
+ { "rm", cmd_rm, RUN_SETUP },
+ { "send-pack", cmd_send_pack, RUN_SETUP },
+ { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
+ { "show", cmd_show, RUN_SETUP },
+ { "show-branch", cmd_show_branch, RUN_SETUP },
+ { "show-ref", cmd_show_ref, RUN_SETUP },
+ { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+ { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
+ { "stripspace", cmd_stripspace },
+ { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
+ { "tag", cmd_tag, RUN_SETUP },
+ { "tar-tree", cmd_tar_tree },
+ { "unpack-file", cmd_unpack_file, RUN_SETUP },
+ { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
+ { "update-index", cmd_update_index, RUN_SETUP },
+ { "update-ref", cmd_update_ref, RUN_SETUP },
+ { "update-server-info", cmd_update_server_info, RUN_SETUP },
+ { "upload-archive", cmd_upload_archive },
+ { "upload-archive--writer", cmd_upload_archive_writer },
+ { "var", cmd_var, RUN_SETUP_GENTLY },
+ { "verify-pack", cmd_verify_pack },
+ { "verify-tag", cmd_verify_tag, RUN_SETUP },
+ { "version", cmd_version },
+ { "whatchanged", cmd_whatchanged, RUN_SETUP },
+ { "write-tree", cmd_write_tree, RUN_SETUP },
+ };
+ int i;
+ static const char ext[] = STRIP_EXTENSION;
+
+ if (sizeof(ext) > 1) {
+ i = strlen(argv[0]) - strlen(ext);
+ if (i > 0 && !strcmp(argv[0] + i, ext)) {
+ char *argv0 = xstrdup(argv[0]);
+ argv[0] = cmd = argv0;
+ argv0[i] = '\0';
+ }
+ }
+
+ /* Turn "git cmd --help" into "git help cmd" */
+ if (argc > 1 && !strcmp(argv[1], "--help")) {
+ argv[1] = argv[0];
+ argv[0] = cmd = "help";
+ }
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ struct cmd_struct *p = commands+i;
+ if (strcmp(p->cmd, cmd))
+ continue;
+ exit(run_builtin(p, argc, argv));
+ }
+}
+
+static void execv_dashed_external(const char **argv)
+{
+ struct strbuf cmd = STRBUF_INIT;
+ const char *tmp;
+ int status;
+
+ if (use_pager == -1)
+ use_pager = check_pager_config(argv[0]);
+ commit_pager_choice();
+
+ strbuf_addf(&cmd, "git-%s", argv[0]);
+
+ /*
+ * argv[0] must be the git command, but the argv array
+ * belongs to the caller, and may be reused in
+ * subsequent loop iterations. Save argv[0] and
+ * restore it on error.
+ */
+ tmp = argv[0];
+ argv[0] = cmd.buf;
+
+ trace_argv_printf(argv, "trace: exec:");
+
+ /*
+ * if we fail because the command is not found, it is
+ * OK to return. Otherwise, we just pass along the status code.
+ */
+ status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
+ if (status >= 0 || errno != ENOENT)
+ exit(status);
+
+ argv[0] = tmp;
+
+ strbuf_release(&cmd);
+}
+
+static int run_argv(int *argcp, const char ***argv)
+{
+ int done_alias = 0;
+
+ while (1) {
+ /* See if it's an internal command */
+ handle_internal_command(*argcp, *argv);
+
+ /* .. then try the external ones */
+ execv_dashed_external(*argv);
+
+ /* It could be an alias -- this works around the insanity
+ * of overriding "git log" with "git show" by having
+ * alias.log = show
+ */
+ if (done_alias || !handle_alias(argcp, argv))
+ break;
+ done_alias = 1;
+ }
+
+ return done_alias;
+}
+
+
+int main(int argc, const char **argv)
+{
+ const char *cmd;
+
+ startup_info = &git_startup_info;
+
+ cmd = git_extract_argv0_path(argv[0]);
+ if (!cmd)
+ cmd = "git-help";
+
+ git_setup_gettext();
+
+ /*
+ * "git-xxxx" is the same as "git xxxx", but we obviously:
+ *
+ * - cannot take flags in between the "git" and the "xxxx".
+ * - cannot execute it externally (since it would just do
+ * the same thing over again)
+ *
+ * So we just directly call the internal command handler, and
+ * die if that one cannot handle it.
+ */
+ if (!prefixcmp(cmd, "git-")) {
+ cmd += 4;
+ argv[0] = cmd;
+ handle_internal_command(argc, argv);
+ die("cannot handle %s internally", cmd);
+ }
+
+ /* Look for flags.. */
+ argv++;
+ argc--;
+ handle_options(&argv, &argc, NULL);
+ if (argc > 0) {
+ if (!prefixcmp(argv[0], "--"))
+ argv[0] += 2;
+ } else {
+ /* The user didn't specify a command; give them help */
+ commit_pager_choice();
+ printf("usage: %s\n\n", git_usage_string);
+ list_common_cmds_help();
+ printf("\n%s\n", git_more_info_string);
+ exit(1);
+ }
+ cmd = argv[0];
+
+ /*
+ * We use PATH to find git commands, but we prepend some higher
+ * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
+ * environment, and the $(gitexecdir) from the Makefile at build
+ * time.
+ */
+ setup_path();
+
+ while (1) {
+ static int done_help = 0;
+ static int was_alias = 0;
+ was_alias = run_argv(&argc, &argv);
+ if (errno != ENOENT)
+ break;
+ if (was_alias) {
+ fprintf(stderr, "Expansion of alias '%s' failed; "
+ "'%s' is not a git command\n",
+ cmd, argv[0]);
+ exit(1);
+ }
+ if (!done_help) {
+ cmd = argv[0] = help_unknown_cmd(cmd);
+ done_help = 1;
+ } else
+ break;
+ }
+
+ fprintf(stderr, "Failed to run command '%s': %s\n",
+ cmd, strerror(errno));
+
+ return 1;
+}
diff --git a/samples/c/hash.c b/samples/c/hash.c
new file mode 100644
index 0000000..4607569
--- /dev/null
+++ b/samples/c/hash.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "hash.h"
+
+#if defined(PPC_SHA1)
+# include "ppc/sha1.h"
+#else
+# include "sha1.h"
+#endif
+
+struct git_hash_ctx {
+ SHA_CTX c;
+};
+
+git_hash_ctx *git_hash_new_ctx(void)
+{
+ git_hash_ctx *ctx = git__malloc(sizeof(*ctx));
+
+ if (!ctx)
+ return NULL;
+
+ SHA1_Init(&ctx->c);
+
+ return ctx;
+}
+
+void git_hash_free_ctx(git_hash_ctx *ctx)
+{
+ git__free(ctx);
+}
+
+void git_hash_init(git_hash_ctx *ctx)
+{
+ assert(ctx);
+ SHA1_Init(&ctx->c);
+}
+
+void git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+ assert(ctx);
+ SHA1_Update(&ctx->c, data, len);
+}
+
+void git_hash_final(git_oid *out, git_hash_ctx *ctx)
+{
+ assert(ctx);
+ SHA1_Final(out->id, &ctx->c);
+}
+
+void git_hash_buf(git_oid *out, const void *data, size_t len)
+{
+ SHA_CTX c;
+
+ SHA1_Init(&c);
+ SHA1_Update(&c, data, len);
+ SHA1_Final(out->id, &c);
+}
+
+void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
+{
+ SHA_CTX c;
+ size_t i;
+
+ SHA1_Init(&c);
+ for (i = 0; i < n; i++)
+ SHA1_Update(&c, vec[i].data, vec[i].len);
+ SHA1_Final(out->id, &c);
+}
diff --git a/samples/c/hello.c b/samples/c/hello.c
new file mode 100644
index 0000000..c3ce445
--- /dev/null
+++ b/samples/c/hello.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main()
+{
+ printf("Hello World\n");
+ return 0;
+}
diff --git a/samples/c/hello.h b/samples/c/hello.h
new file mode 100644
index 0000000..f19e5d3
--- /dev/null
+++ b/samples/c/hello.h
@@ -0,0 +1,6 @@
+#ifndef HELLO_H
+#define HELLO_H
+
+void hello();
+
+#endif
diff --git a/samples/c/http_parser.c b/samples/c/http_parser.c
new file mode 100644
index 0000000..0c11eb8
--- /dev/null
+++ b/samples/c/http_parser.c
@@ -0,0 +1,2059 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * 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.
+ */
+#include "http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+
+#if HTTP_PARSER_DEBUG
+#define SET_ERRNO(e) \
+do { \
+ parser->http_errno = (e); \
+ parser->error_lineno = __LINE__; \
+} while (0)
+#else
+#define SET_ERRNO(e) \
+do { \
+ parser->http_errno = (e); \
+} while(0)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (settings->on_##FOR) { \
+ if (0 != settings->on_##FOR(parser)) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
+ return (ER); \
+ } \
+ } \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (FOR##_mark) { \
+ if (settings->on_##FOR) { \
+ if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
+ return (ER); \
+ } \
+ } \
+ FOR##_mark = NULL; \
+ } \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR) \
+do { \
+ if (!FOR##_mark) { \
+ FOR##_mark = p; \
+ } \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+ {
+#define XX(num, name, string) #string,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0, '!', 0, '#', '$', '%', '&', '\'',
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 0, 0, '*', '+', 0, '-', '.', 0,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ '0', '1', '2', '3', '4', '5', '6', '7',
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ '8', '9', 0, 0, 0, 0, 0, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 'x', 'y', 'z', 0, 0, 0, '^', '_',
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 'x', 'y', 'z', 0, '|', 0, '~', 0 };
+
+
+static const int8_t unhex[256] =
+ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ };
+
+
+#if HTTP_PARSER_STRICT
+# define T 0
+#else
+# define T 1
+#endif
+
+
+static const uint8_t normal_url_char[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, T, 0, 0, T, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0, 1, 1, 0, 1, 1, 1, 1,
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ 1, 1, 1, 1, 1, 1, 1, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 1, 1, 1, 1, 1, 1, 1, 0, };
+
+#undef T
+
+enum state
+ { s_dead = 1 /* important that this is > 0 */
+
+ , s_start_req_or_res
+ , s_res_or_resp_H
+ , s_start_res
+ , s_res_H
+ , s_res_HT
+ , s_res_HTT
+ , s_res_HTTP
+ , s_res_first_http_major
+ , s_res_http_major
+ , s_res_first_http_minor
+ , s_res_http_minor
+ , s_res_first_status_code
+ , s_res_status_code
+ , s_res_status
+ , s_res_line_almost_done
+
+ , s_start_req
+
+ , s_req_method
+ , s_req_spaces_before_url
+ , s_req_schema
+ , s_req_schema_slash
+ , s_req_schema_slash_slash
+ , s_req_host_start
+ , s_req_host_v6_start
+ , s_req_host_v6
+ , s_req_host_v6_end
+ , s_req_host
+ , s_req_port_start
+ , s_req_port
+ , s_req_path
+ , s_req_query_string_start
+ , s_req_query_string
+ , s_req_fragment_start
+ , s_req_fragment
+ , s_req_http_start
+ , s_req_http_H
+ , s_req_http_HT
+ , s_req_http_HTT
+ , s_req_http_HTTP
+ , s_req_first_http_major
+ , s_req_http_major
+ , s_req_first_http_minor
+ , s_req_http_minor
+ , s_req_line_almost_done
+
+ , s_header_field_start
+ , s_header_field
+ , s_header_value_start
+ , s_header_value
+ , s_header_value_lws
+
+ , s_header_almost_done
+
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_parameters
+ , s_chunk_size_almost_done
+
+ , s_headers_almost_done
+ , s_headers_done
+
+ /* Important: 's_headers_done' must be the last 'header' state. All
+ * states beyond this must be 'body' states. It is used for overflow
+ * checking. See the PARSING_HEADER() macro.
+ */
+
+ , s_chunk_data
+ , s_chunk_data_almost_done
+ , s_chunk_data_done
+
+ , s_body_identity
+ , s_body_identity_eof
+
+ , s_message_done
+ };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+ { h_general = 0
+ , h_C
+ , h_CO
+ , h_CON
+
+ , h_matching_connection
+ , h_matching_proxy_connection
+ , h_matching_content_length
+ , h_matching_transfer_encoding
+ , h_matching_upgrade
+
+ , h_connection
+ , h_content_length
+ , h_transfer_encoding
+ , h_upgrade
+
+ , h_matching_transfer_encoding_chunked
+ , h_matching_connection_keep_alive
+ , h_matching_connection_close
+
+ , h_transfer_encoding_chunked
+ , h_connection_keep_alive
+ , h_connection_close
+ };
+
+
+/* Macros for character classes; depends on strict-mode */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c) (tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)])
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) \
+ (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
+#define IS_HOST_CHAR(c) \
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond) \
+do { \
+ if (cond) { \
+ SET_ERRNO(HPE_STRICT); \
+ goto error; \
+ } \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+ const char *name;
+ const char *description;
+} http_strerror_tab[] = {
+ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
+ return s_dead;
+ }
+
+#if HTTP_PARSER_STRICT
+ if (ch == '\t' || ch == '\f') {
+ return s_dead;
+ }
+#endif
+
+ switch (s) {
+ case s_req_spaces_before_url:
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
+ * All methods except CONNECT are followed by '/' or '*'.
+ */
+
+ if (ch == '/' || ch == '*') {
+ return s_req_path;
+ }
+
+ if (IS_ALPHA(ch)) {
+ return s_req_schema;
+ }
+
+ break;
+
+ case s_req_schema:
+ if (IS_ALPHA(ch)) {
+ return s;
+ }
+
+ if (ch == ':') {
+ return s_req_schema_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash:
+ if (ch == '/') {
+ return s_req_schema_slash_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash_slash:
+ if (ch == '/') {
+ return s_req_host_start;
+ }
+
+ break;
+
+ case s_req_host_start:
+ if (ch == '[') {
+ return s_req_host_v6_start;
+ }
+
+ if (IS_HOST_CHAR(ch)) {
+ return s_req_host;
+ }
+
+ break;
+
+ case s_req_host:
+ if (IS_HOST_CHAR(ch)) {
+ return s_req_host;
+ }
+
+ /* FALLTHROUGH */
+ case s_req_host_v6_end:
+ switch (ch) {
+ case ':':
+ return s_req_port_start;
+
+ case '/':
+ return s_req_path;
+
+ case '?':
+ return s_req_query_string_start;
+ }
+
+ break;
+
+ case s_req_host_v6:
+ if (ch == ']') {
+ return s_req_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_req_host_v6_start:
+ if (IS_HEX(ch) || ch == ':') {
+ return s_req_host_v6;
+ }
+ break;
+
+ case s_req_port:
+ switch (ch) {
+ case '/':
+ return s_req_path;
+
+ case '?':
+ return s_req_query_string_start;
+ }
+
+ /* FALLTHROUGH */
+ case s_req_port_start:
+ if (IS_NUM(ch)) {
+ return s_req_port;
+ }
+
+ break;
+
+ case s_req_path:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_query_string_start;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_query_string_start:
+ case s_req_query_string:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_query_string;
+ }
+
+ switch (ch) {
+ case '?':
+ /* allow extra '?' in query string */
+ return s_req_query_string;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_fragment_start:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_fragment;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_fragment;
+
+ case '#':
+ return s;
+ }
+
+ break;
+
+ case s_req_fragment:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ case '#':
+ return s;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* We should never fall out of the switch above unless there's an error */
+ return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len)
+{
+ char c, ch;
+ int8_t unhex_val;
+ const char *p = data;
+ const char *header_field_mark = 0;
+ const char *header_value_mark = 0;
+ const char *url_mark = 0;
+ const char *body_mark = 0;
+
+ /* We're in an error state. Don't bother doing anything. */
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ return 0;
+ }
+
+ if (len == 0) {
+ switch (parser->state) {
+ case s_body_identity_eof:
+ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+ * we got paused.
+ */
+ CALLBACK_NOTIFY_NOADVANCE(message_complete);
+ return 0;
+
+ case s_dead:
+ case s_start_req_or_res:
+ case s_start_res:
+ case s_start_req:
+ return 0;
+
+ default:
+ SET_ERRNO(HPE_INVALID_EOF_STATE);
+ return 1;
+ }
+ }
+
+
+ if (parser->state == s_header_field)
+ header_field_mark = data;
+ if (parser->state == s_header_value)
+ header_value_mark = data;
+ switch (parser->state) {
+ case s_req_path:
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_host_start:
+ case s_req_host_v6_start:
+ case s_req_host_v6:
+ case s_req_host_v6_end:
+ case s_req_host:
+ case s_req_port_start:
+ case s_req_port:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ url_mark = data;
+ break;
+ }
+
+ for (p=data; p != data + len; p++) {
+ ch = *p;
+
+ if (PARSING_HEADER(parser->state)) {
+ ++parser->nread;
+ /* Buffer overflow attack */
+ if (parser->nread > HTTP_MAX_HEADER_SIZE) {
+ SET_ERRNO(HPE_HEADER_OVERFLOW);
+ goto error;
+ }
+ }
+
+ reexecute_byte:
+ switch (parser->state) {
+
+ case s_dead:
+ /* this state is used after a 'Connection: close' message
+ * the parser will error out if it reads another message
+ */
+ if (ch == CR || ch == LF)
+ break;
+
+ SET_ERRNO(HPE_CLOSED_CONNECTION);
+ goto error;
+
+ case s_start_req_or_res:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (ch == 'H') {
+ parser->state = s_res_or_resp_H;
+
+ CALLBACK_NOTIFY(message_begin);
+ } else {
+ parser->type = HTTP_REQUEST;
+ parser->state = s_start_req;
+ goto reexecute_byte;
+ }
+
+ break;
+ }
+
+ case s_res_or_resp_H:
+ if (ch == 'T') {
+ parser->type = HTTP_RESPONSE;
+ parser->state = s_res_HT;
+ } else {
+ if (ch != 'E') {
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ parser->type = HTTP_REQUEST;
+ parser->method = HTTP_HEAD;
+ parser->index = 2;
+ parser->state = s_req_method;
+ }
+ break;
+
+ case s_start_res:
+ {
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ switch (ch) {
+ case 'H':
+ parser->state = s_res_H;
+ break;
+
+ case CR:
+ case LF:
+ break;
+
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ CALLBACK_NOTIFY(message_begin);
+ break;
+ }
+
+ case s_res_H:
+ STRICT_CHECK(ch != 'T');
+ parser->state = s_res_HT;
+ break;
+
+ case s_res_HT:
+ STRICT_CHECK(ch != 'T');
+ parser->state = s_res_HTT;
+ break;
+
+ case s_res_HTT:
+ STRICT_CHECK(ch != 'P');
+ parser->state = s_res_HTTP;
+ break;
+
+ case s_res_HTTP:
+ STRICT_CHECK(ch != '/');
+ parser->state = s_res_first_http_major;
+ break;
+
+ case s_res_first_http_major:
+ if (ch < '0' || ch > '9') {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ parser->state = s_res_http_major;
+ break;
+
+ /* major HTTP version or dot */
+ case s_res_http_major:
+ {
+ if (ch == '.') {
+ parser->state = s_res_first_http_minor;
+ break;
+ }
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (parser->http_major > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_res_first_http_minor:
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ parser->state = s_res_http_minor;
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_res_http_minor:
+ {
+ if (ch == ' ') {
+ parser->state = s_res_first_status_code;
+ break;
+ }
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (parser->http_minor > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_first_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ if (ch == ' ') {
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ parser->status_code = ch - '0';
+ parser->state = s_res_status_code;
+ break;
+ }
+
+ case s_res_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ switch (ch) {
+ case ' ':
+ parser->state = s_res_status;
+ break;
+ case CR:
+ parser->state = s_res_line_almost_done;
+ break;
+ case LF:
+ parser->state = s_header_field_start;
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ break;
+ }
+
+ parser->status_code *= 10;
+ parser->status_code += ch - '0';
+
+ if (parser->status_code > 999) {
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_status:
+ /* the human readable status. e.g. "NOT FOUND"
+ * we are not humans so just ignore this */
+ if (ch == CR) {
+ parser->state = s_res_line_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ parser->state = s_header_field_start;
+ break;
+ }
+ break;
+
+ case s_res_line_almost_done:
+ STRICT_CHECK(ch != LF);
+ parser->state = s_header_field_start;
+ break;
+
+ case s_start_req:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (!IS_ALPHA(ch)) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ parser->method = (enum http_method) 0;
+ parser->index = 1;
+ switch (ch) {
+ case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+ case 'D': parser->method = HTTP_DELETE; break;
+ case 'G': parser->method = HTTP_GET; break;
+ case 'H': parser->method = HTTP_HEAD; break;
+ case 'L': parser->method = HTTP_LOCK; break;
+ case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
+ case 'N': parser->method = HTTP_NOTIFY; break;
+ case 'O': parser->method = HTTP_OPTIONS; break;
+ case 'P': parser->method = HTTP_POST;
+ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+ break;
+ case 'R': parser->method = HTTP_REPORT; break;
+ case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
+ case 'T': parser->method = HTTP_TRACE; break;
+ case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ parser->state = s_req_method;
+
+ CALLBACK_NOTIFY(message_begin);
+
+ break;
+ }
+
+ case s_req_method:
+ {
+ const char *matcher;
+ if (ch == '\0') {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ matcher = method_strings[parser->method];
+ if (ch == ' ' && matcher[parser->index] == '\0') {
+ parser->state = s_req_spaces_before_url;
+ } else if (ch == matcher[parser->index]) {
+ ; /* nada */
+ } else if (parser->method == HTTP_CONNECT) {
+ if (parser->index == 1 && ch == 'H') {
+ parser->method = HTTP_CHECKOUT;
+ } else if (parser->index == 2 && ch == 'P') {
+ parser->method = HTTP_COPY;
+ } else {
+ goto error;
+ }
+ } else if (parser->method == HTTP_MKCOL) {
+ if (parser->index == 1 && ch == 'O') {
+ parser->method = HTTP_MOVE;
+ } else if (parser->index == 1 && ch == 'E') {
+ parser->method = HTTP_MERGE;
+ } else if (parser->index == 1 && ch == '-') {
+ parser->method = HTTP_MSEARCH;
+ } else if (parser->index == 2 && ch == 'A') {
+ parser->method = HTTP_MKACTIVITY;
+ } else {
+ goto error;
+ }
+ } else if (parser->method == HTTP_SUBSCRIBE) {
+ if (parser->index == 1 && ch == 'E') {
+ parser->method = HTTP_SEARCH;
+ } else {
+ goto error;
+ }
+ } else if (parser->index == 1 && parser->method == HTTP_POST) {
+ if (ch == 'R') {
+ parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
+ } else if (ch == 'U') {
+ parser->method = HTTP_PUT; /* or HTTP_PURGE */
+ } else if (ch == 'A') {
+ parser->method = HTTP_PATCH;
+ } else {
+ goto error;
+ }
+ } else if (parser->index == 2) {
+ if (parser->method == HTTP_PUT) {
+ if (ch == 'R') parser->method = HTTP_PURGE;
+ } else if (parser->method == HTTP_UNLOCK) {
+ if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE;
+ }
+ } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
+ parser->method = HTTP_PROPPATCH;
+ } else {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ ++parser->index;
+ break;
+ }
+
+ case s_req_spaces_before_url:
+ {
+ if (ch == ' ') break;
+
+ MARK(url);
+ if (parser->method == HTTP_CONNECT) {
+ parser->state = s_req_host_start;
+ }
+
+ parser->state = parse_url_char((enum state)parser->state, ch);
+ if (parser->state == s_dead) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_host_start:
+ case s_req_host_v6_start:
+ case s_req_host_v6:
+ case s_req_port_start:
+ {
+ switch (ch) {
+ /* No whitespace allowed here */
+ case ' ':
+ case CR:
+ case LF:
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ default:
+ parser->state = parse_url_char((enum state)parser->state, ch);
+ if (parser->state == s_dead) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+
+ break;
+ }
+
+ case s_req_host:
+ case s_req_host_v6_end:
+ case s_req_port:
+ case s_req_path:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ {
+ switch (ch) {
+ case ' ':
+ parser->state = s_req_http_start;
+ CALLBACK_DATA(url);
+ break;
+ case CR:
+ case LF:
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ parser->state = (ch == CR) ?
+ s_req_line_almost_done :
+ s_header_field_start;
+ CALLBACK_DATA(url);
+ break;
+ default:
+ parser->state = parse_url_char((enum state)parser->state, ch);
+ if (parser->state == s_dead) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+ break;
+ }
+
+ case s_req_http_start:
+ switch (ch) {
+ case 'H':
+ parser->state = s_req_http_H;
+ break;
+ case ' ':
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+ break;
+
+ case s_req_http_H:
+ STRICT_CHECK(ch != 'T');
+ parser->state = s_req_http_HT;
+ break;
+
+ case s_req_http_HT:
+ STRICT_CHECK(ch != 'T');
+ parser->state = s_req_http_HTT;
+ break;
+
+ case s_req_http_HTT:
+ STRICT_CHECK(ch != 'P');
+ parser->state = s_req_http_HTTP;
+ break;
+
+ case s_req_http_HTTP:
+ STRICT_CHECK(ch != '/');
+ parser->state = s_req_first_http_major;
+ break;
+
+ /* first digit of major HTTP version */
+ case s_req_first_http_major:
+ if (ch < '1' || ch > '9') {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ parser->state = s_req_http_major;
+ break;
+
+ /* major HTTP version or dot */
+ case s_req_http_major:
+ {
+ if (ch == '.') {
+ parser->state = s_req_first_http_minor;
+ break;
+ }
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (parser->http_major > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_req_first_http_minor:
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ parser->state = s_req_http_minor;
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_req_http_minor:
+ {
+ if (ch == CR) {
+ parser->state = s_req_line_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ parser->state = s_header_field_start;
+ break;
+ }
+
+ /* XXX allow spaces after digit? */
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (parser->http_minor > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* end of request line */
+ case s_req_line_almost_done:
+ {
+ if (ch != LF) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ parser->state = s_header_field_start;
+ break;
+ }
+
+ case s_header_field_start:
+ {
+ if (ch == CR) {
+ parser->state = s_headers_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ /* they might be just sending \n instead of \r\n so this would be
+ * the second \n to denote the end of headers*/
+ parser->state = s_headers_almost_done;
+ goto reexecute_byte;
+ }
+
+ c = TOKEN(ch);
+
+ if (!c) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ MARK(header_field);
+
+ parser->index = 0;
+ parser->state = s_header_field;
+
+ switch (c) {
+ case 'c':
+ parser->header_state = h_C;
+ break;
+
+ case 'p':
+ parser->header_state = h_matching_proxy_connection;
+ break;
+
+ case 't':
+ parser->header_state = h_matching_transfer_encoding;
+ break;
+
+ case 'u':
+ parser->header_state = h_matching_upgrade;
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_field:
+ {
+ c = TOKEN(ch);
+
+ if (c) {
+ switch (parser->header_state) {
+ case h_general:
+ break;
+
+ case h_C:
+ parser->index++;
+ parser->header_state = (c == 'o' ? h_CO : h_general);
+ break;
+
+ case h_CO:
+ parser->index++;
+ parser->header_state = (c == 'n' ? h_CON : h_general);
+ break;
+
+ case h_CON:
+ parser->index++;
+ switch (c) {
+ case 'n':
+ parser->header_state = h_matching_connection;
+ break;
+ case 't':
+ parser->header_state = h_matching_content_length;
+ break;
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+
+ /* connection */
+
+ case h_matching_connection:
+ parser->index++;
+ if (parser->index > sizeof(CONNECTION)-1
+ || c != CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* proxy-connection */
+
+ case h_matching_proxy_connection:
+ parser->index++;
+ if (parser->index > sizeof(PROXY_CONNECTION)-1
+ || c != PROXY_CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* content-length */
+
+ case h_matching_content_length:
+ parser->index++;
+ if (parser->index > sizeof(CONTENT_LENGTH)-1
+ || c != CONTENT_LENGTH[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+ parser->header_state = h_content_length;
+ }
+ break;
+
+ /* transfer-encoding */
+
+ case h_matching_transfer_encoding:
+ parser->index++;
+ if (parser->index > sizeof(TRANSFER_ENCODING)-1
+ || c != TRANSFER_ENCODING[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+ parser->header_state = h_transfer_encoding;
+ }
+ break;
+
+ /* upgrade */
+
+ case h_matching_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE)-1
+ || c != UPGRADE[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ parser->header_state = h_upgrade;
+ }
+ break;
+
+ case h_connection:
+ case h_content_length:
+ case h_transfer_encoding:
+ case h_upgrade:
+ if (ch != ' ') parser->header_state = h_general;
+ break;
+
+ default:
+ assert(0 && "Unknown header_state");
+ break;
+ }
+ break;
+ }
+
+ if (ch == ':') {
+ parser->state = s_header_value_start;
+ CALLBACK_DATA(header_field);
+ break;
+ }
+
+ if (ch == CR) {
+ parser->state = s_header_almost_done;
+ CALLBACK_DATA(header_field);
+ break;
+ }
+
+ if (ch == LF) {
+ parser->state = s_header_field_start;
+ CALLBACK_DATA(header_field);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ case s_header_value_start:
+ {
+ if (ch == ' ' || ch == '\t') break;
+
+ MARK(header_value);
+
+ parser->state = s_header_value;
+ parser->index = 0;
+
+ if (ch == CR) {
+ parser->header_state = h_general;
+ parser->state = s_header_almost_done;
+ CALLBACK_DATA(header_value);
+ break;
+ }
+
+ if (ch == LF) {
+ parser->state = s_header_field_start;
+ CALLBACK_DATA(header_value);
+ break;
+ }
+
+ c = LOWER(ch);
+
+ switch (parser->header_state) {
+ case h_upgrade:
+ parser->flags |= F_UPGRADE;
+ parser->header_state = h_general;
+ break;
+
+ case h_transfer_encoding:
+ /* looking for 'Transfer-Encoding: chunked' */
+ if ('c' == c) {
+ parser->header_state = h_matching_transfer_encoding_chunked;
+ } else {
+ parser->header_state = h_general;
+ }
+ break;
+
+ case h_content_length:
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = ch - '0';
+ break;
+
+ case h_connection:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ parser->header_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ parser->header_state = h_matching_connection_close;
+ } else {
+ parser->header_state = h_general;
+ }
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_value:
+ {
+
+ if (ch == CR) {
+ parser->state = s_header_almost_done;
+ CALLBACK_DATA(header_value);
+ break;
+ }
+
+ if (ch == LF) {
+ parser->state = s_header_almost_done;
+ CALLBACK_DATA_NOADVANCE(header_value);
+ goto reexecute_byte;
+ }
+
+ c = LOWER(ch);
+
+ switch (parser->header_state) {
+ case h_general:
+ break;
+
+ case h_connection:
+ case h_transfer_encoding:
+ assert(0 && "Shouldn't get here.");
+ break;
+
+ case h_content_length:
+ {
+ uint64_t t;
+
+ if (ch == ' ') break;
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 10;
+ t += ch - '0';
+
+ /* Overflow? */
+ if (t < parser->content_length || t == ULLONG_MAX) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ /* Transfer-Encoding: chunked */
+ case h_matching_transfer_encoding_chunked:
+ parser->index++;
+ if (parser->index > sizeof(CHUNKED)-1
+ || c != CHUNKED[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CHUNKED)-2) {
+ parser->header_state = h_transfer_encoding_chunked;
+ }
+ break;
+
+ /* looking for 'Connection: keep-alive' */
+ case h_matching_connection_keep_alive:
+ parser->index++;
+ if (parser->index > sizeof(KEEP_ALIVE)-1
+ || c != KEEP_ALIVE[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+ parser->header_state = h_connection_keep_alive;
+ }
+ break;
+
+ /* looking for 'Connection: close' */
+ case h_matching_connection_close:
+ parser->index++;
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CLOSE)-2) {
+ parser->header_state = h_connection_close;
+ }
+ break;
+
+ case h_transfer_encoding_chunked:
+ case h_connection_keep_alive:
+ case h_connection_close:
+ if (ch != ' ') parser->header_state = h_general;
+ break;
+
+ default:
+ parser->state = s_header_value;
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ parser->state = s_header_value_lws;
+
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ case s_header_value_lws:
+ {
+ if (ch == ' ' || ch == '\t')
+ parser->state = s_header_value_start;
+ else
+ {
+ parser->state = s_header_field_start;
+ goto reexecute_byte;
+ }
+ break;
+ }
+
+ case s_headers_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ if (parser->flags & F_TRAILING) {
+ /* End of a chunked request */
+ parser->state = NEW_MESSAGE();
+ CALLBACK_NOTIFY(message_complete);
+ break;
+ }
+
+ parser->state = s_headers_done;
+
+ /* Set this here so that on_headers_complete() callbacks can see it */
+ parser->upgrade =
+ (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
+
+ /* Here we call the headers_complete callback. This is somewhat
+ * different than other callbacks because if the user returns 1, we
+ * will interpret that as saying that this message has no body. This
+ * is needed for the annoying case of recieving a response to a HEAD
+ * request.
+ *
+ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+ * we have to simulate it by handling a change in errno below.
+ */
+ if (settings->on_headers_complete) {
+ switch (settings->on_headers_complete(parser)) {
+ case 0:
+ break;
+
+ case 1:
+ parser->flags |= F_SKIPBODY;
+ break;
+
+ default:
+ SET_ERRNO(HPE_CB_headers_complete);
+ return p - data; /* Error */
+ }
+ }
+
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ return p - data;
+ }
+
+ goto reexecute_byte;
+ }
+
+ case s_headers_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+
+ /* Exit, the rest of the connect is in a different protocol. */
+ if (parser->upgrade) {
+ parser->state = NEW_MESSAGE();
+ CALLBACK_NOTIFY(message_complete);
+ return (p - data) + 1;
+ }
+
+ if (parser->flags & F_SKIPBODY) {
+ parser->state = NEW_MESSAGE();
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header */
+ parser->state = s_chunk_size_start;
+ } else {
+ if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ parser->state = NEW_MESSAGE();
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->content_length != ULLONG_MAX) {
+ /* Content-Length header given and non-zero */
+ parser->state = s_body_identity;
+ } else {
+ if (parser->type == HTTP_REQUEST ||
+ !http_message_needs_eof(parser)) {
+ /* Assume content-length 0 - read the next */
+ parser->state = NEW_MESSAGE();
+ CALLBACK_NOTIFY(message_complete);
+ } else {
+ /* Read body until EOF */
+ parser->state = s_body_identity_eof;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case s_body_identity:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* The difference between advancing content_length and p is because
+ * the latter will automaticaly advance on the next loop iteration.
+ * Further, if content_length ends up at 0, we want to see the last
+ * byte again for our message complete callback.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ parser->state = s_message_done;
+
+ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+ *
+ * The alternative to doing this is to wait for the next byte to
+ * trigger the data callback, just as in every other case. The
+ * problem with this is that this makes it difficult for the test
+ * harness to distinguish between complete-on-EOF and
+ * complete-on-length. It's not clear that this distinction is
+ * important for applications, but let's keep it for now.
+ */
+ CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+ goto reexecute_byte;
+ }
+
+ break;
+ }
+
+ /* read until EOF */
+ case s_body_identity_eof:
+ MARK(body);
+ p = data + len - 1;
+
+ break;
+
+ case s_message_done:
+ parser->state = NEW_MESSAGE();
+ CALLBACK_NOTIFY(message_complete);
+ break;
+
+ case s_chunk_size_start:
+ {
+ assert(parser->nread == 1);
+ assert(parser->flags & F_CHUNKED);
+
+ unhex_val = unhex[(unsigned char)ch];
+ if (unhex_val == -1) {
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ parser->content_length = unhex_val;
+ parser->state = s_chunk_size;
+ break;
+ }
+
+ case s_chunk_size:
+ {
+ uint64_t t;
+
+ assert(parser->flags & F_CHUNKED);
+
+ if (ch == CR) {
+ parser->state = s_chunk_size_almost_done;
+ break;
+ }
+
+ unhex_val = unhex[(unsigned char)ch];
+
+ if (unhex_val == -1) {
+ if (ch == ';' || ch == ' ') {
+ parser->state = s_chunk_parameters;
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 16;
+ t += unhex_val;
+
+ /* Overflow? */
+ if (t < parser->content_length || t == ULLONG_MAX) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ case s_chunk_parameters:
+ {
+ assert(parser->flags & F_CHUNKED);
+ /* just ignore this shit. TODO check for overflow */
+ if (ch == CR) {
+ parser->state = s_chunk_size_almost_done;
+ break;
+ }
+ break;
+ }
+
+ case s_chunk_size_almost_done:
+ {
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+
+ if (parser->content_length == 0) {
+ parser->flags |= F_TRAILING;
+ parser->state = s_header_field_start;
+ } else {
+ parser->state = s_chunk_data;
+ }
+ break;
+ }
+
+ case s_chunk_data:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* See the explanation in s_body_identity for why the content
+ * length and data pointers are managed this way.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ parser->state = s_chunk_data_almost_done;
+ }
+
+ break;
+ }
+
+ case s_chunk_data_almost_done:
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length == 0);
+ STRICT_CHECK(ch != CR);
+ parser->state = s_chunk_data_done;
+ CALLBACK_DATA(body);
+ break;
+
+ case s_chunk_data_done:
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+ parser->nread = 0;
+ parser->state = s_chunk_size_start;
+ break;
+
+ default:
+ assert(0 && "unhandled state");
+ SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+ goto error;
+ }
+ }
+
+ /* Run callbacks for any marks that we have leftover after we ran our of
+ * bytes. There should be at most one of these set, so it's OK to invoke
+ * them in series (unset marks will not result in callbacks).
+ *
+ * We use the NOADVANCE() variety of callbacks here because 'p' has already
+ * overflowed 'data' and this allows us to correct for the off-by-one that
+ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+ * value that's in-bounds).
+ */
+
+ assert(((header_field_mark ? 1 : 0) +
+ (header_value_mark ? 1 : 0) +
+ (url_mark ? 1 : 0) +
+ (body_mark ? 1 : 0)) <= 1);
+
+ CALLBACK_DATA_NOADVANCE(header_field);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ CALLBACK_DATA_NOADVANCE(url);
+ CALLBACK_DATA_NOADVANCE(body);
+
+ return len;
+
+error:
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+ SET_ERRNO(HPE_UNKNOWN);
+ }
+
+ return (p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (http_parser *parser)
+{
+ if (parser->type == HTTP_REQUEST) {
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 || /* Not Modified */
+ parser->flags & F_SKIPBODY) { /* response to a HEAD request */
+ return 0;
+ }
+
+ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+http_should_keep_alive (http_parser *parser)
+{
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+ return 0;
+ }
+ }
+
+ return !http_message_needs_eof(parser);
+}
+
+
+const char * http_method_str (enum http_method m)
+{
+ return method_strings[m];
+}
+
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+ void *data = parser->data; /* preserve application data */
+ memset(parser, 0, sizeof(*parser));
+ parser->data = data;
+ parser->type = t;
+ parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+ parser->http_errno = HPE_OK;
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+ assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+ return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+ assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+ return http_strerror_tab[err].description;
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+ struct http_parser_url *u)
+{
+ enum state s;
+ const char *p;
+ enum http_parser_url_fields uf, old_uf;
+
+ u->port = u->field_set = 0;
+ s = is_connect ? s_req_host_start : s_req_spaces_before_url;
+ uf = old_uf = UF_MAX;
+
+ for (p = buf; p < buf + buflen; p++) {
+ s = parse_url_char(s, *p);
+
+ /* Figure out the next field that we're operating on */
+ switch (s) {
+ case s_dead:
+ return 1;
+
+ /* Skip delimeters */
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_host_start:
+ case s_req_host_v6_start:
+ case s_req_host_v6_end:
+ case s_req_port_start:
+ case s_req_query_string_start:
+ case s_req_fragment_start:
+ continue;
+
+ case s_req_schema:
+ uf = UF_SCHEMA;
+ break;
+
+ case s_req_host:
+ case s_req_host_v6:
+ uf = UF_HOST;
+ break;
+
+ case s_req_port:
+ uf = UF_PORT;
+ break;
+
+ case s_req_path:
+ uf = UF_PATH;
+ break;
+
+ case s_req_query_string:
+ uf = UF_QUERY;
+ break;
+
+ case s_req_fragment:
+ uf = UF_FRAGMENT;
+ break;
+
+ default:
+ assert(!"Unexpected state");
+ return 1;
+ }
+
+ /* Nothing's changed; soldier on */
+ if (uf == old_uf) {
+ u->field_data[uf].len++;
+ continue;
+ }
+
+ u->field_data[uf].off = p - buf;
+ u->field_data[uf].len = 1;
+
+ u->field_set |= (1 << uf);
+ old_uf = uf;
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+ return 1;
+ }
+
+ /* Make sure we don't end somewhere unexpected */
+ switch (s) {
+ case s_req_host_v6_start:
+ case s_req_host_v6:
+ case s_req_host_v6_end:
+ case s_req_host:
+ case s_req_port_start:
+ return 1;
+ default:
+ break;
+ }
+
+ if (u->field_set & (1 << UF_PORT)) {
+ /* Don't bother with endp; we've already validated the string */
+ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
+
+ /* Ports have a max value of 2^16 */
+ if (v > 0xffff) {
+ return 1;
+ }
+
+ u->port = (uint16_t) v;
+ }
+
+ return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+ /* Users should only be pausing/unpausing a parser that is not in an error
+ * state. In non-debug builds, there's not much that we can do about this
+ * other than ignore it.
+ */
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+ } else {
+ assert(0 && "Attempting to pause parser in error state");
+ }
+}
diff --git a/samples/c/http_parser.h b/samples/c/http_parser.h
new file mode 100644
index 0000000..8ed4180
--- /dev/null
+++ b/samples/c/http_parser.h
@@ -0,0 +1,318 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * 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.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HTTP_PARSER_VERSION_MAJOR 1
+#define HTTP_PARSER_VERSION_MINOR 0
+
+#include <sys/types.h>
+#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+typedef unsigned int size_t;
+typedef int ssize_t;
+#else
+#include <stdint.h>
+#endif
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# define HTTP_PARSER_STRICT 1
+#endif
+
+/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
+ * the error reporting facility.
+ */
+#ifndef HTTP_PARSER_DEBUG
+# define HTTP_PARSER_DEBUG 0
+#endif
+
+
+/* Maximium header size allowed */
+#define HTTP_MAX_HEADER_SIZE (80*1024)
+
+
+typedef struct http_parser http_parser;
+typedef struct http_parser_settings http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * http_data_cb does not return data chunks. It will be call arbitrarally
+ * many times for each string. E.G. you might get 10 callbacks for "on_path"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ /* pathological */ \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ /* webdav */ \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ /* subversion */ \
+ XX(16, REPORT, REPORT) \
+ XX(17, MKACTIVITY, MKACTIVITY) \
+ XX(18, CHECKOUT, CHECKOUT) \
+ XX(19, MERGE, MERGE) \
+ /* upnp */ \
+ XX(20, MSEARCH, M-SEARCH) \
+ XX(21, NOTIFY, NOTIFY) \
+ XX(22, SUBSCRIBE, SUBSCRIBE) \
+ XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
+ /* RFC-5789 */ \
+ XX(24, PATCH, PATCH) \
+ XX(25, PURGE, PURGE) \
+
+enum http_method
+ {
+#define XX(num, name, string) HTTP_##name = num,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+ { F_CHUNKED = 1 << 0
+ , F_CONNECTION_KEEP_ALIVE = 1 << 1
+ , F_CONNECTION_CLOSE = 1 << 2
+ , F_TRAILING = 1 << 3
+ , F_UPGRADE = 1 << 4
+ , F_SKIPBODY = 1 << 5
+ };
+
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX) \
+ /* No error */ \
+ XX(OK, "success") \
+ \
+ /* Callback-related errors */ \
+ XX(CB_message_begin, "the on_message_begin callback failed") \
+ XX(CB_url, "the on_url callback failed") \
+ XX(CB_header_field, "the on_header_field callback failed") \
+ XX(CB_header_value, "the on_header_value callback failed") \
+ XX(CB_headers_complete, "the on_headers_complete callback failed") \
+ XX(CB_body, "the on_body callback failed") \
+ XX(CB_message_complete, "the on_message_complete callback failed") \
+ \
+ /* Parsing-related errors */ \
+ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
+ XX(HEADER_OVERFLOW, \
+ "too many header bytes seen; overflow detected") \
+ XX(CLOSED_CONNECTION, \
+ "data received after completed connection: close message") \
+ XX(INVALID_VERSION, "invalid HTTP version") \
+ XX(INVALID_STATUS, "invalid HTTP status code") \
+ XX(INVALID_METHOD, "invalid HTTP method") \
+ XX(INVALID_URL, "invalid URL") \
+ XX(INVALID_HOST, "invalid host") \
+ XX(INVALID_PORT, "invalid port") \
+ XX(INVALID_PATH, "invalid path") \
+ XX(INVALID_QUERY_STRING, "invalid query string") \
+ XX(INVALID_FRAGMENT, "invalid fragment") \
+ XX(LF_EXPECTED, "LF character expected") \
+ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
+ XX(INVALID_CONTENT_LENGTH, \
+ "invalid character in content-length header") \
+ XX(INVALID_CHUNK_SIZE, \
+ "invalid character in chunk size header") \
+ XX(INVALID_CONSTANT, "invalid constant string") \
+ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+ XX(STRICT, "strict mode assertion failed") \
+ XX(PAUSED, "parser is paused") \
+ XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
+
+/* Get the line number that generated the current error */
+#if HTTP_PARSER_DEBUG
+#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
+#else
+#define HTTP_PARSER_ERRNO_LINE(p) 0
+#endif
+
+
+struct http_parser {
+ /** PRIVATE **/
+ unsigned char type : 2; /* enum http_parser_type */
+ unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
+ unsigned char state; /* enum state from http_parser.c */
+ unsigned char header_state; /* enum header_state from http_parser.c */
+ unsigned char index; /* index into current matcher */
+
+ uint32_t nread; /* # bytes read in various scenarios */
+ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
+
+ /** READ-ONLY **/
+ unsigned short http_major;
+ unsigned short http_minor;
+ unsigned short status_code; /* responses only */
+ unsigned char method; /* requests only */
+ unsigned char http_errno : 7;
+
+ /* 1 = Upgrade header was present and the parser has exited because of that.
+ * 0 = No upgrade header present.
+ * Should be checked when http_parser_execute() returns in addition to
+ * error checking.
+ */
+ unsigned char upgrade : 1;
+
+#if HTTP_PARSER_DEBUG
+ uint32_t error_lineno;
+#endif
+
+ /** PUBLIC **/
+ void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct http_parser_settings {
+ http_cb on_message_begin;
+ http_data_cb on_url;
+ http_data_cb on_header_field;
+ http_data_cb on_header_value;
+ http_cb on_headers_complete;
+ http_data_cb on_body;
+ http_cb on_message_complete;
+};
+
+
+enum http_parser_url_fields
+ { UF_SCHEMA = 0
+ , UF_HOST = 1
+ , UF_PORT = 2
+ , UF_PATH = 3
+ , UF_QUERY = 4
+ , UF_FRAGMENT = 5
+ , UF_MAX = 6
+ };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+ uint16_t field_set; /* Bitmask of (1 << UF_*) values */
+ uint16_t port; /* Converted UF_PORT string */
+
+ struct {
+ uint16_t off; /* Offset into buffer in which field starts */
+ uint16_t len; /* Length of run in buffer */
+ } field_data[UF_MAX];
+};
+
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+size_t http_parser_execute(http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns true, then this will be should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+ int is_connect,
+ struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/samples/c/markdown.c b/samples/c/markdown.c
new file mode 100644
index 0000000..260483d
--- /dev/null
+++ b/samples/c/markdown.c
@@ -0,0 +1,2551 @@
+/* markdown.c - generic markdown parser */
+
+/*
+ * Copyright (c) 2009, Natacha Porté
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "markdown.h"
+#include "stack.h"
+
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#if defined(_WIN32)
+#define strncasecmp _strnicmp
+#endif
+
+#define REF_TABLE_SIZE 8
+
+#define BUFFER_BLOCK 0
+#define BUFFER_SPAN 1
+
+#define MKD_LI_END 8 /* internal list flag */
+
+#define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n)
+#define GPERF_DOWNCASE 1
+#define GPERF_CASE_STRNCMP 1
+#include "html_blocks.h"
+
+/***************
+ * LOCAL TYPES *
+ ***************/
+
+/* link_ref: reference to a link */
+struct link_ref {
+ unsigned int id;
+
+ struct buf *link;
+ struct buf *title;
+
+ struct link_ref *next;
+};
+
+/* char_trigger: function pointer to render active chars */
+/* returns the number of chars taken care of */
+/* data is the pointer of the beginning of the span */
+/* offset is the number of valid chars before data */
+struct sd_markdown;
+typedef size_t
+(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+
+static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+
+enum markdown_char_t {
+ MD_CHAR_NONE = 0,
+ MD_CHAR_EMPHASIS,
+ MD_CHAR_CODESPAN,
+ MD_CHAR_LINEBREAK,
+ MD_CHAR_LINK,
+ MD_CHAR_LANGLE,
+ MD_CHAR_ESCAPE,
+ MD_CHAR_ENTITITY,
+ MD_CHAR_AUTOLINK_URL,
+ MD_CHAR_AUTOLINK_EMAIL,
+ MD_CHAR_AUTOLINK_WWW,
+ MD_CHAR_SUPERSCRIPT,
+};
+
+static char_trigger markdown_char_ptrs[] = {
+ NULL,
+ &char_emphasis,
+ &char_codespan,
+ &char_linebreak,
+ &char_link,
+ &char_langle_tag,
+ &char_escape,
+ &char_entity,
+ &char_autolink_url,
+ &char_autolink_email,
+ &char_autolink_www,
+ &char_superscript,
+};
+
+/* render • structure containing one particular render */
+struct sd_markdown {
+ struct sd_callbacks cb;
+ void *opaque;
+
+ struct link_ref *refs[REF_TABLE_SIZE];
+ uint8_t active_char[256];
+ struct stack work_bufs[2];
+ unsigned int ext_flags;
+ size_t max_nesting;
+ int in_link_body;
+};
+
+/***************************
+ * HELPER FUNCTIONS *
+ ***************************/
+
+static inline struct buf *
+rndr_newbuf(struct sd_markdown *rndr, int type)
+{
+ static const size_t buf_size[2] = {256, 64};
+ struct buf *work = NULL;
+ struct stack *pool = &rndr->work_bufs[type];
+
+ if (pool->size < pool->asize &&
+ pool->item[pool->size] != NULL) {
+ work = pool->item[pool->size++];
+ work->size = 0;
+ } else {
+ work = bufnew(buf_size[type]);
+ stack_push(pool, work);
+ }
+
+ return work;
+}
+
+static inline void
+rndr_popbuf(struct sd_markdown *rndr, int type)
+{
+ rndr->work_bufs[type].size--;
+}
+
+static void
+unscape_text(struct buf *ob, struct buf *src)
+{
+ size_t i = 0, org;
+ while (i < src->size) {
+ org = i;
+ while (i < src->size && src->data[i] != '\\')
+ i++;
+
+ if (i > org)
+ bufput(ob, src->data + org, i - org);
+
+ if (i + 1 >= src->size)
+ break;
+
+ bufputc(ob, src->data[i + 1]);
+ i += 2;
+ }
+}
+
+static unsigned int
+hash_link_ref(const uint8_t *link_ref, size_t length)
+{
+ size_t i;
+ unsigned int hash = 0;
+
+ for (i = 0; i < length; ++i)
+ hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
+
+ return hash;
+}
+
+static struct link_ref *
+add_link_ref(
+ struct link_ref **references,
+ const uint8_t *name, size_t name_size)
+{
+ struct link_ref *ref = calloc(1, sizeof(struct link_ref));
+
+ if (!ref)
+ return NULL;
+
+ ref->id = hash_link_ref(name, name_size);
+ ref->next = references[ref->id % REF_TABLE_SIZE];
+
+ references[ref->id % REF_TABLE_SIZE] = ref;
+ return ref;
+}
+
+static struct link_ref *
+find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
+{
+ unsigned int hash = hash_link_ref(name, length);
+ struct link_ref *ref = NULL;
+
+ ref = references[hash % REF_TABLE_SIZE];
+
+ while (ref != NULL) {
+ if (ref->id == hash)
+ return ref;
+
+ ref = ref->next;
+ }
+
+ return NULL;
+}
+
+static void
+free_link_refs(struct link_ref **references)
+{
+ size_t i;
+
+ for (i = 0; i < REF_TABLE_SIZE; ++i) {
+ struct link_ref *r = references[i];
+ struct link_ref *next;
+
+ while (r) {
+ next = r->next;
+ bufrelease(r->link);
+ bufrelease(r->title);
+ free(r);
+ r = next;
+ }
+ }
+}
+
+/*
+ * Check whether a char is a Markdown space.
+
+ * Right now we only consider spaces the actual
+ * space and a newline: tabs and carriage returns
+ * are filtered out during the preprocessing phase.
+ *
+ * If we wanted to actually be UTF-8 compliant, we
+ * should instead extract an Unicode codepoint from
+ * this character and check for space properties.
+ */
+static inline int
+_isspace(int c)
+{
+ return c == ' ' || c == '\n';
+}
+
+/****************************
+ * INLINE PARSING FUNCTIONS *
+ ****************************/
+
+/* is_mail_autolink • looks for the address part of a mail autolink and '>' */
+/* this is less strict than the original markdown e-mail address matching */
+static size_t
+is_mail_autolink(uint8_t *data, size_t size)
+{
+ size_t i = 0, nb = 0;
+
+ /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */
+ for (i = 0; i < size; ++i) {
+ if (isalnum(data[i]))
+ continue;
+
+ switch (data[i]) {
+ case '@':
+ nb++;
+
+ case '-':
+ case '.':
+ case '_':
+ break;
+
+ case '>':
+ return (nb == 1) ? i + 1 : 0;
+
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* tag_length • returns the length of the given tag, or 0 is it's not valid */
+static size_t
+tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)
+{
+ size_t i, j;
+
+ /* a valid tag can't be shorter than 3 chars */
+ if (size < 3) return 0;
+
+ /* begins with a '<' optionally followed by '/', followed by letter or number */
+ if (data[0] != '<') return 0;
+ i = (data[1] == '/') ? 2 : 1;
+
+ if (!isalnum(data[i]))
+ return 0;
+
+ /* scheme test */
+ *autolink = MKDA_NOT_AUTOLINK;
+
+ /* try to find the beginning of an URI */
+ while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-'))
+ i++;
+
+ if (i > 1 && data[i] == '@') {
+ if ((j = is_mail_autolink(data + i, size - i)) != 0) {
+ *autolink = MKDA_EMAIL;
+ return i + j;
+ }
+ }
+
+ if (i > 2 && data[i] == ':') {
+ *autolink = MKDA_NORMAL;
+ i++;
+ }
+
+ /* completing autolink test: no whitespace or ' or " */
+ if (i >= size)
+ *autolink = MKDA_NOT_AUTOLINK;
+
+ else if (*autolink) {
+ j = i;
+
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == '>' || data[i] == '\'' ||
+ data[i] == '"' || data[i] == ' ' || data[i] == '\n')
+ break;
+ else i++;
+ }
+
+ if (i >= size) return 0;
+ if (i > j && data[i] == '>') return i + 1;
+ /* one of the forbidden chars has been found */
+ *autolink = MKDA_NOT_AUTOLINK;
+ }
+
+ /* looking for sometinhg looking like a tag end */
+ while (i < size && data[i] != '>') i++;
+ if (i >= size) return 0;
+ return i + 1;
+}
+
+/* parse_inline • parses inline markdown elements */
+static void
+parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t i = 0, end = 0;
+ uint8_t action = 0;
+ struct buf work = { 0, 0, 0, 0 };
+
+ if (rndr->work_bufs[BUFFER_SPAN].size +
+ rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
+ return;
+
+ while (i < size) {
+ /* copying inactive chars into the output */
+ while (end < size && (action = rndr->active_char[data[end]]) == 0) {
+ end++;
+ }
+
+ if (rndr->cb.normal_text) {
+ work.data = data + i;
+ work.size = end - i;
+ rndr->cb.normal_text(ob, &work, rndr->opaque);
+ }
+ else
+ bufput(ob, data + i, end - i);
+
+ if (end >= size) break;
+ i = end;
+
+ end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);
+ if (!end) /* no action from the callback */
+ end = i + 1;
+ else {
+ i += end;
+ end = i;
+ }
+ }
+}
+
+/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */
+static size_t
+find_emph_char(uint8_t *data, size_t size, uint8_t c)
+{
+ size_t i = 1;
+
+ while (i < size) {
+ while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')
+ i++;
+
+ if (i == size)
+ return 0;
+
+ if (data[i] == c)
+ return i;
+
+ /* not counting escaped chars */
+ if (i && data[i - 1] == '\\') {
+ i++; continue;
+ }
+
+ if (data[i] == '`') {
+ size_t span_nb = 0, bt;
+ size_t tmp_i = 0;
+
+ /* counting the number of opening backticks */
+ while (i < size && data[i] == '`') {
+ i++; span_nb++;
+ }
+
+ if (i >= size) return 0;
+
+ /* finding the matching closing sequence */
+ bt = 0;
+ while (i < size && bt < span_nb) {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ if (data[i] == '`') bt++;
+ else bt = 0;
+ i++;
+ }
+
+ if (i >= size) return tmp_i;
+ }
+ /* skipping a link */
+ else if (data[i] == '[') {
+ size_t tmp_i = 0;
+ uint8_t cc;
+
+ i++;
+ while (i < size && data[i] != ']') {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ i++;
+ }
+
+ i++;
+ while (i < size && (data[i] == ' ' || data[i] == '\n'))
+ i++;
+
+ if (i >= size)
+ return tmp_i;
+
+ switch (data[i]) {
+ case '[':
+ cc = ']'; break;
+
+ case '(':
+ cc = ')'; break;
+
+ default:
+ if (tmp_i)
+ return tmp_i;
+ else
+ continue;
+ }
+
+ i++;
+ while (i < size && data[i] != cc) {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ i++;
+ }
+
+ if (i >= size)
+ return tmp_i;
+
+ i++;
+ }
+ }
+
+ return 0;
+}
+
+/* parse_emph1 • parsing single emphase */
+/* closed by a symbol not preceded by whitespace and not followed by symbol */
+static size_t
+parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
+{
+ size_t i = 0, len;
+ struct buf *work = 0;
+ int r;
+
+ if (!rndr->cb.emphasis) return 0;
+
+ /* skipping one symbol if coming from emph3 */
+ if (size > 1 && data[0] == c && data[1] == c) i = 1;
+
+ while (i < size) {
+ len = find_emph_char(data + i, size - i, c);
+ if (!len) return 0;
+ i += len;
+ if (i >= size) return 0;
+
+ if (data[i] == c && !_isspace(data[i - 1])) {
+
+ if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
+ if (!(i + 1 == size || _isspace(data[i + 1]) || ispunct(data[i + 1])))
+ continue;
+ }
+
+ work = rndr_newbuf(rndr, BUFFER_SPAN);
+ parse_inline(work, rndr, data, i);
+ r = rndr->cb.emphasis(ob, work, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return r ? i + 1 : 0;
+ }
+ }
+
+ return 0;
+}
+
+/* parse_emph2 • parsing single emphase */
+static size_t
+parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
+{
+ int (*render_method)(struct buf *ob, const struct buf *text, void *opaque);
+ size_t i = 0, len;
+ struct buf *work = 0;
+ int r;
+
+ render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis;
+
+ if (!render_method)
+ return 0;
+
+ while (i < size) {
+ len = find_emph_char(data + i, size - i, c);
+ if (!len) return 0;
+ i += len;
+
+ if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {
+ work = rndr_newbuf(rndr, BUFFER_SPAN);
+ parse_inline(work, rndr, data, i);
+ r = render_method(ob, work, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return r ? i + 2 : 0;
+ }
+ i++;
+ }
+ return 0;
+}
+
+/* parse_emph3 • parsing single emphase */
+/* finds the first closing tag, and delegates to the other emph */
+static size_t
+parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
+{
+ size_t i = 0, len;
+ int r;
+
+ while (i < size) {
+ len = find_emph_char(data + i, size - i, c);
+ if (!len) return 0;
+ i += len;
+
+ /* skip whitespace preceded symbols */
+ if (data[i] != c || _isspace(data[i - 1]))
+ continue;
+
+ if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {
+ /* triple symbol found */
+ struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ parse_inline(work, rndr, data, i);
+ r = rndr->cb.triple_emphasis(ob, work, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return r ? i + 3 : 0;
+
+ } else if (i + 1 < size && data[i + 1] == c) {
+ /* double symbol found, handing over to emph1 */
+ len = parse_emph1(ob, rndr, data - 2, size + 2, c);
+ if (!len) return 0;
+ else return len - 2;
+
+ } else {
+ /* single symbol found, handing over to emph2 */
+ len = parse_emph2(ob, rndr, data - 1, size + 1, c);
+ if (!len) return 0;
+ else return len - 1;
+ }
+ }
+ return 0;
+}
+
+/* char_emphasis • single and double emphasis parsing */
+static size_t
+char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ uint8_t c = data[0];
+ size_t ret;
+
+ if (size > 2 && data[1] != c) {
+ /* whitespace cannot follow an opening emphasis;
+ * strikethrough only takes two characters '~~' */
+ if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)
+ return 0;
+
+ return ret + 1;
+ }
+
+ if (size > 3 && data[1] == c && data[2] != c) {
+ if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)
+ return 0;
+
+ return ret + 2;
+ }
+
+ if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {
+ if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)
+ return 0;
+
+ return ret + 3;
+ }
+
+ return 0;
+}
+
+
+/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */
+static size_t
+char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')
+ return 0;
+
+ /* removing the last space from ob and rendering */
+ while (ob->size && ob->data[ob->size - 1] == ' ')
+ ob->size--;
+
+ return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0;
+}
+
+
+/* char_codespan • '`' parsing a code span (assuming codespan != 0) */
+static size_t
+char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ size_t end, nb = 0, i, f_begin, f_end;
+
+ /* counting the number of backticks in the delimiter */
+ while (nb < size && data[nb] == '`')
+ nb++;
+
+ /* finding the next delimiter */
+ i = 0;
+ for (end = nb; end < size && i < nb; end++) {
+ if (data[end] == '`') i++;
+ else i = 0;
+ }
+
+ if (i < nb && end >= size)
+ return 0; /* no matching delimiter */
+
+ /* trimming outside whitespaces */
+ f_begin = nb;
+ while (f_begin < end && data[f_begin] == ' ')
+ f_begin++;
+
+ f_end = end - nb;
+ while (f_end > nb && data[f_end-1] == ' ')
+ f_end--;
+
+ /* real code span */
+ if (f_begin < f_end) {
+ struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };
+ if (!rndr->cb.codespan(ob, &work, rndr->opaque))
+ end = 0;
+ } else {
+ if (!rndr->cb.codespan(ob, 0, rndr->opaque))
+ end = 0;
+ }
+
+ return end;
+}
+
+
+/* char_escape • '\\' backslash escape */
+static size_t
+char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";
+ struct buf work = { 0, 0, 0, 0 };
+
+ if (size > 1) {
+ if (strchr(escape_chars, data[1]) == NULL)
+ return 0;
+
+ if (rndr->cb.normal_text) {
+ work.data = data + 1;
+ work.size = 1;
+ rndr->cb.normal_text(ob, &work, rndr->opaque);
+ }
+ else bufputc(ob, data[1]);
+ } else if (size == 1) {
+ bufputc(ob, data[0]);
+ }
+
+ return 2;
+}
+
+/* char_entity • '&' escaped when it doesn't belong to an entity */
+/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
+static size_t
+char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ size_t end = 1;
+ struct buf work = { 0, 0, 0, 0 };
+
+ if (end < size && data[end] == '#')
+ end++;
+
+ while (end < size && isalnum(data[end]))
+ end++;
+
+ if (end < size && data[end] == ';')
+ end++; /* real entity */
+ else
+ return 0; /* lone '&' */
+
+ if (rndr->cb.entity) {
+ work.data = data;
+ work.size = end;
+ rndr->cb.entity(ob, &work, rndr->opaque);
+ }
+ else bufput(ob, data, end);
+
+ return end;
+}
+
+/* char_langle_tag • '<' when tags or autolinks are allowed */
+static size_t
+char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ enum mkd_autolink altype = MKDA_NOT_AUTOLINK;
+ size_t end = tag_length(data, size, &altype);
+ struct buf work = { data, end, 0, 0 };
+ int ret = 0;
+
+ if (end > 2) {
+ if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) {
+ struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN);
+ work.data = data + 1;
+ work.size = end - 2;
+ unscape_text(u_link, &work);
+ ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ }
+ else if (rndr->cb.raw_html_tag)
+ ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque);
+ }
+
+ if (!ret) return 0;
+ else return end;
+}
+
+static size_t
+char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ struct buf *link, *link_url, *link_text;
+ size_t link_len, rewind;
+
+ if (!rndr->cb.link || rndr->in_link_body)
+ return 0;
+
+ link = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ if ((link_len = sd_autolink__www(&rewind, link, data, offset, size)) > 0) {
+ link_url = rndr_newbuf(rndr, BUFFER_SPAN);
+ BUFPUTSL(link_url, "http://");
+ bufput(link_url, link->data, link->size);
+
+ ob->size -= rewind;
+ if (rndr->cb.normal_text) {
+ link_text = rndr_newbuf(rndr, BUFFER_SPAN);
+ rndr->cb.normal_text(link_text, link, rndr->opaque);
+ rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ } else {
+ rndr->cb.link(ob, link_url, NULL, link, rndr->opaque);
+ }
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ }
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return link_len;
+}
+
+static size_t
+char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ struct buf *link;
+ size_t link_len, rewind;
+
+ if (!rndr->cb.autolink || rndr->in_link_body)
+ return 0;
+
+ link = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ if ((link_len = sd_autolink__email(&rewind, link, data, offset, size)) > 0) {
+ ob->size -= rewind;
+ rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque);
+ }
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return link_len;
+}
+
+static size_t
+char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ struct buf *link;
+ size_t link_len, rewind;
+
+ if (!rndr->cb.autolink || rndr->in_link_body)
+ return 0;
+
+ link = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ if ((link_len = sd_autolink__url(&rewind, link, data, offset, size)) > 0) {
+ ob->size -= rewind;
+ rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);
+ }
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return link_len;
+}
+
+/* char_link • '[': parsing a link or an image */
+static size_t
+char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ int is_img = (offset && data[-1] == '!'), level;
+ size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
+ struct buf *content = 0;
+ struct buf *link = 0;
+ struct buf *title = 0;
+ struct buf *u_link = 0;
+ size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size;
+ int text_has_nl = 0, ret = 0;
+ int in_title = 0, qtype = 0;
+
+ /* checking whether the correct renderer exists */
+ if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link))
+ goto cleanup;
+
+ /* looking for the matching closing bracket */
+ for (level = 1; i < size; i++) {
+ if (data[i] == '\n')
+ text_has_nl = 1;
+
+ else if (data[i - 1] == '\\')
+ continue;
+
+ else if (data[i] == '[')
+ level++;
+
+ else if (data[i] == ']') {
+ level--;
+ if (level <= 0)
+ break;
+ }
+ }
+
+ if (i >= size)
+ goto cleanup;
+
+ txt_e = i;
+ i++;
+
+ /* skip any amount of whitespace or newline */
+ /* (this is much more laxist than original markdown syntax) */
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ /* inline style link */
+ if (i < size && data[i] == '(') {
+ /* skipping initial whitespace */
+ i++;
+
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ link_b = i;
+
+ /* looking for link end: ' " ) */
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == ')') break;
+ else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
+ else i++;
+ }
+
+ if (i >= size) goto cleanup;
+ link_e = i;
+
+ /* looking for title end if present */
+ if (data[i] == '\'' || data[i] == '"') {
+ qtype = data[i];
+ in_title = 1;
+ i++;
+ title_b = i;
+
+ while (i < size) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == qtype) {in_title = 0; i++;}
+ else if ((data[i] == ')') && !in_title) break;
+ else i++;
+ }
+
+ if (i >= size) goto cleanup;
+
+ /* skipping whitespaces after title */
+ title_e = i - 1;
+ while (title_e > title_b && _isspace(data[title_e]))
+ title_e--;
+
+ /* checking for closing quote presence */
+ if (data[title_e] != '\'' && data[title_e] != '"') {
+ title_b = title_e = 0;
+ link_e = i;
+ }
+ }
+
+ /* remove whitespace at the end of the link */
+ while (link_e > link_b && _isspace(data[link_e - 1]))
+ link_e--;
+
+ /* remove optional angle brackets around the link */
+ if (data[link_b] == '<') link_b++;
+ if (data[link_e - 1] == '>') link_e--;
+
+ /* building escaped link and title */
+ if (link_e > link_b) {
+ link = rndr_newbuf(rndr, BUFFER_SPAN);
+ bufput(link, data + link_b, link_e - link_b);
+ }
+
+ if (title_e > title_b) {
+ title = rndr_newbuf(rndr, BUFFER_SPAN);
+ bufput(title, data + title_b, title_e - title_b);
+ }
+
+ i++;
+ }
+
+ /* reference style link */
+ else if (i < size && data[i] == '[') {
+ struct buf id = { 0, 0, 0, 0 };
+ struct link_ref *lr;
+
+ /* looking for the id */
+ i++;
+ link_b = i;
+ while (i < size && data[i] != ']') i++;
+ if (i >= size) goto cleanup;
+ link_e = i;
+
+ /* finding the link_ref */
+ if (link_b == link_e) {
+ if (text_has_nl) {
+ struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);
+ size_t j;
+
+ for (j = 1; j < txt_e; j++) {
+ if (data[j] != '\n')
+ bufputc(b, data[j]);
+ else if (data[j - 1] != ' ')
+ bufputc(b, ' ');
+ }
+
+ id.data = b->data;
+ id.size = b->size;
+ } else {
+ id.data = data + 1;
+ id.size = txt_e - 1;
+ }
+ } else {
+ id.data = data + link_b;
+ id.size = link_e - link_b;
+ }
+
+ lr = find_link_ref(rndr->refs, id.data, id.size);
+ if (!lr)
+ goto cleanup;
+
+ /* keeping link and title from link_ref */
+ link = lr->link;
+ title = lr->title;
+ i++;
+ }
+
+ /* shortcut reference style link */
+ else {
+ struct buf id = { 0, 0, 0, 0 };
+ struct link_ref *lr;
+
+ /* crafting the id */
+ if (text_has_nl) {
+ struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);
+ size_t j;
+
+ for (j = 1; j < txt_e; j++) {
+ if (data[j] != '\n')
+ bufputc(b, data[j]);
+ else if (data[j - 1] != ' ')
+ bufputc(b, ' ');
+ }
+
+ id.data = b->data;
+ id.size = b->size;
+ } else {
+ id.data = data + 1;
+ id.size = txt_e - 1;
+ }
+
+ /* finding the link_ref */
+ lr = find_link_ref(rndr->refs, id.data, id.size);
+ if (!lr)
+ goto cleanup;
+
+ /* keeping link and title from link_ref */
+ link = lr->link;
+ title = lr->title;
+
+ /* rewinding the whitespace */
+ i = txt_e + 1;
+ }
+
+ /* building content: img alt is escaped, link content is parsed */
+ if (txt_e > 1) {
+ content = rndr_newbuf(rndr, BUFFER_SPAN);
+ if (is_img) {
+ bufput(content, data + 1, txt_e - 1);
+ } else {
+ /* disable autolinking when parsing inline the
+ * content of a link */
+ rndr->in_link_body = 1;
+ parse_inline(content, rndr, data + 1, txt_e - 1);
+ rndr->in_link_body = 0;
+ }
+ }
+
+ if (link) {
+ u_link = rndr_newbuf(rndr, BUFFER_SPAN);
+ unscape_text(u_link, link);
+ }
+
+ /* calling the relevant rendering function */
+ if (is_img) {
+ if (ob->size && ob->data[ob->size - 1] == '!')
+ ob->size -= 1;
+
+ ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque);
+ } else {
+ ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque);
+ }
+
+ /* cleanup */
+cleanup:
+ rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size;
+ return ret ? i : 0;
+}
+
+static size_t
+char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+ size_t sup_start, sup_len;
+ struct buf *sup;
+
+ if (!rndr->cb.superscript)
+ return 0;
+
+ if (size < 2)
+ return 0;
+
+ if (data[1] == '(') {
+ sup_start = sup_len = 2;
+
+ while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')
+ sup_len++;
+
+ if (sup_len == size)
+ return 0;
+ } else {
+ sup_start = sup_len = 1;
+
+ while (sup_len < size && !_isspace(data[sup_len]))
+ sup_len++;
+ }
+
+ if (sup_len - sup_start == 0)
+ return (sup_start == 2) ? 3 : 0;
+
+ sup = rndr_newbuf(rndr, BUFFER_SPAN);
+ parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);
+ rndr->cb.superscript(ob, sup, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+
+ return (sup_start == 2) ? sup_len + 1 : sup_len;
+}
+
+/*********************************
+ * BLOCK-LEVEL PARSING FUNCTIONS *
+ *********************************/
+
+/* is_empty • returns the line length when it is empty, 0 otherwise */
+static size_t
+is_empty(uint8_t *data, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size && data[i] != '\n'; i++)
+ if (data[i] != ' ')
+ return 0;
+
+ return i + 1;
+}
+
+/* is_hrule • returns whether a line is a horizontal rule */
+static int
+is_hrule(uint8_t *data, size_t size)
+{
+ size_t i = 0, n = 0;
+ uint8_t c;
+
+ /* skipping initial spaces */
+ if (size < 3) return 0;
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
+
+ /* looking at the hrule uint8_t */
+ if (i + 2 >= size
+ || (data[i] != '*' && data[i] != '-' && data[i] != '_'))
+ return 0;
+ c = data[i];
+
+ /* the whole line must be the char or whitespace */
+ while (i < size && data[i] != '\n') {
+ if (data[i] == c) n++;
+ else if (data[i] != ' ')
+ return 0;
+
+ i++;
+ }
+
+ return n >= 3;
+}
+
+/* check if a line begins with a code fence; return the
+ * width of the code fence */
+static size_t
+prefix_codefence(uint8_t *data, size_t size)
+{
+ size_t i = 0, n = 0;
+ uint8_t c;
+
+ /* skipping initial spaces */
+ if (size < 3) return 0;
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
+
+ /* looking at the hrule uint8_t */
+ if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))
+ return 0;
+
+ c = data[i];
+
+ /* the whole line must be the uint8_t or whitespace */
+ while (i < size && data[i] == c) {
+ n++; i++;
+ }
+
+ if (n < 3)
+ return 0;
+
+ return i;
+}
+
+/* check if a line is a code fence; return its size if it is */
+static size_t
+is_codefence(uint8_t *data, size_t size, struct buf *syntax)
+{
+ size_t i = 0, syn_len = 0;
+ uint8_t *syn_start;
+
+ i = prefix_codefence(data, size);
+ if (i == 0)
+ return 0;
+
+ while (i < size && data[i] == ' ')
+ i++;
+
+ syn_start = data + i;
+
+ if (i < size && data[i] == '{') {
+ i++; syn_start++;
+
+ while (i < size && data[i] != '}' && data[i] != '\n') {
+ syn_len++; i++;
+ }
+
+ if (i == size || data[i] != '}')
+ return 0;
+
+ /* strip all whitespace at the beginning and the end
+ * of the {} block */
+ while (syn_len > 0 && _isspace(syn_start[0])) {
+ syn_start++; syn_len--;
+ }
+
+ while (syn_len > 0 && _isspace(syn_start[syn_len - 1]))
+ syn_len--;
+
+ i++;
+ } else {
+ while (i < size && !_isspace(data[i])) {
+ syn_len++; i++;
+ }
+ }
+
+ if (syntax) {
+ syntax->data = syn_start;
+ syntax->size = syn_len;
+ }
+
+ while (i < size && data[i] != '\n') {
+ if (!_isspace(data[i]))
+ return 0;
+
+ i++;
+ }
+
+ return i + 1;
+}
+
+/* is_atxheader • returns whether the line is a hash-prefixed header */
+static int
+is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ if (data[0] != '#')
+ return 0;
+
+ if (rndr->ext_flags & MKDEXT_SPACE_HEADERS) {
+ size_t level = 0;
+
+ while (level < size && level < 6 && data[level] == '#')
+ level++;
+
+ if (level < size && data[level] != ' ')
+ return 0;
+ }
+
+ return 1;
+}
+
+/* is_headerline • returns whether the line is a setext-style hdr underline */
+static int
+is_headerline(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ /* test of level 1 header */
+ if (data[i] == '=') {
+ for (i = 1; i < size && data[i] == '='; i++);
+ while (i < size && data[i] == ' ') i++;
+ return (i >= size || data[i] == '\n') ? 1 : 0; }
+
+ /* test of level 2 header */
+ if (data[i] == '-') {
+ for (i = 1; i < size && data[i] == '-'; i++);
+ while (i < size && data[i] == ' ') i++;
+ return (i >= size || data[i] == '\n') ? 2 : 0; }
+
+ return 0;
+}
+
+static int
+is_next_headerline(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ while (i < size && data[i] != '\n')
+ i++;
+
+ if (++i >= size)
+ return 0;
+
+ return is_headerline(data + i, size - i);
+}
+
+/* prefix_quote • returns blockquote prefix length */
+static size_t
+prefix_quote(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i < size && data[i] == '>') {
+ if (i + 1 < size && data[i + 1] == ' ')
+ return i + 2;
+
+ return i + 1;
+ }
+
+ return 0;
+}
+
+/* prefix_code • returns prefix length for block code*/
+static size_t
+prefix_code(uint8_t *data, size_t size)
+{
+ if (size > 3 && data[0] == ' ' && data[1] == ' '
+ && data[2] == ' ' && data[3] == ' ') return 4;
+
+ return 0;
+}
+
+/* prefix_oli • returns ordered list item prefix */
+static size_t
+prefix_oli(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i >= size || data[i] < '0' || data[i] > '9')
+ return 0;
+
+ while (i < size && data[i] >= '0' && data[i] <= '9')
+ i++;
+
+ if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ')
+ return 0;
+
+ if (is_next_headerline(data + i, size - i))
+ return 0;
+
+ return i + 2;
+}
+
+/* prefix_uli • returns ordered list item prefix */
+static size_t
+prefix_uli(uint8_t *data, size_t size)
+{
+ size_t i = 0;
+
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i + 1 >= size ||
+ (data[i] != '*' && data[i] != '+' && data[i] != '-') ||
+ data[i + 1] != ' ')
+ return 0;
+
+ if (is_next_headerline(data + i, size - i))
+ return 0;
+
+ return i + 2;
+}
+
+
+/* parse_block • parsing of one block, returning next uint8_t to parse */
+static void parse_block(struct buf *ob, struct sd_markdown *rndr,
+ uint8_t *data, size_t size);
+
+
+/* parse_blockquote • handles parsing of a blockquote fragment */
+static size_t
+parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t beg, end = 0, pre, work_size = 0;
+ uint8_t *work_data = 0;
+ struct buf *out = 0;
+
+ out = rndr_newbuf(rndr, BUFFER_BLOCK);
+ beg = 0;
+ while (beg < size) {
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
+
+ pre = prefix_quote(data + beg, end - beg);
+
+ if (pre)
+ beg += pre; /* skipping prefix */
+
+ /* empty line followed by non-quote line */
+ else if (is_empty(data + beg, end - beg) &&
+ (end >= size || (prefix_quote(data + end, size - end) == 0 &&
+ !is_empty(data + end, size - end))))
+ break;
+
+ if (beg < end) { /* copy into the in-place working buffer */
+ /* bufput(work, data + beg, end - beg); */
+ if (!work_data)
+ work_data = data + beg;
+ else if (data + beg != work_data + work_size)
+ memmove(work_data + work_size, data + beg, end - beg);
+ work_size += end - beg;
+ }
+ beg = end;
+ }
+
+ parse_block(out, rndr, work_data, work_size);
+ if (rndr->cb.blockquote)
+ rndr->cb.blockquote(ob, out, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ return end;
+}
+
+static size_t
+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);
+
+/* parse_blockquote • handles parsing of a regular paragraph */
+static size_t
+parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t i = 0, end = 0;
+ int level = 0;
+ struct buf work = { data, 0, 0, 0 };
+
+ while (i < size) {
+ for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;
+
+ if (is_empty(data + i, size - i))
+ break;
+
+ if ((level = is_headerline(data + i, size - i)) != 0)
+ break;
+
+ if (is_atxheader(rndr, data + i, size - i) ||
+ is_hrule(data + i, size - i) ||
+ prefix_quote(data + i, size - i)) {
+ end = i;
+ break;
+ }
+
+ /*
+ * Early termination of a paragraph with the same logic
+ * as Markdown 1.0.0. If this logic is applied, the
+ * Markdown 1.0.3 test suite won't pass cleanly
+ *
+ * :: If the first character in a new line is not a letter,
+ * let's check to see if there's some kind of block starting
+ * here
+ */
+ if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalnum(data[i])) {
+ if (prefix_oli(data + i, size - i) ||
+ prefix_uli(data + i, size - i)) {
+ end = i;
+ break;
+ }
+
+ /* see if an html block starts here */
+ if (data[i] == '<' && rndr->cb.blockhtml &&
+ parse_htmlblock(ob, rndr, data + i, size - i, 0)) {
+ end = i;
+ break;
+ }
+
+ /* see if a code fence starts here */
+ if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&
+ is_codefence(data + i, size - i, NULL) != 0) {
+ end = i;
+ break;
+ }
+ }
+
+ i = end;
+ }
+
+ work.size = i;
+ while (work.size && data[work.size - 1] == '\n')
+ work.size--;
+
+ if (!level) {
+ struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
+ parse_inline(tmp, rndr, work.data, work.size);
+ if (rndr->cb.paragraph)
+ rndr->cb.paragraph(ob, tmp, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ } else {
+ struct buf *header_work;
+
+ if (work.size) {
+ size_t beg;
+ i = work.size;
+ work.size -= 1;
+
+ while (work.size && data[work.size] != '\n')
+ work.size -= 1;
+
+ beg = work.size + 1;
+ while (work.size && data[work.size - 1] == '\n')
+ work.size -= 1;
+
+ if (work.size > 0) {
+ struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
+ parse_inline(tmp, rndr, work.data, work.size);
+
+ if (rndr->cb.paragraph)
+ rndr->cb.paragraph(ob, tmp, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ work.data += beg;
+ work.size = i - beg;
+ }
+ else work.size = i;
+ }
+
+ header_work = rndr_newbuf(rndr, BUFFER_SPAN);
+ parse_inline(header_work, rndr, work.data, work.size);
+
+ if (rndr->cb.header)
+ rndr->cb.header(ob, header_work, (int)level, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ }
+
+ return end;
+}
+
+/* parse_fencedcode • handles parsing of a block-level code fragment */
+static size_t
+parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t beg, end;
+ struct buf *work = 0;
+ struct buf lang = { 0, 0, 0, 0 };
+
+ beg = is_codefence(data, size, &lang);
+ if (beg == 0) return 0;
+
+ work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+ while (beg < size) {
+ size_t fence_end;
+ struct buf fence_trail = { 0, 0, 0, 0 };
+
+ fence_end = is_codefence(data + beg, size - beg, &fence_trail);
+ if (fence_end != 0 && fence_trail.size == 0) {
+ beg += fence_end;
+ break;
+ }
+
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
+
+ if (beg < end) {
+ /* verbatim copy to the working buffer,
+ escaping entities */
+ if (is_empty(data + beg, end - beg))
+ bufputc(work, '\n');
+ else bufput(work, data + beg, end - beg);
+ }
+ beg = end;
+ }
+
+ if (work->size && work->data[work->size - 1] != '\n')
+ bufputc(work, '\n');
+
+ if (rndr->cb.blockcode)
+ rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ return beg;
+}
+
+static size_t
+parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t beg, end, pre;
+ struct buf *work = 0;
+
+ work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+ beg = 0;
+ while (beg < size) {
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {};
+ pre = prefix_code(data + beg, end - beg);
+
+ if (pre)
+ beg += pre; /* skipping prefix */
+ else if (!is_empty(data + beg, end - beg))
+ /* non-empty non-prefixed line breaks the pre */
+ break;
+
+ if (beg < end) {
+ /* verbatim copy to the working buffer,
+ escaping entities */
+ if (is_empty(data + beg, end - beg))
+ bufputc(work, '\n');
+ else bufput(work, data + beg, end - beg);
+ }
+ beg = end;
+ }
+
+ while (work->size && work->data[work->size - 1] == '\n')
+ work->size -= 1;
+
+ bufputc(work, '\n');
+
+ if (rndr->cb.blockcode)
+ rndr->cb.blockcode(ob, work, NULL, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ return beg;
+}
+
+/* parse_listitem • parsing of a single list item */
+/* assuming initial prefix is already removed */
+static size_t
+parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)
+{
+ struct buf *work = 0, *inter = 0;
+ size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
+ int in_empty = 0, has_inside_empty = 0, in_fence = 0;
+
+ /* keeping track of the first indentation prefix */
+ while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
+ orgpre++;
+
+ beg = prefix_uli(data, size);
+ if (!beg)
+ beg = prefix_oli(data, size);
+
+ if (!beg)
+ return 0;
+
+ /* skipping to the beginning of the following line */
+ end = beg;
+ while (end < size && data[end - 1] != '\n')
+ end++;
+
+ /* getting working buffers */
+ work = rndr_newbuf(rndr, BUFFER_SPAN);
+ inter = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ /* putting the first line into the working buffer */
+ bufput(work, data + beg, end - beg);
+ beg = end;
+
+ /* process the following lines */
+ while (beg < size) {
+ size_t has_next_uli = 0, has_next_oli = 0;
+
+ end++;
+
+ while (end < size && data[end - 1] != '\n')
+ end++;
+
+ /* process an empty line */
+ if (is_empty(data + beg, end - beg)) {
+ in_empty = 1;
+ beg = end;
+ continue;
+ }
+
+ /* calculating the indentation */
+ i = 0;
+ while (i < 4 && beg + i < end && data[beg + i] == ' ')
+ i++;
+
+ pre = i;
+
+ if (rndr->ext_flags & MKDEXT_FENCED_CODE) {
+ if (is_codefence(data + beg + i, end - beg - i, NULL) != 0)
+ in_fence = !in_fence;
+ }
+
+ /* Only check for new list items if we are **not** inside
+ * a fenced code block */
+ if (!in_fence) {
+ has_next_uli = prefix_uli(data + beg + i, end - beg - i);
+ has_next_oli = prefix_oli(data + beg + i, end - beg - i);
+ }
+
+ /* checking for ul/ol switch */
+ if (in_empty && (
+ ((*flags & MKD_LIST_ORDERED) && has_next_uli) ||
+ (!(*flags & MKD_LIST_ORDERED) && has_next_oli))){
+ *flags |= MKD_LI_END;
+ break; /* the following item must have same list type */
+ }
+
+ /* checking for a new item */
+ if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {
+ if (in_empty)
+ has_inside_empty = 1;
+
+ if (pre == orgpre) /* the following item must have */
+ break; /* the same indentation */
+
+ if (!sublist)
+ sublist = work->size;
+ }
+ /* joining only indented stuff after empty lines;
+ * note that now we only require 1 space of indentation
+ * to continue a list */
+ else if (in_empty && pre == 0) {
+ *flags |= MKD_LI_END;
+ break;
+ }
+ else if (in_empty) {
+ bufputc(work, '\n');
+ has_inside_empty = 1;
+ }
+
+ in_empty = 0;
+
+ /* adding the line without prefix into the working buffer */
+ bufput(work, data + beg + i, end - beg - i);
+ beg = end;
+ }
+
+ /* render of li contents */
+ if (has_inside_empty)
+ *flags |= MKD_LI_BLOCK;
+
+ if (*flags & MKD_LI_BLOCK) {
+ /* intermediate render of block li */
+ if (sublist && sublist < work->size) {
+ parse_block(inter, rndr, work->data, sublist);
+ parse_block(inter, rndr, work->data + sublist, work->size - sublist);
+ }
+ else
+ parse_block(inter, rndr, work->data, work->size);
+ } else {
+ /* intermediate render of inline li */
+ if (sublist && sublist < work->size) {
+ parse_inline(inter, rndr, work->data, sublist);
+ parse_block(inter, rndr, work->data + sublist, work->size - sublist);
+ }
+ else
+ parse_inline(inter, rndr, work->data, work->size);
+ }
+
+ /* render of li itself */
+ if (rndr->cb.listitem)
+ rndr->cb.listitem(ob, inter, *flags, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ return beg;
+}
+
+
+/* parse_list • parsing ordered or unordered list block */
+static size_t
+parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags)
+{
+ struct buf *work = 0;
+ size_t i = 0, j;
+
+ work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+ while (i < size) {
+ j = parse_listitem(work, rndr, data + i, size - i, &flags);
+ i += j;
+
+ if (!j || (flags & MKD_LI_END))
+ break;
+ }
+
+ if (rndr->cb.list)
+ rndr->cb.list(ob, work, flags, rndr->opaque);
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ return i;
+}
+
+/* parse_atxheader • parsing of atx-style headers */
+static size_t
+parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t level = 0;
+ size_t i, end, skip;
+
+ while (level < size && level < 6 && data[level] == '#')
+ level++;
+
+ for (i = level; i < size && data[i] == ' '; i++);
+
+ for (end = i; end < size && data[end] != '\n'; end++);
+ skip = end;
+
+ while (end && data[end - 1] == '#')
+ end--;
+
+ while (end && data[end - 1] == ' ')
+ end--;
+
+ if (end > i) {
+ struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ parse_inline(work, rndr, data + i, end - i);
+
+ if (rndr->cb.header)
+ rndr->cb.header(ob, work, (int)level, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ }
+
+ return skip;
+}
+
+
+/* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
+/* returns the length on match, 0 otherwise */
+static size_t
+htmlblock_end_tag(
+ const char *tag,
+ size_t tag_len,
+ struct sd_markdown *rndr,
+ uint8_t *data,
+ size_t size)
+{
+ size_t i, w;
+
+ /* checking if tag is a match */
+ if (tag_len + 3 >= size ||
+ strncasecmp((char *)data + 2, tag, tag_len) != 0 ||
+ data[tag_len + 2] != '>')
+ return 0;
+
+ /* checking white lines */
+ i = tag_len + 3;
+ w = 0;
+ if (i < size && (w = is_empty(data + i, size - i)) == 0)
+ return 0; /* non-blank after tag */
+ i += w;
+ w = 0;
+
+ if (i < size)
+ w = is_empty(data + i, size - i);
+
+ return i + w;
+}
+
+static size_t
+htmlblock_end(const char *curtag,
+ struct sd_markdown *rndr,
+ uint8_t *data,
+ size_t size,
+ int start_of_line)
+{
+ size_t tag_size = strlen(curtag);
+ size_t i = 1, end_tag;
+ int block_lines = 0;
+
+ while (i < size) {
+ i++;
+ while (i < size && !(data[i - 1] == '<' && data[i] == '/')) {
+ if (data[i] == '\n')
+ block_lines++;
+
+ i++;
+ }
+
+ /* If we are only looking for unindented tags, skip the tag
+ * if it doesn't follow a newline.
+ *
+ * The only exception to this is if the tag is still on the
+ * initial line; in that case it still counts as a closing
+ * tag
+ */
+ if (start_of_line && block_lines > 0 && data[i - 2] != '\n')
+ continue;
+
+ if (i + 2 + tag_size >= size)
+ break;
+
+ end_tag = htmlblock_end_tag(curtag, tag_size, rndr, data + i - 1, size - i + 1);
+ if (end_tag)
+ return i + end_tag - 1;
+ }
+
+ return 0;
+}
+
+
+/* parse_htmlblock • parsing of inline HTML block */
+static size_t
+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render)
+{
+ size_t i, j = 0, tag_end;
+ const char *curtag = NULL;
+ struct buf work = { data, 0, 0, 0 };
+
+ /* identification of the opening tag */
+ if (size < 2 || data[0] != '<')
+ return 0;
+
+ i = 1;
+ while (i < size && data[i] != '>' && data[i] != ' ')
+ i++;
+
+ if (i < size)
+ curtag = find_block_tag((char *)data + 1, (int)i - 1);
+
+ /* handling of special cases */
+ if (!curtag) {
+
+ /* HTML comment, laxist form */
+ if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') {
+ i = 5;
+
+ while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>'))
+ i++;
+
+ i++;
+
+ if (i < size)
+ j = is_empty(data + i, size - i);
+
+ if (j) {
+ work.size = i + j;
+ if (do_render && rndr->cb.blockhtml)
+ rndr->cb.blockhtml(ob, &work, rndr->opaque);
+ return work.size;
+ }
+ }
+
+ /* HR, which is the only self-closing block tag considered */
+ if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
+ i = 3;
+ while (i < size && data[i] != '>')
+ i++;
+
+ if (i + 1 < size) {
+ i++;
+ j = is_empty(data + i, size - i);
+ if (j) {
+ work.size = i + j;
+ if (do_render && rndr->cb.blockhtml)
+ rndr->cb.blockhtml(ob, &work, rndr->opaque);
+ return work.size;
+ }
+ }
+ }
+
+ /* no special case recognised */
+ return 0;
+ }
+
+ /* looking for an unindented matching closing tag */
+ /* followed by a blank line */
+ tag_end = htmlblock_end(curtag, rndr, data, size, 1);
+
+ /* if not found, trying a second pass looking for indented match */
+ /* but not if tag is "ins" or "del" (following original Markdown.pl) */
+ if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {
+ tag_end = htmlblock_end(curtag, rndr, data, size, 0);
+ }
+
+ if (!tag_end)
+ return 0;
+
+ /* the end of the block has been found */
+ work.size = tag_end;
+ if (do_render && rndr->cb.blockhtml)
+ rndr->cb.blockhtml(ob, &work, rndr->opaque);
+
+ return tag_end;
+}
+
+static void
+parse_table_row(
+ struct buf *ob,
+ struct sd_markdown *rndr,
+ uint8_t *data,
+ size_t size,
+ size_t columns,
+ int *col_data,
+ int header_flag)
+{
+ size_t i = 0, col;
+ struct buf *row_work = 0;
+
+ if (!rndr->cb.table_cell || !rndr->cb.table_row)
+ return;
+
+ row_work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ if (i < size && data[i] == '|')
+ i++;
+
+ for (col = 0; col < columns && i < size; ++col) {
+ size_t cell_start, cell_end;
+ struct buf *cell_work;
+
+ cell_work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+ while (i < size && _isspace(data[i]))
+ i++;
+
+ cell_start = i;
+
+ while (i < size && data[i] != '|')
+ i++;
+
+ cell_end = i - 1;
+
+ while (cell_end > cell_start && _isspace(data[cell_end]))
+ cell_end--;
+
+ parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);
+ rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ i++;
+ }
+
+ for (; col < columns; ++col) {
+ struct buf empty_cell = { 0, 0, 0, 0 };
+ rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);
+ }
+
+ rndr->cb.table_row(ob, row_work, rndr->opaque);
+
+ rndr_popbuf(rndr, BUFFER_SPAN);
+}
+
+static size_t
+parse_table_header(
+ struct buf *ob,
+ struct sd_markdown *rndr,
+ uint8_t *data,
+ size_t size,
+ size_t *columns,
+ int **column_data)
+{
+ int pipes;
+ size_t i = 0, col, header_end, under_end;
+
+ pipes = 0;
+ while (i < size && data[i] != '\n')
+ if (data[i++] == '|')
+ pipes++;
+
+ if (i == size || pipes == 0)
+ return 0;
+
+ header_end = i;
+
+ while (header_end > 0 && _isspace(data[header_end - 1]))
+ header_end--;
+
+ if (data[0] == '|')
+ pipes--;
+
+ if (header_end && data[header_end - 1] == '|')
+ pipes--;
+
+ *columns = pipes + 1;
+ *column_data = calloc(*columns, sizeof(int));
+
+ /* Parse the header underline */
+ i++;
+ if (i < size && data[i] == '|')
+ i++;
+
+ under_end = i;
+ while (under_end < size && data[under_end] != '\n')
+ under_end++;
+
+ for (col = 0; col < *columns && i < under_end; ++col) {
+ size_t dashes = 0;
+
+ while (i < under_end && data[i] == ' ')
+ i++;
+
+ if (data[i] == ':') {
+ i++; (*column_data)[col] |= MKD_TABLE_ALIGN_L;
+ dashes++;
+ }
+
+ while (i < under_end && data[i] == '-') {
+ i++; dashes++;
+ }
+
+ if (i < under_end && data[i] == ':') {
+ i++; (*column_data)[col] |= MKD_TABLE_ALIGN_R;
+ dashes++;
+ }
+
+ while (i < under_end && data[i] == ' ')
+ i++;
+
+ if (i < under_end && data[i] != '|')
+ break;
+
+ if (dashes < 3)
+ break;
+
+ i++;
+ }
+
+ if (col < *columns)
+ return 0;
+
+ parse_table_row(
+ ob, rndr, data,
+ header_end,
+ *columns,
+ *column_data,
+ MKD_TABLE_HEADER
+ );
+
+ return under_end + 1;
+}
+
+static size_t
+parse_table(
+ struct buf *ob,
+ struct sd_markdown *rndr,
+ uint8_t *data,
+ size_t size)
+{
+ size_t i;
+
+ struct buf *header_work = 0;
+ struct buf *body_work = 0;
+
+ size_t columns;
+ int *col_data = NULL;
+
+ header_work = rndr_newbuf(rndr, BUFFER_SPAN);
+ body_work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+ i = parse_table_header(header_work, rndr, data, size, &columns, &col_data);
+ if (i > 0) {
+
+ while (i < size) {
+ size_t row_start;
+ int pipes = 0;
+
+ row_start = i;
+
+ while (i < size && data[i] != '\n')
+ if (data[i++] == '|')
+ pipes++;
+
+ if (pipes == 0 || i == size) {
+ i = row_start;
+ break;
+ }
+
+ parse_table_row(
+ body_work,
+ rndr,
+ data + row_start,
+ i - row_start,
+ columns,
+ col_data, 0
+ );
+
+ i++;
+ }
+
+ if (rndr->cb.table)
+ rndr->cb.table(ob, header_work, body_work, rndr->opaque);
+ }
+
+ free(col_data);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+ rndr_popbuf(rndr, BUFFER_BLOCK);
+ return i;
+}
+
+/* parse_block • parsing of one block, returning next uint8_t to parse */
+static void
+parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+ size_t beg, end, i;
+ uint8_t *txt_data;
+ beg = 0;
+
+ if (rndr->work_bufs[BUFFER_SPAN].size +
+ rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
+ return;
+
+ while (beg < size) {
+ txt_data = data + beg;
+ end = size - beg;
+
+ if (is_atxheader(rndr, txt_data, end))
+ beg += parse_atxheader(ob, rndr, txt_data, end);
+
+ else if (data[beg] == '<' && rndr->cb.blockhtml &&
+ (i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0)
+ beg += i;
+
+ else if ((i = is_empty(txt_data, end)) != 0)
+ beg += i;
+
+ else if (is_hrule(txt_data, end)) {
+ if (rndr->cb.hrule)
+ rndr->cb.hrule(ob, rndr->opaque);
+
+ while (beg < size && data[beg] != '\n')
+ beg++;
+
+ beg++;
+ }
+
+ else if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&
+ (i = parse_fencedcode(ob, rndr, txt_data, end)) != 0)
+ beg += i;
+
+ else if ((rndr->ext_flags & MKDEXT_TABLES) != 0 &&
+ (i = parse_table(ob, rndr, txt_data, end)) != 0)
+ beg += i;
+
+ else if (prefix_quote(txt_data, end))
+ beg += parse_blockquote(ob, rndr, txt_data, end);
+
+ else if (prefix_code(txt_data, end))
+ beg += parse_blockcode(ob, rndr, txt_data, end);
+
+ else if (prefix_uli(txt_data, end))
+ beg += parse_list(ob, rndr, txt_data, end, 0);
+
+ else if (prefix_oli(txt_data, end))
+ beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
+
+ else
+ beg += parse_paragraph(ob, rndr, txt_data, end);
+ }
+}
+
+
+
+/*********************
+ * REFERENCE PARSING *
+ *********************/
+
+/* is_ref • returns whether a line is a reference or not */
+static int
+is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
+{
+/* int n; */
+ size_t i = 0;
+ size_t id_offset, id_end;
+ size_t link_offset, link_end;
+ size_t title_offset, title_end;
+ size_t line_end;
+
+ /* up to 3 optional leading spaces */
+ if (beg + 3 >= end) return 0;
+ if (data[beg] == ' ') { i = 1;
+ if (data[beg + 1] == ' ') { i = 2;
+ if (data[beg + 2] == ' ') { i = 3;
+ if (data[beg + 3] == ' ') return 0; } } }
+ i += beg;
+
+ /* id part: anything but a newline between brackets */
+ if (data[i] != '[') return 0;
+ i++;
+ id_offset = i;
+ while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
+ i++;
+ if (i >= end || data[i] != ']') return 0;
+ id_end = i;
+
+ /* spacer: colon (space | tab)* newline? (space | tab)* */
+ i++;
+ if (i >= end || data[i] != ':') return 0;
+ i++;
+ while (i < end && data[i] == ' ') i++;
+ if (i < end && (data[i] == '\n' || data[i] == '\r')) {
+ i++;
+ if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
+ while (i < end && data[i] == ' ') i++;
+ if (i >= end) return 0;
+
+ /* link: whitespace-free sequence, optionally between angle brackets */
+ if (data[i] == '<')
+ i++;
+
+ link_offset = i;
+
+ while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')
+ i++;
+
+ if (data[i - 1] == '>') link_end = i - 1;
+ else link_end = i;
+
+ /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
+ while (i < end && data[i] == ' ') i++;
+ if (i < end && data[i] != '\n' && data[i] != '\r'
+ && data[i] != '\'' && data[i] != '"' && data[i] != '(')
+ return 0;
+ line_end = 0;
+ /* computing end-of-line */
+ if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i;
+ if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+ line_end = i + 1;
+
+ /* optional (space|tab)* spacer after a newline */
+ if (line_end) {
+ i = line_end + 1;
+ while (i < end && data[i] == ' ') i++; }
+
+ /* optional title: any non-newline sequence enclosed in '"()
+ alone on its line */
+ title_offset = title_end = 0;
+ if (i + 1 < end
+ && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
+ i++;
+ title_offset = i;
+ /* looking for EOL */
+ while (i < end && data[i] != '\n' && data[i] != '\r') i++;
+ if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+ title_end = i + 1;
+ else title_end = i;
+ /* stepping back */
+ i -= 1;
+ while (i > title_offset && data[i] == ' ')
+ i -= 1;
+ if (i > title_offset
+ && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
+ line_end = title_end;
+ title_end = i; } }
+
+ if (!line_end || link_end == link_offset)
+ return 0; /* garbage after the link empty link */
+
+ /* a valid ref has been found, filling-in return structures */
+ if (last)
+ *last = line_end;
+
+ if (refs) {
+ struct link_ref *ref;
+
+ ref = add_link_ref(refs, data + id_offset, id_end - id_offset);
+ if (!ref)
+ return 0;
+
+ ref->link = bufnew(link_end - link_offset);
+ bufput(ref->link, data + link_offset, link_end - link_offset);
+
+ if (title_end > title_offset) {
+ ref->title = bufnew(title_end - title_offset);
+ bufput(ref->title, data + title_offset, title_end - title_offset);
+ }
+ }
+
+ return 1;
+}
+
+static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size)
+{
+ size_t i = 0, tab = 0;
+
+ while (i < size) {
+ size_t org = i;
+
+ while (i < size && line[i] != '\t') {
+ i++; tab++;
+ }
+
+ if (i > org)
+ bufput(ob, line + org, i - org);
+
+ if (i >= size)
+ break;
+
+ do {
+ bufputc(ob, ' '); tab++;
+ } while (tab % 4);
+
+ i++;
+ }
+}
+
+/**********************
+ * EXPORTED FUNCTIONS *
+ **********************/
+
+struct sd_markdown *
+sd_markdown_new(
+ unsigned int extensions,
+ size_t max_nesting,
+ const struct sd_callbacks *callbacks,
+ void *opaque)
+{
+ struct sd_markdown *md = NULL;
+
+ assert(max_nesting > 0 && callbacks);
+
+ md = malloc(sizeof(struct sd_markdown));
+ if (!md)
+ return NULL;
+
+ memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));
+
+ stack_init(&md->work_bufs[BUFFER_BLOCK], 4);
+ stack_init(&md->work_bufs[BUFFER_SPAN], 8);
+
+ memset(md->active_char, 0x0, 256);
+
+ if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) {
+ md->active_char['*'] = MD_CHAR_EMPHASIS;
+ md->active_char['_'] = MD_CHAR_EMPHASIS;
+ if (extensions & MKDEXT_STRIKETHROUGH)
+ md->active_char['~'] = MD_CHAR_EMPHASIS;
+ }
+
+ if (md->cb.codespan)
+ md->active_char['`'] = MD_CHAR_CODESPAN;
+
+ if (md->cb.linebreak)
+ md->active_char['\n'] = MD_CHAR_LINEBREAK;
+
+ if (md->cb.image || md->cb.link)
+ md->active_char['['] = MD_CHAR_LINK;
+
+ md->active_char['<'] = MD_CHAR_LANGLE;
+ md->active_char['\\'] = MD_CHAR_ESCAPE;
+ md->active_char['&'] = MD_CHAR_ENTITITY;
+
+ if (extensions & MKDEXT_AUTOLINK) {
+ md->active_char[':'] = MD_CHAR_AUTOLINK_URL;
+ md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
+ md->active_char['w'] = MD_CHAR_AUTOLINK_WWW;
+ }
+
+ if (extensions & MKDEXT_SUPERSCRIPT)
+ md->active_char['^'] = MD_CHAR_SUPERSCRIPT;
+
+ /* Extension data */
+ md->ext_flags = extensions;
+ md->opaque = opaque;
+ md->max_nesting = max_nesting;
+ md->in_link_body = 0;
+
+ return md;
+}
+
+void
+sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md)
+{
+#define MARKDOWN_GROW(x) ((x) + ((x) >> 1))
+ static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
+
+ struct buf *text;
+ size_t beg, end;
+
+ text = bufnew(64);
+ if (!text)
+ return;
+
+ /* Preallocate enough space for our buffer to avoid expanding while copying */
+ bufgrow(text, doc_size);
+
+ /* reset the references table */
+ memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
+
+ /* first pass: looking for references, copying everything else */
+ beg = 0;
+
+ /* Skip a possible UTF-8 BOM, even though the Unicode standard
+ * discourages having these in UTF-8 documents */
+ if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0)
+ beg += 3;
+
+ while (beg < doc_size) /* iterating over lines */
+ if (is_ref(document, beg, doc_size, &end, md->refs))
+ beg = end;
+ else { /* skipping to the next line */
+ end = beg;
+ while (end < doc_size && document[end] != '\n' && document[end] != '\r')
+ end++;
+
+ /* adding the line body if present */
+ if (end > beg)
+ expand_tabs(text, document + beg, end - beg);
+
+ while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {
+ /* add one \n per newline */
+ if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))
+ bufputc(text, '\n');
+ end++;
+ }
+
+ beg = end;
+ }
+
+ /* pre-grow the output buffer to minimize allocations */
+ bufgrow(ob, MARKDOWN_GROW(text->size));
+
+ /* second pass: actual rendering */
+ if (md->cb.doc_header)
+ md->cb.doc_header(ob, md->opaque);
+
+ if (text->size) {
+ /* adding a final newline if not already present */
+ if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r')
+ bufputc(text, '\n');
+
+ parse_block(ob, md, text->data, text->size);
+ }
+
+ if (md->cb.doc_footer)
+ md->cb.doc_footer(ob, md->opaque);
+
+ /* clean-up */
+ bufrelease(text);
+ free_link_refs(md->refs);
+
+ assert(md->work_bufs[BUFFER_SPAN].size == 0);
+ assert(md->work_bufs[BUFFER_BLOCK].size == 0);
+}
+
+void
+sd_markdown_free(struct sd_markdown *md)
+{
+ size_t i;
+
+ for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i)
+ bufrelease(md->work_bufs[BUFFER_SPAN].item[i]);
+
+ for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i)
+ bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);
+
+ stack_free(&md->work_bufs[BUFFER_SPAN]);
+ stack_free(&md->work_bufs[BUFFER_BLOCK]);
+
+ free(md);
+}
+
+void
+sd_version(int *ver_major, int *ver_minor, int *ver_revision)
+{
+ *ver_major = SUNDOWN_VER_MAJOR;
+ *ver_minor = SUNDOWN_VER_MINOR;
+ *ver_revision = SUNDOWN_VER_REVISION;
+}
+
+/* vim: set filetype=c: */
diff --git a/samples/c/process.c b/samples/c/process.c
new file mode 100644
index 0000000..d3eb998
--- /dev/null
+++ b/samples/c/process.c
@@ -0,0 +1,462 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * 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.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#ifdef __APPLE__
+# include <TargetConditionals.h>
+#endif
+
+#if defined(__APPLE__) && !TARGET_OS_IPHONE
+# include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#else
+extern char **environ;
+#endif
+
+
+static void uv__chld(EV_P_ ev_child* watcher, int revents) {
+ int status = watcher->rstatus;
+ int exit_status = 0;
+ int term_signal = 0;
+ uv_process_t *process = watcher->data;
+
+ assert(&process->child_watcher == watcher);
+ assert(revents & EV_CHILD);
+
+ ev_child_stop(EV_A_ &process->child_watcher);
+
+ if (WIFEXITED(status)) {
+ exit_status = WEXITSTATUS(status);
+ }
+
+ if (WIFSIGNALED(status)) {
+ term_signal = WTERMSIG(status);
+ }
+
+ if (process->exit_cb) {
+ process->exit_cb(process, exit_status, term_signal);
+ }
+}
+
+
+int uv__make_socketpair(int fds[2], int flags) {
+#ifdef SOCK_NONBLOCK
+ int fl;
+
+ fl = SOCK_CLOEXEC;
+
+ if (flags & UV__F_NONBLOCK)
+ fl |= SOCK_NONBLOCK;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM|fl, 0, fds) == 0)
+ return 0;
+
+ if (errno != EINVAL)
+ return -1;
+
+ /* errno == EINVAL so maybe the kernel headers lied about
+ * the availability of SOCK_NONBLOCK. This can happen if people
+ * build libuv against newer kernel headers than the kernel
+ * they actually run the software on.
+ */
+#endif
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
+ return -1;
+
+ uv__cloexec(fds[0], 1);
+ uv__cloexec(fds[1], 1);
+
+ if (flags & UV__F_NONBLOCK) {
+ uv__nonblock(fds[0], 1);
+ uv__nonblock(fds[1], 1);
+ }
+
+ return 0;
+}
+
+
+int uv__make_pipe(int fds[2], int flags) {
+#if __linux__
+ int fl;
+
+ fl = UV__O_CLOEXEC;
+
+ if (flags & UV__F_NONBLOCK)
+ fl |= UV__O_NONBLOCK;
+
+ if (uv__pipe2(fds, fl) == 0)
+ return 0;
+
+ if (errno != ENOSYS)
+ return -1;
+#endif
+
+ if (pipe(fds))
+ return -1;
+
+ uv__cloexec(fds[0], 1);
+ uv__cloexec(fds[1], 1);
+
+ if (flags & UV__F_NONBLOCK) {
+ uv__nonblock(fds[0], 1);
+ uv__nonblock(fds[1], 1);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Used for initializing stdio streams like options.stdin_stream. Returns
+ * zero on success.
+ */
+static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
+ int writable) {
+ int fd = -1;
+ switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
+ UV_INHERIT_STREAM)) {
+ case UV_IGNORE:
+ return 0;
+ case UV_CREATE_PIPE:
+ assert(container->data.stream != NULL);
+
+ if (container->data.stream->type != UV_NAMED_PIPE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return uv__make_socketpair(fds, 0);
+ case UV_INHERIT_FD:
+ case UV_INHERIT_STREAM:
+ if (container->flags & UV_INHERIT_FD) {
+ fd = container->data.fd;
+ } else {
+ fd = container->data.stream->fd;
+ }
+
+ if (fd == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fds[writable ? 1 : 0] = fd;
+
+ return 0;
+ default:
+ assert(0 && "Unexpected flags");
+ return -1;
+ }
+}
+
+
+static int uv__process_stdio_flags(uv_stdio_container_t* container,
+ int writable) {
+ if (container->data.stream->type == UV_NAMED_PIPE &&
+ ((uv_pipe_t*)container->data.stream)->ipc) {
+ return UV_STREAM_READABLE | UV_STREAM_WRITABLE;
+ } else if (writable) {
+ return UV_STREAM_WRITABLE;
+ } else {
+ return UV_STREAM_READABLE;
+ }
+}
+
+
+static int uv__process_open_stream(uv_stdio_container_t* container, int fds[2],
+ int writable) {
+ int fd = fds[writable ? 1 : 0];
+ int child_fd = fds[writable ? 0 : 1];
+ int flags;
+
+ /* No need to create stream */
+ if (!(container->flags & UV_CREATE_PIPE) || fd < 0) {
+ return 0;
+ }
+
+ assert(child_fd >= 0);
+ close(child_fd);
+
+ uv__nonblock(fd, 1);
+ flags = uv__process_stdio_flags(container, writable);
+
+ return uv__stream_open((uv_stream_t*)container->data.stream, fd, flags);
+}
+
+
+static void uv__process_close_stream(uv_stdio_container_t* container) {
+ if (!(container->flags & UV_CREATE_PIPE)) return;
+
+ uv__stream_close((uv_stream_t*)container->data.stream);
+}
+
+
+static void uv__process_child_init(uv_process_options_t options,
+ int stdio_count,
+ int* pipes) {
+ int i;
+
+ if (options.flags & UV_PROCESS_DETACHED) {
+ setsid();
+ }
+
+ /* Dup fds */
+ for (i = 0; i < stdio_count; i++) {
+ /*
+ * stdin has swapped ends of pipe
+ * (it's the only one readable stream)
+ */
+ int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2];
+ int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1];
+
+ if (use_fd >= 0) {
+ close(close_fd);
+ } else if (i < 3) {
+ /* `/dev/null` stdin, stdout, stderr even if they've flag UV_IGNORE */
+ use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);
+
+ if (use_fd < 0) {
+ perror("failed to open stdio");
+ _exit(127);
+ }
+ } else {
+ continue;
+ }
+
+ if (i != use_fd) {
+ dup2(use_fd, i);
+ close(use_fd);
+ }
+ }
+
+ if (options.cwd && chdir(options.cwd)) {
+ perror("chdir()");
+ _exit(127);
+ }
+
+ if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
+ perror("setgid()");
+ _exit(127);
+ }
+
+ if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
+ perror("setuid()");
+ _exit(127);
+ }
+
+ environ = options.env;
+
+ execvp(options.file, options.args);
+ perror("execvp()");
+ _exit(127);
+}
+
+
+#ifndef SPAWN_WAIT_EXEC
+# define SPAWN_WAIT_EXEC 1
+#endif
+
+int uv_spawn(uv_loop_t* loop, uv_process_t* process,
+ uv_process_options_t options) {
+ /*
+ * Save environ in the case that we get it clobbered
+ * by the child process.
+ */
+ char** save_our_env = environ;
+
+ int stdio_count = options.stdio_count < 3 ? 3 : options.stdio_count;
+ int* pipes = malloc(2 * stdio_count * sizeof(int));
+
+#if SPAWN_WAIT_EXEC
+ int signal_pipe[2] = { -1, -1 };
+ struct pollfd pfd;
+#endif
+ int status;
+ pid_t pid;
+ int i;
+
+ if (pipes == NULL) {
+ errno = ENOMEM;
+ goto error;
+ }
+
+ assert(options.file != NULL);
+ assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
+ UV_PROCESS_DETACHED |
+ UV_PROCESS_SETGID |
+ UV_PROCESS_SETUID)));
+
+
+ uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
+ loop->counters.process_init++;
+ uv__handle_start(process);
+
+ process->exit_cb = options.exit_cb;
+
+ /* Init pipe pairs */
+ for (i = 0; i < stdio_count; i++) {
+ pipes[i * 2] = -1;
+ pipes[i * 2 + 1] = -1;
+ }
+
+ /* Create socketpairs/pipes, or use raw fd */
+ for (i = 0; i < options.stdio_count; i++) {
+ if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) {
+ goto error;
+ }
+ }
+
+ /* This pipe is used by the parent to wait until
+ * the child has called `execve()`. We need this
+ * to avoid the following race condition:
+ *
+ * if ((pid = fork()) > 0) {
+ * kill(pid, SIGTERM);
+ * }
+ * else if (pid == 0) {
+ * execve("/bin/cat", argp, envp);
+ * }
+ *
+ * The parent sends a signal immediately after forking.
+ * Since the child may not have called `execve()` yet,
+ * there is no telling what process receives the signal,
+ * our fork or /bin/cat.
+ *
+ * To avoid ambiguity, we create a pipe with both ends
+ * marked close-on-exec. Then, after the call to `fork()`,
+ * the parent polls the read end until it sees POLLHUP.
+ */
+#if SPAWN_WAIT_EXEC
+ if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
+ goto error;
+#endif
+
+ pid = fork();
+
+ if (pid == -1) {
+#if SPAWN_WAIT_EXEC
+ close(signal_pipe[0]);
+ close(signal_pipe[1]);
+#endif
+ environ = save_our_env;
+ goto error;
+ }
+
+ if (pid == 0) {
+ /* Child */
+ uv__process_child_init(options, stdio_count, pipes);
+
+ /* Execution never reaches here. */
+ }
+
+ /* Parent. */
+
+ /* Restore environment. */
+ environ = save_our_env;
+
+#if SPAWN_WAIT_EXEC
+ /* POLLHUP signals child has exited or execve()'d. */
+ close(signal_pipe[1]);
+ do {
+ pfd.fd = signal_pipe[0];
+ pfd.events = POLLIN|POLLHUP;
+ pfd.revents = 0;
+ errno = 0, status = poll(&pfd, 1, -1);
+ }
+ while (status == -1 && (errno == EINTR || errno == ENOMEM));
+
+ assert((status == 1) && "poll() on pipe read end failed");
+ close(signal_pipe[0]);
+#endif
+
+ process->pid = pid;
+
+ ev_child_init(&process->child_watcher, uv__chld, pid, 0);
+ ev_child_start(process->loop->ev, &process->child_watcher);
+ process->child_watcher.data = process;
+
+ for (i = 0; i < options.stdio_count; i++) {
+ if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
+ int j;
+ /* Close all opened streams */
+ for (j = 0; j < i; j++) {
+ uv__process_close_stream(&options.stdio[j]);
+ }
+
+ goto error;
+ }
+ }
+
+ free(pipes);
+
+ return 0;
+
+error:
+ uv__set_sys_error(process->loop, errno);
+
+ for (i = 0; i < stdio_count; i++) {
+ close(pipes[i * 2]);
+ close(pipes[i * 2 + 1]);
+ }
+
+ free(pipes);
+
+ return -1;
+}
+
+
+int uv_process_kill(uv_process_t* process, int signum) {
+ int r = kill(process->pid, signum);
+
+ if (r) {
+ uv__set_sys_error(process->loop, errno);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+uv_err_t uv_kill(int pid, int signum) {
+ int r = kill(pid, signum);
+
+ if (r) {
+ return uv__new_sys_error(errno);
+ } else {
+ return uv_ok_;
+ }
+}
+
+
+void uv__process_close(uv_process_t* handle) {
+ ev_child_stop(handle->loop->ev, &handle->child_watcher);
+ uv__handle_stop(handle);
+}
diff --git a/samples/c/rdiscount.c b/samples/c/rdiscount.c
new file mode 100644
index 0000000..86167ff
--- /dev/null
+++ b/samples/c/rdiscount.c
@@ -0,0 +1,129 @@
+#include <stdio.h>
+#include "ruby.h"
+#include "mkdio.h"
+
+static VALUE rb_cRDiscount;
+
+static VALUE
+rb_rdiscount_to_html(int argc, VALUE *argv, VALUE self)
+{
+ /* grab char pointer to markdown input text */
+ char *res;
+ int szres;
+ VALUE encoding;
+ VALUE text = rb_funcall(self, rb_intern("text"), 0);
+ VALUE buf = rb_str_buf_new(1024);
+ Check_Type(text, T_STRING);
+
+ int flags = rb_rdiscount__get_flags(self);
+
+ MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
+
+ if ( mkd_compile(doc, flags) ) {
+ szres = mkd_document(doc, &res);
+
+ if ( szres != EOF ) {
+ rb_str_cat(buf, res, szres);
+ rb_str_cat(buf, "\n", 1);
+ }
+ }
+ mkd_cleanup(doc);
+
+
+ /* force the input encoding */
+ if ( rb_respond_to(text, rb_intern("encoding")) ) {
+ encoding = rb_funcall(text, rb_intern("encoding"), 0);
+ rb_funcall(buf, rb_intern("force_encoding"), 1, encoding);
+ }
+
+ return buf;
+}
+
+static VALUE
+rb_rdiscount_toc_content(int argc, VALUE *argv, VALUE self)
+{
+ char *res;
+ int szres;
+
+ int flags = rb_rdiscount__get_flags(self);
+
+ /* grab char pointer to markdown input text */
+ VALUE text = rb_funcall(self, rb_intern("text"), 0);
+ Check_Type(text, T_STRING);
+
+ /* allocate a ruby string buffer and wrap it in a stream */
+ VALUE buf = rb_str_buf_new(4096);
+
+ MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
+
+ if ( mkd_compile(doc, flags) ) {
+ szres = mkd_toc(doc, &res);
+
+ if ( szres != EOF ) {
+ rb_str_cat(buf, res, szres);
+ rb_str_cat(buf, "\n", 1);
+ }
+ }
+ mkd_cleanup(doc);
+
+ return buf;
+}
+
+int rb_rdiscount__get_flags(VALUE ruby_obj)
+{
+ /* compile flags */
+ int flags = MKD_TABSTOP | MKD_NOHEADER;
+
+ /* smart */
+ if ( rb_funcall(ruby_obj, rb_intern("smart"), 0) != Qtrue )
+ flags = flags | MKD_NOPANTS;
+
+ /* filter_html */
+ if ( rb_funcall(ruby_obj, rb_intern("filter_html"), 0) == Qtrue )
+ flags = flags | MKD_NOHTML;
+
+ /* generate_toc */
+ if ( rb_funcall(ruby_obj, rb_intern("generate_toc"), 0) == Qtrue)
+ flags = flags | MKD_TOC;
+
+ /* no_image */
+ if ( rb_funcall(ruby_obj, rb_intern("no_image"), 0) == Qtrue)
+ flags = flags | MKD_NOIMAGE;
+
+ /* no_links */
+ if ( rb_funcall(ruby_obj, rb_intern("no_links"), 0) == Qtrue)
+ flags = flags | MKD_NOLINKS;
+
+ /* no_tables */
+ if ( rb_funcall(ruby_obj, rb_intern("no_tables"), 0) == Qtrue)
+ flags = flags | MKD_NOTABLES;
+
+ /* strict */
+ if ( rb_funcall(ruby_obj, rb_intern("strict"), 0) == Qtrue)
+ flags = flags | MKD_STRICT;
+
+ /* autolink */
+ if ( rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
+ flags = flags | MKD_AUTOLINK;
+
+ /* safelink */
+ if ( rb_funcall(ruby_obj, rb_intern("safelink"), 0) == Qtrue)
+ flags = flags | MKD_SAFELINK;
+
+ /* no_pseudo_protocols */
+ if ( rb_funcall(ruby_obj, rb_intern("no_pseudo_protocols"), 0) == Qtrue)
+ flags = flags | MKD_NO_EXT;
+
+
+ return flags;
+}
+
+
+void Init_rdiscount()
+{
+ rb_cRDiscount = rb_define_class("RDiscount", rb_cObject);
+ rb_define_method(rb_cRDiscount, "to_html", rb_rdiscount_to_html, -1);
+ rb_define_method(rb_cRDiscount, "toc_content", rb_rdiscount_toc_content, -1);
+}
+
+/* vim: set ts=4 sw=4: */
diff --git a/samples/c/redis.c b/samples/c/redis.c
new file mode 100644
index 0000000..b1f8338
--- /dev/null
+++ b/samples/c/redis.c
@@ -0,0 +1,2538 @@
+/*
+ * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "redis.h"
+#include "slowlog.h"
+#include "bio.h"
+
+#include <time.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+
+/* Our shared "common" objects */
+
+struct sharedObjectsStruct shared;
+
+/* Global vars that are actually used as constants. The following double
+ * values are used for double on-disk serialization, and are initialized
+ * at runtime to avoid strange compiler optimizations. */
+
+double R_Zero, R_PosInf, R_NegInf, R_Nan;
+
+/*================================= Globals ================================= */
+
+/* Global vars */
+struct redisServer server; /* server global state */
+struct redisCommand *commandTable;
+
+/* Our command table.
+ *
+ * Every entry is composed of the following fields:
+ *
+ * name: a string representing the command name.
+ * function: pointer to the C function implementing the command.
+ * arity: number of arguments, it is possible to use -N to say >= N
+ * sflags: command flags as string. See below for a table of flags.
+ * flags: flags as bitmask. Computed by Redis using the 'sflags' field.
+ * get_keys_proc: an optional function to get key arguments from a command.
+ * This is only used when the following three fields are not
+ * enough to specify what arguments are keys.
+ * first_key_index: first argument that is a key
+ * last_key_index: last argument that is a key
+ * key_step: step to get all the keys from first to last argument. For instance
+ * in MSET the step is two since arguments are key,val,key,val,...
+ * microseconds: microseconds of total execution time for this command.
+ * calls: total number of calls of this command.
+ *
+ * The flags, microseconds and calls fields are computed by Redis and should
+ * always be set to zero.
+ *
+ * Command flags are expressed using strings where every character represents
+ * a flag. Later the populateCommandTable() function will take care of
+ * populating the real 'flags' field using this characters.
+ *
+ * This is the meaning of the flags:
+ *
+ * w: write command (may modify the key space).
+ * r: read command (will never modify the key space).
+ * m: may increase memory usage once called. Don't allow if out of memory.
+ * a: admin command, like SAVE or SHUTDOWN.
+ * p: Pub/Sub related command.
+ * f: force replication of this command, regarless of server.dirty.
+ * s: command not allowed in scripts.
+ * R: random command. Command is not deterministic, that is, the same command
+ * with the same arguments, with the same key space, may have different
+ * results. For instance SPOP and RANDOMKEY are two random commands.
+ * S: Sort command output array if called from script, so that the output
+ * is deterministic.
+ */
+struct redisCommand redisCommandTable[] = {
+ {"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
+ {"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
+ {"setex",setexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0},
+ {"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0},
+ {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"del",delCommand,-2,"w",0,noPreloadGetKeys,1,-1,1,0,0},
+ {"exists",existsCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"getbit",getbitCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+ {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+ {"incr",incrCommand,2,"wm",0,NULL,1,1,1,0,0},
+ {"decr",decrCommand,2,"wm",0,NULL,1,1,1,0,0},
+ {"mget",mgetCommand,-2,"r",0,NULL,1,-1,1,0,0},
+ {"rpush",rpushCommand,-3,"wm",0,NULL,1,1,1,0,0},
+ {"lpush",lpushCommand,-3,"wm",0,NULL,1,1,1,0,0},
+ {"rpushx",rpushxCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"lpushx",lpushxCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},
+ {"rpop",rpopCommand,2,"w",0,NULL,1,1,1,0,0},
+ {"lpop",lpopCommand,2,"w",0,NULL,1,1,1,0,0},
+ {"brpop",brpopCommand,-3,"ws",0,NULL,1,1,1,0,0},
+ {"brpoplpush",brpoplpushCommand,4,"wms",0,NULL,1,2,1,0,0},
+ {"blpop",blpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
+ {"llen",llenCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"lindex",lindexCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"lset",lsetCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"lrange",lrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+ {"ltrim",ltrimCommand,4,"w",0,NULL,1,1,1,0,0},
+ {"lrem",lremCommand,4,"w",0,NULL,1,1,1,0,0},
+ {"rpoplpush",rpoplpushCommand,3,"wm",0,NULL,1,2,1,0,0},
+ {"sadd",saddCommand,-3,"wm",0,NULL,1,1,1,0,0},
+ {"srem",sremCommand,-3,"w",0,NULL,1,1,1,0,0},
+ {"smove",smoveCommand,4,"w",0,NULL,1,2,1,0,0},
+ {"sismember",sismemberCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"scard",scardCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"spop",spopCommand,2,"wRs",0,NULL,1,1,1,0,0},
+ {"srandmember",srandmemberCommand,2,"rR",0,NULL,1,1,1,0,0},
+ {"sinter",sinterCommand,-2,"rS",0,NULL,1,-1,1,0,0},
+ {"sinterstore",sinterstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
+ {"sunion",sunionCommand,-2,"rS",0,NULL,1,-1,1,0,0},
+ {"sunionstore",sunionstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
+ {"sdiff",sdiffCommand,-2,"rS",0,NULL,1,-1,1,0,0},
+ {"sdiffstore",sdiffstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
+ {"smembers",sinterCommand,2,"rS",0,NULL,1,1,1,0,0},
+ {"zadd",zaddCommand,-4,"wm",0,NULL,1,1,1,0,0},
+ {"zincrby",zincrbyCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"zrem",zremCommand,-3,"w",0,NULL,1,1,1,0,0},
+ {"zremrangebyscore",zremrangebyscoreCommand,4,"w",0,NULL,1,1,1,0,0},
+ {"zremrangebyrank",zremrangebyrankCommand,4,"w",0,NULL,1,1,1,0,0},
+ {"zunionstore",zunionstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},
+ {"zinterstore",zinterstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},
+ {"zrange",zrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
+ {"zrangebyscore",zrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
+ {"zrevrangebyscore",zrevrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
+ {"zcount",zcountCommand,4,"r",0,NULL,1,1,1,0,0},
+ {"zrevrange",zrevrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
+ {"zcard",zcardCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"zscore",zscoreCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"zrank",zrankCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"zrevrank",zrevrankCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"hset",hsetCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"hsetnx",hsetnxCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"hget",hgetCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"hmset",hmsetCommand,-4,"wm",0,NULL,1,1,1,0,0},
+ {"hmget",hmgetCommand,-3,"r",0,NULL,1,1,1,0,0},
+ {"hincrby",hincrbyCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"hincrbyfloat",hincrbyfloatCommand,4,"wm",0,NULL,1,1,1,0,0},
+ {"hdel",hdelCommand,-3,"w",0,NULL,1,1,1,0,0},
+ {"hlen",hlenCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"hkeys",hkeysCommand,2,"rS",0,NULL,1,1,1,0,0},
+ {"hvals",hvalsCommand,2,"rS",0,NULL,1,1,1,0,0},
+ {"hgetall",hgetallCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"hexists",hexistsCommand,3,"r",0,NULL,1,1,1,0,0},
+ {"incrby",incrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"decrby",decrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"incrbyfloat",incrbyfloatCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},
+ {"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
+ {"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
+ {"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},
+ {"select",selectCommand,2,"r",0,NULL,0,0,0,0,0},
+ {"move",moveCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"rename",renameCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
+ {"renamenx",renamenxCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
+ {"expire",expireCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"expireat",expireatCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"pexpire",pexpireCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"pexpireat",pexpireatCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"keys",keysCommand,2,"rS",0,NULL,0,0,0,0,0},
+ {"dbsize",dbsizeCommand,1,"r",0,NULL,0,0,0,0,0},
+ {"auth",authCommand,2,"rs",0,NULL,0,0,0,0,0},
+ {"ping",pingCommand,1,"r",0,NULL,0,0,0,0,0},
+ {"echo",echoCommand,2,"r",0,NULL,0,0,0,0,0},
+ {"save",saveCommand,1,"ars",0,NULL,0,0,0,0,0},
+ {"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
+ {"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
+ {"shutdown",shutdownCommand,-1,"ar",0,NULL,0,0,0,0,0},
+ {"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0},
+ {"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
+ {"exec",execCommand,1,"s",0,NULL,0,0,0,0,0},
+ {"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0},
+ {"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
+ {"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
+ {"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},
+ {"sort",sortCommand,-2,"wmS",0,NULL,1,1,1,0,0},
+ {"info",infoCommand,-1,"r",0,NULL,0,0,0,0,0},
+ {"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0},
+ {"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
+ {"slaveof",slaveofCommand,3,"as",0,NULL,0,0,0,0,0},
+ {"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
+ {"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
+ {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
+ {"unsubscribe",unsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
+ {"psubscribe",psubscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
+ {"punsubscribe",punsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
+ {"publish",publishCommand,3,"pf",0,NULL,0,0,0,0,0},
+ {"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
+ {"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
+ {"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0},
+ {"restore",restoreCommand,4,"awm",0,NULL,1,1,1,0,0},
+ {"migrate",migrateCommand,6,"aw",0,NULL,0,0,0,0,0},
+ {"asking",askingCommand,1,"r",0,NULL,0,0,0,0,0},
+ {"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0},
+ {"object",objectCommand,-2,"r",0,NULL,2,2,2,0,0},
+ {"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0},
+ {"eval",evalCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
+ {"evalsha",evalShaCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
+ {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
+ {"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0},
+ {"time",timeCommand,1,"rR",0,NULL,0,0,0,0,0},
+ {"bitop",bitopCommand,-4,"wm",0,NULL,2,-1,1,0,0},
+ {"bitcount",bitcountCommand,-2,"r",0,NULL,1,1,1,0,0}
+};
+
+/*============================ Utility functions ============================ */
+
+/* Low level logging. To use only for very big messages, otherwise
+ * redisLog() is to prefer. */
+void redisLogRaw(int level, const char *msg) {
+ const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
+ const char *c = ".-*#";
+ FILE *fp;
+ char buf[64];
+ int rawmode = (level & REDIS_LOG_RAW);
+
+ level &= 0xff; /* clear flags */
+ if (level < server.verbosity) return;
+
+ fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a");
+ if (!fp) return;
+
+ if (rawmode) {
+ fprintf(fp,"%s",msg);
+ } else {
+ int off;
+ struct timeval tv;
+
+ gettimeofday(&tv,NULL);
+ off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec));
+ snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
+ fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg);
+ }
+ fflush(fp);
+
+ if (server.logfile) fclose(fp);
+
+ if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
+}
+
+/* Like redisLogRaw() but with printf-alike support. This is the funciton that
+ * is used across the code. The raw version is only used in order to dump
+ * the INFO output on crash. */
+void redisLog(int level, const char *fmt, ...) {
+ va_list ap;
+ char msg[REDIS_MAX_LOGMSG_LEN];
+
+ if ((level&0xff) < server.verbosity) return;
+
+ va_start(ap, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+
+ redisLogRaw(level,msg);
+}
+
+/* Log a fixed message without printf-alike capabilities, in a way that is
+ * safe to call from a signal handler.
+ *
+ * We actually use this only for signals that are not fatal from the point
+ * of view of Redis. Signals that are going to kill the server anyway and
+ * where we need printf-alike features are served by redisLog(). */
+void redisLogFromHandler(int level, const char *msg) {
+ int fd;
+ char buf[64];
+
+ if ((level&0xff) < server.verbosity ||
+ (server.logfile == NULL && server.daemonize)) return;
+ fd = server.logfile ?
+ open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) :
+ STDOUT_FILENO;
+ if (fd == -1) return;
+ ll2string(buf,sizeof(buf),getpid());
+ if (write(fd,"[",1) == -1) goto err;
+ if (write(fd,buf,strlen(buf)) == -1) goto err;
+ if (write(fd," | signal handler] (",20) == -1) goto err;
+ ll2string(buf,sizeof(buf),time(NULL));
+ if (write(fd,buf,strlen(buf)) == -1) goto err;
+ if (write(fd,") ",2) == -1) goto err;
+ if (write(fd,msg,strlen(msg)) == -1) goto err;
+ if (write(fd,"\n",1) == -1) goto err;
+err:
+ if (server.logfile) close(fd);
+}
+
+/* Redis generally does not try to recover from out of memory conditions
+ * when allocating objects or strings, it is not clear if it will be possible
+ * to report this condition to the client since the networking layer itself
+ * is based on heap allocation for send buffers, so we simply abort.
+ * At least the code will be simpler to read... */
+void oom(const char *msg) {
+ redisLog(REDIS_WARNING, "%s: Out of memory\n",msg);
+ sleep(1);
+ abort();
+}
+
+/* Return the UNIX time in microseconds */
+long long ustime(void) {
+ struct timeval tv;
+ long long ust;
+
+ gettimeofday(&tv, NULL);
+ ust = ((long long)tv.tv_sec)*1000000;
+ ust += tv.tv_usec;
+ return ust;
+}
+
+/* Return the UNIX time in milliseconds */
+long long mstime(void) {
+ return ustime()/1000;
+}
+
+/* After an RDB dump or AOF rewrite we exit from children using _exit() instead of
+ * exit(), because the latter may interact with the same file objects used by
+ * the parent process. However if we are testing the coverage normal exit() is
+ * used in order to obtain the right coverage information. */
+void exitFromChild(int retcode) {
+#ifdef COVERAGE_TEST
+ exit(retcode);
+#else
+ _exit(retcode);
+#endif
+}
+
+/*====================== Hash table type implementation ==================== */
+
+/* This is an hash table type that uses the SDS dynamic strings libary as
+ * keys and radis objects as values (objects can hold SDS strings,
+ * lists, sets). */
+
+void dictVanillaFree(void *privdata, void *val)
+{
+ DICT_NOTUSED(privdata);
+ zfree(val);
+}
+
+void dictListDestructor(void *privdata, void *val)
+{
+ DICT_NOTUSED(privdata);
+ listRelease((list*)val);
+}
+
+int dictSdsKeyCompare(void *privdata, const void *key1,
+ const void *key2)
+{
+ int l1,l2;
+ DICT_NOTUSED(privdata);
+
+ l1 = sdslen((sds)key1);
+ l2 = sdslen((sds)key2);
+ if (l1 != l2) return 0;
+ return memcmp(key1, key2, l1) == 0;
+}
+
+/* A case insensitive version used for the command lookup table. */
+int dictSdsKeyCaseCompare(void *privdata, const void *key1,
+ const void *key2)
+{
+ DICT_NOTUSED(privdata);
+
+ return strcasecmp(key1, key2) == 0;
+}
+
+void dictRedisObjectDestructor(void *privdata, void *val)
+{
+ DICT_NOTUSED(privdata);
+
+ if (val == NULL) return; /* Values of swapped out keys as set to NULL */
+ decrRefCount(val);
+}
+
+void dictSdsDestructor(void *privdata, void *val)
+{
+ DICT_NOTUSED(privdata);
+
+ sdsfree(val);
+}
+
+int dictObjKeyCompare(void *privdata, const void *key1,
+ const void *key2)
+{
+ const robj *o1 = key1, *o2 = key2;
+ return dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);
+}
+
+unsigned int dictObjHash(const void *key) {
+ const robj *o = key;
+ return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
+}
+
+unsigned int dictSdsHash(const void *key) {
+ return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
+}
+
+unsigned int dictSdsCaseHash(const void *key) {
+ return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key));
+}
+
+int dictEncObjKeyCompare(void *privdata, const void *key1,
+ const void *key2)
+{
+ robj *o1 = (robj*) key1, *o2 = (robj*) key2;
+ int cmp;
+
+ if (o1->encoding == REDIS_ENCODING_INT &&
+ o2->encoding == REDIS_ENCODING_INT)
+ return o1->ptr == o2->ptr;
+
+ o1 = getDecodedObject(o1);
+ o2 = getDecodedObject(o2);
+ cmp = dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);
+ decrRefCount(o1);
+ decrRefCount(o2);
+ return cmp;
+}
+
+unsigned int dictEncObjHash(const void *key) {
+ robj *o = (robj*) key;
+
+ if (o->encoding == REDIS_ENCODING_RAW) {
+ return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
+ } else {
+ if (o->encoding == REDIS_ENCODING_INT) {
+ char buf[32];
+ int len;
+
+ len = ll2string(buf,32,(long)o->ptr);
+ return dictGenHashFunction((unsigned char*)buf, len);
+ } else {
+ unsigned int hash;
+
+ o = getDecodedObject(o);
+ hash = dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
+ decrRefCount(o);
+ return hash;
+ }
+ }
+}
+
+/* Sets type hash table */
+dictType setDictType = {
+ dictEncObjHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictEncObjKeyCompare, /* key compare */
+ dictRedisObjectDestructor, /* key destructor */
+ NULL /* val destructor */
+};
+
+/* Sorted sets hash (note: a skiplist is used in addition to the hash table) */
+dictType zsetDictType = {
+ dictEncObjHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictEncObjKeyCompare, /* key compare */
+ dictRedisObjectDestructor, /* key destructor */
+ NULL /* val destructor */
+};
+
+/* Db->dict, keys are sds strings, vals are Redis objects. */
+dictType dbDictType = {
+ dictSdsHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictSdsKeyCompare, /* key compare */
+ dictSdsDestructor, /* key destructor */
+ dictRedisObjectDestructor /* val destructor */
+};
+
+/* Db->expires */
+dictType keyptrDictType = {
+ dictSdsHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictSdsKeyCompare, /* key compare */
+ NULL, /* key destructor */
+ NULL /* val destructor */
+};
+
+/* Command table. sds string -> command struct pointer. */
+dictType commandTableDictType = {
+ dictSdsCaseHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictSdsKeyCaseCompare, /* key compare */
+ dictSdsDestructor, /* key destructor */
+ NULL /* val destructor */
+};
+
+/* Hash type hash table (note that small hashes are represented with zimpaps) */
+dictType hashDictType = {
+ dictEncObjHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictEncObjKeyCompare, /* key compare */
+ dictRedisObjectDestructor, /* key destructor */
+ dictRedisObjectDestructor /* val destructor */
+};
+
+/* Keylist hash table type has unencoded redis objects as keys and
+ * lists as values. It's used for blocking operations (BLPOP) and to
+ * map swapped keys to a list of clients waiting for this keys to be loaded. */
+dictType keylistDictType = {
+ dictObjHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictObjKeyCompare, /* key compare */
+ dictRedisObjectDestructor, /* key destructor */
+ dictListDestructor /* val destructor */
+};
+
+/* Cluster nodes hash table, mapping nodes addresses 1.2.3.4:6379 to
+ * clusterNode structures. */
+dictType clusterNodesDictType = {
+ dictSdsHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictSdsKeyCompare, /* key compare */
+ dictSdsDestructor, /* key destructor */
+ NULL /* val destructor */
+};
+
+int htNeedsResize(dict *dict) {
+ long long size, used;
+
+ size = dictSlots(dict);
+ used = dictSize(dict);
+ return (size && used && size > DICT_HT_INITIAL_SIZE &&
+ (used*100/size < REDIS_HT_MINFILL));
+}
+
+/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
+ * we resize the hash table to save memory */
+void tryResizeHashTables(void) {
+ int j;
+
+ for (j = 0; j < server.dbnum; j++) {
+ if (htNeedsResize(server.db[j].dict))
+ dictResize(server.db[j].dict);
+ if (htNeedsResize(server.db[j].expires))
+ dictResize(server.db[j].expires);
+ }
+}
+
+/* Our hash table implementation performs rehashing incrementally while
+ * we write/read from the hash table. Still if the server is idle, the hash
+ * table will use two tables for a long time. So we try to use 1 millisecond
+ * of CPU time at every serverCron() loop in order to rehash some key. */
+void incrementallyRehash(void) {
+ int j;
+
+ for (j = 0; j < server.dbnum; j++) {
+ /* Keys dictionary */
+ if (dictIsRehashing(server.db[j].dict)) {
+ dictRehashMilliseconds(server.db[j].dict,1);
+ break; /* already used our millisecond for this loop... */
+ }
+ /* Expires */
+ if (dictIsRehashing(server.db[j].expires)) {
+ dictRehashMilliseconds(server.db[j].expires,1);
+ break; /* already used our millisecond for this loop... */
+ }
+ }
+}
+
+/* This function is called once a background process of some kind terminates,
+ * as we want to avoid resizing the hash tables when there is a child in order
+ * to play well with copy-on-write (otherwise when a resize happens lots of
+ * memory pages are copied). The goal of this function is to update the ability
+ * for dict.c to resize the hash tables accordingly to the fact we have o not
+ * running childs. */
+void updateDictResizePolicy(void) {
+ if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
+ dictEnableResize();
+ else
+ dictDisableResize();
+}
+
+/* ======================= Cron: called every 100 ms ======================== */
+
+/* Try to expire a few timed out keys. The algorithm used is adaptive and
+ * will use few CPU cycles if there are few expiring keys, otherwise
+ * it will get more aggressive to avoid that too much memory is used by
+ * keys that can be removed from the keyspace. */
+void activeExpireCycle(void) {
+ int j, iteration = 0;
+ long long start = ustime(), timelimit;
+
+ /* We can use at max REDIS_EXPIRELOOKUPS_TIME_PERC percentage of CPU time
+ * per iteration. Since this function gets called with a frequency of
+ * REDIS_HZ times per second, the following is the max amount of
+ * microseconds we can spend in this function. */
+ timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/REDIS_HZ/100;
+ if (timelimit <= 0) timelimit = 1;
+
+ for (j = 0; j < server.dbnum; j++) {
+ int expired;
+ redisDb *db = server.db+j;
+
+ /* Continue to expire if at the end of the cycle more than 25%
+ * of the keys were expired. */
+ do {
+ unsigned long num = dictSize(db->expires);
+ unsigned long slots = dictSlots(db->expires);
+ long long now = mstime();
+
+ /* When there are less than 1% filled slots getting random
+ * keys is expensive, so stop here waiting for better times...
+ * The dictionary will be resized asap. */
+ if (num && slots > DICT_HT_INITIAL_SIZE &&
+ (num*100/slots < 1)) break;
+
+ /* The main collection cycle. Sample random keys among keys
+ * with an expire set, checking for expired ones. */
+ expired = 0;
+ if (num > REDIS_EXPIRELOOKUPS_PER_CRON)
+ num = REDIS_EXPIRELOOKUPS_PER_CRON;
+ while (num--) {
+ dictEntry *de;
+ long long t;
+
+ if ((de = dictGetRandomKey(db->expires)) == NULL) break;
+ t = dictGetSignedIntegerVal(de);
+ if (now > t) {
+ sds key = dictGetKey(de);
+ robj *keyobj = createStringObject(key,sdslen(key));
+
+ propagateExpire(db,keyobj);
+ dbDelete(db,keyobj);
+ decrRefCount(keyobj);
+ expired++;
+ server.stat_expiredkeys++;
+ }
+ }
+ /* We can't block forever here even if there are many keys to
+ * expire. So after a given amount of milliseconds return to the
+ * caller waiting for the other active expire cycle. */
+ iteration++;
+ if ((iteration & 0xf) == 0 && /* check once every 16 cycles. */
+ (ustime()-start) > timelimit) return;
+ } while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4);
+ }
+}
+
+void updateLRUClock(void) {
+ server.lruclock = (server.unixtime/REDIS_LRU_CLOCK_RESOLUTION) &
+ REDIS_LRU_CLOCK_MAX;
+}
+
+
+/* Add a sample to the operations per second array of samples. */
+void trackOperationsPerSecond(void) {
+ long long t = mstime() - server.ops_sec_last_sample_time;
+ long long ops = server.stat_numcommands - server.ops_sec_last_sample_ops;
+ long long ops_sec;
+
+ ops_sec = t > 0 ? (ops*1000/t) : 0;
+
+ server.ops_sec_samples[server.ops_sec_idx] = ops_sec;
+ server.ops_sec_idx = (server.ops_sec_idx+1) % REDIS_OPS_SEC_SAMPLES;
+ server.ops_sec_last_sample_time = mstime();
+ server.ops_sec_last_sample_ops = server.stat_numcommands;
+}
+
+/* Return the mean of all the samples. */
+long long getOperationsPerSecond(void) {
+ int j;
+ long long sum = 0;
+
+ for (j = 0; j < REDIS_OPS_SEC_SAMPLES; j++)
+ sum += server.ops_sec_samples[j];
+ return sum / REDIS_OPS_SEC_SAMPLES;
+}
+
+/* Check for timeouts. Returns non-zero if the client was terminated */
+int clientsCronHandleTimeout(redisClient *c) {
+ time_t now = server.unixtime;
+
+ if (server.maxidletime &&
+ !(c->flags & REDIS_SLAVE) && /* no timeout for slaves */
+ !(c->flags & REDIS_MASTER) && /* no timeout for masters */
+ !(c->flags & REDIS_BLOCKED) && /* no timeout for BLPOP */
+ dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */
+ listLength(c->pubsub_patterns) == 0 &&
+ (now - c->lastinteraction > server.maxidletime))
+ {
+ redisLog(REDIS_VERBOSE,"Closing idle client");
+ freeClient(c);
+ return 1;
+ } else if (c->flags & REDIS_BLOCKED) {
+ if (c->bpop.timeout != 0 && c->bpop.timeout < now) {
+ addReply(c,shared.nullmultibulk);
+ unblockClientWaitingData(c);
+ }
+ }
+ return 0;
+}
+
+/* The client query buffer is an sds.c string that can end with a lot of
+ * free space not used, this function reclaims space if needed.
+ *
+ * The funciton always returns 0 as it never terminates the client. */
+int clientsCronResizeQueryBuffer(redisClient *c) {
+ size_t querybuf_size = sdsAllocSize(c->querybuf);
+ time_t idletime = server.unixtime - c->lastinteraction;
+
+ /* There are two conditions to resize the query buffer:
+ * 1) Query buffer is > BIG_ARG and too big for latest peak.
+ * 2) Client is inactive and the buffer is bigger than 1k. */
+ if (((querybuf_size > REDIS_MBULK_BIG_ARG) &&
+ (querybuf_size/(c->querybuf_peak+1)) > 2) ||
+ (querybuf_size > 1024 && idletime > 2))
+ {
+ /* Only resize the query buffer if it is actually wasting space. */
+ if (sdsavail(c->querybuf) > 1024) {
+ c->querybuf = sdsRemoveFreeSpace(c->querybuf);
+ }
+ }
+ /* Reset the peak again to capture the peak memory usage in the next
+ * cycle. */
+ c->querybuf_peak = 0;
+ return 0;
+}
+
+void clientsCron(void) {
+ /* Make sure to process at least 1/(REDIS_HZ*10) of clients per call.
+ * Since this function is called REDIS_HZ times per second we are sure that
+ * in the worst case we process all the clients in 10 seconds.
+ * In normal conditions (a reasonable number of clients) we process
+ * all the clients in a shorter time. */
+ int numclients = listLength(server.clients);
+ int iterations = numclients/(REDIS_HZ*10);
+
+ if (iterations < 50)
+ iterations = (numclients < 50) ? numclients : 50;
+ while(listLength(server.clients) && iterations--) {
+ redisClient *c;
+ listNode *head;
+
+ /* Rotate the list, take the current head, process.
+ * This way if the client must be removed from the list it's the
+ * first element and we don't incur into O(N) computation. */
+ listRotate(server.clients);
+ head = listFirst(server.clients);
+ c = listNodeValue(head);
+ /* The following functions do different service checks on the client.
+ * The protocol is that they return non-zero if the client was
+ * terminated. */
+ if (clientsCronHandleTimeout(c)) continue;
+ if (clientsCronResizeQueryBuffer(c)) continue;
+ }
+}
+
+/* This is our timer interrupt, called REDIS_HZ times per second.
+ * Here is where we do a number of things that need to be done asynchronously.
+ * For instance:
+ *
+ * - Active expired keys collection (it is also performed in a lazy way on
+ * lookup).
+ * - Software watchdong.
+ * - Update some statistic.
+ * - Incremental rehashing of the DBs hash tables.
+ * - Triggering BGSAVE / AOF rewrite, and handling of terminated children.
+ * - Clients timeout of differnet kinds.
+ * - Replication reconnection.
+ * - Many more...
+ *
+ * Everything directly called here will be called REDIS_HZ times per second,
+ * so in order to throttle execution of things we want to do less frequently
+ * a macro is used: run_with_period(milliseconds) { .... }
+ */
+
+/* Using the following macro you can run code inside serverCron() with the
+ * specified period, specified in milliseconds.
+ * The actual resolution depends on REDIS_HZ. */
+#define run_with_period(_ms_) if (!(loops % ((_ms_)/(1000/REDIS_HZ))))
+
+int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
+ int j, loops = server.cronloops;
+ REDIS_NOTUSED(eventLoop);
+ REDIS_NOTUSED(id);
+ REDIS_NOTUSED(clientData);
+
+ /* Software watchdog: deliver the SIGALRM that will reach the signal
+ * handler if we don't return here fast enough. */
+ if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);
+
+ /* We take a cached value of the unix time in the global state because
+ * with virtual memory and aging there is to store the current time
+ * in objects at every object access, and accuracy is not needed.
+ * To access a global var is faster than calling time(NULL) */
+ server.unixtime = time(NULL);
+
+ run_with_period(100) trackOperationsPerSecond();
+
+ /* We have just 22 bits per object for LRU information.
+ * So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
+ * 2^22 bits with 10 seconds resoluton is more or less 1.5 years.
+ *
+ * Note that even if this will wrap after 1.5 years it's not a problem,
+ * everything will still work but just some object will appear younger
+ * to Redis. But for this to happen a given object should never be touched
+ * for 1.5 years.
+ *
+ * Note that you can change the resolution altering the
+ * REDIS_LRU_CLOCK_RESOLUTION define.
+ */
+ updateLRUClock();
+
+ /* Record the max memory used since the server was started. */
+ if (zmalloc_used_memory() > server.stat_peak_memory)
+ server.stat_peak_memory = zmalloc_used_memory();
+
+ /* We received a SIGTERM, shutting down here in a safe way, as it is
+ * not ok doing so inside the signal handler. */
+ if (server.shutdown_asap) {
+ if (prepareForShutdown(0) == REDIS_OK) exit(0);
+ redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
+ }
+
+ /* Show some info about non-empty databases */
+ run_with_period(5000) {
+ for (j = 0; j < server.dbnum; j++) {
+ long long size, used, vkeys;
+
+ size = dictSlots(server.db[j].dict);
+ used = dictSize(server.db[j].dict);
+ vkeys = dictSize(server.db[j].expires);
+ if (used || vkeys) {
+ redisLog(REDIS_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);
+ /* dictPrintStats(server.dict); */
+ }
+ }
+ }
+
+ /* We don't want to resize the hash tables while a bacground saving
+ * is in progress: the saving child is created using fork() that is
+ * implemented with a copy-on-write semantic in most modern systems, so
+ * if we resize the HT while there is the saving child at work actually
+ * a lot of memory movements in the parent will cause a lot of pages
+ * copied. */
+ if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {
+ tryResizeHashTables();
+ if (server.activerehashing) incrementallyRehash();
+ }
+
+ /* Show information about connected clients */
+ run_with_period(5000) {
+ redisLog(REDIS_VERBOSE,"%d clients connected (%d slaves), %zu bytes in use",
+ listLength(server.clients)-listLength(server.slaves),
+ listLength(server.slaves),
+ zmalloc_used_memory());
+ }
+
+ /* We need to do a few operations on clients asynchronously. */
+ clientsCron();
+
+ /* Start a scheduled AOF rewrite if this was requested by the user while
+ * a BGSAVE was in progress. */
+ if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&
+ server.aof_rewrite_scheduled)
+ {
+ rewriteAppendOnlyFileBackground();
+ }
+
+ /* Check if a background saving or AOF rewrite in progress terminated. */
+ if (server.rdb_child_pid != -1 || server.aof_child_pid != -1) {
+ int statloc;
+ pid_t pid;
+
+ if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
+ int exitcode = WEXITSTATUS(statloc);
+ int bysignal = 0;
+
+ if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);
+
+ if (pid == server.rdb_child_pid) {
+ backgroundSaveDoneHandler(exitcode,bysignal);
+ } else {
+ backgroundRewriteDoneHandler(exitcode,bysignal);
+ }
+ updateDictResizePolicy();
+ }
+ } else {
+ /* If there is not a background saving/rewrite in progress check if
+ * we have to save/rewrite now */
+ for (j = 0; j < server.saveparamslen; j++) {
+ struct saveparam *sp = server.saveparams+j;
+
+ if (server.dirty >= sp->changes &&
+ server.unixtime-server.lastsave > sp->seconds) {
+ redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
+ sp->changes, sp->seconds);
+ rdbSaveBackground(server.rdb_filename);
+ break;
+ }
+ }
+
+ /* Trigger an AOF rewrite if needed */
+ if (server.rdb_child_pid == -1 &&
+ server.aof_child_pid == -1 &&
+ server.aof_rewrite_perc &&
+ server.aof_current_size > server.aof_rewrite_min_size)
+ {
+ long long base = server.aof_rewrite_base_size ?
+ server.aof_rewrite_base_size : 1;
+ long long growth = (server.aof_current_size*100/base) - 100;
+ if (growth >= server.aof_rewrite_perc) {
+ redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
+ rewriteAppendOnlyFileBackground();
+ }
+ }
+ }
+
+
+ /* If we postponed an AOF buffer flush, let's try to do it every time the
+ * cron function is called. */
+ if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);
+
+ /* Expire a few keys per cycle, only if this is a master.
+ * On slaves we wait for DEL operations synthesized by the master
+ * in order to guarantee a strict consistency. */
+ if (server.masterhost == NULL) activeExpireCycle();
+
+ /* Close clients that need to be closed asynchronous */
+ freeClientsInAsyncFreeQueue();
+
+ /* Replication cron function -- used to reconnect to master and
+ * to detect transfer failures. */
+ run_with_period(1000) replicationCron();
+
+ /* Run other sub-systems specific cron jobs */
+ run_with_period(1000) {
+ if (server.cluster_enabled) clusterCron();
+ }
+
+ server.cronloops++;
+ return 1000/REDIS_HZ;
+}
+
+/* This function gets called every time Redis is entering the
+ * main loop of the event driven library, that is, before to sleep
+ * for ready file descriptors. */
+void beforeSleep(struct aeEventLoop *eventLoop) {
+ REDIS_NOTUSED(eventLoop);
+ listNode *ln;
+ redisClient *c;
+
+ /* Try to process pending commands for clients that were just unblocked. */
+ while (listLength(server.unblocked_clients)) {
+ ln = listFirst(server.unblocked_clients);
+ redisAssert(ln != NULL);
+ c = ln->value;
+ listDelNode(server.unblocked_clients,ln);
+ c->flags &= ~REDIS_UNBLOCKED;
+
+ /* Process remaining data in the input buffer. */
+ if (c->querybuf && sdslen(c->querybuf) > 0) {
+ server.current_client = c;
+ processInputBuffer(c);
+ server.current_client = NULL;
+ }
+ }
+
+ /* Write the AOF buffer on disk */
+ flushAppendOnlyFile(0);
+}
+
+/* =========================== Server initialization ======================== */
+
+void createSharedObjects(void) {
+ int j;
+
+ shared.crlf = createObject(REDIS_STRING,sdsnew("\r\n"));
+ shared.ok = createObject(REDIS_STRING,sdsnew("+OK\r\n"));
+ shared.err = createObject(REDIS_STRING,sdsnew("-ERR\r\n"));
+ shared.emptybulk = createObject(REDIS_STRING,sdsnew("$0\r\n\r\n"));
+ shared.czero = createObject(REDIS_STRING,sdsnew(":0\r\n"));
+ shared.cone = createObject(REDIS_STRING,sdsnew(":1\r\n"));
+ shared.cnegone = createObject(REDIS_STRING,sdsnew(":-1\r\n"));
+ shared.nullbulk = createObject(REDIS_STRING,sdsnew("$-1\r\n"));
+ shared.nullmultibulk = createObject(REDIS_STRING,sdsnew("*-1\r\n"));
+ shared.emptymultibulk = createObject(REDIS_STRING,sdsnew("*0\r\n"));
+ shared.pong = createObject(REDIS_STRING,sdsnew("+PONG\r\n"));
+ shared.queued = createObject(REDIS_STRING,sdsnew("+QUEUED\r\n"));
+ shared.wrongtypeerr = createObject(REDIS_STRING,sdsnew(
+ "-ERR Operation against a key holding the wrong kind of value\r\n"));
+ shared.nokeyerr = createObject(REDIS_STRING,sdsnew(
+ "-ERR no such key\r\n"));
+ shared.syntaxerr = createObject(REDIS_STRING,sdsnew(
+ "-ERR syntax error\r\n"));
+ shared.sameobjecterr = createObject(REDIS_STRING,sdsnew(
+ "-ERR source and destination objects are the same\r\n"));
+ shared.outofrangeerr = createObject(REDIS_STRING,sdsnew(
+ "-ERR index out of range\r\n"));
+ shared.noscripterr = createObject(REDIS_STRING,sdsnew(
+ "-NOSCRIPT No matching script. Please use EVAL.\r\n"));
+ shared.loadingerr = createObject(REDIS_STRING,sdsnew(
+ "-LOADING Redis is loading the dataset in memory\r\n"));
+ shared.slowscripterr = createObject(REDIS_STRING,sdsnew(
+ "-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\r\n"));
+ shared.masterdownerr = createObject(REDIS_STRING,sdsnew(
+ "-MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'.\r\n"));
+ shared.bgsaveerr = createObject(REDIS_STRING,sdsnew(
+ "-MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.\r\n"));
+ shared.roslaveerr = createObject(REDIS_STRING,sdsnew(
+ "-READONLY You can't write against a read only slave.\r\n"));
+ shared.oomerr = createObject(REDIS_STRING,sdsnew(
+ "-OOM command not allowed when used memory > 'maxmemory'.\r\n"));
+ shared.space = createObject(REDIS_STRING,sdsnew(" "));
+ shared.colon = createObject(REDIS_STRING,sdsnew(":"));
+ shared.plus = createObject(REDIS_STRING,sdsnew("+"));
+
+ for (j = 0; j < REDIS_SHARED_SELECT_CMDS; j++) {
+ shared.select[j] = createObject(REDIS_STRING,
+ sdscatprintf(sdsempty(),"select %d\r\n", j));
+ }
+ shared.messagebulk = createStringObject("$7\r\nmessage\r\n",13);
+ shared.pmessagebulk = createStringObject("$8\r\npmessage\r\n",14);
+ shared.subscribebulk = createStringObject("$9\r\nsubscribe\r\n",15);
+ shared.unsubscribebulk = createStringObject("$11\r\nunsubscribe\r\n",18);
+ shared.psubscribebulk = createStringObject("$10\r\npsubscribe\r\n",17);
+ shared.punsubscribebulk = createStringObject("$12\r\npunsubscribe\r\n",19);
+ shared.del = createStringObject("DEL",3);
+ shared.rpop = createStringObject("RPOP",4);
+ shared.lpop = createStringObject("LPOP",4);
+ for (j = 0; j < REDIS_SHARED_INTEGERS; j++) {
+ shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j);
+ shared.integers[j]->encoding = REDIS_ENCODING_INT;
+ }
+ for (j = 0; j < REDIS_SHARED_BULKHDR_LEN; j++) {
+ shared.mbulkhdr[j] = createObject(REDIS_STRING,
+ sdscatprintf(sdsempty(),"*%d\r\n",j));
+ shared.bulkhdr[j] = createObject(REDIS_STRING,
+ sdscatprintf(sdsempty(),"$%d\r\n",j));
+ }
+}
+
+void initServerConfig() {
+ getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
+ server.runid[REDIS_RUN_ID_SIZE] = '\0';
+ server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
+ server.port = REDIS_SERVERPORT;
+ server.bindaddr = NULL;
+ server.unixsocket = NULL;
+ server.unixsocketperm = 0;
+ server.ipfd = -1;
+ server.sofd = -1;
+ server.dbnum = REDIS_DEFAULT_DBNUM;
+ server.verbosity = REDIS_NOTICE;
+ server.maxidletime = REDIS_MAXIDLETIME;
+ server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
+ server.saveparams = NULL;
+ server.loading = 0;
+ server.logfile = NULL; /* NULL = log on standard output */
+ server.syslog_enabled = 0;
+ server.syslog_ident = zstrdup("redis");
+ server.syslog_facility = LOG_LOCAL0;
+ server.daemonize = 0;
+ server.aof_state = REDIS_AOF_OFF;
+ server.aof_fsync = AOF_FSYNC_EVERYSEC;
+ server.aof_no_fsync_on_rewrite = 0;
+ server.aof_rewrite_perc = REDIS_AOF_REWRITE_PERC;
+ server.aof_rewrite_min_size = REDIS_AOF_REWRITE_MIN_SIZE;
+ server.aof_rewrite_base_size = 0;
+ server.aof_rewrite_scheduled = 0;
+ server.aof_last_fsync = time(NULL);
+ server.aof_rewrite_time_last = -1;
+ server.aof_rewrite_time_start = -1;
+ server.aof_delayed_fsync = 0;
+ server.aof_fd = -1;
+ server.aof_selected_db = -1; /* Make sure the first time will not match */
+ server.aof_flush_postponed_start = 0;
+ server.pidfile = zstrdup("/var/run/redis.pid");
+ server.rdb_filename = zstrdup("dump.rdb");
+ server.aof_filename = zstrdup("appendonly.aof");
+ server.requirepass = NULL;
+ server.rdb_compression = 1;
+ server.rdb_checksum = 1;
+ server.activerehashing = 1;
+ server.maxclients = REDIS_MAX_CLIENTS;
+ server.bpop_blocked_clients = 0;
+ server.maxmemory = 0;
+ server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
+ server.maxmemory_samples = 3;
+ server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;
+ server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;
+ server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
+ server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;
+ server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;
+ server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;
+ server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;
+ server.shutdown_asap = 0;
+ server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD;
+ server.repl_timeout = REDIS_REPL_TIMEOUT;
+ server.cluster_enabled = 0;
+ server.cluster.configfile = zstrdup("nodes.conf");
+ server.lua_caller = NULL;
+ server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
+ server.lua_client = NULL;
+ server.lua_timedout = 0;
+
+ updateLRUClock();
+ resetServerSaveParams();
+
+ appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
+ appendServerSaveParams(300,100); /* save after 5 minutes and 100 changes */
+ appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */
+ /* Replication related */
+ server.masterauth = NULL;
+ server.masterhost = NULL;
+ server.masterport = 6379;
+ server.master = NULL;
+ server.repl_state = REDIS_REPL_NONE;
+ server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
+ server.repl_serve_stale_data = 1;
+ server.repl_slave_ro = 1;
+ server.repl_down_since = time(NULL);
+
+ /* Client output buffer limits */
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].hard_limit_bytes = 0;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].soft_limit_bytes = 0;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].soft_limit_seconds = 0;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].hard_limit_bytes = 1024*1024*256;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].soft_limit_bytes = 1024*1024*64;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].soft_limit_seconds = 60;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].hard_limit_bytes = 1024*1024*32;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].soft_limit_bytes = 1024*1024*8;
+ server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].soft_limit_seconds = 60;
+
+ /* Double constants initialization */
+ R_Zero = 0.0;
+ R_PosInf = 1.0/R_Zero;
+ R_NegInf = -1.0/R_Zero;
+ R_Nan = R_Zero/R_Zero;
+
+ /* Command table -- we intiialize it here as it is part of the
+ * initial configuration, since command names may be changed via
+ * redis.conf using the rename-command directive. */
+ server.commands = dictCreate(&commandTableDictType,NULL);
+ populateCommandTable();
+ server.delCommand = lookupCommandByCString("del");
+ server.multiCommand = lookupCommandByCString("multi");
+ server.lpushCommand = lookupCommandByCString("lpush");
+
+ /* Slow log */
+ server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
+ server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
+
+ /* Debugging */
+ server.assert_failed = "<no assertion failed>";
+ server.assert_file = "<no file>";
+ server.assert_line = 0;
+ server.bug_report_start = 0;
+ server.watchdog_period = 0;
+}
+
+/* This function will try to raise the max number of open files accordingly to
+ * the configured max number of clients. It will also account for 32 additional
+ * file descriptors as we need a few more for persistence, listening
+ * sockets, log files and so forth.
+ *
+ * If it will not be possible to set the limit accordingly to the configured
+ * max number of clients, the function will do the reverse setting
+ * server.maxclients to the value that we can actually handle. */
+void adjustOpenFilesLimit(void) {
+ rlim_t maxfiles = server.maxclients+32;
+ struct rlimit limit;
+
+ if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {
+ redisLog(REDIS_WARNING,"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.",
+ strerror(errno));
+ server.maxclients = 1024-32;
+ } else {
+ rlim_t oldlimit = limit.rlim_cur;
+
+ /* Set the max number of files if the current limit is not enough
+ * for our needs. */
+ if (oldlimit < maxfiles) {
+ rlim_t f;
+
+ f = maxfiles;
+ while(f > oldlimit) {
+ limit.rlim_cur = f;
+ limit.rlim_max = f;
+ if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
+ f -= 128;
+ }
+ if (f < oldlimit) f = oldlimit;
+ if (f != maxfiles) {
+ server.maxclients = f-32;
+ redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.",
+ (int) maxfiles, strerror(errno), (int) server.maxclients);
+ } else {
+ redisLog(REDIS_NOTICE,"Max number of open files set to %d",
+ (int) maxfiles);
+ }
+ }
+ }
+}
+
+void initServer() {
+ int j;
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ setupSignalHandlers();
+
+ if (server.syslog_enabled) {
+ openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
+ server.syslog_facility);
+ }
+
+ server.current_client = NULL;
+ server.clients = listCreate();
+ server.clients_to_close = listCreate();
+ server.slaves = listCreate();
+ server.monitors = listCreate();
+ server.unblocked_clients = listCreate();
+
+ createSharedObjects();
+ adjustOpenFilesLimit();
+ server.el = aeCreateEventLoop(server.maxclients+1024);
+ server.db = zmalloc(sizeof(redisDb)*server.dbnum);
+
+ if (server.port != 0) {
+ server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
+ if (server.ipfd == ANET_ERR) {
+ redisLog(REDIS_WARNING, "Opening port %d: %s",
+ server.port, server.neterr);
+ exit(1);
+ }
+ }
+ if (server.unixsocket != NULL) {
+ unlink(server.unixsocket); /* don't care if this fails */
+ server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
+ if (server.sofd == ANET_ERR) {
+ redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);
+ exit(1);
+ }
+ }
+ if (server.ipfd < 0 && server.sofd < 0) {
+ redisLog(REDIS_WARNING, "Configured to not listen anywhere, exiting.");
+ exit(1);
+ }
+ for (j = 0; j < server.dbnum; j++) {
+ server.db[j].dict = dictCreate(&dbDictType,NULL);
+ server.db[j].expires = dictCreate(&keyptrDictType,NULL);
+ server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
+ server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
+ server.db[j].id = j;
+ }
+ server.pubsub_channels = dictCreate(&keylistDictType,NULL);
+ server.pubsub_patterns = listCreate();
+ listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);
+ listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);
+ server.cronloops = 0;
+ server.rdb_child_pid = -1;
+ server.aof_child_pid = -1;
+ aofRewriteBufferReset();
+ server.aof_buf = sdsempty();
+ server.lastsave = time(NULL);
+ server.rdb_save_time_last = -1;
+ server.rdb_save_time_start = -1;
+ server.dirty = 0;
+ server.stat_numcommands = 0;
+ server.stat_numconnections = 0;
+ server.stat_expiredkeys = 0;
+ server.stat_evictedkeys = 0;
+ server.stat_starttime = time(NULL);
+ server.stat_keyspace_misses = 0;
+ server.stat_keyspace_hits = 0;
+ server.stat_peak_memory = 0;
+ server.stat_fork_time = 0;
+ server.stat_rejected_conn = 0;
+ memset(server.ops_sec_samples,0,sizeof(server.ops_sec_samples));
+ server.ops_sec_idx = 0;
+ server.ops_sec_last_sample_time = mstime();
+ server.ops_sec_last_sample_ops = 0;
+ server.unixtime = time(NULL);
+ server.lastbgsave_status = REDIS_OK;
+ server.stop_writes_on_bgsave_err = 1;
+ aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
+ if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
+ acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");
+ if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
+ acceptUnixHandler,NULL) == AE_ERR) oom("creating file event");
+
+ if (server.aof_state == REDIS_AOF_ON) {
+ server.aof_fd = open(server.aof_filename,
+ O_WRONLY|O_APPEND|O_CREAT,0644);
+ if (server.aof_fd == -1) {
+ redisLog(REDIS_WARNING, "Can't open the append-only file: %s",
+ strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* 32 bit instances are limited to 4GB of address space, so if there is
+ * no explicit limit in the user provided configuration we set a limit
+ * at 3.5GB using maxmemory with 'noeviction' policy'. This saves
+ * useless crashes of the Redis instance. */
+ if (server.arch_bits == 32 && server.maxmemory == 0) {
+ redisLog(REDIS_WARNING,"Warning: 32 bit instance detected but no memory limit set. Setting 3.5 GB maxmemory limit with 'noeviction' policy now.");
+ server.maxmemory = 3584LL*(1024*1024); /* 3584 MB = 3.5 GB */
+ server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
+ }
+
+ if (server.cluster_enabled) clusterInit();
+ scriptingInit();
+ slowlogInit();
+ bioInit();
+}
+
+/* Populates the Redis Command Table starting from the hard coded list
+ * we have on top of redis.c file. */
+void populateCommandTable(void) {
+ int j;
+ int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
+
+ for (j = 0; j < numcommands; j++) {
+ struct redisCommand *c = redisCommandTable+j;
+ char *f = c->sflags;
+ int retval;
+
+ while(*f != '\0') {
+ switch(*f) {
+ case 'w': c->flags |= REDIS_CMD_WRITE; break;
+ case 'r': c->flags |= REDIS_CMD_READONLY; break;
+ case 'm': c->flags |= REDIS_CMD_DENYOOM; break;
+ case 'a': c->flags |= REDIS_CMD_ADMIN; break;
+ case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
+ case 'f': c->flags |= REDIS_CMD_FORCE_REPLICATION; break;
+ case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
+ case 'R': c->flags |= REDIS_CMD_RANDOM; break;
+ case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;
+ default: redisPanic("Unsupported command flag"); break;
+ }
+ f++;
+ }
+
+ retval = dictAdd(server.commands, sdsnew(c->name), c);
+ assert(retval == DICT_OK);
+ }
+}
+
+void resetCommandTableStats(void) {
+ int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
+ int j;
+
+ for (j = 0; j < numcommands; j++) {
+ struct redisCommand *c = redisCommandTable+j;
+
+ c->microseconds = 0;
+ c->calls = 0;
+ }
+}
+
+/* ========================== Redis OP Array API ============================ */
+
+void redisOpArrayInit(redisOpArray *oa) {
+ oa->ops = NULL;
+ oa->numops = 0;
+}
+
+int redisOpArrayAppend(redisOpArray *oa, struct redisCommand *cmd, int dbid,
+ robj **argv, int argc, int target)
+{
+ redisOp *op;
+
+ oa->ops = zrealloc(oa->ops,sizeof(redisOp)*(oa->numops+1));
+ op = oa->ops+oa->numops;
+ op->cmd = cmd;
+ op->dbid = dbid;
+ op->argv = argv;
+ op->argc = argc;
+ op->target = target;
+ oa->numops++;
+ return oa->numops;
+}
+
+void redisOpArrayFree(redisOpArray *oa) {
+ while(oa->numops) {
+ int j;
+ redisOp *op;
+
+ oa->numops--;
+ op = oa->ops+oa->numops;
+ for (j = 0; j < op->argc; j++)
+ decrRefCount(op->argv[j]);
+ zfree(op->argv);
+ }
+ zfree(oa->ops);
+}
+
+/* ====================== Commands lookup and execution ===================== */
+
+struct redisCommand *lookupCommand(sds name) {
+ return dictFetchValue(server.commands, name);
+}
+
+struct redisCommand *lookupCommandByCString(char *s) {
+ struct redisCommand *cmd;
+ sds name = sdsnew(s);
+
+ cmd = dictFetchValue(server.commands, name);
+ sdsfree(name);
+ return cmd;
+}
+
+/* Propagate the specified command (in the context of the specified database id)
+ * to AOF, Slaves and Monitors.
+ *
+ * flags are an xor between:
+ * + REDIS_PROPAGATE_NONE (no propagation of command at all)
+ * + REDIS_PROPAGATE_AOF (propagate into the AOF file if is enabled)
+ * + REDIS_PROPAGATE_REPL (propagate into the replication link)
+ */
+void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
+ int flags)
+{
+ if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)
+ feedAppendOnlyFile(cmd,dbid,argv,argc);
+ if (flags & REDIS_PROPAGATE_REPL && listLength(server.slaves))
+ replicationFeedSlaves(server.slaves,dbid,argv,argc);
+}
+
+/* Used inside commands to schedule the propagation of additional commands
+ * after the current command is propagated to AOF / Replication. */
+void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
+ int target)
+{
+ redisOpArrayAppend(&server.also_propagate,cmd,dbid,argv,argc,target);
+}
+
+/* Call() is the core of Redis execution of a command */
+void call(redisClient *c, int flags) {
+ long long dirty, start = ustime(), duration;
+
+ /* Sent the command to clients in MONITOR mode, only if the commands are
+ * not geneated from reading an AOF. */
+ if (listLength(server.monitors) && !server.loading)
+ replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
+
+ /* Call the command. */
+ redisOpArrayInit(&server.also_propagate);
+ dirty = server.dirty;
+ c->cmd->proc(c);
+ dirty = server.dirty-dirty;
+ duration = ustime()-start;
+
+ /* When EVAL is called loading the AOF we don't want commands called
+ * from Lua to go into the slowlog or to populate statistics. */
+ if (server.loading && c->flags & REDIS_LUA_CLIENT)
+ flags &= ~(REDIS_CALL_SLOWLOG | REDIS_CALL_STATS);
+
+ /* Log the command into the Slow log if needed, and populate the
+ * per-command statistics that we show in INFO commandstats. */
+ if (flags & REDIS_CALL_SLOWLOG)
+ slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
+ if (flags & REDIS_CALL_STATS) {
+ c->cmd->microseconds += duration;
+ c->cmd->calls++;
+ }
+
+ /* Propagate the command into the AOF and replication link */
+ if (flags & REDIS_CALL_PROPAGATE) {
+ int flags = REDIS_PROPAGATE_NONE;
+
+ if (c->cmd->flags & REDIS_CMD_FORCE_REPLICATION)
+ flags |= REDIS_PROPAGATE_REPL;
+ if (dirty)
+ flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);
+ if (flags != REDIS_PROPAGATE_NONE)
+ propagate(c->cmd,c->db->id,c->argv,c->argc,flags);
+ }
+ /* Commands such as LPUSH or BRPOPLPUSH may propagate an additional
+ * PUSH command. */
+ if (server.also_propagate.numops) {
+ int j;
+ redisOp *rop;
+
+ for (j = 0; j < server.also_propagate.numops; j++) {
+ rop = &server.also_propagate.ops[j];
+ propagate(rop->cmd, rop->dbid, rop->argv, rop->argc, rop->target);
+ }
+ redisOpArrayFree(&server.also_propagate);
+ }
+ server.stat_numcommands++;
+}
+
+/* If this function gets called we already read a whole
+ * command, argments are in the client argv/argc fields.
+ * processCommand() execute the command or prepare the
+ * server for a bulk read from the client.
+ *
+ * If 1 is returned the client is still alive and valid and
+ * and other operations can be performed by the caller. Otherwise
+ * if 0 is returned the client was destroied (i.e. after QUIT). */
+int processCommand(redisClient *c) {
+ /* The QUIT command is handled separately. Normal command procs will
+ * go through checking for replication and QUIT will cause trouble
+ * when FORCE_REPLICATION is enabled and would be implemented in
+ * a regular command proc. */
+ if (!strcasecmp(c->argv[0]->ptr,"quit")) {
+ addReply(c,shared.ok);
+ c->flags |= REDIS_CLOSE_AFTER_REPLY;
+ return REDIS_ERR;
+ }
+
+ /* Now lookup the command and check ASAP about trivial error conditions
+ * such as wrong arity, bad command name and so forth. */
+ c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
+ if (!c->cmd) {
+ addReplyErrorFormat(c,"unknown command '%s'",
+ (char*)c->argv[0]->ptr);
+ return REDIS_OK;
+ } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
+ (c->argc < -c->cmd->arity)) {
+ addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
+ c->cmd->name);
+ return REDIS_OK;
+ }
+
+ /* Check if the user is authenticated */
+ if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
+ {
+ addReplyError(c,"operation not permitted");
+ return REDIS_OK;
+ }
+
+ /* If cluster is enabled, redirect here */
+ if (server.cluster_enabled &&
+ !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0)) {
+ int hashslot;
+
+ if (server.cluster.state != REDIS_CLUSTER_OK) {
+ addReplyError(c,"The cluster is down. Check with CLUSTER INFO for more information");
+ return REDIS_OK;
+ } else {
+ int ask;
+ clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&ask);
+ if (n == NULL) {
+ addReplyError(c,"Multi keys request invalid in cluster");
+ return REDIS_OK;
+ } else if (n != server.cluster.myself) {
+ addReplySds(c,sdscatprintf(sdsempty(),
+ "-%s %d %s:%d\r\n", ask ? "ASK" : "MOVED",
+ hashslot,n->ip,n->port));
+ return REDIS_OK;
+ }
+ }
+ }
+
+ /* Handle the maxmemory directive.
+ *
+ * First we try to free some memory if possible (if there are volatile
+ * keys in the dataset). If there are not the only thing we can do
+ * is returning an error. */
+ if (server.maxmemory) {
+ int retval = freeMemoryIfNeeded();
+ if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {
+ addReply(c, shared.oomerr);
+ return REDIS_OK;
+ }
+ }
+
+ /* Don't accept write commands if there are problems persisting on disk. */
+ if (server.stop_writes_on_bgsave_err &&
+ server.saveparamslen > 0
+ && server.lastbgsave_status == REDIS_ERR &&
+ c->cmd->flags & REDIS_CMD_WRITE)
+ {
+ addReply(c, shared.bgsaveerr);
+ return REDIS_OK;
+ }
+
+ /* Don't accept wirte commands if this is a read only slave. But
+ * accept write commands if this is our master. */
+ if (server.masterhost && server.repl_slave_ro &&
+ !(c->flags & REDIS_MASTER) &&
+ c->cmd->flags & REDIS_CMD_WRITE)
+ {
+ addReply(c, shared.roslaveerr);
+ return REDIS_OK;
+ }
+
+ /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
+ if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
+ &&
+ c->cmd->proc != subscribeCommand &&
+ c->cmd->proc != unsubscribeCommand &&
+ c->cmd->proc != psubscribeCommand &&
+ c->cmd->proc != punsubscribeCommand) {
+ addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
+ return REDIS_OK;
+ }
+
+ /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and
+ * we are a slave with a broken link with master. */
+ if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED &&
+ server.repl_serve_stale_data == 0 &&
+ c->cmd->proc != infoCommand && c->cmd->proc != slaveofCommand)
+ {
+ addReply(c, shared.masterdownerr);
+ return REDIS_OK;
+ }
+
+ /* Loading DB? Return an error if the command is not INFO */
+ if (server.loading && c->cmd->proc != infoCommand) {
+ addReply(c, shared.loadingerr);
+ return REDIS_OK;
+ }
+
+ /* Lua script too slow? Only allow SHUTDOWN NOSAVE and SCRIPT KILL. */
+ if (server.lua_timedout &&
+ !(c->cmd->proc == shutdownCommand &&
+ c->argc == 2 &&
+ tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
+ !(c->cmd->proc == scriptCommand &&
+ c->argc == 2 &&
+ tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))
+ {
+ addReply(c, shared.slowscripterr);
+ return REDIS_OK;
+ }
+
+ /* Exec the command */
+ if (c->flags & REDIS_MULTI &&
+ c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
+ c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
+ {
+ queueMultiCommand(c);
+ addReply(c,shared.queued);
+ } else {
+ call(c,REDIS_CALL_FULL);
+ }
+ return REDIS_OK;
+}
+
+/*================================== Shutdown =============================== */
+
+int prepareForShutdown(int flags) {
+ int save = flags & REDIS_SHUTDOWN_SAVE;
+ int nosave = flags & REDIS_SHUTDOWN_NOSAVE;
+
+ redisLog(REDIS_WARNING,"User requested shutdown...");
+ /* Kill the saving child if there is a background saving in progress.
+ We want to avoid race conditions, for instance our saving child may
+ overwrite the synchronous saving did by SHUTDOWN. */
+ if (server.rdb_child_pid != -1) {
+ redisLog(REDIS_WARNING,"There is a child saving an .rdb. Killing it!");
+ kill(server.rdb_child_pid,SIGKILL);
+ rdbRemoveTempFile(server.rdb_child_pid);
+ }
+ if (server.aof_state != REDIS_AOF_OFF) {
+ /* Kill the AOF saving child as the AOF we already have may be longer
+ * but contains the full dataset anyway. */
+ if (server.aof_child_pid != -1) {
+ redisLog(REDIS_WARNING,
+ "There is a child rewriting the AOF. Killing it!");
+ kill(server.aof_child_pid,SIGKILL);
+ }
+ /* Append only file: fsync() the AOF and exit */
+ redisLog(REDIS_NOTICE,"Calling fsync() on the AOF file.");
+ aof_fsync(server.aof_fd);
+ }
+ if ((server.saveparamslen > 0 && !nosave) || save) {
+ redisLog(REDIS_NOTICE,"Saving the final RDB snapshot before exiting.");
+ /* Snapshotting. Perform a SYNC SAVE and exit */
+ if (rdbSave(server.rdb_filename) != REDIS_OK) {
+ /* Ooops.. error saving! The best we can do is to continue
+ * operating. Note that if there was a background saving process,
+ * in the next cron() Redis will be notified that the background
+ * saving aborted, handling special stuff like slaves pending for
+ * synchronization... */
+ redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit.");
+ return REDIS_ERR;
+ }
+ }
+ if (server.daemonize) {
+ redisLog(REDIS_NOTICE,"Removing the pid file.");
+ unlink(server.pidfile);
+ }
+ /* Close the listening sockets. Apparently this allows faster restarts. */
+ if (server.ipfd != -1) close(server.ipfd);
+ if (server.sofd != -1) close(server.sofd);
+ if (server.unixsocket) {
+ redisLog(REDIS_NOTICE,"Removing the unix socket file.");
+ unlink(server.unixsocket); /* don't care if this fails */
+ }
+
+ redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
+ return REDIS_OK;
+}
+
+/*================================== Commands =============================== */
+
+void authCommand(redisClient *c) {
+ if (!server.requirepass) {
+ addReplyError(c,"Client sent AUTH, but no password is set");
+ } else if (!strcmp(c->argv[1]->ptr, server.requirepass)) {
+ c->authenticated = 1;
+ addReply(c,shared.ok);
+ } else {
+ c->authenticated = 0;
+ addReplyError(c,"invalid password");
+ }
+}
+
+void pingCommand(redisClient *c) {
+ addReply(c,shared.pong);
+}
+
+void echoCommand(redisClient *c) {
+ addReplyBulk(c,c->argv[1]);
+}
+
+void timeCommand(redisClient *c) {
+ struct timeval tv;
+
+ /* gettimeofday() can only fail if &tv is a bad addresss so we
+ * don't check for errors. */
+ gettimeofday(&tv,NULL);
+ addReplyMultiBulkLen(c,2);
+ addReplyBulkLongLong(c,tv.tv_sec);
+ addReplyBulkLongLong(c,tv.tv_usec);
+}
+
+/* Convert an amount of bytes into a human readable string in the form
+ * of 100B, 2G, 100M, 4K, and so forth. */
+void bytesToHuman(char *s, unsigned long long n) {
+ double d;
+
+ if (n < 1024) {
+ /* Bytes */
+ sprintf(s,"%lluB",n);
+ return;
+ } else if (n < (1024*1024)) {
+ d = (double)n/(1024);
+ sprintf(s,"%.2fK",d);
+ } else if (n < (1024LL*1024*1024)) {
+ d = (double)n/(1024*1024);
+ sprintf(s,"%.2fM",d);
+ } else if (n < (1024LL*1024*1024*1024)) {
+ d = (double)n/(1024LL*1024*1024);
+ sprintf(s,"%.2fG",d);
+ }
+}
+
+/* Create the string returned by the INFO command. This is decoupled
+ * by the INFO command itself as we need to report the same information
+ * on memory corruption problems. */
+sds genRedisInfoString(char *section) {
+ sds info = sdsempty();
+ time_t uptime = server.unixtime-server.stat_starttime;
+ int j, numcommands;
+ struct rusage self_ru, c_ru;
+ unsigned long lol, bib;
+ int allsections = 0, defsections = 0;
+ int sections = 0;
+
+ if (section) {
+ allsections = strcasecmp(section,"all") == 0;
+ defsections = strcasecmp(section,"default") == 0;
+ }
+
+ getrusage(RUSAGE_SELF, &self_ru);
+ getrusage(RUSAGE_CHILDREN, &c_ru);
+ getClientsMaxBuffers(&lol,&bib);
+
+ /* Server */
+ if (allsections || defsections || !strcasecmp(section,"server")) {
+ struct utsname name;
+
+ if (sections++) info = sdscat(info,"\r\n");
+ uname(&name);
+ info = sdscatprintf(info,
+ "# Server\r\n"
+ "redis_version:%s\r\n"
+ "redis_git_sha1:%s\r\n"
+ "redis_git_dirty:%d\r\n"
+ "os:%s %s %s\r\n"
+ "arch_bits:%d\r\n"
+ "multiplexing_api:%s\r\n"
+ "gcc_version:%d.%d.%d\r\n"
+ "process_id:%ld\r\n"
+ "run_id:%s\r\n"
+ "tcp_port:%d\r\n"
+ "uptime_in_seconds:%ld\r\n"
+ "uptime_in_days:%ld\r\n"
+ "lru_clock:%ld\r\n",
+ REDIS_VERSION,
+ redisGitSHA1(),
+ strtol(redisGitDirty(),NULL,10) > 0,
+ name.sysname, name.release, name.machine,
+ server.arch_bits,
+ aeGetApiName(),
+#ifdef __GNUC__
+ __GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__,
+#else
+ 0,0,0,
+#endif
+ (long) getpid(),
+ server.runid,
+ server.port,
+ uptime,
+ uptime/(3600*24),
+ (unsigned long) server.lruclock);
+ }
+
+ /* Clients */
+ if (allsections || defsections || !strcasecmp(section,"clients")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Clients\r\n"
+ "connected_clients:%lu\r\n"
+ "client_longest_output_list:%lu\r\n"
+ "client_biggest_input_buf:%lu\r\n"
+ "blocked_clients:%d\r\n",
+ listLength(server.clients)-listLength(server.slaves),
+ lol, bib,
+ server.bpop_blocked_clients);
+ }
+
+ /* Memory */
+ if (allsections || defsections || !strcasecmp(section,"memory")) {
+ char hmem[64];
+ char peak_hmem[64];
+
+ bytesToHuman(hmem,zmalloc_used_memory());
+ bytesToHuman(peak_hmem,server.stat_peak_memory);
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Memory\r\n"
+ "used_memory:%zu\r\n"
+ "used_memory_human:%s\r\n"
+ "used_memory_rss:%zu\r\n"
+ "used_memory_peak:%zu\r\n"
+ "used_memory_peak_human:%s\r\n"
+ "used_memory_lua:%lld\r\n"
+ "mem_fragmentation_ratio:%.2f\r\n"
+ "mem_allocator:%s\r\n",
+ zmalloc_used_memory(),
+ hmem,
+ zmalloc_get_rss(),
+ server.stat_peak_memory,
+ peak_hmem,
+ ((long long)lua_gc(server.lua,LUA_GCCOUNT,0))*1024LL,
+ zmalloc_get_fragmentation_ratio(),
+ ZMALLOC_LIB
+ );
+ }
+
+ /* Persistence */
+ if (allsections || defsections || !strcasecmp(section,"persistence")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Persistence\r\n"
+ "loading:%d\r\n"
+ "rdb_changes_since_last_save:%lld\r\n"
+ "rdb_bgsave_in_progress:%d\r\n"
+ "rdb_last_save_time:%ld\r\n"
+ "rdb_last_bgsave_status:%s\r\n"
+ "rdb_last_bgsave_time_sec:%ld\r\n"
+ "rdb_current_bgsave_time_sec:%ld\r\n"
+ "aof_enabled:%d\r\n"
+ "aof_rewrite_in_progress:%d\r\n"
+ "aof_rewrite_scheduled:%d\r\n"
+ "aof_last_rewrite_time_sec:%ld\r\n"
+ "aof_current_rewrite_time_sec:%ld\r\n",
+ server.loading,
+ server.dirty,
+ server.rdb_child_pid != -1,
+ server.lastsave,
+ server.lastbgsave_status == REDIS_OK ? "ok" : "err",
+ server.rdb_save_time_last,
+ (server.rdb_child_pid == -1) ?
+ -1 : time(NULL)-server.rdb_save_time_start,
+ server.aof_state != REDIS_AOF_OFF,
+ server.aof_child_pid != -1,
+ server.aof_rewrite_scheduled,
+ server.aof_rewrite_time_last,
+ (server.aof_child_pid == -1) ?
+ -1 : time(NULL)-server.aof_rewrite_time_start);
+
+ if (server.aof_state != REDIS_AOF_OFF) {
+ info = sdscatprintf(info,
+ "aof_current_size:%lld\r\n"
+ "aof_base_size:%lld\r\n"
+ "aof_pending_rewrite:%d\r\n"
+ "aof_buffer_length:%zu\r\n"
+ "aof_rewrite_buffer_length:%zu\r\n"
+ "aof_pending_bio_fsync:%llu\r\n"
+ "aof_delayed_fsync:%lu\r\n",
+ (long long) server.aof_current_size,
+ (long long) server.aof_rewrite_base_size,
+ server.aof_rewrite_scheduled,
+ sdslen(server.aof_buf),
+ aofRewriteBufferSize(),
+ bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC),
+ server.aof_delayed_fsync);
+ }
+
+ if (server.loading) {
+ double perc;
+ time_t eta, elapsed;
+ off_t remaining_bytes = server.loading_total_bytes-
+ server.loading_loaded_bytes;
+
+ perc = ((double)server.loading_loaded_bytes /
+ server.loading_total_bytes) * 100;
+
+ elapsed = server.unixtime-server.loading_start_time;
+ if (elapsed == 0) {
+ eta = 1; /* A fake 1 second figure if we don't have
+ enough info */
+ } else {
+ eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes;
+ }
+
+ info = sdscatprintf(info,
+ "loading_start_time:%ld\r\n"
+ "loading_total_bytes:%llu\r\n"
+ "loading_loaded_bytes:%llu\r\n"
+ "loading_loaded_perc:%.2f\r\n"
+ "loading_eta_seconds:%ld\r\n"
+ ,(unsigned long) server.loading_start_time,
+ (unsigned long long) server.loading_total_bytes,
+ (unsigned long long) server.loading_loaded_bytes,
+ perc,
+ eta
+ );
+ }
+ }
+
+ /* Stats */
+ if (allsections || defsections || !strcasecmp(section,"stats")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Stats\r\n"
+ "total_connections_received:%lld\r\n"
+ "total_commands_processed:%lld\r\n"
+ "instantaneous_ops_per_sec:%lld\r\n"
+ "rejected_connections:%lld\r\n"
+ "expired_keys:%lld\r\n"
+ "evicted_keys:%lld\r\n"
+ "keyspace_hits:%lld\r\n"
+ "keyspace_misses:%lld\r\n"
+ "pubsub_channels:%ld\r\n"
+ "pubsub_patterns:%lu\r\n"
+ "latest_fork_usec:%lld\r\n",
+ server.stat_numconnections,
+ server.stat_numcommands,
+ getOperationsPerSecond(),
+ server.stat_rejected_conn,
+ server.stat_expiredkeys,
+ server.stat_evictedkeys,
+ server.stat_keyspace_hits,
+ server.stat_keyspace_misses,
+ dictSize(server.pubsub_channels),
+ listLength(server.pubsub_patterns),
+ server.stat_fork_time);
+ }
+
+ /* Replication */
+ if (allsections || defsections || !strcasecmp(section,"replication")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Replication\r\n"
+ "role:%s\r\n",
+ server.masterhost == NULL ? "master" : "slave");
+ if (server.masterhost) {
+ info = sdscatprintf(info,
+ "master_host:%s\r\n"
+ "master_port:%d\r\n"
+ "master_link_status:%s\r\n"
+ "master_last_io_seconds_ago:%d\r\n"
+ "master_sync_in_progress:%d\r\n"
+ ,server.masterhost,
+ server.masterport,
+ (server.repl_state == REDIS_REPL_CONNECTED) ?
+ "up" : "down",
+ server.master ?
+ ((int)(server.unixtime-server.master->lastinteraction)) : -1,
+ server.repl_state == REDIS_REPL_TRANSFER
+ );
+
+ if (server.repl_state == REDIS_REPL_TRANSFER) {
+ info = sdscatprintf(info,
+ "master_sync_left_bytes:%ld\r\n"
+ "master_sync_last_io_seconds_ago:%d\r\n"
+ ,(long)server.repl_transfer_left,
+ (int)(server.unixtime-server.repl_transfer_lastio)
+ );
+ }
+
+ if (server.repl_state != REDIS_REPL_CONNECTED) {
+ info = sdscatprintf(info,
+ "master_link_down_since_seconds:%ld\r\n",
+ (long)server.unixtime-server.repl_down_since);
+ }
+ }
+ info = sdscatprintf(info,
+ "connected_slaves:%lu\r\n",
+ listLength(server.slaves));
+ if (listLength(server.slaves)) {
+ int slaveid = 0;
+ listNode *ln;
+ listIter li;
+
+ listRewind(server.slaves,&li);
+ while((ln = listNext(&li))) {
+ redisClient *slave = listNodeValue(ln);
+ char *state = NULL;
+ char ip[32];
+ int port;
+
+ if (anetPeerToString(slave->fd,ip,&port) == -1) continue;
+ switch(slave->replstate) {
+ case REDIS_REPL_WAIT_BGSAVE_START:
+ case REDIS_REPL_WAIT_BGSAVE_END:
+ state = "wait_bgsave";
+ break;
+ case REDIS_REPL_SEND_BULK:
+ state = "send_bulk";
+ break;
+ case REDIS_REPL_ONLINE:
+ state = "online";
+ break;
+ }
+ if (state == NULL) continue;
+ info = sdscatprintf(info,"slave%d:%s,%d,%s\r\n",
+ slaveid,ip,port,state);
+ slaveid++;
+ }
+ }
+ }
+
+ /* CPU */
+ if (allsections || defsections || !strcasecmp(section,"cpu")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# CPU\r\n"
+ "used_cpu_sys:%.2f\r\n"
+ "used_cpu_user:%.2f\r\n"
+ "used_cpu_sys_children:%.2f\r\n"
+ "used_cpu_user_children:%.2f\r\n",
+ (float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
+ (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
+ (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000,
+ (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000);
+ }
+
+ /* cmdtime */
+ if (allsections || !strcasecmp(section,"commandstats")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info, "# Commandstats\r\n");
+ numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
+ for (j = 0; j < numcommands; j++) {
+ struct redisCommand *c = redisCommandTable+j;
+
+ if (!c->calls) continue;
+ info = sdscatprintf(info,
+ "cmdstat_%s:calls=%lld,usec=%lld,usec_per_call=%.2f\r\n",
+ c->name, c->calls, c->microseconds,
+ (c->calls == 0) ? 0 : ((float)c->microseconds/c->calls));
+ }
+ }
+
+ /* Cluster */
+ if (allsections || defsections || !strcasecmp(section,"cluster")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Cluster\r\n"
+ "cluster_enabled:%d\r\n",
+ server.cluster_enabled);
+ }
+
+ /* Key space */
+ if (allsections || defsections || !strcasecmp(section,"keyspace")) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info, "# Keyspace\r\n");
+ for (j = 0; j < server.dbnum; j++) {
+ long long keys, vkeys;
+
+ keys = dictSize(server.db[j].dict);
+ vkeys = dictSize(server.db[j].expires);
+ if (keys || vkeys) {
+ info = sdscatprintf(info, "db%d:keys=%lld,expires=%lld\r\n",
+ j, keys, vkeys);
+ }
+ }
+ }
+ return info;
+}
+
+void infoCommand(redisClient *c) {
+ char *section = c->argc == 2 ? c->argv[1]->ptr : "default";
+
+ if (c->argc > 2) {
+ addReply(c,shared.syntaxerr);
+ return;
+ }
+ sds info = genRedisInfoString(section);
+ addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n",
+ (unsigned long)sdslen(info)));
+ addReplySds(c,info);
+ addReply(c,shared.crlf);
+}
+
+void monitorCommand(redisClient *c) {
+ /* ignore MONITOR if aleady slave or in monitor mode */
+ if (c->flags & REDIS_SLAVE) return;
+
+ c->flags |= (REDIS_SLAVE|REDIS_MONITOR);
+ c->slaveseldb = 0;
+ listAddNodeTail(server.monitors,c);
+ addReply(c,shared.ok);
+}
+
+/* ============================ Maxmemory directive ======================== */
+
+/* This function gets called when 'maxmemory' is set on the config file to limit
+ * the max memory used by the server, before processing a command.
+ *
+ * The goal of the function is to free enough memory to keep Redis under the
+ * configured memory limit.
+ *
+ * The function starts calculating how many bytes should be freed to keep
+ * Redis under the limit, and enters a loop selecting the best keys to
+ * evict accordingly to the configured policy.
+ *
+ * If all the bytes needed to return back under the limit were freed the
+ * function returns REDIS_OK, otherwise REDIS_ERR is returned, and the caller
+ * should block the execution of commands that will result in more memory
+ * used by the server.
+ */
+int freeMemoryIfNeeded(void) {
+ size_t mem_used, mem_tofree, mem_freed;
+ int slaves = listLength(server.slaves);
+
+ /* Remove the size of slaves output buffers and AOF buffer from the
+ * count of used memory. */
+ mem_used = zmalloc_used_memory();
+ if (slaves) {
+ listIter li;
+ listNode *ln;
+
+ listRewind(server.slaves,&li);
+ while((ln = listNext(&li))) {
+ redisClient *slave = listNodeValue(ln);
+ unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave);
+ if (obuf_bytes > mem_used)
+ mem_used = 0;
+ else
+ mem_used -= obuf_bytes;
+ }
+ }
+ if (server.aof_state != REDIS_AOF_OFF) {
+ mem_used -= sdslen(server.aof_buf);
+ mem_used -= aofRewriteBufferSize();
+ }
+
+ /* Check if we are over the memory limit. */
+ if (mem_used <= server.maxmemory) return REDIS_OK;
+
+ if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION)
+ return REDIS_ERR; /* We need to free memory, but policy forbids. */
+
+ /* Compute how much memory we need to free. */
+ mem_tofree = mem_used - server.maxmemory;
+ mem_freed = 0;
+ while (mem_freed < mem_tofree) {
+ int j, k, keys_freed = 0;
+
+ for (j = 0; j < server.dbnum; j++) {
+ long bestval = 0; /* just to prevent warning */
+ sds bestkey = NULL;
+ struct dictEntry *de;
+ redisDb *db = server.db+j;
+ dict *dict;
+
+ if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
+ server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM)
+ {
+ dict = server.db[j].dict;
+ } else {
+ dict = server.db[j].expires;
+ }
+ if (dictSize(dict) == 0) continue;
+
+ /* volatile-random and allkeys-random policy */
+ if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM ||
+ server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM)
+ {
+ de = dictGetRandomKey(dict);
+ bestkey = dictGetKey(de);
+ }
+
+ /* volatile-lru and allkeys-lru policy */
+ else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
+ server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
+ {
+ for (k = 0; k < server.maxmemory_samples; k++) {
+ sds thiskey;
+ long thisval;
+ robj *o;
+
+ de = dictGetRandomKey(dict);
+ thiskey = dictGetKey(de);
+ /* When policy is volatile-lru we need an additonal lookup
+ * to locate the real key, as dict is set to db->expires. */
+ if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
+ de = dictFind(db->dict, thiskey);
+ o = dictGetVal(de);
+ thisval = estimateObjectIdleTime(o);
+
+ /* Higher idle time is better candidate for deletion */
+ if (bestkey == NULL || thisval > bestval) {
+ bestkey = thiskey;
+ bestval = thisval;
+ }
+ }
+ }
+
+ /* volatile-ttl */
+ else if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_TTL) {
+ for (k = 0; k < server.maxmemory_samples; k++) {
+ sds thiskey;
+ long thisval;
+
+ de = dictGetRandomKey(dict);
+ thiskey = dictGetKey(de);
+ thisval = (long) dictGetVal(de);
+
+ /* Expire sooner (minor expire unix timestamp) is better
+ * candidate for deletion */
+ if (bestkey == NULL || thisval < bestval) {
+ bestkey = thiskey;
+ bestval = thisval;
+ }
+ }
+ }
+
+ /* Finally remove the selected key. */
+ if (bestkey) {
+ long long delta;
+
+ robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
+ propagateExpire(db,keyobj);
+ /* We compute the amount of memory freed by dbDelete() alone.
+ * It is possible that actually the memory needed to propagate
+ * the DEL in AOF and replication link is greater than the one
+ * we are freeing removing the key, but we can't account for
+ * that otherwise we would never exit the loop.
+ *
+ * AOF and Output buffer memory will be freed eventually so
+ * we only care about memory used by the key space. */
+ delta = (long long) zmalloc_used_memory();
+ dbDelete(db,keyobj);
+ delta -= (long long) zmalloc_used_memory();
+ mem_freed += delta;
+ server.stat_evictedkeys++;
+ decrRefCount(keyobj);
+ keys_freed++;
+
+ /* When the memory to free starts to be big enough, we may
+ * start spending so much time here that is impossible to
+ * deliver data to the slaves fast enough, so we force the
+ * transmission here inside the loop. */
+ if (slaves) flushSlavesOutputBuffers();
+ }
+ }
+ if (!keys_freed) return REDIS_ERR; /* nothing to free... */
+ }
+ return REDIS_OK;
+}
+
+/* =================================== Main! ================================ */
+
+#ifdef __linux__
+int linuxOvercommitMemoryValue(void) {
+ FILE *fp = fopen("/proc/sys/vm/overcommit_memory","r");
+ char buf[64];
+
+ if (!fp) return -1;
+ if (fgets(buf,64,fp) == NULL) {
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ return atoi(buf);
+}
+
+void linuxOvercommitMemoryWarning(void) {
+ if (linuxOvercommitMemoryValue() == 0) {
+ redisLog(REDIS_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");
+ }
+}
+#endif /* __linux__ */
+
+void createPidFile(void) {
+ /* Try to write the pid file in a best-effort way. */
+ FILE *fp = fopen(server.pidfile,"w");
+ if (fp) {
+ fprintf(fp,"%d\n",(int)getpid());
+ fclose(fp);
+ }
+}
+
+void daemonize(void) {
+ int fd;
+
+ if (fork() != 0) exit(0); /* parent exits */
+ setsid(); /* create a new session */
+
+ /* Every output goes to /dev/null. If Redis is daemonized but
+ * the 'logfile' is set to 'stdout' in the configuration file
+ * it will not log at all. */
+ if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO) close(fd);
+ }
+}
+
+void version() {
+ printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d\n",
+ REDIS_VERSION,
+ redisGitSHA1(),
+ atoi(redisGitDirty()) > 0,
+ ZMALLOC_LIB,
+ sizeof(long) == 4 ? 32 : 64);
+ exit(0);
+}
+
+void usage() {
+ fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n");
+ fprintf(stderr," ./redis-server - (read config from stdin)\n");
+ fprintf(stderr," ./redis-server -v or --version\n");
+ fprintf(stderr," ./redis-server -h or --help\n");
+ fprintf(stderr," ./redis-server --test-memory <megabytes>\n\n");
+ fprintf(stderr,"Examples:\n");
+ fprintf(stderr," ./redis-server (run the server with default conf)\n");
+ fprintf(stderr," ./redis-server /etc/redis/6379.conf\n");
+ fprintf(stderr," ./redis-server --port 7777\n");
+ fprintf(stderr," ./redis-server --port 7777 --slaveof 127.0.0.1 8888\n");
+ fprintf(stderr," ./redis-server /etc/myredis.conf --loglevel verbose\n");
+ exit(1);
+}
+
+void redisAsciiArt(void) {
+#include "asciilogo.h"
+ char *buf = zmalloc(1024*16);
+
+ snprintf(buf,1024*16,ascii_logo,
+ REDIS_VERSION,
+ redisGitSHA1(),
+ strtol(redisGitDirty(),NULL,10) > 0,
+ (sizeof(long) == 8) ? "64" : "32",
+ server.cluster_enabled ? "cluster" : "stand alone",
+ server.port,
+ (long) getpid()
+ );
+ redisLogRaw(REDIS_NOTICE|REDIS_LOG_RAW,buf);
+ zfree(buf);
+}
+
+static void sigtermHandler(int sig) {
+ REDIS_NOTUSED(sig);
+
+ redisLogFromHandler(REDIS_WARNING,"Received SIGTERM, scheduling shutdown...");
+ server.shutdown_asap = 1;
+}
+
+void setupSignalHandlers(void) {
+ struct sigaction act;
+
+ /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
+ * Otherwise, sa_handler is used. */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = sigtermHandler;
+ sigaction(SIGTERM, &act, NULL);
+
+#ifdef HAVE_BACKTRACE
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
+ act.sa_sigaction = sigsegvHandler;
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+#endif
+ return;
+}
+
+void memtest(size_t megabytes, int passes);
+
+int main(int argc, char **argv) {
+ long long start;
+ struct timeval tv;
+
+ /* We need to initialize our libraries, and the server configuration. */
+ zmalloc_enable_thread_safeness();
+ srand(time(NULL)^getpid());
+ gettimeofday(&tv,NULL);
+ dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
+ initServerConfig();
+
+ if (argc >= 2) {
+ int j = 1; /* First option to parse in argv[] */
+ sds options = sdsempty();
+ char *configfile = NULL;
+
+ /* Handle special options --help and --version */
+ if (strcmp(argv[1], "-v") == 0 ||
+ strcmp(argv[1], "--version") == 0) version();
+ if (strcmp(argv[1], "--help") == 0 ||
+ strcmp(argv[1], "-h") == 0) usage();
+ if (strcmp(argv[1], "--test-memory") == 0) {
+ if (argc == 3) {
+ memtest(atoi(argv[2]),50);
+ exit(0);
+ } else {
+ fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n");
+ fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n");
+ exit(1);
+ }
+ }
+
+ /* First argument is the config file name? */
+ if (argv[j][0] != '-' || argv[j][1] != '-')
+ configfile = argv[j++];
+ /* All the other options are parsed and conceptually appended to the
+ * configuration file. For instance --port 6380 will generate the
+ * string "port 6380\n" to be parsed after the actual file name
+ * is parsed, if any. */
+ while(j != argc) {
+ if (argv[j][0] == '-' && argv[j][1] == '-') {
+ /* Option name */
+ if (sdslen(options)) options = sdscat(options,"\n");
+ options = sdscat(options,argv[j]+2);
+ options = sdscat(options," ");
+ } else {
+ /* Option argument */
+ options = sdscatrepr(options,argv[j],strlen(argv[j]));
+ options = sdscat(options," ");
+ }
+ j++;
+ }
+ resetServerSaveParams();
+ loadServerConfig(configfile,options);
+ sdsfree(options);
+ } else {
+ redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
+ }
+ if (server.daemonize) daemonize();
+ initServer();
+ if (server.daemonize) createPidFile();
+ redisAsciiArt();
+ redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
+#ifdef __linux__
+ linuxOvercommitMemoryWarning();
+#endif
+ start = ustime();
+ if (server.aof_state == REDIS_AOF_ON) {
+ if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
+ redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
+ } else {
+ if (rdbLoad(server.rdb_filename) == REDIS_OK) {
+ redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
+ (float)(ustime()-start)/1000000);
+ } else if (errno != ENOENT) {
+ redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
+ exit(1);
+ }
+ }
+ if (server.ipfd > 0)
+ redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
+ if (server.sofd > 0)
+ redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
+ aeSetBeforeSleepProc(server.el,beforeSleep);
+ aeMain(server.el);
+ aeDeleteEventLoop(server.el);
+ return 0;
+}
+
+/* The End */
diff --git a/samples/c/yajl.c b/samples/c/yajl.c
new file mode 100644
index 0000000..50bca44
--- /dev/null
+++ b/samples/c/yajl.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2010, Lloyd Hilaiel.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. Neither the name of Lloyd Hilaiel nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "api/yajl_parse.h"
+#include "yajl_lex.h"
+#include "yajl_parser.h"
+#include "yajl_alloc.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+const char *
+yajl_status_to_string(yajl_status stat)
+{
+ const char * statStr = "unknown";
+ switch (stat) {
+ case yajl_status_ok:
+ statStr = "ok, no error";
+ break;
+ case yajl_status_client_canceled:
+ statStr = "client canceled parse";
+ break;
+ case yajl_status_insufficient_data:
+ statStr = "eof was met before the parse could complete";
+ break;
+ case yajl_status_error:
+ statStr = "parse error";
+ break;
+ }
+ return statStr;
+}
+
+yajl_handle
+yajl_alloc(const yajl_callbacks * callbacks,
+ const yajl_parser_config * config,
+ const yajl_alloc_funcs * afs,
+ void * ctx)
+{
+ unsigned int allowComments = 0;
+ unsigned int validateUTF8 = 0;
+ yajl_handle hand = NULL;
+ yajl_alloc_funcs afsBuffer;
+
+ /* first order of business is to set up memory allocation routines */
+ if (afs != NULL) {
+ if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
+ {
+ return NULL;
+ }
+ } else {
+ yajl_set_default_alloc_funcs(&afsBuffer);
+ afs = &afsBuffer;
+ }
+
+ hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
+
+ /* copy in pointers to allocation routines */
+ memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
+
+ if (config != NULL) {
+ allowComments = config->allowComments;
+ validateUTF8 = config->checkUTF8;
+ }
+
+ hand->callbacks = callbacks;
+ hand->ctx = ctx;
+ hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8);
+ hand->bytesConsumed = 0;
+ hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
+ yajl_bs_init(hand->stateStack, &(hand->alloc));
+
+ yajl_bs_push(hand->stateStack, yajl_state_start);
+
+ return hand;
+}
+
+void
+yajl_reset_parser(yajl_handle hand) {
+ hand->lexer = yajl_lex_realloc(hand->lexer);
+}
+
+void
+yajl_free(yajl_handle handle)
+{
+ yajl_bs_free(handle->stateStack);
+ yajl_buf_free(handle->decodeBuf);
+ yajl_lex_free(handle->lexer);
+ YA_FREE(&(handle->alloc), handle);
+}
+
+yajl_status
+yajl_parse(yajl_handle hand, const unsigned char * jsonText,
+ unsigned int jsonTextLen)
+{
+ yajl_status status;
+ status = yajl_do_parse(hand, jsonText, jsonTextLen);
+ return status;
+}
+
+yajl_status
+yajl_parse_complete(yajl_handle hand)
+{
+ /* The particular case we want to handle is a trailing number.
+ * Further input consisting of digits could cause our interpretation
+ * of the number to change (buffered "1" but "2" comes in).
+ * A very simple approach to this is to inject whitespace to terminate
+ * any number in the lex buffer.
+ */
+ return yajl_parse(hand, (const unsigned char *)" ", 1);
+}
+
+unsigned char *
+yajl_get_error(yajl_handle hand, int verbose,
+ const unsigned char * jsonText, unsigned int jsonTextLen)
+{
+ return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
+}
+
+unsigned int
+yajl_get_bytes_consumed(yajl_handle hand)
+{
+ if (!hand) return 0;
+ else return hand->bytesConsumed;
+}
+
+
+void
+yajl_free_error(yajl_handle hand, unsigned char * str)
+{
+ /* use memory allocation functions if set */
+ YA_FREE(&(hand->alloc), str);
+}
+
+/* XXX: add utility routines to parse from file */
diff --git a/samples/coffee/browser.coffee b/samples/coffee/browser.coffee
new file mode 100644
index 0000000..5ed0bea
--- /dev/null
+++ b/samples/coffee/browser.coffee
@@ -0,0 +1,55 @@
+# Override exported methods for non-Node.js engines.
+
+CoffeeScript = require './coffee-script'
+CoffeeScript.require = require
+
+# Use standard JavaScript `eval` to eval code.
+CoffeeScript.eval = (code, options = {}) ->
+ options.bare ?= on
+ eval CoffeeScript.compile code, options
+
+# Running code does not provide access to this scope.
+CoffeeScript.run = (code, options = {}) ->
+ options.bare = on
+ Function(CoffeeScript.compile code, options)()
+
+# If we're not in a browser environment, we're finished with the public API.
+return unless window?
+
+# Load a remote script from the current domain via XHR.
+CoffeeScript.load = (url, callback) ->
+ xhr = new (window.ActiveXObject or XMLHttpRequest)('Microsoft.XMLHTTP')
+ xhr.open 'GET', url, true
+ xhr.overrideMimeType 'text/plain' if 'overrideMimeType' of xhr
+ xhr.onreadystatechange = ->
+ if xhr.readyState is 4
+ if xhr.status in [0, 200]
+ CoffeeScript.run xhr.responseText
+ else
+ throw new Error "Could not load #{url}"
+ callback() if callback
+ xhr.send null
+
+# Activate CoffeeScript in the browser by having it compile and evaluate
+# all script tags with a content-type of `text/coffeescript`.
+# This happens on page load.
+runScripts = ->
+ scripts = document.getElementsByTagName 'script'
+ coffees = (s for s in scripts when s.type is 'text/coffeescript')
+ index = 0
+ length = coffees.length
+ do execute = ->
+ script = coffees[index++]
+ if script?.type is 'text/coffeescript'
+ if script.src
+ CoffeeScript.load script.src, execute
+ else
+ CoffeeScript.run script.innerHTML
+ execute()
+ null
+
+# Listen for window load, both in browsers and in IE.
+if window.addEventListener
+ addEventListener 'DOMContentLoaded', runScripts, no
+else
+ attachEvent 'onload', runScripts
diff --git a/samples/coffee/classes.coffee b/samples/coffee/classes.coffee
new file mode 100644
index 0000000..2b2a718
--- /dev/null
+++ b/samples/coffee/classes.coffee
@@ -0,0 +1,21 @@
+class Animal
+ constructor: (@name) ->
+
+ move: (meters) ->
+ alert @name + " moved " + meters + "m."
+
+class Snake extends Animal
+ move: ->
+ alert "Slithering..."
+ super 5
+
+class Horse extends Animal
+ move: ->
+ alert "Galloping..."
+ super 45
+
+sam = new Snake "Sammy the Python"
+tom = new Horse "Tommy the Palomino"
+
+sam.move()
+tom.move()
diff --git a/samples/coffee/coffee-script.coffee b/samples/coffee/coffee-script.coffee
new file mode 100644
index 0000000..2933aeb
--- /dev/null
+++ b/samples/coffee/coffee-script.coffee
@@ -0,0 +1,130 @@
+# CoffeeScript can be used both on the server, as a command-line compiler based
+# on Node.js/V8, or to run CoffeeScripts directly in the browser. This module
+# contains the main entry functions for tokenizing, parsing, and compiling
+# source CoffeeScript into JavaScript.
+#
+# If included on a webpage, it will automatically sniff out, compile, and
+# execute all scripts present in `text/coffeescript` tags.
+
+fs = require 'fs'
+path = require 'path'
+{Lexer,RESERVED} = require './lexer'
+{parser} = require './parser'
+vm = require 'vm'
+
+# TODO: Remove registerExtension when fully deprecated.
+if require.extensions
+ require.extensions['.coffee'] = (module, filename) ->
+ content = compile fs.readFileSync(filename, 'utf8'), {filename}
+ module._compile content, filename
+else if require.registerExtension
+ require.registerExtension '.coffee', (content) -> compile content
+
+# The current CoffeeScript version number.
+exports.VERSION = '1.3.3'
+
+# Words that cannot be used as identifiers in CoffeeScript code
+exports.RESERVED = RESERVED
+
+# Expose helpers for testing.
+exports.helpers = require './helpers'
+
+# Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
+# compiler.
+exports.compile = compile = (code, options = {}) ->
+ {merge} = exports.helpers
+ try
+ js = (parser.parse lexer.tokenize code).compile options
+ return js unless options.header
+ catch err
+ err.message = "In #{options.filename}, #{err.message}" if options.filename
+ throw err
+ header = "Generated by CoffeeScript #{@VERSION}"
+ "// #{header}\n#{js}"
+
+# Tokenize a string of CoffeeScript code, and return the array of tokens.
+exports.tokens = (code, options) ->
+ lexer.tokenize code, options
+
+# Parse a string of CoffeeScript code or an array of lexed tokens, and
+# return the AST. You can then compile it by calling `.compile()` on the root,
+# or traverse it by using `.traverseChildren()` with a callback.
+exports.nodes = (source, options) ->
+ if typeof source is 'string'
+ parser.parse lexer.tokenize source, options
+ else
+ parser.parse source
+
+# Compile and execute a string of CoffeeScript (on the server), correctly
+# setting `__filename`, `__dirname`, and relative `require()`.
+exports.run = (code, options = {}) ->
+ mainModule = require.main
+
+ # Set the filename.
+ mainModule.filename = process.argv[1] =
+ if options.filename then fs.realpathSync(options.filename) else '.'
+
+ # Clear the module cache.
+ mainModule.moduleCache and= {}
+
+ # Assign paths for node_modules loading
+ mainModule.paths = require('module')._nodeModulePaths path.dirname fs.realpathSync options.filename
+
+ # Compile.
+ if path.extname(mainModule.filename) isnt '.coffee' or require.extensions
+ mainModule._compile compile(code, options), mainModule.filename
+ else
+ mainModule._compile code, mainModule.filename
+
+# Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
+# The CoffeeScript REPL uses this to run the input.
+exports.eval = (code, options = {}) ->
+ return unless code = code.trim()
+ Script = vm.Script
+ if Script
+ if options.sandbox?
+ if options.sandbox instanceof Script.createContext().constructor
+ sandbox = options.sandbox
+ else
+ sandbox = Script.createContext()
+ sandbox[k] = v for own k, v of options.sandbox
+ sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox
+ else
+ sandbox = global
+ sandbox.__filename = options.filename || 'eval'
+ sandbox.__dirname = path.dirname sandbox.__filename
+ # define module/require only if they chose not to specify their own
+ unless sandbox isnt global or sandbox.module or sandbox.require
+ Module = require 'module'
+ sandbox.module = _module = new Module(options.modulename || 'eval')
+ sandbox.require = _require = (path) -> Module._load path, _module, true
+ _module.filename = sandbox.__filename
+ _require[r] = require[r] for r in Object.getOwnPropertyNames require when r isnt 'paths'
+ # use the same hack node currently uses for their own REPL
+ _require.paths = _module.paths = Module._nodeModulePaths process.cwd()
+ _require.resolve = (request) -> Module._resolveFilename request, _module
+ o = {}
+ o[k] = v for own k, v of options
+ o.bare = on # ensure return value
+ js = compile code, o
+ if sandbox is global
+ vm.runInThisContext js
+ else
+ vm.runInContext js, sandbox
+
+# Instantiate a Lexer for our use here.
+lexer = new Lexer
+
+# The real Lexer produces a generic stream of tokens. This object provides a
+# thin wrapper around it, compatible with the Jison API. We can then pass it
+# directly as a "Jison lexer".
+parser.lexer =
+ lex: ->
+ [tag, @yytext, @yylineno] = @tokens[@pos++] or ['']
+ tag
+ setInput: (@tokens) ->
+ @pos = 0
+ upcomingInput: ->
+ ""
+
+parser.yy = require './nodes'
diff --git a/samples/coffee/empty.coffee b/samples/coffee/empty.coffee
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/coffee/empty.coffee
diff --git a/samples/coffee/hello.coffee b/samples/coffee/hello.coffee
new file mode 100644
index 0000000..4b65a57
--- /dev/null
+++ b/samples/coffee/hello.coffee
@@ -0,0 +1 @@
+console.log "Hello, World!"
diff --git a/samples/coffee/intro.coffee b/samples/coffee/intro.coffee
new file mode 100644
index 0000000..0db65dd
--- /dev/null
+++ b/samples/coffee/intro.coffee
@@ -0,0 +1,28 @@
+# Assignment:
+number = 42
+opposite = true
+
+# Conditions:
+number = -42 if opposite
+
+# Functions:
+square = (x) -> x * x
+
+# Arrays:
+list = [1, 2, 3, 4, 5]
+
+# Objects:
+math =
+ root: Math.sqrt
+ square: square
+ cube: (x) -> x * square x
+
+# Splats:
+race = (winner, runners...) ->
+ print winner, runners
+
+# Existence:
+alert "I knew it!" if elvis?
+
+# Array comprehensions:
+cubes = (math.cube num for num in list)
diff --git a/samples/coffee/lexer.coffee b/samples/coffee/lexer.coffee
new file mode 100644
index 0000000..56e8e32
--- /dev/null
+++ b/samples/coffee/lexer.coffee
@@ -0,0 +1,709 @@
+# The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
+# matches against the beginning of the source code. When a match is found,
+# a token is produced, we consume the match, and start again. Tokens are in the
+# form:
+#
+# [tag, value, lineNumber]
+#
+# Which is a format that can be fed directly into [Jison](http://github.com/zaach/jison).
+
+{Rewriter, INVERSES} = require './rewriter'
+
+# Import the helpers we need.
+{count, starts, compact, last} = require './helpers'
+
+# The Lexer Class
+# ---------------
+
+# The Lexer class reads a stream of CoffeeScript and divvies it up into tagged
+# tokens. Some potential ambiguity in the grammar has been avoided by
+# pushing some extra smarts into the Lexer.
+exports.Lexer = class Lexer
+
+ # **tokenize** is the Lexer's main method. Scan by attempting to match tokens
+ # one at a time, using a regular expression anchored at the start of the
+ # remaining code, or a custom recursive token-matching method
+ # (for interpolations). When the next token has been recorded, we move forward
+ # within the code past the token, and begin again.
+ #
+ # Each tokenizing method is responsible for returning the number of characters
+ # it has consumed.
+ #
+ # Before returning the token stream, run it through the [Rewriter](rewriter.html)
+ # unless explicitly asked not to.
+ tokenize: (code, opts = {}) ->
+ code = "\n#{code}" if WHITESPACE.test code
+ code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
+
+ @code = code # The remainder of the source code.
+ @line = opts.line or 0 # The current line.
+ @indent = 0 # The current indentation level.
+ @indebt = 0 # The over-indentation at the current level.
+ @outdebt = 0 # The under-outdentation at the current level.
+ @indents = [] # The stack of all current indentation levels.
+ @ends = [] # The stack for pairing up tokens.
+ @tokens = [] # Stream of parsed tokens in the form `['TYPE', value, line]`.
+
+ # At every position, run through this list of attempted matches,
+ # short-circuiting if any of them succeed. Their order determines precedence:
+ # `@literalToken` is the fallback catch-all.
+ i = 0
+ while @chunk = code[i..]
+ i += @identifierToken() or
+ @commentToken() or
+ @whitespaceToken() or
+ @lineToken() or
+ @heredocToken() or
+ @stringToken() or
+ @numberToken() or
+ @regexToken() or
+ @jsToken() or
+ @literalToken()
+
+ @closeIndentation()
+ @error "missing #{tag}" if tag = @ends.pop()
+ return @tokens if opts.rewrite is off
+ (new Rewriter).rewrite @tokens
+
+ # Tokenizers
+ # ----------
+
+ # Matches identifying literals: variables, keywords, method names, etc.
+ # Check to ensure that JavaScript reserved words aren't being used as
+ # identifiers. Because CoffeeScript reserves a handful of keywords that are
+ # allowed in JavaScript, we're careful not to tag them as keywords when
+ # referenced as property names here, so you can still do `jQuery.is()` even
+ # though `is` means `===` otherwise.
+ identifierToken: ->
+ return 0 unless match = IDENTIFIER.exec @chunk
+ [input, id, colon] = match
+
+ if id is 'own' and @tag() is 'FOR'
+ @token 'OWN', id
+ return id.length
+ forcedIdentifier = colon or
+ (prev = last @tokens) and (prev[0] in ['.', '?.', '::'] or
+ not prev.spaced and prev[0] is '@')
+ tag = 'IDENTIFIER'
+
+ if not forcedIdentifier and (id in JS_KEYWORDS or id in COFFEE_KEYWORDS)
+ tag = id.toUpperCase()
+ if tag is 'WHEN' and @tag() in LINE_BREAK
+ tag = 'LEADING_WHEN'
+ else if tag is 'FOR'
+ @seenFor = yes
+ else if tag is 'UNLESS'
+ tag = 'IF'
+ else if tag in UNARY
+ tag = 'UNARY'
+ else if tag in RELATION
+ if tag isnt 'INSTANCEOF' and @seenFor
+ tag = 'FOR' + tag
+ @seenFor = no
+ else
+ tag = 'RELATION'
+ if @value() is '!'
+ @tokens.pop()
+ id = '!' + id
+
+ if id in JS_FORBIDDEN
+ if forcedIdentifier
+ tag = 'IDENTIFIER'
+ id = new String id
+ id.reserved = yes
+ else if id in RESERVED
+ @error "reserved word \"#{id}\""
+
+ unless forcedIdentifier
+ id = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES
+ tag = switch id
+ when '!' then 'UNARY'
+ when '==', '!=' then 'COMPARE'
+ when '&&', '||' then 'LOGIC'
+ when 'true', 'false' then 'BOOL'
+ when 'break', 'continue' then 'STATEMENT'
+ else tag
+
+ @token tag, id
+ @token ':', ':' if colon
+ input.length
+
+ # Matches numbers, including decimals, hex, and exponential notation.
+ # Be careful not to interfere with ranges-in-progress.
+ numberToken: ->
+ return 0 unless match = NUMBER.exec @chunk
+ number = match[0]
+ if /^0[BOX]/.test number
+ @error "radix prefix '#{number}' must be lowercase"
+ else if /E/.test(number) and not /^0x/.test number
+ @error "exponential notation '#{number}' must be indicated with a lowercase 'e'"
+ else if /^0\d*[89]/.test number
+ @error "decimal literal '#{number}' must not be prefixed with '0'"
+ else if /^0\d+/.test number
+ @error "octal literal '#{number}' must be prefixed with '0o'"
+ lexedLength = number.length
+ if octalLiteral = /^0o([0-7]+)/.exec number
+ number = '0x' + (parseInt octalLiteral[1], 8).toString 16
+ if binaryLiteral = /^0b([01]+)/.exec number
+ number = '0x' + (parseInt binaryLiteral[1], 2).toString 16
+ @token 'NUMBER', number
+ lexedLength
+
+ # Matches strings, including multi-line strings. Ensures that quotation marks
+ # are balanced within the string's contents, and within nested interpolations.
+ stringToken: ->
+ switch @chunk.charAt 0
+ when "'"
+ return 0 unless match = SIMPLESTR.exec @chunk
+ @token 'STRING', (string = match[0]).replace MULTILINER, '\\\n'
+ when '"'
+ return 0 unless string = @balancedString @chunk, '"'
+ if 0 < string.indexOf '#{', 1
+ @interpolateString string[1...-1]
+ else
+ @token 'STRING', @escapeLines string
+ else
+ return 0
+ if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
+ @error "octal escape sequences #{string} are not allowed"
+ @line += count string, '\n'
+ string.length
+
+ # Matches heredocs, adjusting indentation to the correct level, as heredocs
+ # preserve whitespace, but ignore indentation to the left.
+ heredocToken: ->
+ return 0 unless match = HEREDOC.exec @chunk
+ heredoc = match[0]
+ quote = heredoc.charAt 0
+ doc = @sanitizeHeredoc match[2], quote: quote, indent: null
+ if quote is '"' and 0 <= doc.indexOf '#{'
+ @interpolateString doc, heredoc: yes
+ else
+ @token 'STRING', @makeString doc, quote, yes
+ @line += count heredoc, '\n'
+ heredoc.length
+
+ # Matches and consumes comments.
+ commentToken: ->
+ return 0 unless match = @chunk.match COMMENT
+ [comment, here] = match
+ if here
+ @token 'HERECOMMENT', @sanitizeHeredoc here,
+ herecomment: true, indent: Array(@indent + 1).join(' ')
+ @line += count comment, '\n'
+ comment.length
+
+ # Matches JavaScript interpolated directly into the source via backticks.
+ jsToken: ->
+ return 0 unless @chunk.charAt(0) is '`' and match = JSTOKEN.exec @chunk
+ @token 'JS', (script = match[0])[1...-1]
+ script.length
+
+ # Matches regular expression literals. Lexing regular expressions is difficult
+ # to distinguish from division, so we borrow some basic heuristics from
+ # JavaScript and Ruby.
+ regexToken: ->
+ return 0 if @chunk.charAt(0) isnt '/'
+ if match = HEREGEX.exec @chunk
+ length = @heregexToken match
+ @line += count match[0], '\n'
+ return length
+
+ prev = last @tokens
+ return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
+ return 0 unless match = REGEX.exec @chunk
+ [match, regex, flags] = match
+ if regex[..1] is '/*' then @error 'regular expressions cannot begin with `*`'
+ if regex is '//' then regex = '/(?:)/'
+ @token 'REGEX', "#{regex}#{flags}"
+ match.length
+
+ # Matches multiline extended regular expressions.
+ heregexToken: (match) ->
+ [heregex, body, flags] = match
+ if 0 > body.indexOf '#{'
+ re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/')
+ if re.match /^\*/ then @error 'regular expressions cannot begin with `*`'
+ @token 'REGEX', "/#{ re or '(?:)' }/#{flags}"
+ return heregex.length
+ @token 'IDENTIFIER', 'RegExp'
+ @tokens.push ['CALL_START', '(']
+ tokens = []
+ for [tag, value] in @interpolateString(body, regex: yes)
+ if tag is 'TOKENS'
+ tokens.push value...
+ else
+ continue unless value = value.replace HEREGEX_OMIT, ''
+ value = value.replace /\\/g, '\\\\'
+ tokens.push ['STRING', @makeString(value, '"', yes)]
+ tokens.push ['+', '+']
+ tokens.pop()
+ @tokens.push ['STRING', '""'], ['+', '+'] unless tokens[0]?[0] is 'STRING'
+ @tokens.push tokens...
+ @tokens.push [',', ','], ['STRING', '"' + flags + '"'] if flags
+ @token ')', ')'
+ heregex.length
+
+ # Matches newlines, indents, and outdents, and determines which is which.
+ # If we can detect that the current line is continued onto the the next line,
+ # then the newline is suppressed:
+ #
+ # elements
+ # .each( ... )
+ # .map( ... )
+ #
+ # Keeps track of the level of indentation, because a single outdent token
+ # can close multiple indents, so we need to know how far in we happen to be.
+ lineToken: ->
+ return 0 unless match = MULTI_DENT.exec @chunk
+ indent = match[0]
+ @line += count indent, '\n'
+ @seenFor = no
+ size = indent.length - 1 - indent.lastIndexOf '\n'
+ noNewlines = @unfinished()
+ if size - @indebt is @indent
+ if noNewlines then @suppressNewlines() else @newlineToken()
+ return indent.length
+ if size > @indent
+ if noNewlines
+ @indebt = size - @indent
+ @suppressNewlines()
+ return indent.length
+ diff = size - @indent + @outdebt
+ @token 'INDENT', diff
+ @indents.push diff
+ @ends.push 'OUTDENT'
+ @outdebt = @indebt = 0
+ else
+ @indebt = 0
+ @outdentToken @indent - size, noNewlines
+ @indent = size
+ indent.length
+
+ # Record an outdent token or multiple tokens, if we happen to be moving back
+ # inwards past several recorded indents.
+ outdentToken: (moveOut, noNewlines) ->
+ while moveOut > 0
+ len = @indents.length - 1
+ if @indents[len] is undefined
+ moveOut = 0
+ else if @indents[len] is @outdebt
+ moveOut -= @outdebt
+ @outdebt = 0
+ else if @indents[len] < @outdebt
+ @outdebt -= @indents[len]
+ moveOut -= @indents[len]
+ else
+ dent = @indents.pop() - @outdebt
+ moveOut -= dent
+ @outdebt = 0
+ @pair 'OUTDENT'
+ @token 'OUTDENT', dent
+ @outdebt -= moveOut if dent
+ @tokens.pop() while @value() is ';'
+ @token 'TERMINATOR', '\n' unless @tag() is 'TERMINATOR' or noNewlines
+ this
+
+ # Matches and consumes non-meaningful whitespace. Tag the previous token
+ # as being "spaced", because there are some cases where it makes a difference.
+ whitespaceToken: ->
+ return 0 unless (match = WHITESPACE.exec @chunk) or
+ (nline = @chunk.charAt(0) is '\n')
+ prev = last @tokens
+ prev[if match then 'spaced' else 'newLine'] = true if prev
+ if match then match[0].length else 0
+
+ # Generate a newline token. Consecutive newlines get merged together.
+ newlineToken: ->
+ @tokens.pop() while @value() is ';'
+ @token 'TERMINATOR', '\n' unless @tag() is 'TERMINATOR'
+ this
+
+ # Use a `\` at a line-ending to suppress the newline.
+ # The slash is removed here once its job is done.
+ suppressNewlines: ->
+ @tokens.pop() if @value() is '\\'
+ this
+
+ # We treat all other single characters as a token. E.g.: `( ) , . !`
+ # Multi-character operators are also literal tokens, so that Jison can assign
+ # the proper order of operations. There are some symbols that we tag specially
+ # here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
+ # parentheses that indicate a method call from regular parentheses, and so on.
+ literalToken: ->
+ if match = OPERATOR.exec @chunk
+ [value] = match
+ @tagParameters() if CODE.test value
+ else
+ value = @chunk.charAt 0
+ tag = value
+ prev = last @tokens
+ if value is '=' and prev
+ if not prev[1].reserved and prev[1] in JS_FORBIDDEN
+ @error "reserved word \"#{@value()}\" can't be assigned"
+ if prev[1] in ['||', '&&']
+ prev[0] = 'COMPOUND_ASSIGN'
+ prev[1] += '='
+ return value.length
+ if value is ';'
+ @seenFor = no
+ tag = 'TERMINATOR'
+ else if value in MATH then tag = 'MATH'
+ else if value in COMPARE then tag = 'COMPARE'
+ else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'
+ else if value in UNARY then tag = 'UNARY'
+ else if value in SHIFT then tag = 'SHIFT'
+ else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC'
+ else if prev and not prev.spaced
+ if value is '(' and prev[0] in CALLABLE
+ prev[0] = 'FUNC_EXIST' if prev[0] is '?'
+ tag = 'CALL_START'
+ else if value is '[' and prev[0] in INDEXABLE
+ tag = 'INDEX_START'
+ switch prev[0]
+ when '?' then prev[0] = 'INDEX_SOAK'
+ switch value
+ when '(', '{', '[' then @ends.push INVERSES[value]
+ when ')', '}', ']' then @pair value
+ @token tag, value
+ value.length
+
+ # Token Manipulators
+ # ------------------
+
+ # Sanitize a heredoc or herecomment by
+ # erasing all external indentation on the left-hand side.
+ sanitizeHeredoc: (doc, options) ->
+ {indent, herecomment} = options
+ if herecomment
+ if HEREDOC_ILLEGAL.test doc
+ @error "block comment cannot contain \"*/\", starting"
+ return doc if doc.indexOf('\n') <= 0
+ else
+ while match = HEREDOC_INDENT.exec doc
+ attempt = match[1]
+ indent = attempt if indent is null or 0 < attempt.length < indent.length
+ doc = doc.replace /// \n #{indent} ///g, '\n' if indent
+ doc = doc.replace /^\n/, '' unless herecomment
+ doc
+
+ # A source of ambiguity in our grammar used to be parameter lists in function
+ # definitions versus argument lists in function calls. Walk backwards, tagging
+ # parameters specially in order to make things easier for the parser.
+ tagParameters: ->
+ return this if @tag() isnt ')'
+ stack = []
+ {tokens} = this
+ i = tokens.length
+ tokens[--i][0] = 'PARAM_END'
+ while tok = tokens[--i]
+ switch tok[0]
+ when ')'
+ stack.push tok
+ when '(', 'CALL_START'
+ if stack.length then stack.pop()
+ else if tok[0] is '('
+ tok[0] = 'PARAM_START'
+ return this
+ else return this
+ this
+
+ # Close up all remaining open blocks at the end of the file.
+ closeIndentation: ->
+ @outdentToken @indent
+
+ # Matches a balanced group such as a single or double-quoted string. Pass in
+ # a series of delimiters, all of which must be nested correctly within the
+ # contents of the string. This method allows us to have strings within
+ # interpolations within strings, ad infinitum.
+ balancedString: (str, end) ->
+ continueCount = 0
+ stack = [end]
+ for i in [1...str.length]
+ if continueCount
+ --continueCount
+ continue
+ switch letter = str.charAt i
+ when '\\'
+ ++continueCount
+ continue
+ when end
+ stack.pop()
+ unless stack.length
+ return str[0..i]
+ end = stack[stack.length - 1]
+ continue
+ if end is '}' and letter in ['"', "'"]
+ stack.push end = letter
+ else if end is '}' and letter is '/' and match = (HEREGEX.exec(str[i..]) or REGEX.exec(str[i..]))
+ continueCount += match[0].length - 1
+ else if end is '}' and letter is '{'
+ stack.push end = '}'
+ else if end is '"' and prev is '#' and letter is '{'
+ stack.push end = '}'
+ prev = letter
+ @error "missing #{ stack.pop() }, starting"
+
+ # Expand variables and expressions inside double-quoted strings using
+ # Ruby-like notation for substitution of arbitrary expressions.
+ #
+ # "Hello #{name.capitalize()}."
+ #
+ # If it encounters an interpolation, this method will recursively create a
+ # new Lexer, tokenize the interpolated contents, and merge them into the
+ # token stream.
+ interpolateString: (str, options = {}) ->
+ {heredoc, regex} = options
+ tokens = []
+ pi = 0
+ i = -1
+ while letter = str.charAt i += 1
+ if letter is '\\'
+ i += 1
+ continue
+ unless letter is '#' and str.charAt(i+1) is '{' and
+ (expr = @balancedString str[i + 1..], '}')
+ continue
+ tokens.push ['NEOSTRING', str[pi...i]] if pi < i
+ inner = expr[1...-1]
+ if inner.length
+ nested = new Lexer().tokenize inner, line: @line, rewrite: off
+ nested.pop()
+ nested.shift() if nested[0]?[0] is 'TERMINATOR'
+ if len = nested.length
+ if len > 1
+ nested.unshift ['(', '(', @line]
+ nested.push [')', ')', @line]
+ tokens.push ['TOKENS', nested]
+ i += expr.length
+ pi = i + 1
+ tokens.push ['NEOSTRING', str[pi..]] if i > pi < str.length
+ return tokens if regex
+ return @token 'STRING', '""' unless tokens.length
+ tokens.unshift ['', ''] unless tokens[0][0] is 'NEOSTRING'
+ @token '(', '(' if interpolated = tokens.length > 1
+ for [tag, value], i in tokens
+ @token '+', '+' if i
+ if tag is 'TOKENS'
+ @tokens.push value...
+ else
+ @token 'STRING', @makeString value, '"', heredoc
+ @token ')', ')' if interpolated
+ tokens
+
+ # Pairs up a closing token, ensuring that all listed pairs of tokens are
+ # correctly balanced throughout the course of the token stream.
+ pair: (tag) ->
+ unless tag is wanted = last @ends
+ @error "unmatched #{tag}" unless 'OUTDENT' is wanted
+ # Auto-close INDENT to support syntax like this:
+ #
+ # el.click((event) ->
+ # el.hide())
+ #
+ @indent -= size = last @indents
+ @outdentToken size, true
+ return @pair tag
+ @ends.pop()
+
+ # Helpers
+ # -------
+
+ # Add a token to the results, taking note of the line number.
+ token: (tag, value) ->
+ @tokens.push [tag, value, @line]
+
+ # Peek at a tag in the current token stream.
+ tag: (index, tag) ->
+ (tok = last @tokens, index) and if tag then tok[0] = tag else tok[0]
+
+ # Peek at a value in the current token stream.
+ value: (index, val) ->
+ (tok = last @tokens, index) and if val then tok[1] = val else tok[1]
+
+ # Are we in the midst of an unfinished expression?
+ unfinished: ->
+ LINE_CONTINUER.test(@chunk) or
+ @tag() in ['\\', '.', '?.', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
+ 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
+
+ # Converts newlines for string literals.
+ escapeLines: (str, heredoc) ->
+ str.replace MULTILINER, if heredoc then '\\n' else ''
+
+ # Constructs a string token by escaping quotes and newlines.
+ makeString: (body, quote, heredoc) ->
+ return quote + quote unless body
+ body = body.replace /\\([\s\S])/g, (match, contents) ->
+ if contents in ['\n', quote] then contents else match
+ body = body.replace /// #{quote} ///g, '\\$&'
+ quote + @escapeLines(body, heredoc) + quote
+
+ # Throws a syntax error on the current `@line`.
+ error: (message) ->
+ throw SyntaxError "#{message} on line #{ @line + 1}"
+
+# Constants
+# ---------
+
+# Keywords that CoffeeScript shares in common with JavaScript.
+JS_KEYWORDS = [
+ 'true', 'false', 'null', 'this'
+ 'new', 'delete', 'typeof', 'in', 'instanceof'
+ 'return', 'throw', 'break', 'continue', 'debugger'
+ 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally'
+ 'class', 'extends', 'super'
+]
+
+# CoffeeScript-only keywords.
+COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when']
+
+COFFEE_ALIAS_MAP =
+ and : '&&'
+ or : '||'
+ is : '=='
+ isnt : '!='
+ not : '!'
+ yes : 'true'
+ no : 'false'
+ on : 'true'
+ off : 'false'
+
+COFFEE_ALIASES = (key for key of COFFEE_ALIAS_MAP)
+COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat COFFEE_ALIASES
+
+# The list of keywords that are reserved by JavaScript, but not used, or are
+# used by CoffeeScript internally. We throw an error when these are encountered,
+# to avoid having a JavaScript error at runtime.
+RESERVED = [
+ 'case', 'default', 'function', 'var', 'void', 'with'
+ 'const', 'let', 'enum', 'export', 'import', 'native'
+ '__hasProp', '__extends', '__slice', '__bind', '__indexOf'
+ 'implements', 'interface', 'let', 'package',
+ 'private', 'protected', 'public', 'static', 'yield'
+]
+
+STRICT_PROSCRIBED = ['arguments', 'eval']
+
+# The superset of both JavaScript keywords and reserved words, none of which may
+# be used as identifiers or properties.
+JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED)
+
+exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED)
+exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED
+
+# Token matching regexes.
+IDENTIFIER = /// ^
+ ( [$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]* )
+ ( [^\n\S]* : (?!:) )? # Is this a property name?
+///
+
+NUMBER = ///
+ ^ 0b[01]+ | # binary
+ ^ 0o[0-7]+ | # octal
+ ^ 0x[\da-f]+ | # hex
+ ^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
+///i
+
+HEREDOC = /// ^ ("""|''') ([\s\S]*?) (?:\n[^\n\S]*)? \1 ///
+
+OPERATOR = /// ^ (
+ ?: [-=]> # function
+ | [-+*/%<>&|^!?=]= # compound assign / compare
+ | >>>=? # zero-fill right shift
+ | ([-+:])\1 # doubles
+ | ([&|<>])\2=? # logic / shift
+ | \?\. # soak access
+ | \.{2,3} # range or splat
+) ///
+
+WHITESPACE = /^[^\n\S]+/
+
+COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/
+
+CODE = /^[-=]>/
+
+MULTI_DENT = /^(?:\n[^\n\S]*)+/
+
+SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/
+
+JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/
+
+# Regex-matching-regexes.
+REGEX = /// ^
+ (/ (?! [\s=] ) # disallow leading whitespace or equals signs
+ [^ [ / \n \\ ]* # every other thing
+ (?:
+ (?: \\[\s\S] # anything escaped
+ | \[ # character class
+ [^ \] \n \\ ]*
+ (?: \\[\s\S] [^ \] \n \\ ]* )*
+ ]
+ ) [^ [ / \n \\ ]*
+ )*
+ /) ([imgy]{0,4}) (?!\w)
+///
+
+HEREGEX = /// ^ /{3} ([\s\S]+?) /{3} ([imgy]{0,4}) (?!\w) ///
+
+HEREGEX_OMIT = /\s+(?:#.*)?/g
+
+# Token cleaning regexes.
+MULTILINER = /\n/g
+
+HEREDOC_INDENT = /\n+([^\n\S]*)/g
+
+HEREDOC_ILLEGAL = /\*\//
+
+LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
+
+TRAILING_SPACES = /\s+$/
+
+# Compound assignment tokens.
+COMPOUND_ASSIGN = [
+ '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
+]
+
+# Unary tokens.
+UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO']
+
+# Logical tokens.
+LOGIC = ['&&', '||', '&', '|', '^']
+
+# Bit-shifting tokens.
+SHIFT = ['<<', '>>', '>>>']
+
+# Comparison tokens.
+COMPARE = ['==', '!=', '<', '>', '<=', '>=']
+
+# Mathematical tokens.
+MATH = ['*', '/', '%']
+
+# Relational tokens that are negatable with `not` prefix.
+RELATION = ['IN', 'OF', 'INSTANCEOF']
+
+# Boolean tokens.
+BOOL = ['TRUE', 'FALSE']
+
+# Tokens which a regular expression will never immediately follow, but which
+# a division operator might.
+#
+# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
+#
+# Our list is shorter, due to sans-parentheses method calls.
+NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--', ']']
+
+# If the previous token is not spaced, there are more preceding tokens that
+# force a division parse:
+NOT_SPACED_REGEX = NOT_REGEX.concat ')', '}', 'THIS', 'IDENTIFIER', 'STRING'
+
+# Tokens which could legitimately be invoked or indexed. An opening
+# parentheses or bracket following these tokens will be recorded as the start
+# of a function invocation or indexing operation.
+CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']
+INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED'
+
+# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
+# occurs at the start of a line. We disambiguate these from trailing whens to
+# avoid an ambiguity in the grammar.
+LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
diff --git a/samples/coffee/rack_application.coffee b/samples/coffee/rack_application.coffee
new file mode 100644
index 0000000..674313b
--- /dev/null
+++ b/samples/coffee/rack_application.coffee
@@ -0,0 +1,256 @@
+# The `RackApplication` class is responsible for managing a
+# [Nack](http://josh.github.com/nack/) pool for a given Rack
+# application. Incoming HTTP requests are dispatched to
+# `RackApplication` instances by an `HttpServer`, where they are
+# subsequently handled by a pool of Nack worker processes. By default,
+# Pow tells Nack to use a maximum of two worker processes per
+# application, but this can be overridden with the configuration's
+# `workers` option.
+#
+# Before creating the Nack pool, Pow executes the `.powrc` and
+# `.powenv` scripts if they're present in the application root,
+# captures their environment variables, and passes them along to the
+# Nack worker processes. This lets you modify your `RUBYOPT` to use
+# different Ruby options, for example.
+#
+# If [rvm](http://rvm.beginrescueend.com/) is installed and an
+# `.rvmrc` file is present in the application's root, Pow will load
+# both before creating the Nack pool. This makes it easy to run an
+# app with a specific version of Ruby.
+#
+# Nack workers remain running until they're killed, restarted (by
+# touching the `tmp/restart.txt` file in the application root), or
+# until the application has not served requests for the length of time
+# specified in the configuration's `timeout` option (15 minutes by
+# default).
+
+async = require "async"
+fs = require "fs"
+nack = require "nack"
+
+{bufferLines, pause, sourceScriptEnv} = require "./util"
+{join, exists, basename, resolve} = require "path"
+
+module.exports = class RackApplication
+ # Create a `RackApplication` for the given configuration and
+ # root path. The application begins life in the uninitialized
+ # state.
+ constructor: (@configuration, @root, @firstHost) ->
+ @logger = @configuration.getLogger join "apps", basename @root
+ @readyCallbacks = []
+ @quitCallbacks = []
+ @statCallbacks = []
+
+ # Queue `callback` to be invoked when the application becomes ready,
+ # then start the initialization process. If the application's state
+ # is ready, the callback is invoked immediately.
+ ready: (callback) ->
+ if @state is "ready"
+ callback()
+ else
+ @readyCallbacks.push callback
+ @initialize()
+
+ # Tell the application to quit and queue `callback` to be invoked
+ # when all workers have exited. If the application has already quit,
+ # the callback is invoked immediately.
+ quit: (callback) ->
+ if @state
+ @quitCallbacks.push callback if callback
+ @terminate()
+ else
+ callback?()
+
+ # Stat `tmp/restart.txt` in the application root and invoke the
+ # given callback with a single argument indicating whether or not
+ # the file has been touched since the last call to
+ # `queryRestartFile`.
+ queryRestartFile: (callback) ->
+ fs.stat join(@root, "tmp/restart.txt"), (err, stats) =>
+ if err
+ @mtime = null
+ callback false
+ else
+ lastMtime = @mtime
+ @mtime = stats.mtime.getTime()
+ callback lastMtime isnt @mtime
+
+ # Check to see if `tmp/always_restart.txt` is present in the
+ # application root, and set the pool's `runOnce` option
+ # accordingly. Invoke `callback` when the existence check has
+ # finished. (Multiple calls to this method are aggregated.)
+ setPoolRunOnceFlag: (callback) ->
+ unless @statCallbacks.length
+ exists join(@root, "tmp/always_restart.txt"), (alwaysRestart) =>
+ @pool.runOnce = alwaysRestart
+ statCallback() for statCallback in @statCallbacks
+ @statCallbacks = []
+
+ @statCallbacks.push callback
+
+ # Collect environment variables from `.powrc` and `.powenv`, in that
+ # order, if present. The idea is that `.powrc` files can be checked
+ # into a source code repository for global configuration, leaving
+ # `.powenv` free for any necessary local overrides.
+ loadScriptEnvironment: (env, callback) ->
+ async.reduce [".powrc", ".envrc", ".powenv"], env, (env, filename, callback) =>
+ exists script = join(@root, filename), (scriptExists) ->
+ if scriptExists
+ sourceScriptEnv script, env, callback
+ else
+ callback null, env
+ , callback
+
+ # If `.rvmrc` and `$HOME/.rvm/scripts/rvm` are present, load rvm,
+ # source `.rvmrc`, and invoke `callback` with the resulting
+ # environment variables. If `.rvmrc` is present but rvm is not
+ # installed, invoke `callback` without sourcing `.rvmrc`.
+ # Before loading rvm, Pow invokes a helper script that shows a
+ # deprecation notice if it has not yet been displayed.
+ loadRvmEnvironment: (env, callback) ->
+ exists script = join(@root, ".rvmrc"), (rvmrcExists) =>
+ if rvmrcExists
+ exists rvm = @configuration.rvmPath, (rvmExists) =>
+ if rvmExists
+ libexecPath = resolve "#{__dirname}/../libexec"
+ before = """
+ '#{libexecPath}/pow_rvm_deprecation_notice' '#{[@firstHost]}'
+ source '#{rvm}' > /dev/null
+ """.trim()
+ sourceScriptEnv script, env, {before}, callback
+ else
+ callback null, env
+ else
+ callback null, env
+
+ # Stat `tmp/restart.txt` to cache its mtime, then load the
+ # application's full environment from `.powrc`, `.powenv`, and
+ # `.rvmrc`.
+ loadEnvironment: (callback) ->
+ @queryRestartFile =>
+ @loadScriptEnvironment @configuration.env, (err, env) =>
+ if err then callback err
+ else @loadRvmEnvironment env, (err, env) =>
+ if err then callback err
+ else callback null, env
+
+ # Begin the initialization process if the application is in the
+ # uninitialized state. (If the application is terminating, queue a
+ # call to `initialize` after all workers have exited.)
+ initialize: ->
+ if @state
+ if @state is "terminating"
+ @quit => @initialize()
+ return
+
+ @state = "initializing"
+
+ # Load the application's environment. If an error is raised or
+ # either of the environment scripts exits with a non-zero status,
+ # reset the application's state and log the error.
+ @loadEnvironment (err, env) =>
+ if err
+ @state = null
+ @logger.error err.message
+ @logger.error "stdout: #{err.stdout}"
+ @logger.error "stderr: #{err.stderr}"
+
+ # Set the application's state to ready. Then create the Nack
+ # pool instance using the `workers` and `timeout` options from
+ # the application's environment or the global configuration.
+ else
+ @state = "ready"
+
+ @pool = nack.createPool join(@root, "config.ru"),
+ env: env
+ size: env?.POW_WORKERS ? @configuration.workers
+ idle: (env?.POW_TIMEOUT ? @configuration.timeout) * 1000
+
+ # Log the workers' stderr and stdout, and log each worker's
+ # PID as it spawns and exits.
+ bufferLines @pool.stdout, (line) => @logger.info line
+ bufferLines @pool.stderr, (line) => @logger.warning line
+
+ @pool.on "worker:spawn", (process) =>
+ @logger.debug "nack worker #{process.child.pid} spawned"
+
+ @pool.on "worker:exit", (process) =>
+ @logger.debug "nack worker exited"
+
+ # Invoke and remove all queued callbacks, passing along the
+ # error, if any.
+ readyCallback err for readyCallback in @readyCallbacks
+ @readyCallbacks = []
+
+ # Begin the termination process. (If the application is initializing,
+ # wait until it is ready before shutting down.)
+ terminate: ->
+ if @state is "initializing"
+ @ready => @terminate()
+
+ else if @state is "ready"
+ @state = "terminating"
+
+ # Instruct all workers to exit. After the processes have
+ # terminated, reset the application's state, then invoke and
+ # remove all queued callbacks.
+ @pool.quit =>
+ @state = null
+ @mtime = null
+ @pool = null
+
+ quitCallback() for quitCallback in @quitCallbacks
+ @quitCallbacks = []
+
+ # Handle an incoming HTTP request. Wait until the application is in
+ # the ready state, restart the workers if necessary, then pass the
+ # request along to the Nack pool. If the Nack worker raises an
+ # exception handling the request, reset the application.
+ handle: (req, res, next, callback) ->
+ resume = pause req
+ @ready (err) =>
+ return next err if err
+ @setPoolRunOnceFlag =>
+ @restartIfNecessary =>
+ req.proxyMetaVariables =
+ SERVER_PORT: @configuration.dstPort.toString()
+ try
+ @pool.proxy req, res, (err) =>
+ @quit() if err
+ next err
+ finally
+ resume()
+ callback?()
+
+ # Terminate the application, re-initialize it, and invoke the given
+ # callback when the application's state becomes ready.
+ restart: (callback) ->
+ @quit =>
+ @ready callback
+
+ # Restart the application if `tmp/restart.txt` has been touched
+ # since the last call to this function.
+ restartIfNecessary: (callback) ->
+ @queryRestartFile (mtimeChanged) =>
+ if mtimeChanged
+ @restart callback
+ else
+ callback()
+
+ # Append RVM autoload boilerplate to the application's `.powrc`
+ # file. This is called by the RVM deprecation notice mini-app.
+ writeRvmBoilerplate: ->
+ powrc = join @root, ".powrc"
+ boilerplate = @constructor.rvmBoilerplate
+
+ fs.readFile powrc, "utf8", (err, contents) ->
+ contents ?= ""
+ if contents.indexOf(boilerplate) is -1
+ fs.writeFile powrc, "#{boilerplate}\n#{contents}"
+
+ @rvmBoilerplate: """
+ if [ -f "$rvm_path/scripts/rvm" ] && [ -f ".rvmrc" ]; then
+ source "$rvm_path/scripts/rvm"
+ source ".rvmrc"
+ fi
+ """
diff --git a/samples/coffee/xipd.coffee b/samples/coffee/xipd.coffee
new file mode 100644
index 0000000..8b49f1f
--- /dev/null
+++ b/samples/coffee/xipd.coffee
@@ -0,0 +1,110 @@
+dnsserver = require "dnsserver"
+
+exports.Server = class Server extends dnsserver.Server
+ NS_T_A = 1
+ NS_T_NS = 2
+ NS_T_CNAME = 5
+ NS_T_SOA = 6
+ NS_C_IN = 1
+ NS_RCODE_NXDOMAIN = 3
+
+ constructor: (domain, @rootAddress) ->
+ super
+ @domain = domain.toLowerCase()
+ @soa = createSOA @domain
+ @on "request", @handleRequest
+
+ handleRequest: (req, res) =>
+ question = req.question
+ subdomain = @extractSubdomain question.name
+
+ if subdomain? and isARequest question
+ res.addRR question.name, NS_T_A, NS_C_IN, 600, subdomain.getAddress()
+ else if subdomain?.isEmpty() and isNSRequest question
+ res.addRR question.name, NS_T_SOA, NS_C_IN, 600, @soa, true
+ else
+ res.header.rcode = NS_RCODE_NXDOMAIN
+
+ res.send()
+
+ extractSubdomain: (name) ->
+ Subdomain.extract name, @domain, @rootAddress
+
+ isARequest = (question) ->
+ question.type is NS_T_A and question.class is NS_C_IN
+
+ isNSRequest = (question) ->
+ question.type is NS_T_NS and question.class is NS_C_IN
+
+ createSOA = (domain) ->
+ mname = "ns-1.#{domain}"
+ rname = "hostmaster.#{domain}"
+ serial = parseInt new Date().getTime() / 1000
+ refresh = 28800
+ retry = 7200
+ expire = 604800
+ minimum = 3600
+ dnsserver.createSOA mname, rname, serial, refresh, retry, expire, minimum
+
+exports.createServer = (domain, address = "127.0.0.1") ->
+ new Server domain, address
+
+exports.Subdomain = class Subdomain
+ @extract: (name, domain, address) ->
+ return unless name
+ name = name.toLowerCase()
+ offset = name.length - domain.length
+
+ if domain is name.slice offset
+ subdomain = if 0 >= offset then null else name.slice 0, offset - 1
+ new constructor subdomain, address if constructor = @for subdomain
+
+ @for: (subdomain = "") ->
+ if IPAddressSubdomain.pattern.test subdomain
+ IPAddressSubdomain
+ else if EncodedSubdomain.pattern.test subdomain
+ EncodedSubdomain
+ else
+ Subdomain
+
+ constructor: (@subdomain, @address) ->
+ @labels = subdomain?.split(".") ? []
+ @length = @labels.length
+
+ isEmpty: ->
+ @length is 0
+
+ getAddress: ->
+ @address
+
+class IPAddressSubdomain extends Subdomain
+ @pattern = /// (^|\.)
+ ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
+ (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
+ $ ///
+
+ getAddress: ->
+ @labels.slice(-4).join "."
+
+class EncodedSubdomain extends Subdomain
+ @pattern = /(^|\.)[a-z0-9]{1,7}$/
+
+ getAddress: ->
+ decode @labels[@length - 1]
+
+exports.encode = encode = (ip) ->
+ value = 0
+ for byte, index in ip.split "."
+ value += parseInt(byte, 10) << (index * 8)
+ (value >>> 0).toString 36
+
+PATTERN = /^[a-z0-9]{1,7}$/
+
+exports.decode = decode = (string) ->
+ return unless PATTERN.test string
+ value = parseInt string, 36
+ ip = []
+ for i in [1..4]
+ ip.push value & 0xFF
+ value >>= 8
+ ip.join "."
diff --git a/samples/coq/interval_discr.v b/samples/coq/interval_discr.v
new file mode 100644
index 0000000..ed2c0e3
--- /dev/null
+++ b/samples/coq/interval_discr.v
@@ -0,0 +1,419 @@
+(** Sketch of the proof of {p:nat|p<=n} = {p:nat|p<=m} -> n=m
+
+ - preliminary results on the irrelevance of boundedness proofs
+ - introduce the notion of finite cardinal |A|
+ - prove that |{p:nat|p<=n}| = n
+ - prove that |A| = n /\ |A| = m -> n = m if equality is decidable on A
+ - prove that equality is decidable on A
+ - conclude
+*)
+
+(** * Preliminary results on [nat] and [le] *)
+
+(** Proving axiom K on [nat] *)
+
+Require Import Eqdep_dec.
+Require Import Arith.
+
+Theorem eq_rect_eq_nat :
+ forall (p:nat) (Q:nat->Type) (x:Q p) (h:p=p), x = eq_rect p Q x p h.
+Proof.
+intros.
+apply K_dec_set with (p := h).
+apply eq_nat_dec.
+reflexivity.
+Qed.
+
+(** Proving unicity of proofs of [(n<=m)%nat] *)
+
+Scheme le_ind' := Induction for le Sort Prop.
+
+Theorem le_uniqueness_proof : forall (n m : nat) (p q : n <= m), p = q.
+Proof.
+induction p using le_ind'; intro q.
+ replace (le_n n) with
+ (eq_rect _ (fun n0 => n <= n0) (le_n n) _ (refl_equal n)).
+ 2:reflexivity.
+ generalize (refl_equal n).
+ pattern n at 2 4 6 10, q; case q; [intro | intros m l e].
+ rewrite <- eq_rect_eq_nat; trivial.
+ contradiction (le_Sn_n m); rewrite <- e; assumption.
+ replace (le_S n m p) with
+ (eq_rect _ (fun n0 => n <= n0) (le_S n m p) _ (refl_equal (S m))).
+ 2:reflexivity.
+ generalize (refl_equal (S m)).
+ pattern (S m) at 1 3 4 6, q; case q; [intro Heq | intros m0 l HeqS].
+ contradiction (le_Sn_n m); rewrite Heq; assumption.
+ injection HeqS; intro Heq; generalize l HeqS.
+ rewrite <- Heq; intros; rewrite <- eq_rect_eq_nat.
+ rewrite (IHp l0); reflexivity.
+Qed.
+
+(** Proving irrelevance of boundedness proofs while building
+ elements of interval *)
+
+Lemma dep_pair_intro :
+ forall (n x y:nat) (Hx : x<=n) (Hy : y<=n), x=y ->
+ exist (fun x => x <= n) x Hx = exist (fun x => x <= n) y Hy.
+Proof.
+intros n x y Hx Hy Heq.
+generalize Hy.
+rewrite <- Heq.
+intros.
+rewrite (le_uniqueness_proof x n Hx Hy0).
+reflexivity.
+Qed.
+
+(** * Proving that {p:nat|p<=n} = {p:nat|p<=m} -> n=m *)
+
+(** Definition of having finite cardinality [n+1] for a set [A] *)
+
+Definition card (A:Set) n :=
+ exists f,
+ (forall x:A, f x <= n) /\
+ (forall x y:A, f x = f y -> x = y) /\
+ (forall m, m <= n -> exists x:A, f x = m).
+
+Require Import Arith.
+
+(** Showing that the interval [0;n] has cardinality [n+1] *)
+
+Theorem card_interval : forall n, card {x:nat|x<=n} n.
+Proof.
+intro n.
+exists (fun x:{x:nat|x<=n} => proj1_sig x).
+split.
+(* bounded *)
+intro x; apply (proj2_sig x).
+split.
+(* injectivity *)
+intros (p,Hp) (q,Hq).
+simpl.
+intro Hpq.
+apply dep_pair_intro; assumption.
+(* surjectivity *)
+intros m Hmn.
+exists (exist (fun x : nat => x <= n) m Hmn).
+reflexivity.
+Qed.
+
+(** Showing that equality on the interval [0;n] is decidable *)
+
+Lemma interval_dec :
+ forall n (x y : {m:nat|m<=n}), {x=y}+{x<>y}.
+Proof.
+intros n (p,Hp).
+induction p; intros ([|q],Hq).
+left.
+ apply dep_pair_intro.
+ reflexivity.
+right.
+ intro H; discriminate H.
+right.
+ intro H; discriminate H.
+assert (Hp' : p <= n).
+ apply le_Sn_le; assumption.
+assert (Hq' : q <= n).
+ apply le_Sn_le; assumption.
+destruct (IHp Hp' (exist (fun m => m <= n) q Hq'))
+ as [Heq|Hneq].
+left.
+ injection Heq; intro Heq'.
+ apply dep_pair_intro.
+ apply eq_S.
+ assumption.
+right.
+ intro HeqS.
+ injection HeqS; intro Heq.
+ apply Hneq.
+ apply dep_pair_intro.
+ assumption.
+Qed.
+
+(** Showing that the cardinality relation is functional on decidable sets *)
+
+Lemma card_inj_aux :
+ forall (A:Type) f g n,
+ (forall x:A, f x <= 0) ->
+ (forall x y:A, f x = f y -> x = y) ->
+ (forall m, m <= S n -> exists x:A, g x = m)
+ -> False.
+Proof.
+intros A f g n Hfbound Hfinj Hgsurj.
+destruct (Hgsurj (S n) (le_n _)) as (x,Hx).
+destruct (Hgsurj n (le_S _ _ (le_n _))) as (x',Hx').
+assert (Hfx : 0 = f x).
+apply le_n_O_eq.
+apply Hfbound.
+assert (Hfx' : 0 = f x').
+apply le_n_O_eq.
+apply Hfbound.
+assert (x=x').
+apply Hfinj.
+rewrite <- Hfx.
+rewrite <- Hfx'.
+reflexivity.
+rewrite H in Hx.
+rewrite Hx' in Hx.
+apply (n_Sn _ Hx).
+Qed.
+
+(** For [dec_restrict], we use a lemma on the negation of equality
+that requires proof-irrelevance. It should be possible to avoid this
+lemma by generalizing over a first-order definition of [x<>y], say
+[neq] such that [{x=y}+{neq x y}] and [~(x=y /\ neq x y)]; for such
+[neq], unicity of proofs could be proven *)
+
+ Require Import Classical.
+ Lemma neq_dep_intro :
+ forall (A:Set) (z x y:A) (p:x<>z) (q:y<>z), x=y ->
+ exist (fun x => x <> z) x p = exist (fun x => x <> z) y q.
+ Proof.
+ intros A z x y p q Heq.
+ generalize q; clear q; rewrite <- Heq; intro q.
+ rewrite (proof_irrelevance _ p q); reflexivity.
+ Qed.
+
+Lemma dec_restrict :
+ forall (A:Set),
+ (forall x y :A, {x=y}+{x<>y}) ->
+ forall z (x y :{a:A|a<>z}), {x=y}+{x<>y}.
+Proof.
+intros A Hdec z (x,Hx) (y,Hy).
+destruct (Hdec x y) as [Heq|Hneq].
+left; apply neq_dep_intro; assumption.
+right; intro Heq; injection Heq; exact Hneq.
+Qed.
+
+Lemma pred_inj : forall n m,
+ 0 <> n -> 0 <> m -> pred m = pred n -> m = n.
+Proof.
+destruct n.
+intros m H; destruct H; reflexivity.
+destruct m.
+intros _ H; destruct H; reflexivity.
+simpl; intros _ _ H.
+rewrite H.
+reflexivity.
+Qed.
+
+Lemma le_neq_lt : forall n m, n <= m -> n<>m -> n < m.
+Proof.
+intros n m Hle Hneq.
+destruct (le_lt_eq_dec n m Hle).
+assumption.
+contradiction.
+Qed.
+
+Lemma inj_restrict :
+ forall (A:Set) (f:A->nat) x y z,
+ (forall x y : A, f x = f y -> x = y)
+ -> x <> z -> f y < f z -> f z <= f x
+ -> pred (f x) = f y
+ -> False.
+
+(* Search error sans le type de f !! *)
+Proof.
+intros A f x y z Hfinj Hneqx Hfy Hfx Heq.
+assert (f z <> f x).
+ apply sym_not_eq.
+ intro Heqf.
+ apply Hneqx.
+ apply Hfinj.
+ assumption.
+assert (f x = S (f y)).
+ assert (0 < f x).
+ apply le_lt_trans with (f z).
+ apply le_O_n.
+ apply le_neq_lt; assumption.
+ apply pred_inj.
+ apply O_S.
+ apply lt_O_neq; assumption.
+ exact Heq.
+assert (f z <= f y).
+destruct (le_lt_or_eq _ _ Hfx).
+ apply lt_n_Sm_le.
+ rewrite <- H0.
+ assumption.
+ contradiction Hneqx.
+ symmetry.
+ apply Hfinj.
+ assumption.
+contradiction (lt_not_le (f y) (f z)).
+Qed.
+
+Theorem card_inj : forall m n (A:Set),
+ (forall x y :A, {x=y}+{x<>y}) ->
+ card A m -> card A n -> m = n.
+Proof.
+induction m; destruct n;
+intros A Hdec
+ (f,(Hfbound,(Hfinj,Hfsurj)))
+ (g,(Hgbound,(Hginj,Hgsurj))).
+(* 0/0 *)
+reflexivity.
+(* 0/Sm *)
+destruct (card_inj_aux _ _ _ _ Hfbound Hfinj Hgsurj).
+(* Sn/0 *)
+destruct (card_inj_aux _ _ _ _ Hgbound Hginj Hfsurj).
+(* Sn/Sm *)
+destruct (Hgsurj (S n) (le_n _)) as (xSn,HSnx).
+rewrite IHm with (n:=n) (A := {x:A|x<>xSn}).
+reflexivity.
+(* decidability of eq on {x:A|x<>xSm} *)
+apply dec_restrict.
+assumption.
+(* cardinality of {x:A|x<>xSn} is m *)
+pose (f' := fun x' : {x:A|x<>xSn} =>
+ let (x,Hneq) := x' in
+ if le_lt_dec (f xSn) (f x)
+ then pred (f x)
+ else f x).
+exists f'.
+split.
+(* f' is bounded *)
+unfold f'.
+intros (x,_).
+destruct (le_lt_dec (f xSn) (f x)) as [Hle|Hge].
+change m with (pred (S m)).
+apply le_pred.
+apply Hfbound.
+apply le_S_n.
+apply le_trans with (f xSn).
+exact Hge.
+apply Hfbound.
+split.
+(* f' is injective *)
+unfold f'.
+intros (x,Hneqx) (y,Hneqy) Heqf'.
+destruct (le_lt_dec (f xSn) (f x)) as [Hlefx|Hgefx];
+destruct (le_lt_dec (f xSn) (f y)) as [Hlefy|Hgefy].
+(* f xSn <= f x et f xSn <= f y *)
+assert (Heq : x = y).
+ apply Hfinj.
+ assert (f xSn <> f y).
+ apply sym_not_eq.
+ intro Heqf.
+ apply Hneqy.
+ apply Hfinj.
+ assumption.
+ assert (0 < f y).
+ apply le_lt_trans with (f xSn).
+ apply le_O_n.
+ apply le_neq_lt; assumption.
+ assert (f xSn <> f x).
+ apply sym_not_eq.
+ intro Heqf.
+ apply Hneqx.
+ apply Hfinj.
+ assumption.
+ assert (0 < f x).
+ apply le_lt_trans with (f xSn).
+ apply le_O_n.
+ apply le_neq_lt; assumption.
+ apply pred_inj.
+ apply lt_O_neq; assumption.
+ apply lt_O_neq; assumption.
+ assumption.
+apply neq_dep_intro; assumption.
+(* f y < f xSn <= f x *)
+destruct (inj_restrict A f x y xSn); assumption.
+(* f x < f xSn <= f y *)
+symmetry in Heqf'.
+destruct (inj_restrict A f y x xSn); assumption.
+(* f x < f xSn et f y < f xSn *)
+assert (Heq : x=y).
+ apply Hfinj; assumption.
+apply neq_dep_intro; assumption.
+(* f' is surjective *)
+intros p Hlep.
+destruct (le_lt_dec (f xSn) p) as [Hle|Hlt].
+(* case f xSn <= p *)
+destruct (Hfsurj (S p) (le_n_S _ _ Hlep)) as (x,Hx).
+assert (Hneq : x <> xSn).
+ intro Heqx.
+ rewrite Heqx in Hx.
+ rewrite Hx in Hle.
+ apply le_Sn_n with p; assumption.
+exists (exist (fun a => a<>xSn) x Hneq).
+unfold f'.
+destruct (le_lt_dec (f xSn) (f x)) as [Hle'|Hlt'].
+rewrite Hx; reflexivity.
+rewrite Hx in Hlt'.
+contradiction (le_not_lt (f xSn) p).
+apply lt_trans with (S p).
+apply lt_n_Sn.
+assumption.
+(* case p < f xSn *)
+destruct (Hfsurj p (le_S _ _ Hlep)) as (x,Hx).
+assert (Hneq : x <> xSn).
+ intro Heqx.
+ rewrite Heqx in Hx.
+ rewrite Hx in Hlt.
+ apply (lt_irrefl p).
+ assumption.
+exists (exist (fun a => a<>xSn) x Hneq).
+unfold f'.
+destruct (le_lt_dec (f xSn) (f x)) as [Hle'|Hlt'].
+ rewrite Hx in Hle'.
+ contradiction (lt_irrefl p).
+ apply lt_le_trans with (f xSn); assumption.
+ assumption.
+(* cardinality of {x:A|x<>xSn} is n *)
+pose (g' := fun x' : {x:A|x<>xSn} =>
+ let (x,Hneq) := x' in
+ if Hdec x xSn then 0 else g x).
+exists g'.
+split.
+(* g is bounded *)
+unfold g'.
+intros (x,_).
+destruct (Hdec x xSn) as [_|Hneq].
+apply le_O_n.
+assert (Hle_gx:=Hgbound x).
+destruct (le_lt_or_eq _ _ Hle_gx).
+apply lt_n_Sm_le.
+assumption.
+contradiction Hneq.
+apply Hginj.
+rewrite HSnx.
+assumption.
+split.
+(* g is injective *)
+unfold g'.
+intros (x,Hneqx) (y,Hneqy) Heqg'.
+destruct (Hdec x xSn) as [Heqx|_].
+contradiction Hneqx.
+destruct (Hdec y xSn) as [Heqy|_].
+contradiction Hneqy.
+assert (Heq : x=y).
+ apply Hginj; assumption.
+apply neq_dep_intro; assumption.
+(* g is surjective *)
+intros p Hlep.
+destruct (Hgsurj p (le_S _ _ Hlep)) as (x,Hx).
+assert (Hneq : x<>xSn).
+ intro Heq.
+ rewrite Heq in Hx.
+ rewrite Hx in HSnx.
+ rewrite HSnx in Hlep.
+ contradiction (le_Sn_n _ Hlep).
+exists (exist (fun a => a<>xSn) x Hneq).
+simpl.
+destruct (Hdec x xSn) as [Heqx|_].
+contradiction Hneq.
+assumption.
+Qed.
+
+(** Conclusion *)
+
+Theorem interval_discr :
+ forall n m, {p:nat|p<=n} = {p:nat|p<=m} -> n=m.
+Proof.
+intros n m Heq.
+apply card_inj with (A := {p:nat|p<=n}).
+apply interval_dec.
+apply card_interval.
+rewrite Heq.
+apply card_interval.
+Qed.
diff --git a/samples/cpp/bar.h b/samples/cpp/bar.h
new file mode 100644
index 0000000..a87343c
--- /dev/null
+++ b/samples/cpp/bar.h
@@ -0,0 +1,10 @@
+class Bar
+{
+ protected:
+
+ char *name;
+
+ public:
+
+ void hello();
+}
diff --git a/samples/cpp/bar.hpp b/samples/cpp/bar.hpp
new file mode 100644
index 0000000..a87343c
--- /dev/null
+++ b/samples/cpp/bar.hpp
@@ -0,0 +1,10 @@
+class Bar
+{
+ protected:
+
+ char *name;
+
+ public:
+
+ void hello();
+}
diff --git a/samples/cpp/cuda.cu b/samples/cpp/cuda.cu
new file mode 100644
index 0000000..ddef40c
--- /dev/null
+++ b/samples/cpp/cuda.cu
@@ -0,0 +1,39 @@
+void foo()
+{
+ cudaArray* cu_array;
+ texture<float, 2, cudaReadModeElementType> tex;
+
+ // Allocate array
+ cudaChannelFormatDesc description = cudaCreateChannelDesc<float>();
+ cudaMallocArray(&cu_array, &description, width, height);
+
+ // Copy image data to array
+ cudaMemcpyToArray(cu_array, image, width*height*sizeof(float), cudaMemcpyHostToDevice);
+
+ // Set texture parameters (default)
+ tex.addressMode[0] = cudaAddressModeClamp;
+ tex.addressMode[1] = cudaAddressModeClamp;
+ tex.filterMode = cudaFilterModePoint;
+ tex.normalized = false; // do not normalize coordinates
+
+ // Bind the array to the texture
+ cudaBindTextureToArray(tex, cu_array);
+
+ // Run kernel
+ dim3 blockDim(16, 16, 1);
+ dim3 gridDim((width + blockDim.x - 1)/ blockDim.x, (height + blockDim.y - 1) / blockDim.y, 1);
+ kernel<<< gridDim, blockDim, 0 >>>(d_data, height, width);
+
+ // Unbind the array from the texture
+ cudaUnbindTexture(tex);
+} //end foo()
+
+__global__ void kernel(float* odata, int height, int width)
+{
+ unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
+ unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;
+ if (x < width && y < height) {
+ float c = tex2D(tex, x, y);
+ odata[y*width+x] = c;
+ }
+}
diff --git a/samples/cpp/env.cpp b/samples/cpp/env.cpp
new file mode 100644
index 0000000..beddc19
--- /dev/null
+++ b/samples/cpp/env.cpp
@@ -0,0 +1,81 @@
+/*
+ This file is part of the PhantomJS project from Ofi Labs.
+
+ Copyright (C) 2012 execjosh, http://execjosh.blogspot.com
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "env.h"
+
+#include <QCoreApplication>
+#include <QString>
+#include <QVariantMap>
+
+static Env *env_instance = (Env *)NULL;
+
+Env *Env::instance()
+{
+ if ((Env *)NULL == env_instance)
+ env_instance = new Env();
+
+ return env_instance;
+}
+
+Env::Env()
+ : QObject(QCoreApplication::instance())
+{
+}
+
+// public:
+
+void Env::parse(const char **envp)
+{
+ const char **env = (const char **)NULL;
+ QString envvar, name, value;
+ int indexOfEquals;
+ // Loop for each of the <NAME>=<VALUE> pairs and split them into a map
+ for (env = envp; *env != (const char *)NULL; env++) {
+ envvar = QString(*env);
+ indexOfEquals = envvar.indexOf('=');
+ if (0 >= indexOfEquals) {
+ // Should never happen because names cannot contain "=" and cannot
+ // be empty. If it does happen, then just ignore this record.
+ // See: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
+ continue;
+ }
+ // Extract name and value (if it exists) from envvar
+ // NOTE:
+ // QString::mid() will gracefully return an empty QString when the
+ // specified position index is >= the length() of the string
+ name = envvar.left(indexOfEquals);
+ value = envvar.mid(indexOfEquals + 1);
+ m_map.insert(name, value);
+ }
+}
+
+QVariantMap Env::asVariantMap() const
+{
+ return m_map;
+}
diff --git a/samples/cpp/env.h b/samples/cpp/env.h
new file mode 100644
index 0000000..6d72b39
--- /dev/null
+++ b/samples/cpp/env.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of the PhantomJS project from Ofi Labs.
+
+ Copyright (C) 2012 execjosh, http://execjosh.blogspot.com
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef ENV_H
+#define ENV_H
+
+#include <QObject>
+#include <QVariantMap>
+
+class Env : public QObject
+{
+ Q_OBJECT
+
+public:
+ static Env *instance();
+
+ void parse(const char ** envp);
+ QVariantMap asVariantMap() const;
+
+private:
+ Env();
+
+ QVariantMap m_map;
+};
+
+#endif // ENV_H
diff --git a/samples/cpp/hello.cpp b/samples/cpp/hello.cpp
new file mode 100644
index 0000000..d36f6e6
--- /dev/null
+++ b/samples/cpp/hello.cpp
@@ -0,0 +1,8 @@
+#include <iostream>
+
+using namespace std;
+
+int main()
+{
+ cout << "Hello World" << endl;
+}
diff --git a/samples/cpp/key.cpp b/samples/cpp/key.cpp
new file mode 100644
index 0000000..57ab842
--- /dev/null
+++ b/samples/cpp/key.cpp
@@ -0,0 +1,382 @@
+// Copyright (c) 2009-2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <map>
+
+#include <openssl/ecdsa.h>
+#include <openssl/obj_mac.h>
+
+#include "key.h"
+
+// Generate a private key from just the secret parameter
+int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
+{
+ int ok = 0;
+ BN_CTX *ctx = NULL;
+ EC_POINT *pub_key = NULL;
+
+ if (!eckey) return 0;
+
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+
+ if ((ctx = BN_CTX_new()) == NULL)
+ goto err;
+
+ pub_key = EC_POINT_new(group);
+
+ if (pub_key == NULL)
+ goto err;
+
+ if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
+ goto err;
+
+ EC_KEY_set_private_key(eckey,priv_key);
+ EC_KEY_set_public_key(eckey,pub_key);
+
+ ok = 1;
+
+err:
+
+ if (pub_key)
+ EC_POINT_free(pub_key);
+ if (ctx != NULL)
+ BN_CTX_free(ctx);
+
+ return(ok);
+}
+
+// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
+// recid selects which key is recovered
+// if check is nonzero, additional checks are performed
+int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
+{
+ if (!eckey) return 0;
+
+ int ret = 0;
+ BN_CTX *ctx = NULL;
+
+ BIGNUM *x = NULL;
+ BIGNUM *e = NULL;
+ BIGNUM *order = NULL;
+ BIGNUM *sor = NULL;
+ BIGNUM *eor = NULL;
+ BIGNUM *field = NULL;
+ EC_POINT *R = NULL;
+ EC_POINT *O = NULL;
+ EC_POINT *Q = NULL;
+ BIGNUM *rr = NULL;
+ BIGNUM *zero = NULL;
+ int n = 0;
+ int i = recid / 2;
+
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
+ BN_CTX_start(ctx);
+ order = BN_CTX_get(ctx);
+ if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
+ x = BN_CTX_get(ctx);
+ if (!BN_copy(x, order)) { ret=-1; goto err; }
+ if (!BN_mul_word(x, i)) { ret=-1; goto err; }
+ if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
+ field = BN_CTX_get(ctx);
+ if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
+ if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
+ if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
+ if (check)
+ {
+ if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
+ if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
+ }
+ if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ n = EC_GROUP_get_degree(group);
+ e = BN_CTX_get(ctx);
+ if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
+ if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
+ zero = BN_CTX_get(ctx);
+ if (!BN_zero(zero)) { ret=-1; goto err; }
+ if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
+ rr = BN_CTX_get(ctx);
+ if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
+ sor = BN_CTX_get(ctx);
+ if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
+ eor = BN_CTX_get(ctx);
+ if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
+ if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
+ if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
+
+ ret = 1;
+
+err:
+ if (ctx) {
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ }
+ if (R != NULL) EC_POINT_free(R);
+ if (O != NULL) EC_POINT_free(O);
+ if (Q != NULL) EC_POINT_free(Q);
+ return ret;
+}
+
+void CKey::SetCompressedPubKey()
+{
+ EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
+ fCompressedPubKey = true;
+}
+
+void CKey::Reset()
+{
+ fCompressedPubKey = false;
+ pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+ if (pkey == NULL)
+ throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed");
+ fSet = false;
+}
+
+CKey::CKey()
+{
+ Reset();
+}
+
+CKey::CKey(const CKey& b)
+{
+ pkey = EC_KEY_dup(b.pkey);
+ if (pkey == NULL)
+ throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed");
+ fSet = b.fSet;
+}
+
+CKey& CKey::operator=(const CKey& b)
+{
+ if (!EC_KEY_copy(pkey, b.pkey))
+ throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed");
+ fSet = b.fSet;
+ return (*this);
+}
+
+CKey::~CKey()
+{
+ EC_KEY_free(pkey);
+}
+
+bool CKey::IsNull() const
+{
+ return !fSet;
+}
+
+bool CKey::IsCompressed() const
+{
+ return fCompressedPubKey;
+}
+
+void CKey::MakeNewKey(bool fCompressed)
+{
+ if (!EC_KEY_generate_key(pkey))
+ throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed");
+ if (fCompressed)
+ SetCompressedPubKey();
+ fSet = true;
+}
+
+bool CKey::SetPrivKey(const CPrivKey& vchPrivKey)
+{
+ const unsigned char* pbegin = &vchPrivKey[0];
+ if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size()))
+ return false;
+ fSet = true;
+ return true;
+}
+
+bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed)
+{
+ EC_KEY_free(pkey);
+ pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+ if (pkey == NULL)
+ throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed");
+ if (vchSecret.size() != 32)
+ throw key_error("CKey::SetSecret() : secret must be 32 bytes");
+ BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new());
+ if (bn == NULL)
+ throw key_error("CKey::SetSecret() : BN_bin2bn failed");
+ if (!EC_KEY_regenerate_key(pkey,bn))
+ {
+ BN_clear_free(bn);
+ throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed");
+ }
+ BN_clear_free(bn);
+ fSet = true;
+ if (fCompressed || fCompressedPubKey)
+ SetCompressedPubKey();
+ return true;
+}
+
+CSecret CKey::GetSecret(bool &fCompressed) const
+{
+ CSecret vchRet;
+ vchRet.resize(32);
+ const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
+ int nBytes = BN_num_bytes(bn);
+ if (bn == NULL)
+ throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed");
+ int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
+ if (n != nBytes)
+ throw key_error("CKey::GetSecret(): BN_bn2bin failed");
+ fCompressed = fCompressedPubKey;
+ return vchRet;
+}
+
+CPrivKey CKey::GetPrivKey() const
+{
+ int nSize = i2d_ECPrivateKey(pkey, NULL);
+ if (!nSize)
+ throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
+ CPrivKey vchPrivKey(nSize, 0);
+ unsigned char* pbegin = &vchPrivKey[0];
+ if (i2d_ECPrivateKey(pkey, &pbegin) != nSize)
+ throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size");
+ return vchPrivKey;
+}
+
+bool CKey::SetPubKey(const CPubKey& vchPubKey)
+{
+ const unsigned char* pbegin = &vchPubKey.vchPubKey[0];
+ if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size()))
+ return false;
+ fSet = true;
+ if (vchPubKey.vchPubKey.size() == 33)
+ SetCompressedPubKey();
+ return true;
+}
+
+CPubKey CKey::GetPubKey() const
+{
+ int nSize = i2o_ECPublicKey(pkey, NULL);
+ if (!nSize)
+ throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
+ std::vector<unsigned char> vchPubKey(nSize, 0);
+ unsigned char* pbegin = &vchPubKey[0];
+ if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
+ throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");
+ return CPubKey(vchPubKey);
+}
+
+bool CKey::Sign(uint256 hash, std::vector<unsigned char>& vchSig)
+{
+ unsigned int nSize = ECDSA_size(pkey);
+ vchSig.resize(nSize); // Make sure it is big enough
+ if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey))
+ {
+ vchSig.clear();
+ return false;
+ }
+ vchSig.resize(nSize); // Shrink to fit actual size
+ return true;
+}
+
+// create a compact signature (65 bytes), which allows reconstructing the used public key
+// The format is one header byte, followed by two times 32 bytes for the serialized r and s values.
+// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y,
+// 0x1D = second key with even y, 0x1E = second key with odd y
+bool CKey::SignCompact(uint256 hash, std::vector<unsigned char>& vchSig)
+{
+ bool fOk = false;
+ ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
+ if (sig==NULL)
+ return false;
+ vchSig.clear();
+ vchSig.resize(65,0);
+ int nBitsR = BN_num_bits(sig->r);
+ int nBitsS = BN_num_bits(sig->s);
+ if (nBitsR <= 256 && nBitsS <= 256)
+ {
+ int nRecId = -1;
+ for (int i=0; i<4; i++)
+ {
+ CKey keyRec;
+ keyRec.fSet = true;
+ if (fCompressedPubKey)
+ keyRec.SetCompressedPubKey();
+ if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1)
+ if (keyRec.GetPubKey() == this->GetPubKey())
+ {
+ nRecId = i;
+ break;
+ }
+ }
+
+ if (nRecId == -1)
+ throw key_error("CKey::SignCompact() : unable to construct recoverable key");
+
+ vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0);
+ BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]);
+ BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]);
+ fOk = true;
+ }
+ ECDSA_SIG_free(sig);
+ return fOk;
+}
+
+// reconstruct public key from a compact signature
+// This is only slightly more CPU intensive than just verifying it.
+// If this function succeeds, the recovered public key is guaranteed to be valid
+// (the signature is a valid signature of the given data for that key)
+bool CKey::SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig)
+{
+ if (vchSig.size() != 65)
+ return false;
+ int nV = vchSig[0];
+ if (nV<27 || nV>=35)
+ return false;
+ ECDSA_SIG *sig = ECDSA_SIG_new();
+ BN_bin2bn(&vchSig[1],32,sig->r);
+ BN_bin2bn(&vchSig[33],32,sig->s);
+
+ EC_KEY_free(pkey);
+ pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+ if (nV >= 31)
+ {
+ SetCompressedPubKey();
+ nV -= 4;
+ }
+ if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1)
+ {
+ fSet = true;
+ ECDSA_SIG_free(sig);
+ return true;
+ }
+ return false;
+}
+
+bool CKey::Verify(uint256 hash, const std::vector<unsigned char>& vchSig)
+{
+ // -1 = error, 0 = bad sig, 1 = good
+ if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
+ return false;
+
+ return true;
+}
+
+bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
+{
+ CKey key;
+ if (!key.SetCompactSignature(hash, vchSig))
+ return false;
+ if (GetPubKey() != key.GetPubKey())
+ return false;
+
+ return true;
+}
+
+bool CKey::IsValid()
+{
+ if (!fSet)
+ return false;
+
+ bool fCompr;
+ CSecret secret = GetSecret(fCompr);
+ CKey key2;
+ key2.SetSecret(secret, fCompr);
+ return GetPubKey() == key2.GetPubKey();
+}
diff --git a/samples/cpp/key.h b/samples/cpp/key.h
new file mode 100644
index 0000000..945c499
--- /dev/null
+++ b/samples/cpp/key.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef BITCOIN_KEY_H
+#define BITCOIN_KEY_H
+
+#include <stdexcept>
+#include <vector>
+
+#include "allocators.h"
+#include "serialize.h"
+#include "uint256.h"
+#include "util.h"
+
+#include <openssl/ec.h> // for EC_KEY definition
+
+// secp160k1
+// const unsigned int PRIVATE_KEY_SIZE = 192;
+// const unsigned int PUBLIC_KEY_SIZE = 41;
+// const unsigned int SIGNATURE_SIZE = 48;
+//
+// secp192k1
+// const unsigned int PRIVATE_KEY_SIZE = 222;
+// const unsigned int PUBLIC_KEY_SIZE = 49;
+// const unsigned int SIGNATURE_SIZE = 57;
+//
+// secp224k1
+// const unsigned int PRIVATE_KEY_SIZE = 250;
+// const unsigned int PUBLIC_KEY_SIZE = 57;
+// const unsigned int SIGNATURE_SIZE = 66;
+//
+// secp256k1:
+// const unsigned int PRIVATE_KEY_SIZE = 279;
+// const unsigned int PUBLIC_KEY_SIZE = 65;
+// const unsigned int SIGNATURE_SIZE = 72;
+//
+// see www.keylength.com
+// script supports up to 75 for single byte push
+
+class key_error : public std::runtime_error
+{
+public:
+ explicit key_error(const std::string& str) : std::runtime_error(str) {}
+};
+
+/** A reference to a CKey: the Hash160 of its serialized public key */
+class CKeyID : public uint160
+{
+public:
+ CKeyID() : uint160(0) { }
+ CKeyID(const uint160 &in) : uint160(in) { }
+};
+
+/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
+class CScriptID : public uint160
+{
+public:
+ CScriptID() : uint160(0) { }
+ CScriptID(const uint160 &in) : uint160(in) { }
+};
+
+/** An encapsulated public key. */
+class CPubKey {
+private:
+ std::vector<unsigned char> vchPubKey;
+ friend class CKey;
+
+public:
+ CPubKey() { }
+ CPubKey(const std::vector<unsigned char> &vchPubKeyIn) : vchPubKey(vchPubKeyIn) { }
+ friend bool operator==(const CPubKey &a, const CPubKey &b) { return a.vchPubKey == b.vchPubKey; }
+ friend bool operator!=(const CPubKey &a, const CPubKey &b) { return a.vchPubKey != b.vchPubKey; }
+ friend bool operator<(const CPubKey &a, const CPubKey &b) { return a.vchPubKey < b.vchPubKey; }
+
+ IMPLEMENT_SERIALIZE(
+ READWRITE(vchPubKey);
+ )
+
+ CKeyID GetID() const {
+ return CKeyID(Hash160(vchPubKey));
+ }
+
+ uint256 GetHash() const {
+ return Hash(vchPubKey.begin(), vchPubKey.end());
+ }
+
+ bool IsValid() const {
+ return vchPubKey.size() == 33 || vchPubKey.size() == 65;
+ }
+
+ bool IsCompressed() const {
+ return vchPubKey.size() == 33;
+ }
+
+ std::vector<unsigned char> Raw() const {
+ return vchPubKey;
+ }
+};
+
+
+// secure_allocator is defined in serialize.h
+// CPrivKey is a serialized private key, with all parameters included (279 bytes)
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
+// CSecret is a serialization of just the secret parameter (32 bytes)
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
+
+/** An encapsulated OpenSSL Elliptic Curve key (public and/or private) */
+class CKey
+{
+protected:
+ EC_KEY* pkey;
+ bool fSet;
+ bool fCompressedPubKey;
+
+ void SetCompressedPubKey();
+
+public:
+
+ void Reset();
+
+ CKey();
+ CKey(const CKey& b);
+
+ CKey& operator=(const CKey& b);
+
+ ~CKey();
+
+ bool IsNull() const;
+ bool IsCompressed() const;
+
+ void MakeNewKey(bool fCompressed);
+ bool SetPrivKey(const CPrivKey& vchPrivKey);
+ bool SetSecret(const CSecret& vchSecret, bool fCompressed = false);
+ CSecret GetSecret(bool &fCompressed) const;
+ CPrivKey GetPrivKey() const;
+ bool SetPubKey(const CPubKey& vchPubKey);
+ CPubKey GetPubKey() const;
+
+ bool Sign(uint256 hash, std::vector<unsigned char>& vchSig);
+
+ // create a compact signature (65 bytes), which allows reconstructing the used public key
+ // The format is one header byte, followed by two times 32 bytes for the serialized r and s values.
+ // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y,
+ // 0x1D = second key with even y, 0x1E = second key with odd y
+ bool SignCompact(uint256 hash, std::vector<unsigned char>& vchSig);
+
+ // reconstruct public key from a compact signature
+ // This is only slightly more CPU intensive than just verifying it.
+ // If this function succeeds, the recovered public key is guaranteed to be valid
+ // (the signature is a valid signature of the given data for that key)
+ bool SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig);
+
+ bool Verify(uint256 hash, const std::vector<unsigned char>& vchSig);
+
+ // Verify a compact signature
+ bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig);
+
+ bool IsValid();
+};
+
+#endif
diff --git a/samples/cpp/main.cpp b/samples/cpp/main.cpp
new file mode 100644
index 0000000..8ca481d
--- /dev/null
+++ b/samples/cpp/main.cpp
@@ -0,0 +1,74 @@
+/*
+ This file is part of the PhantomJS project from Ofi Labs.
+
+ Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "consts.h"
+#include "utils.h"
+#include "env.h"
+#include "phantom.h"
+
+#ifdef Q_OS_LINUX
+#include "client/linux/handler/exception_handler.h"
+#endif
+
+#include <QApplication>
+
+#if QT_VERSION != QT_VERSION_CHECK(4, 8, 0)
+#error Something is wrong with the setup. Please report to the mailing list!
+#endif
+
+int main(int argc, char** argv, const char** envp)
+{
+#ifdef Q_OS_LINUX
+ google_breakpad::ExceptionHandler eh("/tmp", NULL, Utils::exceptionHandler, NULL, true);
+#endif
+
+ // Registering an alternative Message Handler
+ qInstallMsgHandler(Utils::messageHandler);
+
+ QApplication app(argc, argv);
+
+#ifdef STATIC_BUILD
+ Q_INIT_RESOURCE(WebKit);
+ Q_INIT_RESOURCE(InspectorBackendStub);
+#endif
+
+ app.setWindowIcon(QIcon(":/phantomjs-icon.png"));
+ app.setApplicationName("PhantomJS");
+ app.setOrganizationName("Ofi Labs");
+ app.setOrganizationDomain("www.ofilabs.com");
+ app.setApplicationVersion(PHANTOMJS_VERSION_STRING);
+
+ Env::instance()->parse(envp);
+
+ Phantom phantom;
+ if (phantom.execute()) {
+ app.exec();
+ }
+ return phantom.returnValue();
+}
diff --git a/samples/cpp/scanner.cc b/samples/cpp/scanner.cc
new file mode 100644
index 0000000..f24af2e
--- /dev/null
+++ b/samples/cpp/scanner.cc
@@ -0,0 +1,1088 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Features shared by parsing and pre-parsing scanners.
+
+#include "scanner.h"
+
+#include "../include/v8stdint.h"
+#include "char-predicates-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Scanner
+
+Scanner::Scanner(UnicodeCache* unicode_cache)
+ : unicode_cache_(unicode_cache),
+ octal_pos_(Location::invalid()),
+ harmony_scoping_(false),
+ harmony_modules_(false) { }
+
+
+void Scanner::Initialize(Utf16CharacterStream* source) {
+ source_ = source;
+ // Need to capture identifiers in order to recognize "get" and "set"
+ // in object literals.
+ Init();
+ // Skip initial whitespace allowing HTML comment ends just like
+ // after a newline and scan first token.
+ has_line_terminator_before_next_ = true;
+ SkipWhiteSpace();
+ Scan();
+}
+
+
+uc32 Scanner::ScanHexNumber(int expected_length) {
+ ASSERT(expected_length <= 4); // prevent overflow
+
+ uc32 digits[4] = { 0, 0, 0, 0 };
+ uc32 x = 0;
+ for (int i = 0; i < expected_length; i++) {
+ digits[i] = c0_;
+ int d = HexValue(c0_);
+ if (d < 0) {
+ // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes
+ // should be illegal, but other JS VMs just return the
+ // non-escaped version of the original character.
+
+ // Push back digits that we have advanced past.
+ for (int j = i-1; j >= 0; j--) {
+ PushBack(digits[j]);
+ }
+ return -1;
+ }
+ x = x * 16 + d;
+ Advance();
+ }
+
+ return x;
+}
+
+
+// Ensure that tokens can be stored in a byte.
+STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
+
+// Table of one-character tokens, by character (0x00..0x7f only).
+static const byte one_char_tokens[] = {
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::LPAREN, // 0x28
+ Token::RPAREN, // 0x29
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::COMMA, // 0x2c
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::COLON, // 0x3a
+ Token::SEMICOLON, // 0x3b
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::CONDITIONAL, // 0x3f
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::LBRACK, // 0x5b
+ Token::ILLEGAL,
+ Token::RBRACK, // 0x5d
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::LBRACE, // 0x7b
+ Token::ILLEGAL,
+ Token::RBRACE, // 0x7d
+ Token::BIT_NOT, // 0x7e
+ Token::ILLEGAL
+};
+
+
+Token::Value Scanner::Next() {
+ current_ = next_;
+ has_line_terminator_before_next_ = false;
+ has_multiline_comment_before_next_ = false;
+ if (static_cast<unsigned>(c0_) <= 0x7f) {
+ Token::Value token = static_cast<Token::Value>(one_char_tokens[c0_]);
+ if (token != Token::ILLEGAL) {
+ int pos = source_pos();
+ next_.token = token;
+ next_.location.beg_pos = pos;
+ next_.location.end_pos = pos + 1;
+ Advance();
+ return current_.token;
+ }
+ }
+ Scan();
+ return current_.token;
+}
+
+
+static inline bool IsByteOrderMark(uc32 c) {
+ // The Unicode value U+FFFE is guaranteed never to be assigned as a
+ // Unicode character; this implies that in a Unicode context the
+ // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF
+ // character expressed in little-endian byte order (since it could
+ // not be a U+FFFE character expressed in big-endian byte
+ // order). Nevertheless, we check for it to be compatible with
+ // Spidermonkey.
+ return c == 0xFEFF || c == 0xFFFE;
+}
+
+
+bool Scanner::SkipWhiteSpace() {
+ int start_position = source_pos();
+
+ while (true) {
+ // We treat byte-order marks (BOMs) as whitespace for better
+ // compatibility with Spidermonkey and other JavaScript engines.
+ while (unicode_cache_->IsWhiteSpace(c0_) || IsByteOrderMark(c0_)) {
+ // IsWhiteSpace() includes line terminators!
+ if (unicode_cache_->IsLineTerminator(c0_)) {
+ // Ignore line terminators, but remember them. This is necessary
+ // for automatic semicolon insertion.
+ has_line_terminator_before_next_ = true;
+ }
+ Advance();
+ }
+
+ // If there is an HTML comment end '-->' at the beginning of a
+ // line (with only whitespace in front of it), we treat the rest
+ // of the line as a comment. This is in line with the way
+ // SpiderMonkey handles it.
+ if (c0_ == '-' && has_line_terminator_before_next_) {
+ Advance();
+ if (c0_ == '-') {
+ Advance();
+ if (c0_ == '>') {
+ // Treat the rest of the line as a comment.
+ SkipSingleLineComment();
+ // Continue skipping white space after the comment.
+ continue;
+ }
+ PushBack('-'); // undo Advance()
+ }
+ PushBack('-'); // undo Advance()
+ }
+ // Return whether or not we skipped any characters.
+ return source_pos() != start_position;
+ }
+}
+
+
+Token::Value Scanner::SkipSingleLineComment() {
+ Advance();
+
+ // The line terminator at the end of the line is not considered
+ // to be part of the single-line comment; it is recognized
+ // separately by the lexical grammar and becomes part of the
+ // stream of input elements for the syntactic grammar (see
+ // ECMA-262, section 7.4).
+ while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) {
+ Advance();
+ }
+
+ return Token::WHITESPACE;
+}
+
+
+Token::Value Scanner::SkipMultiLineComment() {
+ ASSERT(c0_ == '*');
+ Advance();
+
+ while (c0_ >= 0) {
+ uc32 ch = c0_;
+ Advance();
+ if (unicode_cache_->IsLineTerminator(ch)) {
+ // Following ECMA-262, section 7.4, a comment containing
+ // a newline will make the comment count as a line-terminator.
+ has_multiline_comment_before_next_ = true;
+ }
+ // If we have reached the end of the multi-line comment, we
+ // consume the '/' and insert a whitespace. This way all
+ // multi-line comments are treated as whitespace.
+ if (ch == '*' && c0_ == '/') {
+ c0_ = ' ';
+ return Token::WHITESPACE;
+ }
+ }
+
+ // Unterminated multi-line comment.
+ return Token::ILLEGAL;
+}
+
+
+Token::Value Scanner::ScanHtmlComment() {
+ // Check for <!-- comments.
+ ASSERT(c0_ == '!');
+ Advance();
+ if (c0_ == '-') {
+ Advance();
+ if (c0_ == '-') return SkipSingleLineComment();
+ PushBack('-'); // undo Advance()
+ }
+ PushBack('!'); // undo Advance()
+ ASSERT(c0_ == '!');
+ return Token::LT;
+}
+
+
+void Scanner::Scan() {
+ next_.literal_chars = NULL;
+ Token::Value token;
+ do {
+ // Remember the position of the next token
+ next_.location.beg_pos = source_pos();
+
+ switch (c0_) {
+ case ' ':
+ case '\t':
+ Advance();
+ token = Token::WHITESPACE;
+ break;
+
+ case '\n':
+ Advance();
+ has_line_terminator_before_next_ = true;
+ token = Token::WHITESPACE;
+ break;
+
+ case '"': case '\'':
+ token = ScanString();
+ break;
+
+ case '<':
+ // < <= << <<= <!--
+ Advance();
+ if (c0_ == '=') {
+ token = Select(Token::LTE);
+ } else if (c0_ == '<') {
+ token = Select('=', Token::ASSIGN_SHL, Token::SHL);
+ } else if (c0_ == '!') {
+ token = ScanHtmlComment();
+ } else {
+ token = Token::LT;
+ }
+ break;
+
+ case '>':
+ // > >= >> >>= >>> >>>=
+ Advance();
+ if (c0_ == '=') {
+ token = Select(Token::GTE);
+ } else if (c0_ == '>') {
+ // >> >>= >>> >>>=
+ Advance();
+ if (c0_ == '=') {
+ token = Select(Token::ASSIGN_SAR);
+ } else if (c0_ == '>') {
+ token = Select('=', Token::ASSIGN_SHR, Token::SHR);
+ } else {
+ token = Token::SAR;
+ }
+ } else {
+ token = Token::GT;
+ }
+ break;
+
+ case '=':
+ // = == ===
+ Advance();
+ if (c0_ == '=') {
+ token = Select('=', Token::EQ_STRICT, Token::EQ);
+ } else {
+ token = Token::ASSIGN;
+ }
+ break;
+
+ case '!':
+ // ! != !==
+ Advance();
+ if (c0_ == '=') {
+ token = Select('=', Token::NE_STRICT, Token::NE);
+ } else {
+ token = Token::NOT;
+ }
+ break;
+
+ case '+':
+ // + ++ +=
+ Advance();
+ if (c0_ == '+') {
+ token = Select(Token::INC);
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_ADD);
+ } else {
+ token = Token::ADD;
+ }
+ break;
+
+ case '-':
+ // - -- --> -=
+ Advance();
+ if (c0_ == '-') {
+ Advance();
+ if (c0_ == '>' && has_line_terminator_before_next_) {
+ // For compatibility with SpiderMonkey, we skip lines that
+ // start with an HTML comment end '-->'.
+ token = SkipSingleLineComment();
+ } else {
+ token = Token::DEC;
+ }
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_SUB);
+ } else {
+ token = Token::SUB;
+ }
+ break;
+
+ case '*':
+ // * *=
+ token = Select('=', Token::ASSIGN_MUL, Token::MUL);
+ break;
+
+ case '%':
+ // % %=
+ token = Select('=', Token::ASSIGN_MOD, Token::MOD);
+ break;
+
+ case '/':
+ // / // /* /=
+ Advance();
+ if (c0_ == '/') {
+ token = SkipSingleLineComment();
+ } else if (c0_ == '*') {
+ token = SkipMultiLineComment();
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_DIV);
+ } else {
+ token = Token::DIV;
+ }
+ break;
+
+ case '&':
+ // & && &=
+ Advance();
+ if (c0_ == '&') {
+ token = Select(Token::AND);
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_BIT_AND);
+ } else {
+ token = Token::BIT_AND;
+ }
+ break;
+
+ case '|':
+ // | || |=
+ Advance();
+ if (c0_ == '|') {
+ token = Select(Token::OR);
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_BIT_OR);
+ } else {
+ token = Token::BIT_OR;
+ }
+ break;
+
+ case '^':
+ // ^ ^=
+ token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
+ break;
+
+ case '.':
+ // . Number
+ Advance();
+ if (IsDecimalDigit(c0_)) {
+ token = ScanNumber(true);
+ } else {
+ token = Token::PERIOD;
+ }
+ break;
+
+ case ':':
+ token = Select(Token::COLON);
+ break;
+
+ case ';':
+ token = Select(Token::SEMICOLON);
+ break;
+
+ case ',':
+ token = Select(Token::COMMA);
+ break;
+
+ case '(':
+ token = Select(Token::LPAREN);
+ break;
+
+ case ')':
+ token = Select(Token::RPAREN);
+ break;
+
+ case '[':
+ token = Select(Token::LBRACK);
+ break;
+
+ case ']':
+ token = Select(Token::RBRACK);
+ break;
+
+ case '{':
+ token = Select(Token::LBRACE);
+ break;
+
+ case '}':
+ token = Select(Token::RBRACE);
+ break;
+
+ case '?':
+ token = Select(Token::CONDITIONAL);
+ break;
+
+ case '~':
+ token = Select(Token::BIT_NOT);
+ break;
+
+ default:
+ if (unicode_cache_->IsIdentifierStart(c0_)) {
+ token = ScanIdentifierOrKeyword();
+ } else if (IsDecimalDigit(c0_)) {
+ token = ScanNumber(false);
+ } else if (SkipWhiteSpace()) {
+ token = Token::WHITESPACE;
+ } else if (c0_ < 0) {
+ token = Token::EOS;
+ } else {
+ token = Select(Token::ILLEGAL);
+ }
+ break;
+ }
+
+ // Continue scanning for tokens as long as we're just skipping
+ // whitespace.
+ } while (token == Token::WHITESPACE);
+
+ next_.location.end_pos = source_pos();
+ next_.token = token;
+}
+
+
+void Scanner::SeekForward(int pos) {
+ // After this call, we will have the token at the given position as
+ // the "next" token. The "current" token will be invalid.
+ if (pos == next_.location.beg_pos) return;
+ int current_pos = source_pos();
+ ASSERT_EQ(next_.location.end_pos, current_pos);
+ // Positions inside the lookahead token aren't supported.
+ ASSERT(pos >= current_pos);
+ if (pos != current_pos) {
+ source_->SeekForward(pos - source_->pos());
+ Advance();
+ // This function is only called to seek to the location
+ // of the end of a function (at the "}" token). It doesn't matter
+ // whether there was a line terminator in the part we skip.
+ has_line_terminator_before_next_ = false;
+ has_multiline_comment_before_next_ = false;
+ }
+ Scan();
+}
+
+
+bool Scanner::ScanEscape() {
+ uc32 c = c0_;
+ Advance();
+
+ // Skip escaped newlines.
+ if (unicode_cache_->IsLineTerminator(c)) {
+ // Allow CR+LF newlines in multiline string literals.
+ if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance();
+ // Allow LF+CR newlines in multiline string literals.
+ if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance();
+ return true;
+ }
+
+ switch (c) {
+ case '\'': // fall through
+ case '"' : // fall through
+ case '\\': break;
+ case 'b' : c = '\b'; break;
+ case 'f' : c = '\f'; break;
+ case 'n' : c = '\n'; break;
+ case 'r' : c = '\r'; break;
+ case 't' : c = '\t'; break;
+ case 'u' : {
+ c = ScanHexNumber(4);
+ if (c < 0) return false;
+ break;
+ }
+ case 'v' : c = '\v'; break;
+ case 'x' : {
+ c = ScanHexNumber(2);
+ if (c < 0) return false;
+ break;
+ }
+ case '0' : // fall through
+ case '1' : // fall through
+ case '2' : // fall through
+ case '3' : // fall through
+ case '4' : // fall through
+ case '5' : // fall through
+ case '6' : // fall through
+ case '7' : c = ScanOctalEscape(c, 2); break;
+ }
+
+ // According to ECMA-262, section 7.8.4, characters not covered by the
+ // above cases should be illegal, but they are commonly handled as
+ // non-escaped characters by JS VMs.
+ AddLiteralChar(c);
+ return true;
+}
+
+
+// Octal escapes of the forms '\0xx' and '\xxx' are not a part of
+// ECMA-262. Other JS VMs support them.
+uc32 Scanner::ScanOctalEscape(uc32 c, int length) {
+ uc32 x = c - '0';
+ int i = 0;
+ for (; i < length; i++) {
+ int d = c0_ - '0';
+ if (d < 0 || d > 7) break;
+ int nx = x * 8 + d;
+ if (nx >= 256) break;
+ x = nx;
+ Advance();
+ }
+ // Anything except '\0' is an octal escape sequence, illegal in strict mode.
+ // Remember the position of octal escape sequences so that an error
+ // can be reported later (in strict mode).
+ // We don't report the error immediately, because the octal escape can
+ // occur before the "use strict" directive.
+ if (c != '0' || i > 0) {
+ octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1);
+ }
+ return x;
+}
+
+
+Token::Value Scanner::ScanString() {
+ uc32 quote = c0_;
+ Advance(); // consume quote
+
+ LiteralScope literal(this);
+ while (c0_ != quote && c0_ >= 0
+ && !unicode_cache_->IsLineTerminator(c0_)) {
+ uc32 c = c0_;
+ Advance();
+ if (c == '\\') {
+ if (c0_ < 0 || !ScanEscape()) return Token::ILLEGAL;
+ } else {
+ AddLiteralChar(c);
+ }
+ }
+ if (c0_ != quote) return Token::ILLEGAL;
+ literal.Complete();
+
+ Advance(); // consume quote
+ return Token::STRING;
+}
+
+
+void Scanner::ScanDecimalDigits() {
+ while (IsDecimalDigit(c0_))
+ AddLiteralCharAdvance();
+}
+
+
+Token::Value Scanner::ScanNumber(bool seen_period) {
+ ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction
+
+ enum { DECIMAL, HEX, OCTAL } kind = DECIMAL;
+
+ LiteralScope literal(this);
+ if (seen_period) {
+ // we have already seen a decimal point of the float
+ AddLiteralChar('.');
+ ScanDecimalDigits(); // we know we have at least one digit
+
+ } else {
+ // if the first character is '0' we must check for octals and hex
+ if (c0_ == '0') {
+ int start_pos = source_pos(); // For reporting octal positions.
+ AddLiteralCharAdvance();
+
+ // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number
+ if (c0_ == 'x' || c0_ == 'X') {
+ // hex number
+ kind = HEX;
+ AddLiteralCharAdvance();
+ if (!IsHexDigit(c0_)) {
+ // we must have at least one hex digit after 'x'/'X'
+ return Token::ILLEGAL;
+ }
+ while (IsHexDigit(c0_)) {
+ AddLiteralCharAdvance();
+ }
+ } else if ('0' <= c0_ && c0_ <= '7') {
+ // (possible) octal number
+ kind = OCTAL;
+ while (true) {
+ if (c0_ == '8' || c0_ == '9') {
+ kind = DECIMAL;
+ break;
+ }
+ if (c0_ < '0' || '7' < c0_) {
+ // Octal literal finished.
+ octal_pos_ = Location(start_pos, source_pos());
+ break;
+ }
+ AddLiteralCharAdvance();
+ }
+ }
+ }
+
+ // Parse decimal digits and allow trailing fractional part.
+ if (kind == DECIMAL) {
+ ScanDecimalDigits(); // optional
+ if (c0_ == '.') {
+ AddLiteralCharAdvance();
+ ScanDecimalDigits(); // optional
+ }
+ }
+ }
+
+ // scan exponent, if any
+ if (c0_ == 'e' || c0_ == 'E') {
+ ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
+ if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed
+ // scan exponent
+ AddLiteralCharAdvance();
+ if (c0_ == '+' || c0_ == '-')
+ AddLiteralCharAdvance();
+ if (!IsDecimalDigit(c0_)) {
+ // we must have at least one decimal digit after 'e'/'E'
+ return Token::ILLEGAL;
+ }
+ ScanDecimalDigits();
+ }
+
+ // The source character immediately following a numeric literal must
+ // not be an identifier start or a decimal digit; see ECMA-262
+ // section 7.8.3, page 17 (note that we read only one decimal digit
+ // if the value is 0).
+ if (IsDecimalDigit(c0_) || unicode_cache_->IsIdentifierStart(c0_))
+ return Token::ILLEGAL;
+
+ literal.Complete();
+
+ return Token::NUMBER;
+}
+
+
+uc32 Scanner::ScanIdentifierUnicodeEscape() {
+ Advance();
+ if (c0_ != 'u') return -1;
+ Advance();
+ uc32 result = ScanHexNumber(4);
+ if (result < 0) PushBack('u');
+ return result;
+}
+
+
+// ----------------------------------------------------------------------------
+// Keyword Matcher
+
+#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
+ KEYWORD_GROUP('b') \
+ KEYWORD("break", Token::BREAK) \
+ KEYWORD_GROUP('c') \
+ KEYWORD("case", Token::CASE) \
+ KEYWORD("catch", Token::CATCH) \
+ KEYWORD("class", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("const", Token::CONST) \
+ KEYWORD("continue", Token::CONTINUE) \
+ KEYWORD_GROUP('d') \
+ KEYWORD("debugger", Token::DEBUGGER) \
+ KEYWORD("default", Token::DEFAULT) \
+ KEYWORD("delete", Token::DELETE) \
+ KEYWORD("do", Token::DO) \
+ KEYWORD_GROUP('e') \
+ KEYWORD("else", Token::ELSE) \
+ KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("export", harmony_modules \
+ ? Token::EXPORT : Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("extends", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD_GROUP('f') \
+ KEYWORD("false", Token::FALSE_LITERAL) \
+ KEYWORD("finally", Token::FINALLY) \
+ KEYWORD("for", Token::FOR) \
+ KEYWORD("function", Token::FUNCTION) \
+ KEYWORD_GROUP('i') \
+ KEYWORD("if", Token::IF) \
+ KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("import", harmony_modules \
+ ? Token::IMPORT : Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("in", Token::IN) \
+ KEYWORD("instanceof", Token::INSTANCEOF) \
+ KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('l') \
+ KEYWORD("let", harmony_scoping \
+ ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('n') \
+ KEYWORD("new", Token::NEW) \
+ KEYWORD("null", Token::NULL_LITERAL) \
+ KEYWORD_GROUP('p') \
+ KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('r') \
+ KEYWORD("return", Token::RETURN) \
+ KEYWORD_GROUP('s') \
+ KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("super", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("switch", Token::SWITCH) \
+ KEYWORD_GROUP('t') \
+ KEYWORD("this", Token::THIS) \
+ KEYWORD("throw", Token::THROW) \
+ KEYWORD("true", Token::TRUE_LITERAL) \
+ KEYWORD("try", Token::TRY) \
+ KEYWORD("typeof", Token::TYPEOF) \
+ KEYWORD_GROUP('v') \
+ KEYWORD("var", Token::VAR) \
+ KEYWORD("void", Token::VOID) \
+ KEYWORD_GROUP('w') \
+ KEYWORD("while", Token::WHILE) \
+ KEYWORD("with", Token::WITH) \
+ KEYWORD_GROUP('y') \
+ KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD)
+
+
+static Token::Value KeywordOrIdentifierToken(const char* input,
+ int input_length,
+ bool harmony_scoping,
+ bool harmony_modules) {
+ ASSERT(input_length >= 1);
+ const int kMinLength = 2;
+ const int kMaxLength = 10;
+ if (input_length < kMinLength || input_length > kMaxLength) {
+ return Token::IDENTIFIER;
+ }
+ switch (input[0]) {
+ default:
+#define KEYWORD_GROUP_CASE(ch) \
+ break; \
+ case ch:
+#define KEYWORD(keyword, token) \
+ { \
+ /* 'keyword' is a char array, so sizeof(keyword) is */ \
+ /* strlen(keyword) plus 1 for the NUL char. */ \
+ const int keyword_length = sizeof(keyword) - 1; \
+ STATIC_ASSERT(keyword_length >= kMinLength); \
+ STATIC_ASSERT(keyword_length <= kMaxLength); \
+ if (input_length == keyword_length && \
+ input[1] == keyword[1] && \
+ (keyword_length <= 2 || input[2] == keyword[2]) && \
+ (keyword_length <= 3 || input[3] == keyword[3]) && \
+ (keyword_length <= 4 || input[4] == keyword[4]) && \
+ (keyword_length <= 5 || input[5] == keyword[5]) && \
+ (keyword_length <= 6 || input[6] == keyword[6]) && \
+ (keyword_length <= 7 || input[7] == keyword[7]) && \
+ (keyword_length <= 8 || input[8] == keyword[8]) && \
+ (keyword_length <= 9 || input[9] == keyword[9])) { \
+ return token; \
+ } \
+ }
+ KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
+ }
+ return Token::IDENTIFIER;
+}
+
+
+Token::Value Scanner::ScanIdentifierOrKeyword() {
+ ASSERT(unicode_cache_->IsIdentifierStart(c0_));
+ LiteralScope literal(this);
+ // Scan identifier start character.
+ if (c0_ == '\\') {
+ uc32 c = ScanIdentifierUnicodeEscape();
+ // Only allow legal identifier start characters.
+ if (c < 0 ||
+ c == '\\' || // No recursive escapes.
+ !unicode_cache_->IsIdentifierStart(c)) {
+ return Token::ILLEGAL;
+ }
+ AddLiteralChar(c);
+ return ScanIdentifierSuffix(&literal);
+ }
+
+ uc32 first_char = c0_;
+ Advance();
+ AddLiteralChar(first_char);
+
+ // Scan the rest of the identifier characters.
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
+ if (c0_ != '\\') {
+ uc32 next_char = c0_;
+ Advance();
+ AddLiteralChar(next_char);
+ continue;
+ }
+ // Fallthrough if no longer able to complete keyword.
+ return ScanIdentifierSuffix(&literal);
+ }
+
+ literal.Complete();
+
+ if (next_.literal_chars->is_ascii()) {
+ Vector<const char> chars = next_.literal_chars->ascii_literal();
+ return KeywordOrIdentifierToken(chars.start(),
+ chars.length(),
+ harmony_scoping_,
+ harmony_modules_);
+ }
+
+ return Token::IDENTIFIER;
+}
+
+
+Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal) {
+ // Scan the rest of the identifier characters.
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
+ if (c0_ == '\\') {
+ uc32 c = ScanIdentifierUnicodeEscape();
+ // Only allow legal identifier part characters.
+ if (c < 0 ||
+ c == '\\' ||
+ !unicode_cache_->IsIdentifierPart(c)) {
+ return Token::ILLEGAL;
+ }
+ AddLiteralChar(c);
+ } else {
+ AddLiteralChar(c0_);
+ Advance();
+ }
+ }
+ literal->Complete();
+
+ return Token::IDENTIFIER;
+}
+
+
+bool Scanner::ScanRegExpPattern(bool seen_equal) {
+ // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags
+ bool in_character_class = false;
+
+ // Previous token is either '/' or '/=', in the second case, the
+ // pattern starts at =.
+ next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1);
+ next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0);
+
+ // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5,
+ // the scanner should pass uninterpreted bodies to the RegExp
+ // constructor.
+ LiteralScope literal(this);
+ if (seen_equal) {
+ AddLiteralChar('=');
+ }
+
+ while (c0_ != '/' || in_character_class) {
+ if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false;
+ if (c0_ == '\\') { // Escape sequence.
+ AddLiteralCharAdvance();
+ if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false;
+ AddLiteralCharAdvance();
+ // If the escape allows more characters, i.e., \x??, \u????, or \c?,
+ // only "safe" characters are allowed (letters, digits, underscore),
+ // otherwise the escape isn't valid and the invalid character has
+ // its normal meaning. I.e., we can just continue scanning without
+ // worrying whether the following characters are part of the escape
+ // or not, since any '/', '\\' or '[' is guaranteed to not be part
+ // of the escape sequence.
+
+ // TODO(896): At some point, parse RegExps more throughly to capture
+ // octal esacpes in strict mode.
+ } else { // Unescaped character.
+ if (c0_ == '[') in_character_class = true;
+ if (c0_ == ']') in_character_class = false;
+ AddLiteralCharAdvance();
+ }
+ }
+ Advance(); // consume '/'
+
+ literal.Complete();
+
+ return true;
+}
+
+
+bool Scanner::ScanLiteralUnicodeEscape() {
+ ASSERT(c0_ == '\\');
+ uc32 chars_read[6] = {'\\', 'u', 0, 0, 0, 0};
+ Advance();
+ int i = 1;
+ if (c0_ == 'u') {
+ i++;
+ while (i < 6) {
+ Advance();
+ if (!IsHexDigit(c0_)) break;
+ chars_read[i] = c0_;
+ i++;
+ }
+ }
+ if (i < 6) {
+ // Incomplete escape. Undo all advances and return false.
+ while (i > 0) {
+ i--;
+ PushBack(chars_read[i]);
+ }
+ return false;
+ }
+ // Complete escape. Add all chars to current literal buffer.
+ for (int i = 0; i < 6; i++) {
+ AddLiteralChar(chars_read[i]);
+ }
+ return true;
+}
+
+
+bool Scanner::ScanRegExpFlags() {
+ // Scan regular expression flags.
+ LiteralScope literal(this);
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
+ if (c0_ != '\\') {
+ AddLiteralCharAdvance();
+ } else {
+ if (!ScanLiteralUnicodeEscape()) {
+ break;
+ }
+ }
+ }
+ literal.Complete();
+
+ next_.location.end_pos = source_pos() - 1;
+ return true;
+}
+
+} } // namespace v8::internal
diff --git a/samples/cpp/scanner.h b/samples/cpp/scanner.h
new file mode 100644
index 0000000..4de413b
--- /dev/null
+++ b/samples/cpp/scanner.h
@@ -0,0 +1,576 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Features shared by parsing and pre-parsing scanners.
+
+#ifndef V8_SCANNER_H_
+#define V8_SCANNER_H_
+
+#include "allocation.h"
+#include "char-predicates.h"
+#include "checks.h"
+#include "globals.h"
+#include "token.h"
+#include "unicode-inl.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+
+// General collection of (multi-)bit-flags that can be passed to scanners and
+// parsers to signify their (initial) mode of operation.
+enum ParsingFlags {
+ kNoParsingFlags = 0,
+ // Embed LanguageMode values in parsing flags, i.e., equivalent to:
+ // CLASSIC_MODE = 0,
+ // STRICT_MODE,
+ // EXTENDED_MODE,
+ kLanguageModeMask = 0x03,
+ kAllowLazy = 0x04,
+ kAllowNativesSyntax = 0x08,
+ kAllowModules = 0x10
+};
+
+STATIC_ASSERT((kLanguageModeMask & CLASSIC_MODE) == CLASSIC_MODE);
+STATIC_ASSERT((kLanguageModeMask & STRICT_MODE) == STRICT_MODE);
+STATIC_ASSERT((kLanguageModeMask & EXTENDED_MODE) == EXTENDED_MODE);
+
+
+// Returns the value (0 .. 15) of a hexadecimal character c.
+// If c is not a legal hexadecimal character, returns a value < 0.
+inline int HexValue(uc32 c) {
+ c -= '0';
+ if (static_cast<unsigned>(c) <= 9) return c;
+ c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36.
+ if (static_cast<unsigned>(c) <= 5) return c + 10;
+ return -1;
+}
+
+
+// ---------------------------------------------------------------------
+// Buffered stream of UTF-16 code units, using an internal UTF-16 buffer.
+// A code unit is a 16 bit value representing either a 16 bit code point
+// or one part of a surrogate pair that make a single 21 bit code point.
+
+class Utf16CharacterStream {
+ public:
+ Utf16CharacterStream() : pos_(0) { }
+ virtual ~Utf16CharacterStream() { }
+
+ // Returns and advances past the next UTF-16 code unit in the input
+ // stream. If there are no more code units, it returns a negative
+ // value.
+ inline uc32 Advance() {
+ if (buffer_cursor_ < buffer_end_ || ReadBlock()) {
+ pos_++;
+ return static_cast<uc32>(*(buffer_cursor_++));
+ }
+ // Note: currently the following increment is necessary to avoid a
+ // parser problem! The scanner treats the final kEndOfInput as
+ // a code unit with a position, and does math relative to that
+ // position.
+ pos_++;
+
+ return kEndOfInput;
+ }
+
+ // Return the current position in the code unit stream.
+ // Starts at zero.
+ inline unsigned pos() const { return pos_; }
+
+ // Skips forward past the next code_unit_count UTF-16 code units
+ // in the input, or until the end of input if that comes sooner.
+ // Returns the number of code units actually skipped. If less
+ // than code_unit_count,
+ inline unsigned SeekForward(unsigned code_unit_count) {
+ unsigned buffered_chars =
+ static_cast<unsigned>(buffer_end_ - buffer_cursor_);
+ if (code_unit_count <= buffered_chars) {
+ buffer_cursor_ += code_unit_count;
+ pos_ += code_unit_count;
+ return code_unit_count;
+ }
+ return SlowSeekForward(code_unit_count);
+ }
+
+ // Pushes back the most recently read UTF-16 code unit (or negative
+ // value if at end of input), i.e., the value returned by the most recent
+ // call to Advance.
+ // Must not be used right after calling SeekForward.
+ virtual void PushBack(int32_t code_unit) = 0;
+
+ protected:
+ static const uc32 kEndOfInput = -1;
+
+ // Ensures that the buffer_cursor_ points to the code_unit at
+ // position pos_ of the input, if possible. If the position
+ // is at or after the end of the input, return false. If there
+ // are more code_units available, return true.
+ virtual bool ReadBlock() = 0;
+ virtual unsigned SlowSeekForward(unsigned code_unit_count) = 0;
+
+ const uc16* buffer_cursor_;
+ const uc16* buffer_end_;
+ unsigned pos_;
+};
+
+
+class UnicodeCache {
+// ---------------------------------------------------------------------
+// Caching predicates used by scanners.
+ public:
+ UnicodeCache() {}
+ typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder;
+
+ StaticResource<Utf8Decoder>* utf8_decoder() {
+ return &utf8_decoder_;
+ }
+
+ bool IsIdentifierStart(unibrow::uchar c) { return kIsIdentifierStart.get(c); }
+ bool IsIdentifierPart(unibrow::uchar c) { return kIsIdentifierPart.get(c); }
+ bool IsLineTerminator(unibrow::uchar c) { return kIsLineTerminator.get(c); }
+ bool IsWhiteSpace(unibrow::uchar c) { return kIsWhiteSpace.get(c); }
+
+ private:
+ unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart;
+ unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart;
+ unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator;
+ unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace;
+ StaticResource<Utf8Decoder> utf8_decoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnicodeCache);
+};
+
+
+// ----------------------------------------------------------------------------
+// LiteralBuffer - Collector of chars of literals.
+
+class LiteralBuffer {
+ public:
+ LiteralBuffer() : is_ascii_(true), position_(0), backing_store_() { }
+
+ ~LiteralBuffer() {
+ if (backing_store_.length() > 0) {
+ backing_store_.Dispose();
+ }
+ }
+
+ INLINE(void AddChar(uint32_t code_unit)) {
+ if (position_ >= backing_store_.length()) ExpandBuffer();
+ if (is_ascii_) {
+ if (code_unit < kMaxAsciiCharCodeU) {
+ backing_store_[position_] = static_cast<byte>(code_unit);
+ position_ += kASCIISize;
+ return;
+ }
+ ConvertToUtf16();
+ }
+ ASSERT(code_unit < 0x10000u);
+ *reinterpret_cast<uc16*>(&backing_store_[position_]) = code_unit;
+ position_ += kUC16Size;
+ }
+
+ bool is_ascii() { return is_ascii_; }
+
+ Vector<const uc16> utf16_literal() {
+ ASSERT(!is_ascii_);
+ ASSERT((position_ & 0x1) == 0);
+ return Vector<const uc16>(
+ reinterpret_cast<const uc16*>(backing_store_.start()),
+ position_ >> 1);
+ }
+
+ Vector<const char> ascii_literal() {
+ ASSERT(is_ascii_);
+ return Vector<const char>(
+ reinterpret_cast<const char*>(backing_store_.start()),
+ position_);
+ }
+
+ int length() {
+ return is_ascii_ ? position_ : (position_ >> 1);
+ }
+
+ void Reset() {
+ position_ = 0;
+ is_ascii_ = true;
+ }
+
+ private:
+ static const int kInitialCapacity = 16;
+ static const int kGrowthFactory = 4;
+ static const int kMinConversionSlack = 256;
+ static const int kMaxGrowth = 1 * MB;
+ inline int NewCapacity(int min_capacity) {
+ int capacity = Max(min_capacity, backing_store_.length());
+ int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth);
+ return new_capacity;
+ }
+
+ void ExpandBuffer() {
+ Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity));
+ memcpy(new_store.start(), backing_store_.start(), position_);
+ backing_store_.Dispose();
+ backing_store_ = new_store;
+ }
+
+ void ConvertToUtf16() {
+ ASSERT(is_ascii_);
+ Vector<byte> new_store;
+ int new_content_size = position_ * kUC16Size;
+ if (new_content_size >= backing_store_.length()) {
+ // Ensure room for all currently read code units as UC16 as well
+ // as the code unit about to be stored.
+ new_store = Vector<byte>::New(NewCapacity(new_content_size));
+ } else {
+ new_store = backing_store_;
+ }
+ char* src = reinterpret_cast<char*>(backing_store_.start());
+ uc16* dst = reinterpret_cast<uc16*>(new_store.start());
+ for (int i = position_ - 1; i >= 0; i--) {
+ dst[i] = src[i];
+ }
+ if (new_store.start() != backing_store_.start()) {
+ backing_store_.Dispose();
+ backing_store_ = new_store;
+ }
+ position_ = new_content_size;
+ is_ascii_ = false;
+ }
+
+ bool is_ascii_;
+ int position_;
+ Vector<byte> backing_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(LiteralBuffer);
+};
+
+
+// ----------------------------------------------------------------------------
+// JavaScript Scanner.
+
+class Scanner {
+ public:
+ // Scoped helper for literal recording. Automatically drops the literal
+ // if aborting the scanning before it's complete.
+ class LiteralScope {
+ public:
+ explicit LiteralScope(Scanner* self)
+ : scanner_(self), complete_(false) {
+ scanner_->StartLiteral();
+ }
+ ~LiteralScope() {
+ if (!complete_) scanner_->DropLiteral();
+ }
+ void Complete() {
+ scanner_->TerminateLiteral();
+ complete_ = true;
+ }
+
+ private:
+ Scanner* scanner_;
+ bool complete_;
+ };
+
+ // Representation of an interval of source positions.
+ struct Location {
+ Location(int b, int e) : beg_pos(b), end_pos(e) { }
+ Location() : beg_pos(0), end_pos(0) { }
+
+ bool IsValid() const {
+ return beg_pos >= 0 && end_pos >= beg_pos;
+ }
+
+ static Location invalid() { return Location(-1, -1); }
+
+ int beg_pos;
+ int end_pos;
+ };
+
+ // -1 is outside of the range of any real source code.
+ static const int kNoOctalLocation = -1;
+
+ typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder;
+
+ explicit Scanner(UnicodeCache* scanner_contants);
+
+ void Initialize(Utf16CharacterStream* source);
+
+ // Returns the next token and advances input.
+ Token::Value Next();
+ // Returns the current token again.
+ Token::Value current_token() { return current_.token; }
+ // Returns the location information for the current token
+ // (the token last returned by Next()).
+ Location location() const { return current_.location; }
+ // Returns the literal string, if any, for the current token (the
+ // token last returned by Next()). The string is 0-terminated.
+ // Literal strings are collected for identifiers, strings, and
+ // numbers.
+ // These functions only give the correct result if the literal
+ // was scanned between calls to StartLiteral() and TerminateLiteral().
+ Vector<const char> literal_ascii_string() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->ascii_literal();
+ }
+ Vector<const uc16> literal_utf16_string() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->utf16_literal();
+ }
+ bool is_literal_ascii() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->is_ascii();
+ }
+ int literal_length() const {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->length();
+ }
+
+ bool literal_contains_escapes() const {
+ Location location = current_.location;
+ int source_length = (location.end_pos - location.beg_pos);
+ if (current_.token == Token::STRING) {
+ // Subtract delimiters.
+ source_length -= 2;
+ }
+ return current_.literal_chars->length() != source_length;
+ }
+
+ // Similar functions for the upcoming token.
+
+ // One token look-ahead (past the token returned by Next()).
+ Token::Value peek() const { return next_.token; }
+
+ Location peek_location() const { return next_.location; }
+
+ // Returns the literal string for the next token (the token that
+ // would be returned if Next() were called).
+ Vector<const char> next_literal_ascii_string() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->ascii_literal();
+ }
+ Vector<const uc16> next_literal_utf16_string() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->utf16_literal();
+ }
+ bool is_next_literal_ascii() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->is_ascii();
+ }
+ int next_literal_length() const {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->length();
+ }
+
+ UnicodeCache* unicode_cache() { return unicode_cache_; }
+
+ static const int kCharacterLookaheadBufferSize = 1;
+
+ // Scans octal escape sequence. Also accepts "\0" decimal escape sequence.
+ uc32 ScanOctalEscape(uc32 c, int length);
+
+ // Returns the location of the last seen octal literal.
+ Location octal_position() const { return octal_pos_; }
+ void clear_octal_position() { octal_pos_ = Location::invalid(); }
+
+ // Seek forward to the given position. This operation does not
+ // work in general, for instance when there are pushed back
+ // characters, but works for seeking forward until simple delimiter
+ // tokens, which is what it is used for.
+ void SeekForward(int pos);
+
+ bool HarmonyScoping() const {
+ return harmony_scoping_;
+ }
+ void SetHarmonyScoping(bool scoping) {
+ harmony_scoping_ = scoping;
+ }
+ bool HarmonyModules() const {
+ return harmony_modules_;
+ }
+ void SetHarmonyModules(bool modules) {
+ harmony_modules_ = modules;
+ }
+
+
+ // Returns true if there was a line terminator before the peek'ed token,
+ // possibly inside a multi-line comment.
+ bool HasAnyLineTerminatorBeforeNext() const {
+ return has_line_terminator_before_next_ ||
+ has_multiline_comment_before_next_;
+ }
+
+ // Scans the input as a regular expression pattern, previous
+ // character(s) must be /(=). Returns true if a pattern is scanned.
+ bool ScanRegExpPattern(bool seen_equal);
+ // Returns true if regexp flags are scanned (always since flags can
+ // be empty).
+ bool ScanRegExpFlags();
+
+ // Tells whether the buffer contains an identifier (no escapes).
+ // Used for checking if a property name is an identifier.
+ static bool IsIdentifier(unibrow::CharacterStream* buffer);
+
+ private:
+ // The current and look-ahead token.
+ struct TokenDesc {
+ Token::Value token;
+ Location location;
+ LiteralBuffer* literal_chars;
+ };
+
+ // Call this after setting source_ to the input.
+ void Init() {
+ // Set c0_ (one character ahead)
+ STATIC_ASSERT(kCharacterLookaheadBufferSize == 1);
+ Advance();
+ // Initialize current_ to not refer to a literal.
+ current_.literal_chars = NULL;
+ }
+
+ // Literal buffer support
+ inline void StartLiteral() {
+ LiteralBuffer* free_buffer = (current_.literal_chars == &literal_buffer1_) ?
+ &literal_buffer2_ : &literal_buffer1_;
+ free_buffer->Reset();
+ next_.literal_chars = free_buffer;
+ }
+
+ INLINE(void AddLiteralChar(uc32 c)) {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ next_.literal_chars->AddChar(c);
+ }
+
+ // Complete scanning of a literal.
+ inline void TerminateLiteral() {
+ // Does nothing in the current implementation.
+ }
+
+ // Stops scanning of a literal and drop the collected characters,
+ // e.g., due to an encountered error.
+ inline void DropLiteral() {
+ next_.literal_chars = NULL;
+ }
+
+ inline void AddLiteralCharAdvance() {
+ AddLiteralChar(c0_);
+ Advance();
+ }
+
+ // Low-level scanning support.
+ void Advance() { c0_ = source_->Advance(); }
+ void PushBack(uc32 ch) {
+ source_->PushBack(c0_);
+ c0_ = ch;
+ }
+
+ inline Token::Value Select(Token::Value tok) {
+ Advance();
+ return tok;
+ }
+
+ inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) {
+ Advance();
+ if (c0_ == next) {
+ Advance();
+ return then;
+ } else {
+ return else_;
+ }
+ }
+
+ uc32 ScanHexNumber(int expected_length);
+
+ // Scans a single JavaScript token.
+ void Scan();
+
+ bool SkipWhiteSpace();
+ Token::Value SkipSingleLineComment();
+ Token::Value SkipMultiLineComment();
+ // Scans a possible HTML comment -- begins with '<!'.
+ Token::Value ScanHtmlComment();
+
+ void ScanDecimalDigits();
+ Token::Value ScanNumber(bool seen_period);
+ Token::Value ScanIdentifierOrKeyword();
+ Token::Value ScanIdentifierSuffix(LiteralScope* literal);
+
+ Token::Value ScanString();
+
+ // Scans an escape-sequence which is part of a string and adds the
+ // decoded character to the current literal. Returns true if a pattern
+ // is scanned.
+ bool ScanEscape();
+ // Decodes a Unicode escape-sequence which is part of an identifier.
+ // If the escape sequence cannot be decoded the result is kBadChar.
+ uc32 ScanIdentifierUnicodeEscape();
+ // Scans a Unicode escape-sequence and adds its characters,
+ // uninterpreted, to the current literal. Used for parsing RegExp
+ // flags.
+ bool ScanLiteralUnicodeEscape();
+
+ // Return the current source position.
+ int source_pos() {
+ return source_->pos() - kCharacterLookaheadBufferSize;
+ }
+
+ UnicodeCache* unicode_cache_;
+
+ // Buffers collecting literal strings, numbers, etc.
+ LiteralBuffer literal_buffer1_;
+ LiteralBuffer literal_buffer2_;
+
+ TokenDesc current_; // desc for current token (as returned by Next())
+ TokenDesc next_; // desc for next token (one token look-ahead)
+
+ // Input stream. Must be initialized to an Utf16CharacterStream.
+ Utf16CharacterStream* source_;
+
+
+ // Start position of the octal literal last scanned.
+ Location octal_pos_;
+
+ // One Unicode character look-ahead; c0_ < 0 at the end of the input.
+ uc32 c0_;
+
+ // Whether there is a line terminator whitespace character after
+ // the current token, and before the next. Does not count newlines
+ // inside multiline comments.
+ bool has_line_terminator_before_next_;
+ // Whether there is a multi-line comment that contains a
+ // line-terminator after the current token, and before the next.
+ bool has_multiline_comment_before_next_;
+ // Whether we scan 'let' as a keyword for harmony block-scoped let bindings.
+ bool harmony_scoping_;
+ // Whether we scan 'module', 'import', 'export' as keywords.
+ bool harmony_modules_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SCANNER_H_
diff --git a/samples/cpp/utils.h b/samples/cpp/utils.h
new file mode 100644
index 0000000..cbc993e
--- /dev/null
+++ b/samples/cpp/utils.h
@@ -0,0 +1,71 @@
+/*
+ This file is part of the PhantomJS project from Ofi Labs.
+
+ Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
+ Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QtGlobal>
+#include <QWebFrame>
+#include <QFile>
+
+#include "csconverter.h"
+#include "encoding.h"
+
+class QTemporaryFile;
+/**
+ * Aggregate common utility functions.
+ * Functions are static methods.
+ * It's important to notice that, at the moment, this class can't be instantiated by design.
+ */
+class Utils
+{
+public:
+ static void showUsage();
+ static void messageHandler(QtMsgType type, const char *msg);
+ static bool exceptionHandler(const char* dump_path, const char* minidump_id, void* context, bool succeeded);
+ static QVariant coffee2js(const QString &script);
+ static bool injectJsInFrame(const QString &jsFilePath, const QString &libraryPath, QWebFrame *targetFrame, const bool startingScript = false);
+ static bool injectJsInFrame(const QString &jsFilePath, const Encoding &jsFileEnc, const QString &libraryPath, QWebFrame *targetFrame, const bool startingScript = false);
+ static QString readResourceFileUtf8(const QString &resourceFilePath);
+
+ static bool loadJSForDebug(const QString &jsFilePath, const Encoding &jsFileEnc, const QString &libraryPath, QWebFrame *targetFrame, const bool autorun = false);
+ static bool loadJSForDebug(const QString &jsFilePath, const QString &libraryPath, QWebFrame *targetFrame, const bool autorun = false);
+ static void cleanupFromDebug();
+
+private:
+ static QString findScript(const QString &jsFilePath, const QString& libraryPath);
+ static QString jsFromScriptFile(const QString& scriptPath, const Encoding& enc);
+ Utils(); //< This class shouldn't be instantiated
+
+ static QTemporaryFile* m_tempHarness; //< We want to make sure to clean up after ourselves
+ static QTemporaryFile* m_tempWrapper;
+};
+
+#endif // UTILS_H
diff --git a/samples/cpp/v8.cc b/samples/cpp/v8.cc
new file mode 100644
index 0000000..2910a07
--- /dev/null
+++ b/samples/cpp/v8.cc
@@ -0,0 +1,288 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "assembler.h"
+#include "isolate.h"
+#include "elements.h"
+#include "bootstrapper.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "frames.h"
+#include "heap-profiler.h"
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "log.h"
+#include "once.h"
+#include "platform.h"
+#include "runtime-profiler.h"
+#include "serialize.h"
+#include "store-buffer.h"
+
+namespace v8 {
+namespace internal {
+
+V8_DECLARE_ONCE(init_once);
+
+bool V8::is_running_ = false;
+bool V8::has_been_set_up_ = false;
+bool V8::has_been_disposed_ = false;
+bool V8::has_fatal_error_ = false;
+bool V8::use_crankshaft_ = true;
+List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL;
+
+static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER;
+
+static EntropySource entropy_source;
+
+
+bool V8::Initialize(Deserializer* des) {
+ FlagList::EnforceFlagImplications();
+
+ InitializeOncePerProcess();
+
+ // The current thread may not yet had entered an isolate to run.
+ // Note the Isolate::Current() may be non-null because for various
+ // initialization purposes an initializing thread may be assigned an isolate
+ // but not actually enter it.
+ if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
+ i::Isolate::EnterDefaultIsolate();
+ }
+
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData() != NULL);
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData()->thread_id().Equals(
+ i::ThreadId::Current()));
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData()->isolate() ==
+ i::Isolate::Current());
+
+ if (IsDead()) return false;
+
+ Isolate* isolate = Isolate::Current();
+ if (isolate->IsInitialized()) return true;
+
+ is_running_ = true;
+ has_been_set_up_ = true;
+ has_fatal_error_ = false;
+ has_been_disposed_ = false;
+
+ return isolate->Init(des);
+}
+
+
+void V8::SetFatalError() {
+ is_running_ = false;
+ has_fatal_error_ = true;
+}
+
+
+void V8::TearDown() {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate->IsDefaultIsolate());
+
+ if (!has_been_set_up_ || has_been_disposed_) return;
+
+ ElementsAccessor::TearDown();
+ LOperand::TearDownCaches();
+ RegisteredExtension::UnregisterAll();
+
+ isolate->TearDown();
+ delete isolate;
+
+ is_running_ = false;
+ has_been_disposed_ = true;
+
+ delete call_completed_callbacks_;
+ call_completed_callbacks_ = NULL;
+
+ OS::TearDown();
+}
+
+
+static void seed_random(uint32_t* state) {
+ for (int i = 0; i < 2; ++i) {
+ if (FLAG_random_seed != 0) {
+ state[i] = FLAG_random_seed;
+ } else if (entropy_source != NULL) {
+ uint32_t val;
+ ScopedLock lock(entropy_mutex.Pointer());
+ entropy_source(reinterpret_cast<unsigned char*>(&val), sizeof(uint32_t));
+ state[i] = val;
+ } else {
+ state[i] = random();
+ }
+ }
+}
+
+
+// Random number generator using George Marsaglia's MWC algorithm.
+static uint32_t random_base(uint32_t* state) {
+ // Initialize seed using the system random().
+ // No non-zero seed will ever become zero again.
+ if (state[0] == 0) seed_random(state);
+
+ // Mix the bits. Never replaces state[i] with 0 if it is nonzero.
+ state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16);
+ state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16);
+
+ return (state[0] << 14) + (state[1] & 0x3FFFF);
+}
+
+
+void V8::SetEntropySource(EntropySource source) {
+ entropy_source = source;
+}
+
+
+void V8::SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver resolver) {
+ StackFrame::SetReturnAddressLocationResolver(resolver);
+}
+
+
+// Used by JavaScript APIs
+uint32_t V8::Random(Context* context) {
+ ASSERT(context->IsGlobalContext());
+ ByteArray* seed = context->random_seed();
+ return random_base(reinterpret_cast<uint32_t*>(seed->GetDataStartAddress()));
+}
+
+
+// Used internally by the JIT and memory allocator for security
+// purposes. So, we keep a different state to prevent informations
+// leaks that could be used in an exploit.
+uint32_t V8::RandomPrivate(Isolate* isolate) {
+ ASSERT(isolate == Isolate::Current());
+ return random_base(isolate->private_random_seed());
+}
+
+
+bool V8::IdleNotification(int hint) {
+ // Returning true tells the caller that there is no need to call
+ // IdleNotification again.
+ if (!FLAG_use_idle_notification) return true;
+
+ // Tell the heap that it may want to adjust.
+ return HEAP->IdleNotification(hint);
+}
+
+
+void V8::AddCallCompletedCallback(CallCompletedCallback callback) {
+ if (call_completed_callbacks_ == NULL) { // Lazy init.
+ call_completed_callbacks_ = new List<CallCompletedCallback>();
+ }
+ for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+ if (callback == call_completed_callbacks_->at(i)) return;
+ }
+ call_completed_callbacks_->Add(callback);
+}
+
+
+void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) {
+ if (call_completed_callbacks_ == NULL) return;
+ for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+ if (callback == call_completed_callbacks_->at(i)) {
+ call_completed_callbacks_->Remove(i);
+ }
+ }
+}
+
+
+void V8::FireCallCompletedCallback(Isolate* isolate) {
+ if (call_completed_callbacks_ == NULL) return;
+ HandleScopeImplementer* handle_scope_implementer =
+ isolate->handle_scope_implementer();
+ if (!handle_scope_implementer->CallDepthIsZero()) return;
+ // Fire callbacks. Increase call depth to prevent recursive callbacks.
+ handle_scope_implementer->IncrementCallDepth();
+ for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+ call_completed_callbacks_->at(i)();
+ }
+ handle_scope_implementer->DecrementCallDepth();
+}
+
+
+// Use a union type to avoid type-aliasing optimizations in GCC.
+typedef union {
+ double double_value;
+ uint64_t uint64_t_value;
+} double_int_union;
+
+
+Object* V8::FillHeapNumberWithRandom(Object* heap_number,
+ Context* context) {
+ double_int_union r;
+ uint64_t random_bits = Random(context);
+ // Convert 32 random bits to 0.(32 random bits) in a double
+ // by computing:
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
+ static const double binary_million = 1048576.0;
+ r.double_value = binary_million;
+ r.uint64_t_value |= random_bits;
+ r.double_value -= binary_million;
+
+ HeapNumber::cast(heap_number)->set_value(r.double_value);
+ return heap_number;
+}
+
+void V8::InitializeOncePerProcessImpl() {
+ OS::SetUp();
+
+ use_crankshaft_ = FLAG_crankshaft;
+
+ if (Serializer::enabled()) {
+ use_crankshaft_ = false;
+ }
+
+ CPU::SetUp();
+ if (!CPU::SupportsCrankshaft()) {
+ use_crankshaft_ = false;
+ }
+
+ OS::PostSetUp();
+
+ RuntimeProfiler::GlobalSetUp();
+
+ ElementsAccessor::InitializeOncePerProcess();
+
+ if (FLAG_stress_compaction) {
+ FLAG_force_marking_deque_overflows = true;
+ FLAG_gc_global = true;
+ FLAG_max_new_space_size = (1 << (kPageSizeBits - 10)) * 2;
+ }
+
+ LOperand::SetUpCaches();
+ SetUpJSCallerSavedCodeData();
+ SamplerRegistry::SetUp();
+ ExternalReference::SetUp();
+}
+
+void V8::InitializeOncePerProcess() {
+ CallOnce(&init_once, &InitializeOncePerProcessImpl);
+}
+
+} } // namespace v8::internal
diff --git a/samples/cpp/v8.h b/samples/cpp/v8.h
new file mode 100644
index 0000000..67716d8
--- /dev/null
+++ b/samples/cpp/v8.h
@@ -0,0 +1,152 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Top include for all V8 .cc files.
+//
+
+#ifndef V8_V8_H_
+#define V8_V8_H_
+
+#if defined(GOOGLE3)
+// Google3 special flag handling.
+#if defined(DEBUG) && defined(NDEBUG)
+// V8 only uses DEBUG and whenever it is set we are building a debug
+// version of V8. We do not use NDEBUG and simply undef it here for
+// consistency.
+#undef NDEBUG
+#endif
+#endif // defined(GOOGLE3)
+
+// V8 only uses DEBUG, but included external files
+// may use NDEBUG - make sure they are consistent.
+#if defined(DEBUG) && defined(NDEBUG)
+#error both DEBUG and NDEBUG are set
+#endif
+
+// Basic includes
+#include "../include/v8.h"
+#include "v8globals.h"
+#include "v8checks.h"
+#include "allocation.h"
+#include "v8utils.h"
+#include "flags.h"
+
+// Objects & heap
+#include "objects-inl.h"
+#include "spaces-inl.h"
+#include "heap-inl.h"
+#include "incremental-marking-inl.h"
+#include "mark-compact-inl.h"
+#include "log-inl.h"
+#include "cpu-profiler-inl.h"
+#include "handles-inl.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+class Deserializer;
+
+class V8 : public AllStatic {
+ public:
+ // Global actions.
+
+ // If Initialize is called with des == NULL, the initial state is
+ // created from scratch. If a non-null Deserializer is given, the
+ // initial state is created by reading the deserialized data into an
+ // empty heap.
+ static bool Initialize(Deserializer* des);
+ static void TearDown();
+ static bool IsRunning() { return is_running_; }
+ static bool UseCrankshaft() { return use_crankshaft_; }
+ // To be dead you have to have lived
+ // TODO(isolates): move IsDead to Isolate.
+ static bool IsDead() { return has_fatal_error_ || has_been_disposed_; }
+ static void SetFatalError();
+
+ // Report process out of memory. Implementation found in api.cc.
+ static void FatalProcessOutOfMemory(const char* location,
+ bool take_snapshot = false);
+
+ // Allows an entropy source to be provided for use in random number
+ // generation.
+ static void SetEntropySource(EntropySource source);
+ // Support for return-address rewriting profilers.
+ static void SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver resolver);
+ // Random number generation support. Not cryptographically safe.
+ static uint32_t Random(Context* context);
+ // We use random numbers internally in memory allocation and in the
+ // compilers for security. In order to prevent information leaks we
+ // use a separate random state for internal random number
+ // generation.
+ static uint32_t RandomPrivate(Isolate* isolate);
+ static Object* FillHeapNumberWithRandom(Object* heap_number,
+ Context* context);
+
+ // Idle notification directly from the API.
+ static bool IdleNotification(int hint);
+
+ static void AddCallCompletedCallback(CallCompletedCallback callback);
+ static void RemoveCallCompletedCallback(CallCompletedCallback callback);
+ static void FireCallCompletedCallback(Isolate* isolate);
+
+ private:
+ static void InitializeOncePerProcessImpl();
+ static void InitializeOncePerProcess();
+
+ // True if engine is currently running
+ static bool is_running_;
+ // True if V8 has ever been run
+ static bool has_been_set_up_;
+ // True if error has been signaled for current engine
+ // (reset to false if engine is restarted)
+ static bool has_fatal_error_;
+ // True if engine has been shut down
+ // (reset if engine is restarted)
+ static bool has_been_disposed_;
+ // True if we are using the crankshaft optimizing compiler.
+ static bool use_crankshaft_;
+ // List of callbacks when a Call completes.
+ static List<CallCompletedCallback>* call_completed_callbacks_;
+};
+
+
+// JavaScript defines two kinds of 'nil'.
+enum NilValue { kNullValue, kUndefinedValue };
+
+
+// JavaScript defines two kinds of equality.
+enum EqualityKind { kStrictEquality, kNonStrictEquality };
+
+
+} } // namespace v8::internal
+
+namespace i = v8::internal;
+
+#endif // V8_V8_H_
diff --git a/samples/dart/point.dart b/samples/dart/point.dart
new file mode 100644
index 0000000..5bb74b2
--- /dev/null
+++ b/samples/dart/point.dart
@@ -0,0 +1,15 @@
+class Point {
+ Point(this.x, this.y);
+ distanceTo(Point other) {
+ var dx = x - other.x;
+ var dy = y - other.y;
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+ var x, y;
+}
+
+main() {
+ Point p = new Point(2, 3);
+ Point q = new Point(3, 4);
+ print('distance from p to q = ${p.distanceTo(q)}');
+}
diff --git a/samples/delphi/program.dpr b/samples/delphi/program.dpr
new file mode 100644
index 0000000..d2d71f1
--- /dev/null
+++ b/samples/delphi/program.dpr
@@ -0,0 +1,14 @@
+program gmail;
+
+uses
+ Forms,
+ Unit2 in 'Unit2.pas' {Form2};
+
+{$R *.res}
+
+begin
+ Application.Initialize;
+ Application.MainFormOnTaskbar := True;
+ Application.CreateForm(TForm2, Form2);
+ Application.Run;
+end.
diff --git a/samples/diff/dude-thing-okay--001.patch b/samples/diff/dude-thing-okay--001.patch
new file mode 100644
index 0000000..d3dbba1
--- /dev/null
+++ b/samples/diff/dude-thing-okay--001.patch
@@ -0,0 +1,4 @@
+diff --git a/lib/linguist.rb b/lib/linguist.rb
+index d472341..8ad9ffb 100644
+--- a/lib/linguist.rb
++++ b/lib/linguist.rb
diff --git a/samples/emacs/dude.el b/samples/emacs/dude.el
new file mode 100644
index 0000000..70c6117
--- /dev/null
+++ b/samples/emacs/dude.el
@@ -0,0 +1 @@
+(print "Dude!")
diff --git a/samples/gas/hello.s b/samples/gas/hello.s
new file mode 100644
index 0000000..278f03c
--- /dev/null
+++ b/samples/gas/hello.s
@@ -0,0 +1,63 @@
+ .cstring
+LC0:
+ .ascii "Hello World\0"
+ .text
+.globl _main
+_main:
+LFB3:
+ pushq %rbp
+LCFI0:
+ movq %rsp, %rbp
+LCFI1:
+ leaq LC0(%rip), %rdi
+ call _puts
+ movl $0, %eax
+ leave
+ ret
+LFE3:
+ .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
+EH_frame1:
+ .set L$set$0,LECIE1-LSCIE1
+ .long L$set$0
+LSCIE1:
+ .long 0x0
+ .byte 0x1
+ .ascii "zR\0"
+ .byte 0x1
+ .byte 0x78
+ .byte 0x10
+ .byte 0x1
+ .byte 0x10
+ .byte 0xc
+ .byte 0x7
+ .byte 0x8
+ .byte 0x90
+ .byte 0x1
+ .align 3
+LECIE1:
+.globl _main.eh
+_main.eh:
+LSFDE1:
+ .set L$set$1,LEFDE1-LASFDE1
+ .long L$set$1
+LASFDE1:
+ .long LASFDE1-EH_frame1
+ .quad LFB3-.
+ .set L$set$2,LFE3-LFB3
+ .quad L$set$2
+ .byte 0x0
+ .byte 0x4
+ .set L$set$3,LCFI0-LFB3
+ .long L$set$3
+ .byte 0xe
+ .byte 0x10
+ .byte 0x86
+ .byte 0x2
+ .byte 0x4
+ .set L$set$4,LCFI1-LCFI0
+ .long L$set$4
+ .byte 0xd
+ .byte 0x6
+ .align 3
+LEFDE1:
+ .subsections_via_symbols
diff --git a/samples/gosu/Hello.gst b/samples/gosu/Hello.gst
new file mode 100644
index 0000000..6addbf7
--- /dev/null
+++ b/samples/gosu/Hello.gst
@@ -0,0 +1,7 @@
+<%!-- defined in Hello.gst --%>
+
+<%@ params( users : Collection <User> ) %>
+
+<% for( user in users ) { %>
+
+${user.LastName}, ${user.FirstName}, ${user.Department} <% } %> \ No newline at end of file
diff --git a/samples/gosu/Hello.gsx b/samples/gosu/Hello.gsx
new file mode 100644
index 0000000..2f850e9
--- /dev/null
+++ b/samples/gosu/Hello.gsx
@@ -0,0 +1,9 @@
+package example
+
+enhancement Hello : String {
+
+ function toPerson() : Person {
+ var vals = this.split(",")
+ return new Person( vals[0], vals[1] as int, Relationship.valueOf( vals[2] ) ) )
+ }
+} \ No newline at end of file
diff --git a/samples/gosu/Person.gs b/samples/gosu/Person.gs
new file mode 100644
index 0000000..eb8372d
--- /dev/null
+++ b/samples/gosu/Person.gs
@@ -0,0 +1,106 @@
+package example
+
+uses java.util.*
+
+uses java.io.File
+
+class Person extends Contact implements IEmailable {
+
+ var _name : String
+ var _age : Integer as Age
+ var _relationship : Relationship as readonly RelationshipOfPerson
+
+ delegate _emailHelper represents IEmailable
+
+ enum Relationship {
+ FRIEND,
+ FAMILY,
+ BUSINESS_CONTACT
+ }
+
+ // Map of names to people
+ static var ALL_PEOPLE = new HashMap<String, Person>()
+
+ /* Constructs a new Person */
+ construct( name : String, age : Integer, relationship : Relationship ) {
+ _name = name
+ _age = age
+ _relationship = relationship
+ _emailHelper = new EmailHelper( this )
+ }
+
+ property get Name():String{
+ return _name
+ }
+
+ property set Name(name : String){
+ _name = name
+ }
+
+ /* Implement IEmailable#getEmailName() */
+ override function getEmailName():String{
+ return Name
+ }
+
+ function incrementAge() {
+ _age++
+ }
+
+ @Deprecated
+ function printPersonInfo() {
+ print( "Person { Name : ${Name}, Age : ${Age}, Relationship : ${RelationshipOfPerson} }" )
+ }
+
+ static function addPerson(p : Person){
+ if(ALL_PEOPLE.containsKey(p?.Name)) {
+ throw new IllegalArgumentException( "There is already someone named '${p.Name}'." )
+ }
+ ALL_PEOPLE[p.Name] = p
+ }
+
+ static function addAllPeople( contacts : List<Contact> ) {
+ for( contact in contacts ) {
+ if( contact typeis Person and not ALL_PEOPLE.containsKey( contact.Name )) {
+ addPerson( contact )
+ }
+ }
+ }
+
+ static function getAllPeopleOlderThanNOrderedByName( age : int ) {
+ var allPeople = ALL_PEOPLE.Values
+
+ return allPeople.where( \ p -> p.Age > age ).orderBy( \ p -> p.Name )
+ }
+
+ static function loadPersonFromDB( id : Integer ) {
+ using( var conn = DBConnectionManager.getConnection(),
+ var stmt = conn.prepareStatement( "SELECT name, age, relationship FROM PEOPLE WHERE ID=?") ){
+
+ stmt.setInt( 0, 0 )
+ var result = stmt.executeQuery()
+ if( result.next() ) {
+ addPerson( new Person( result.getString( "name" ),
+ result.getInt( "age" ),
+ Relationship.valueOf( result.getString( "relationship" ) ) ) )
+
+ }
+ }
+ }
+
+ /* Loads in people from a CSV */
+ static function loadFromFile( file : File ) {
+ file.eachLine( \ line -> {
+ if( line.HasContent ) {
+ addPerson( line.toPerson() )
+ }
+ })
+ }
+
+ /* Save people to a CSV */
+ static function saveToFile( file : File ) {
+ using( var writer = new FileWriter( file ) ) {
+ print( PersonCSVTemplate.renderToString( ALL_PEOPLE.Values ) )
+ PersonCSVTemplate.render( writer, ALL_PEOPLE.Values )
+ }
+ }
+} \ No newline at end of file
diff --git a/samples/gosu/hello.gsp b/samples/gosu/hello.gsp
new file mode 100644
index 0000000..ce47b77
--- /dev/null
+++ b/samples/gosu/hello.gsp
@@ -0,0 +1 @@
+print("hello") \ No newline at end of file
diff --git a/samples/gosu/hello.vark b/samples/gosu/hello.vark
new file mode 100644
index 0000000..3fc6934
--- /dev/null
+++ b/samples/gosu/hello.vark
@@ -0,0 +1,3 @@
+function hello() {
+ print("hello")
+} \ No newline at end of file
diff --git a/samples/groovy/build.gradle b/samples/groovy/build.gradle
new file mode 100644
index 0000000..c1ac812
--- /dev/null
+++ b/samples/groovy/build.gradle
@@ -0,0 +1,17 @@
+task echoDirListViaAntBuilder() {
+ description = 'Uses the built-in AntBuilder instance to echo and list files'
+ //Docs: http://ant.apache.org/manual/Types/fileset.html
+
+ //Echo the Gradle project name via the ant echo plugin
+ ant.echo(message: project.name)
+ ant.echo(path)
+ ant.echo("${projectDir}/samples")
+
+ //Gather list of files in a subdirectory
+ ant.fileScanner{
+ fileset(dir:"samples")
+ }.each{
+ //Print each file to screen with the CWD (projectDir) path removed.
+ println it.toString() - "${projectDir}"
+ }
+}
diff --git a/samples/groovy/script.groovy b/samples/groovy/script.groovy
new file mode 100755
index 0000000..1ee357a
--- /dev/null
+++ b/samples/groovy/script.groovy
@@ -0,0 +1,2 @@
+#!/usr/bin/env groovy
+println "Groovy!"
diff --git a/samples/gsp/bar.gsp b/samples/gsp/bar.gsp
new file mode 100644
index 0000000..4a7eabf
--- /dev/null
+++ b/samples/gsp/bar.gsp
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Testing with SiteMesh and Resources</title>
+ <meta name="layout" content="blankMain"/>
+ <r:require module="style"/>
+</head>
+<body>
+</body>
+</html>
diff --git a/samples/gsp/hello-pagedirective.gsp b/samples/gsp/hello-pagedirective.gsp
new file mode 100644
index 0000000..d632536
--- /dev/null
+++ b/samples/gsp/hello-pagedirective.gsp
@@ -0,0 +1,16 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+
+<html>
+ <head>
+ <title>Using page directive tag</title>
+ </head>
+ <body>
+ <div class="center">
+ <a href="#" alt="Download" id="downloadButton">Download</a>
+ </div>
+ <div class="center">
+ <a href="#" alt="Print" id="printButton">Print</a>
+ </div>
+ </body>
+</html>
+
diff --git a/samples/gsp/hello-resources.gsp b/samples/gsp/hello-resources.gsp
new file mode 100644
index 0000000..15c0edd
--- /dev/null
+++ b/samples/gsp/hello-resources.gsp
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Testing with Resources</title>
+ <r:require module="style"/>
+</head>
+<body>
+</body>
+</html>
diff --git a/samples/gsp/hello-var.gsp b/samples/gsp/hello-var.gsp
new file mode 100644
index 0000000..b16da17
--- /dev/null
+++ b/samples/gsp/hello-var.gsp
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Testing with SiteMesh and ${example}</title>
+
+</head>
+<body>
+</body>
+</html>
diff --git a/samples/haml/hello.haml b/samples/haml/hello.haml
new file mode 100644
index 0000000..accbf54
--- /dev/null
+++ b/samples/haml/hello.haml
@@ -0,0 +1,3 @@
+%p
+ Hello,
+ World!
diff --git a/samples/ini/.gitconfig b/samples/ini/.gitconfig
new file mode 100644
index 0000000..c583979
--- /dev/null
+++ b/samples/ini/.gitconfig
@@ -0,0 +1,3 @@
+[user]
+ name = Josh Peek
+ email = josh@github.com
diff --git a/samples/ioke/hello.ik b/samples/ioke/hello.ik
new file mode 100644
index 0000000..0adcd0e
--- /dev/null
+++ b/samples/ioke/hello.ik
@@ -0,0 +1,3 @@
+#!/usr/bin/env ioke
+
+"Hello world." println
diff --git a/samples/java/HtmlDomParserContext.java b/samples/java/HtmlDomParserContext.java
new file mode 100644
index 0000000..6c13e3d
--- /dev/null
+++ b/samples/java/HtmlDomParserContext.java
@@ -0,0 +1,243 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * 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 nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+import nokogiri.HtmlDocument;
+import nokogiri.NokogiriService;
+import nokogiri.XmlDocument;
+
+import org.apache.xerces.parsers.DOMParser;
+import org.apache.xerces.xni.Augmentations;
+import org.apache.xerces.xni.QName;
+import org.apache.xerces.xni.XMLAttributes;
+import org.apache.xerces.xni.XNIException;
+import org.apache.xerces.xni.parser.XMLDocumentFilter;
+import org.apache.xerces.xni.parser.XMLParserConfiguration;
+import org.cyberneko.html.HTMLConfiguration;
+import org.cyberneko.html.filters.DefaultFilter;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+/**
+ * Parser for HtmlDocument. This class actually parses HtmlDocument using NekoHtml.
+ *
+ * @author sergio
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class HtmlDomParserContext extends XmlDomParserContext {
+
+ public HtmlDomParserContext(Ruby runtime, IRubyObject options) {
+ super(runtime, options);
+ }
+
+ public HtmlDomParserContext(Ruby runtime, IRubyObject encoding, IRubyObject options) {
+ super(runtime, encoding, options);
+ }
+
+ @Override
+ protected void initErrorHandler() {
+ if (options.strict) {
+ errorHandler = new NokogiriStrictErrorHandler(options.noError, options.noWarning);
+ } else {
+ errorHandler = new NokogiriNonStrictErrorHandler4NekoHtml(options.noError, options.noWarning);
+ }
+ }
+
+ @Override
+ protected void initParser(Ruby runtime) {
+ XMLParserConfiguration config = new HTMLConfiguration();
+ XMLDocumentFilter removeNSAttrsFilter = new RemoveNSAttrsFilter();
+ XMLDocumentFilter elementValidityCheckFilter = new ElementValidityCheckFilter(errorHandler);
+ //XMLDocumentFilter[] filters = { removeNSAttrsFilter, elementValidityCheckFilter};
+ XMLDocumentFilter[] filters = { elementValidityCheckFilter};
+
+ config.setErrorHandler(this.errorHandler);
+ parser = new DOMParser(config);
+
+ // see http://nekohtml.sourceforge.net/settings.html for details
+ setProperty("http://cyberneko.org/html/properties/default-encoding", java_encoding);
+ setProperty("http://cyberneko.org/html/properties/names/elems", "lower");
+ setProperty("http://cyberneko.org/html/properties/names/attrs", "lower");
+ setProperty("http://cyberneko.org/html/properties/filters", filters);
+ setFeature("http://cyberneko.org/html/features/report-errors", true);
+ setFeature("http://xml.org/sax/features/namespaces", false);
+ setFeature("http://cyberneko.org/html/features/insert-doctype", true);
+ }
+
+ /**
+ * Enable NekoHTML feature for balancing tags in a document fragment.
+ *
+ * This method is used in XmlNode#in_context method.
+ */
+ public void enableDocumentFragment() {
+ setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment", true);
+ }
+
+ @Override
+ protected XmlDocument getNewEmptyDocument(ThreadContext context) {
+ IRubyObject[] args = new IRubyObject[0];
+ return (XmlDocument) XmlDocument.rbNew(context, getNokogiriClass(context.getRuntime(), "Nokogiri::HTML::Document"), args);
+ }
+
+ @Override
+ protected XmlDocument wrapDocument(ThreadContext context,
+ RubyClass klazz,
+ Document document) {
+ HtmlDocument htmlDocument = (HtmlDocument) NokogiriService.HTML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), klazz);
+ htmlDocument.setDocumentNode(context, document);
+ if (ruby_encoding.isNil()) {
+ // ruby_encoding might have detected by HtmlDocument::EncodingReader
+ if (detected_encoding != null && !detected_encoding.isNil()) {
+ ruby_encoding = detected_encoding;
+ } else {
+ // no encoding given & no encoding detected, then try to get it
+ String charset = tryGetCharsetFromHtml5MetaTag(document);
+ ruby_encoding = stringOrNil(context.getRuntime(), charset);
+ }
+ }
+ htmlDocument.setEncoding(ruby_encoding);
+ htmlDocument.setParsedEncoding(java_encoding);
+ return htmlDocument;
+ }
+
+ // NekoHtml doesn't understand HTML5 meta tag format. This fails to detect charset
+ // from an HTML5 style meta tag. Luckily, the meta tag and charset exists in DOM tree
+ // so, this method attempts to find the charset.
+ private String tryGetCharsetFromHtml5MetaTag(Document document) {
+ if (!"html".equalsIgnoreCase(document.getDocumentElement().getNodeName())) return null;
+ NodeList list = document.getDocumentElement().getChildNodes();
+ for (int i = 0; i < list.getLength(); i++) {
+ if ("head".equalsIgnoreCase(list.item(i).getNodeName())) {
+ NodeList headers = list.item(i).getChildNodes();
+ for (int j = 0; j < headers.getLength(); j++) {
+ if ("meta".equalsIgnoreCase(headers.item(j).getNodeName())) {
+ NamedNodeMap nodeMap = headers.item(j).getAttributes();
+ for (int k = 0; k < nodeMap.getLength(); k++) {
+ if ("charset".equalsIgnoreCase(nodeMap.item(k).getNodeName())) {
+ return nodeMap.item(k).getNodeValue();
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Filter to strip out attributes that pertain to XML namespaces.
+ */
+ public static class RemoveNSAttrsFilter extends DefaultFilter {
+ @Override
+ public void startElement(QName element, XMLAttributes attrs,
+ Augmentations augs) throws XNIException {
+ int i;
+ for (i = 0; i < attrs.getLength(); ++i) {
+ if (isNamespace(attrs.getQName(i))) {
+ attrs.removeAttributeAt(i);
+ --i;
+ }
+ }
+
+ element.uri = null;
+ super.startElement(element, attrs, augs);
+ }
+ }
+
+ public static class ElementValidityCheckFilter extends DefaultFilter {
+ private NokogiriErrorHandler errorHandler;
+
+ private ElementValidityCheckFilter(NokogiriErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ // element names from xhtml1-strict.dtd
+ private static String[][] element_names = {
+ {"a", "abbr", "acronym", "address", "area"},
+ {"b", "base", "basefont", "bdo", "big", "blockquote", "body", "br", "button"},
+ {"caption", "cite", "code", "col", "colgroup"},
+ {"dd", "del", "dfn", "div", "dl", "dt"},
+ {"em"},
+ {"fieldset", "font", "form", "frame", "frameset"},
+ {}, // g
+ {"h1", "h2", "h3", "h4", "h5", "h6", "head", "hr", "html"},
+ {"i", "iframe", "img", "input", "ins"},
+ {}, // j
+ {"kbd"},
+ {"label", "legend", "li", "link"},
+ {"map", "meta"},
+ {"noframes", "noscript"},
+ {"object", "ol", "optgroup", "option"},
+ {"p", "param", "pre"},
+ {"q"},
+ {}, // r
+ {"s", "samp", "script", "select", "small", "span", "strike", "strong", "style", "sub", "sup"},
+ {"table", "tbody", "td", "textarea", "tfoot", "th", "thead", "title", "tr", "tt"},
+ {"u", "ul"},
+ {"var"},
+ {}, // w
+ {}, // x
+ {}, // y
+ {} // z
+ };
+
+ private boolean isValid(String testee) {
+ char[] c = testee.toCharArray();
+ int index = new Integer(c[0]) - 97;
+ if (index > 25) return false;
+ for (int i=0; i<element_names[index].length; i++) {
+ if (testee.equals(element_names[index][i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void startElement(QName name, XMLAttributes attrs, Augmentations augs) throws XNIException {
+ if (!isValid(name.rawname)) {
+ errorHandler.getErrors().add(new Exception("Tag " + name.rawname + " invalid"));
+ }
+ super.startElement(name, attrs, augs);
+ }
+ }
+}
diff --git a/samples/java/Hudson.java b/samples/java/Hudson.java
new file mode 100644
index 0000000..aff8df4
--- /dev/null
+++ b/samples/java/Hudson.java
@@ -0,0 +1,322 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
+ * Erik Ramfelt, Koichi Fujikawa, Red Hat, Inc., Seiji Sogabe,
+ * Stephen Connolly, Tom Huybrechts, Yahoo! Inc., Alan Harder, CloudBees, Inc.
+ *
+ * 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 hudson.model;
+
+import hudson.ExtensionListView;
+import hudson.Functions;
+import hudson.Platform;
+import hudson.PluginManager;
+import hudson.cli.declarative.CLIResolver;
+import hudson.model.listeners.ItemListener;
+import hudson.slaves.ComputerListener;
+import hudson.util.CopyOnWriteList;
+import hudson.util.FormValidation;
+import jenkins.model.Jenkins;
+import org.jvnet.hudson.reactor.ReactorException;
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.Stapler;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import java.io.File;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static hudson.Util.fixEmpty;
+
+public class Hudson extends Jenkins {
+
+ /**
+ * List of registered {@link hudson.model.listeners.ItemListener}s.
+ * @deprecated as of 1.286
+ */
+ private transient final CopyOnWriteList<ItemListener> itemListeners = ExtensionListView.createCopyOnWriteList(ItemListener.class);
+
+ /**
+ * List of registered {@link hudson.slaves.ComputerListener}s.
+ * @deprecated as of 1.286
+ */
+ private transient final CopyOnWriteList<ComputerListener> computerListeners = ExtensionListView.createCopyOnWriteList(ComputerListener.class);
+
+
+ @CLIResolver
+ public static Hudson getInstance() {
+ return (Hudson)Jenkins.getInstance();
+ }
+
+ public Hudson(File root, ServletContext context) throws IOException, InterruptedException, ReactorException {
+ this(root,context,null);
+ }
+
+ public Hudson(File root, ServletContext context, PluginManager pluginManager) throws IOException, InterruptedException, ReactorException {
+ super(root, context, pluginManager);
+ }
+
+ /**
+ * Gets all the installed {@link ItemListener}s.
+ *
+ * @deprecated as of 1.286.
+ * Use {@link ItemListener#all()}.
+ */
+ public CopyOnWriteList<ItemListener> getJobListeners() {
+ return itemListeners;
+ }
+
+ /**
+ * Gets all the installed {@link ComputerListener}s.
+ *
+ * @deprecated as of 1.286.
+ * Use {@link ComputerListener#all()}.
+ */
+ public CopyOnWriteList<ComputerListener> getComputerListeners() {
+ return computerListeners;
+ }
+
+ /**
+ * Gets the slave node of the give name, hooked under this Hudson.
+ *
+ * @deprecated
+ * Use {@link #getNode(String)}. Since 1.252.
+ */
+ public Slave getSlave(String name) {
+ Node n = getNode(name);
+ if (n instanceof Slave)
+ return (Slave)n;
+ return null;
+ }
+
+ /**
+ * @deprecated
+ * Use {@link #getNodes()}. Since 1.252.
+ */
+ public List<Slave> getSlaves() {
+ return (List)slaves;
+ }
+
+ /**
+ * Updates the slave list.
+ *
+ * @deprecated
+ * Use {@link #setNodes(List)}. Since 1.252.
+ */
+ public void setSlaves(List<Slave> slaves) throws IOException {
+ setNodes(slaves);
+ }
+
+ /**
+ * @deprecated
+ * Left only for the compatibility of URLs.
+ * Should not be invoked for any other purpose.
+ */
+ public TopLevelItem getJob(String name) {
+ return getItem(name);
+ }
+
+ /**
+ * @deprecated
+ * Used only for mapping jobs to URL in a case-insensitive fashion.
+ */
+ public TopLevelItem getJobCaseInsensitive(String name) {
+ String match = Functions.toEmailSafeString(name);
+ for(TopLevelItem item : getItems()) {
+ if(Functions.toEmailSafeString(item.getName()).equalsIgnoreCase(match)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated as of 1.317
+ * Use {@link #doQuietDown()} instead.
+ */
+ public synchronized void doQuietDown(StaplerResponse rsp) throws IOException, ServletException {
+ doQuietDown().generateResponse(null, rsp, this);
+ }
+
+ /**
+ * RSS feed for log entries.
+ *
+ * @deprecated
+ * As on 1.267, moved to "/log/rss..."
+ */
+ public void doLogRss( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
+ String qs = req.getQueryString();
+ rsp.sendRedirect2("./log/rss"+(qs==null?"":'?'+qs));
+ }
+
+ /**
+ * @deprecated as of 1.294
+ * Define your own check method, instead of relying on this generic one.
+ */
+ public void doFieldCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
+ doFieldCheck(
+ fixEmpty(req.getParameter("value")),
+ fixEmpty(req.getParameter("type")),
+ fixEmpty(req.getParameter("errorText")),
+ fixEmpty(req.getParameter("warningText"))).generateResponse(req,rsp,this);
+ }
+
+ /**
+ * Checks if the value for a field is set; if not an error or warning text is displayed.
+ * If the parameter "value" is not set then the parameter "errorText" is displayed
+ * as an error text. If the parameter "errorText" is not set, then the parameter "warningText"
+ * is displayed as a warning text.
+ * <p>
+ * If the text is set and the parameter "type" is set, it will validate that the value is of the
+ * correct type. Supported types are "number, "number-positive" and "number-negative".
+ *
+ * @deprecated as of 1.324
+ * Either use client-side validation (e.g. class="required number")
+ * or define your own check method, instead of relying on this generic one.
+ */
+ public FormValidation doFieldCheck(@QueryParameter(fixEmpty=true) String value,
+ @QueryParameter(fixEmpty=true) String type,
+ @QueryParameter(fixEmpty=true) String errorText,
+ @QueryParameter(fixEmpty=true) String warningText) {
+ if (value == null) {
+ if (errorText != null)
+ return FormValidation.error(errorText);
+ if (warningText != null)
+ return FormValidation.warning(warningText);
+ return FormValidation.error("No error or warning text was set for fieldCheck().");
+ }
+
+ if (type != null) {
+ try {
+ if (type.equalsIgnoreCase("number")) {
+ NumberFormat.getInstance().parse(value);
+ } else if (type.equalsIgnoreCase("number-positive")) {
+ if (NumberFormat.getInstance().parse(value).floatValue() <= 0)
+ return FormValidation.error(Messages.Hudson_NotAPositiveNumber());
+ } else if (type.equalsIgnoreCase("number-negative")) {
+ if (NumberFormat.getInstance().parse(value).floatValue() >= 0)
+ return FormValidation.error(Messages.Hudson_NotANegativeNumber());
+ }
+ } catch (ParseException e) {
+ return FormValidation.error(Messages.Hudson_NotANumber());
+ }
+ }
+
+ return FormValidation.ok();
+ }
+
+ /**
+ * @deprecated
+ * Use {@link Functions#isWindows()}.
+ */
+ public static boolean isWindows() {
+ return File.pathSeparatorChar==';';
+ }
+
+ /**
+ * @deprecated
+ * Use {@link hudson.Platform#isDarwin()}
+ */
+ public static boolean isDarwin() {
+ return Platform.isDarwin();
+ }
+
+ /**
+ * @deprecated since 2007-12-18.
+ * Use {@link #checkPermission(hudson.security.Permission)}
+ */
+ public static boolean adminCheck() throws IOException {
+ return adminCheck(Stapler.getCurrentRequest(), Stapler.getCurrentResponse());
+ }
+
+ /**
+ * @deprecated since 2007-12-18.
+ * Use {@link #checkPermission(hudson.security.Permission)}
+ */
+ public static boolean adminCheck(StaplerRequest req,StaplerResponse rsp) throws IOException {
+ if (isAdmin(req)) return true;
+
+ rsp.sendError(StaplerResponse.SC_FORBIDDEN);
+ return false;
+ }
+
+ /**
+ * Checks if the current user (for which we are processing the current request)
+ * has the admin access.
+ *
+ * @deprecated since 2007-12-18.
+ * This method is deprecated when Hudson moved from simple Unix root-like model
+ * of "admin gets to do everything, and others don't have any privilege" to more
+ * complex {@link hudson.security.ACL} and {@link hudson.security.Permission} based scheme.
+ *
+ * <p>
+ * For a quick migration, use {@code Hudson.getInstance().getACL().hasPermission(Hudson.ADMINISTER)}
+ * To check if the user has the 'administer' role in Hudson.
+ *
+ * <p>
+ * But ideally, your plugin should first identify a suitable {@link hudson.security.Permission} (or create one,
+ * if appropriate), then identify a suitable {@link hudson.security.AccessControlled} object to check its permission
+ * against.
+ */
+ public static boolean isAdmin() {
+ return Jenkins.getInstance().getACL().hasPermission(ADMINISTER);
+ }
+
+ /**
+ * @deprecated since 2007-12-18.
+ * Define a custom {@link hudson.security.Permission} and check against ACL.
+ * See {@link #isAdmin()} for more instructions.
+ */
+ public static boolean isAdmin(StaplerRequest req) {
+ return isAdmin();
+ }
+
+ static {
+ XSTREAM.alias("hudson",Hudson.class);
+ }
+
+ /**
+ * @deprecated only here for backward comp
+ */
+ public static final class MasterComputer extends Jenkins.MasterComputer {
+ // no op
+ }
+
+ /**
+ * @deprecated only here for backward comp
+ */
+ public static class CloudList extends Jenkins.CloudList {
+ public CloudList(Jenkins h) {
+ super(h);
+ }
+
+ public CloudList() {// needed for XStream deserialization
+ super();
+ }
+ }
+}
diff --git a/samples/java/NokogiriService.java b/samples/java/NokogiriService.java
new file mode 100644
index 0000000..b497675
--- /dev/null
+++ b/samples/java/NokogiriService.java
@@ -0,0 +1,598 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * 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 nokogiri;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyModule;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.load.BasicLibraryService;
+
+/**
+ * Class to provide Nokogiri. This class is used to make "require 'nokogiri'" work
+ * in JRuby. Also, this class holds a Ruby type cache and allocators of Ruby types.
+ *
+ * @author headius
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriService implements BasicLibraryService {
+ public static final String nokogiriClassCacheGvarName = "$NOKOGIRI_CLASS_CACHE";
+ public static Map<String, RubyClass> nokogiriClassCache;
+
+ public boolean basicLoad(Ruby ruby) {
+ init(ruby);
+ createNokogiriClassCahce(ruby);
+ return true;
+ }
+
+ private static void createNokogiriClassCahce(Ruby ruby) {
+ nokogiriClassCache = Collections.synchronizedMap(new HashMap<String, RubyClass>());
+ nokogiriClassCache.put("Nokogiri::EncodingHandler", (RubyClass)ruby.getClassFromPath("Nokogiri::EncodingHandler"));
+ nokogiriClassCache.put("Nokogiri::HTML::Document", (RubyClass)ruby.getClassFromPath("Nokogiri::HTML::Document"));
+ nokogiriClassCache.put("Nokogiri::HTML::ElementDescription", (RubyClass)ruby.getClassFromPath("Nokogiri::HTML::ElementDescription"));
+ nokogiriClassCache.put("Nokogiri::XML::Attr", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Attr"));
+ nokogiriClassCache.put("Nokogiri::XML::Document", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Document"));
+ nokogiriClassCache.put("Nokogiri::XML::DocumentFragment", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::DocumentFragment"));
+ nokogiriClassCache.put("Nokogiri::XML::DTD", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::DTD"));
+ nokogiriClassCache.put("Nokogiri::XML::Text", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Text"));
+ nokogiriClassCache.put("Nokogiri::XML::Comment", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Comment"));
+ nokogiriClassCache.put("Nokogiri::XML::Element", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Element"));
+ nokogiriClassCache.put("Nokogiri::XML::ElementContent", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::ElementContent"));
+ nokogiriClassCache.put("Nokogiri::XML::ElementDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::ElementDecl"));
+ nokogiriClassCache.put("Nokogiri::XML::EntityDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::EntityDecl"));
+ nokogiriClassCache.put("Nokogiri::XML::EntityReference", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::EntityReference"));
+ nokogiriClassCache.put("Nokogiri::XML::ProcessingInstruction", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::ProcessingInstruction"));
+ nokogiriClassCache.put("Nokogiri::XML::CDATA", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::CDATA"));
+ nokogiriClassCache.put("Nokogiri::XML::Node", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Node"));
+ nokogiriClassCache.put("Nokogiri::XML::NodeSet", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::NodeSet"));
+ nokogiriClassCache.put("Nokogiri::XML::Namespace", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Namespace"));
+ nokogiriClassCache.put("Nokogiri::XML::SyntaxError", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::SyntaxError"));
+ nokogiriClassCache.put("Nokogiri::XML::Reader", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Reader"));
+ nokogiriClassCache.put("Nokogiri::XML::RelaxNG", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::RelaxNG"));
+ nokogiriClassCache.put("Nokogiri::XML::Schema", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Schema"));
+ nokogiriClassCache.put("Nokogiri::XML::XPathContext", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::XPathContext"));
+ nokogiriClassCache.put("Nokogiri::XML::AttributeDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::AttributeDecl"));
+ nokogiriClassCache.put("Nokogiri::XML::SAX::ParserContext", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::SAX::ParserContext"));
+ }
+
+ private void init(Ruby ruby) {
+ RubyModule nokogiri = ruby.defineModule("Nokogiri");
+ RubyModule xmlModule = nokogiri.defineModuleUnder("XML");
+ RubyModule xmlSaxModule = xmlModule.defineModuleUnder("SAX");
+ RubyModule htmlModule = nokogiri.defineModuleUnder("HTML");
+ RubyModule htmlSaxModule = htmlModule.defineModuleUnder("SAX");
+ RubyModule xsltModule = nokogiri.defineModuleUnder("XSLT");
+
+ createNokogiriModule(ruby, nokogiri);
+ createSyntaxErrors(ruby, nokogiri, xmlModule);
+ RubyClass xmlNode = createXmlModule(ruby, xmlModule);
+ createHtmlModule(ruby, htmlModule);
+ createDocuments(ruby, xmlModule, htmlModule, xmlNode);
+ createSaxModule(ruby, xmlSaxModule, htmlSaxModule);
+ createXsltModule(ruby, xsltModule);
+ }
+
+ private void createNokogiriModule(Ruby ruby, RubyModule nokogiri) {;
+ RubyClass encHandler = nokogiri.defineClassUnder("EncodingHandler", ruby.getObject(), ENCODING_HANDLER_ALLOCATOR);
+ encHandler.defineAnnotatedMethods(EncodingHandler.class);
+ }
+
+ private void createSyntaxErrors(Ruby ruby, RubyModule nokogiri, RubyModule xmlModule) {
+ RubyClass syntaxError = nokogiri.defineClassUnder("SyntaxError", ruby.getStandardError(), ruby.getStandardError().getAllocator());
+ RubyClass xmlSyntaxError = xmlModule.defineClassUnder("SyntaxError", syntaxError, XML_SYNTAXERROR_ALLOCATOR);
+ xmlSyntaxError.defineAnnotatedMethods(XmlSyntaxError.class);
+ }
+
+ private RubyClass createXmlModule(Ruby ruby, RubyModule xmlModule) {
+ RubyClass node = xmlModule.defineClassUnder("Node", ruby.getObject(), XML_NODE_ALLOCATOR);
+ node.defineAnnotatedMethods(XmlNode.class);
+
+ RubyClass attr = xmlModule.defineClassUnder("Attr", node, XML_ATTR_ALLOCATOR);
+ attr.defineAnnotatedMethods(XmlAttr.class);
+
+ RubyClass attrDecl = xmlModule.defineClassUnder("AttributeDecl", node, XML_ATTRIBUTE_DECL_ALLOCATOR);
+ attrDecl.defineAnnotatedMethods(XmlAttributeDecl.class);
+
+ RubyClass characterData = xmlModule.defineClassUnder("CharacterData", node, null);
+
+ RubyClass comment = xmlModule.defineClassUnder("Comment", characterData, XML_COMMENT_ALLOCATOR);
+ comment.defineAnnotatedMethods(XmlComment.class);
+
+ RubyClass text = xmlModule.defineClassUnder("Text", characterData, XML_TEXT_ALLOCATOR);
+ text.defineAnnotatedMethods(XmlText.class);
+
+ RubyModule cdata = xmlModule.defineClassUnder("CDATA", text, XML_CDATA_ALLOCATOR);
+ cdata.defineAnnotatedMethods(XmlCdata.class);
+
+ RubyClass dtd = xmlModule.defineClassUnder("DTD", node, XML_DTD_ALLOCATOR);
+ dtd.defineAnnotatedMethods(XmlDtd.class);
+
+ RubyClass documentFragment = xmlModule.defineClassUnder("DocumentFragment", node, XML_DOCUMENT_FRAGMENT_ALLOCATOR);
+ documentFragment.defineAnnotatedMethods(XmlDocumentFragment.class);
+
+ RubyClass element = xmlModule.defineClassUnder("Element", node, XML_ELEMENT_ALLOCATOR);
+ element.defineAnnotatedMethods(XmlElement.class);
+
+ RubyClass elementContent = xmlModule.defineClassUnder("ElementContent", ruby.getObject(), XML_ELEMENT_CONTENT_ALLOCATOR);
+ elementContent.defineAnnotatedMethods(XmlElementContent.class);
+
+ RubyClass elementDecl = xmlModule.defineClassUnder("ElementDecl", node, XML_ELEMENT_DECL_ALLOCATOR);
+ elementDecl.defineAnnotatedMethods(XmlElementDecl.class);
+
+ RubyClass entityDecl = xmlModule.defineClassUnder("EntityDecl", node, XML_ENTITY_DECL_ALLOCATOR);
+ entityDecl.defineAnnotatedMethods(XmlEntityDecl.class);
+
+ entityDecl.defineConstant("INTERNAL_GENERAL", RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_GENERAL));
+ entityDecl.defineConstant("EXTERNAL_GENERAL_PARSED", RubyFixnum.newFixnum(ruby, XmlEntityDecl.EXTERNAL_GENERAL_PARSED));
+ entityDecl.defineConstant("EXTERNAL_GENERAL_UNPARSED", RubyFixnum.newFixnum(ruby, XmlEntityDecl.EXTERNAL_GENERAL_UNPARSED));
+ entityDecl.defineConstant("INTERNAL_PARAMETER", RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_PARAMETER));
+ entityDecl.defineConstant("EXTERNAL_PARAMETER", RubyFixnum.newFixnum(ruby, XmlEntityDecl.EXTERNAL_PARAMETER));
+ entityDecl.defineConstant("INTERNAL_PREDEFINED", RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_PREDEFINED));
+
+ RubyClass entref = xmlModule.defineClassUnder("EntityReference", node, XML_ENTITY_REFERENCE_ALLOCATOR);
+ entref.defineAnnotatedMethods(XmlEntityReference.class);
+
+ RubyClass namespace = xmlModule.defineClassUnder("Namespace", ruby.getObject(), XML_NAMESPACE_ALLOCATOR);
+ namespace.defineAnnotatedMethods(XmlNamespace.class);
+
+ RubyClass nodeSet = xmlModule.defineClassUnder("NodeSet", ruby.getObject(), XML_NODESET_ALLOCATOR);
+ nodeSet.defineAnnotatedMethods(XmlNodeSet.class);
+
+ RubyClass pi = xmlModule.defineClassUnder("ProcessingInstruction", node, XML_PROCESSING_INSTRUCTION_ALLOCATOR);
+ pi.defineAnnotatedMethods(XmlProcessingInstruction.class);
+
+ RubyClass reader = xmlModule.defineClassUnder("Reader", ruby.getObject(), XML_READER_ALLOCATOR);
+ reader.defineAnnotatedMethods(XmlReader.class);
+
+ RubyClass schema = xmlModule.defineClassUnder("Schema", ruby.getObject(), XML_SCHEMA_ALLOCATOR);
+ schema.defineAnnotatedMethods(XmlSchema.class);
+
+ RubyClass relaxng = xmlModule.defineClassUnder("RelaxNG", schema, XML_RELAXNG_ALLOCATOR);
+ relaxng.defineAnnotatedMethods(XmlRelaxng.class);
+
+ RubyClass xpathContext = xmlModule.defineClassUnder("XPathContext", ruby.getObject(), XML_XPATHCONTEXT_ALLOCATOR);
+ xpathContext.defineAnnotatedMethods(XmlXpathContext.class);
+
+ return node;
+ }
+
+ private void createHtmlModule(Ruby ruby, RubyModule htmlModule) {
+ RubyClass htmlElemDesc = htmlModule.defineClassUnder("ElementDescription", ruby.getObject(), HTML_ELEMENT_DESCRIPTION_ALLOCATOR);
+ htmlElemDesc.defineAnnotatedMethods(HtmlElementDescription.class);
+
+ RubyClass htmlEntityLookup = htmlModule.defineClassUnder("EntityLookup", ruby.getObject(), HTML_ENTITY_LOOKUP_ALLOCATOR);
+ htmlEntityLookup.defineAnnotatedMethods(HtmlEntityLookup.class);
+ }
+
+ private void createDocuments(Ruby ruby, RubyModule xmlModule, RubyModule htmlModule, RubyClass node) {
+ RubyClass xmlDocument = xmlModule.defineClassUnder("Document", node, XML_DOCUMENT_ALLOCATOR);
+ xmlDocument.defineAnnotatedMethods(XmlDocument.class);
+
+ //RubyModule htmlDoc = html.defineOrGetClassUnder("Document", document);
+ RubyModule htmlDocument = htmlModule.defineClassUnder("Document", xmlDocument, HTML_DOCUMENT_ALLOCATOR);
+ htmlDocument.defineAnnotatedMethods(HtmlDocument.class);
+ }
+
+ private void createSaxModule(Ruby ruby, RubyModule xmlSaxModule, RubyModule htmlSaxModule) {
+ RubyClass xmlSaxParserContext = xmlSaxModule.defineClassUnder("ParserContext", ruby.getObject(), XML_SAXPARSER_CONTEXT_ALLOCATOR);
+ xmlSaxParserContext.defineAnnotatedMethods(XmlSaxParserContext.class);
+
+ RubyClass xmlSaxPushParser = xmlSaxModule.defineClassUnder("PushParser", ruby.getObject(), XML_SAXPUSHPARSER_ALLOCATOR);
+ xmlSaxPushParser.defineAnnotatedMethods(XmlSaxPushParser.class);
+
+ RubyClass htmlSaxParserContext = htmlSaxModule.defineClassUnder("ParserContext", xmlSaxParserContext, HTML_SAXPARSER_CONTEXT_ALLOCATOR);
+ htmlSaxParserContext.defineAnnotatedMethods(HtmlSaxParserContext.class);
+ }
+
+ private void createXsltModule(Ruby ruby, RubyModule xsltModule) {
+ RubyClass stylesheet = xsltModule.defineClassUnder("Stylesheet", ruby.getObject(), XSLT_STYLESHEET_ALLOCATOR);
+ stylesheet.defineAnnotatedMethods(XsltStylesheet.class);
+ xsltModule.defineAnnotatedMethod(XsltStylesheet.class, "register");
+ }
+
+ private static ObjectAllocator ENCODING_HANDLER_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new EncodingHandler(runtime, klazz, "");
+ }
+ };
+
+ public static final ObjectAllocator HTML_DOCUMENT_ALLOCATOR = new ObjectAllocator() {
+ private HtmlDocument htmlDocument = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (htmlDocument == null) htmlDocument = new HtmlDocument(runtime, klazz);
+ try {
+ HtmlDocument clone = (HtmlDocument) htmlDocument.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new HtmlDocument(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator HTML_SAXPARSER_CONTEXT_ALLOCATOR = new ObjectAllocator() {
+ private HtmlSaxParserContext htmlSaxParserContext = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (htmlSaxParserContext == null) htmlSaxParserContext = new HtmlSaxParserContext(runtime, klazz);
+ try {
+ HtmlSaxParserContext clone = (HtmlSaxParserContext) htmlSaxParserContext.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new HtmlSaxParserContext(runtime, klazz);
+ }
+ }
+ };
+
+ private static ObjectAllocator HTML_ELEMENT_DESCRIPTION_ALLOCATOR =
+ new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new HtmlElementDescription(runtime, klazz);
+ }
+ };
+
+ private static ObjectAllocator HTML_ENTITY_LOOKUP_ALLOCATOR =
+ new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new HtmlEntityLookup(runtime, klazz);
+ }
+ };
+
+ public static final ObjectAllocator XML_ATTR_ALLOCATOR = new ObjectAllocator() {
+ private XmlAttr xmlAttr = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlAttr == null) xmlAttr = new XmlAttr(runtime, klazz);
+ try {
+ XmlAttr clone = (XmlAttr) xmlAttr.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlAttr(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_CDATA_ALLOCATOR = new ObjectAllocator() {
+ private XmlCdata xmlCdata = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlCdata == null) xmlCdata = new XmlCdata(runtime, klazz);
+ try {
+ XmlCdata clone = (XmlCdata) xmlCdata.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlCdata(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_COMMENT_ALLOCATOR = new ObjectAllocator() {
+ private XmlComment xmlComment = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlComment == null) xmlComment = new XmlComment(runtime, klazz);
+ try {
+ XmlComment clone = (XmlComment) xmlComment.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlComment(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_DOCUMENT_ALLOCATOR = new ObjectAllocator() {
+ private XmlDocument xmlDocument = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlDocument == null) xmlDocument = new XmlDocument(runtime, klazz);
+ try {
+ XmlDocument clone = (XmlDocument) xmlDocument.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlDocument(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_DOCUMENT_FRAGMENT_ALLOCATOR = new ObjectAllocator() {
+ private XmlDocumentFragment xmlDocumentFragment = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlDocumentFragment == null) xmlDocumentFragment = new XmlDocumentFragment(runtime, klazz);
+ try {
+ XmlDocumentFragment clone = (XmlDocumentFragment)xmlDocumentFragment.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlDocumentFragment(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_DTD_ALLOCATOR = new ObjectAllocator() {
+ private XmlDtd xmlDtd = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlDtd == null) xmlDtd = new XmlDtd(runtime, klazz);
+ try {
+ XmlDtd clone = (XmlDtd)xmlDtd.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlDtd(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_ELEMENT_ALLOCATOR = new ObjectAllocator() {
+ private XmlElement xmlElement = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlElement == null) xmlElement = new XmlElement(runtime, klazz);
+ try {
+ XmlElement clone = (XmlElement)xmlElement.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlElement(runtime, klazz);
+ }
+ }
+ };
+
+ public static ObjectAllocator XML_ELEMENT_DECL_ALLOCATOR = new ObjectAllocator() {
+ private XmlElementDecl xmlElementDecl = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlElementDecl == null) xmlElementDecl = new XmlElementDecl(runtime, klazz);
+ try {
+ XmlElementDecl clone = (XmlElementDecl)xmlElementDecl.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlElementDecl(runtime, klazz);
+ }
+ }
+ };
+
+ public static ObjectAllocator XML_ENTITY_REFERENCE_ALLOCATOR = new ObjectAllocator() {
+ private XmlEntityReference xmlEntityRef = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlEntityRef == null) xmlEntityRef = new XmlEntityReference(runtime, klazz);
+ try {
+ XmlEntityReference clone = (XmlEntityReference)xmlEntityRef.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlEntityReference(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_NAMESPACE_ALLOCATOR = new ObjectAllocator() {
+ private XmlNamespace xmlNamespace = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlNamespace == null) xmlNamespace = new XmlNamespace(runtime, klazz);
+ try {
+ XmlNamespace clone = (XmlNamespace) xmlNamespace.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlNamespace(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_NODE_ALLOCATOR = new ObjectAllocator() {
+ private XmlNode xmlNode = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlNode == null) xmlNode = new XmlNode(runtime, klazz);
+ try {
+ XmlNode clone = (XmlNode) xmlNode.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlNode(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_NODESET_ALLOCATOR = new ObjectAllocator() {
+ private XmlNodeSet xmlNodeSet = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlNodeSet == null) xmlNodeSet = new XmlNodeSet(runtime, klazz);
+ try {
+ XmlNodeSet clone = (XmlNodeSet) xmlNodeSet.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ xmlNodeSet = new XmlNodeSet(runtime, klazz);
+ xmlNodeSet.setNodes(RubyArray.newEmptyArray(runtime));
+ return xmlNodeSet;
+ }
+ }
+ };
+
+ public static ObjectAllocator XML_PROCESSING_INSTRUCTION_ALLOCATOR = new ObjectAllocator() {
+ private XmlProcessingInstruction xmlProcessingInstruction = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlProcessingInstruction == null) xmlProcessingInstruction = new XmlProcessingInstruction(runtime, klazz);
+ try {
+ XmlProcessingInstruction clone = (XmlProcessingInstruction)xmlProcessingInstruction.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlProcessingInstruction(runtime, klazz);
+ }
+ }
+ };
+
+ public static ObjectAllocator XML_READER_ALLOCATOR = new ObjectAllocator() {
+ private XmlReader xmlReader = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlReader == null) xmlReader = new XmlReader(runtime, klazz);
+ try {
+ XmlReader clone = (XmlReader) xmlReader.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ xmlReader = new XmlReader(runtime, klazz);
+ return xmlReader;
+ }
+ }
+ };
+
+ private static ObjectAllocator XML_ATTRIBUTE_DECL_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new XmlAttributeDecl(runtime, klazz);
+ }
+ };
+
+ private static ObjectAllocator XML_ENTITY_DECL_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new XmlEntityDecl(runtime, klazz);
+ }
+ };
+
+ private static ObjectAllocator XML_ELEMENT_CONTENT_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ throw runtime.newNotImplementedError("not implemented");
+ }
+ };
+
+ public static final ObjectAllocator XML_RELAXNG_ALLOCATOR = new ObjectAllocator() {
+ private XmlRelaxng xmlRelaxng = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlRelaxng == null) xmlRelaxng = new XmlRelaxng(runtime, klazz);
+ try {
+ XmlRelaxng clone = (XmlRelaxng) xmlRelaxng.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlRelaxng(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_SAXPARSER_CONTEXT_ALLOCATOR = new ObjectAllocator() {
+ private XmlSaxParserContext xmlSaxParserContext = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlSaxParserContext == null) xmlSaxParserContext = new XmlSaxParserContext(runtime, klazz);
+ try {
+ XmlSaxParserContext clone = (XmlSaxParserContext) xmlSaxParserContext.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlSaxParserContext(runtime, klazz);
+ }
+ }
+ };
+
+ private static ObjectAllocator XML_SAXPUSHPARSER_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new XmlSaxPushParser(runtime, klazz);
+ }
+ };
+
+ public static final ObjectAllocator XML_SCHEMA_ALLOCATOR = new ObjectAllocator() {
+ private XmlSchema xmlSchema = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlSchema == null) xmlSchema = new XmlSchema(runtime, klazz);
+ try {
+ XmlSchema clone = (XmlSchema) xmlSchema.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlSchema(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_SYNTAXERROR_ALLOCATOR = new ObjectAllocator() {
+ private XmlSyntaxError xmlSyntaxError = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlSyntaxError == null) xmlSyntaxError = new XmlSyntaxError(runtime, klazz);
+ try {
+ XmlSyntaxError clone = (XmlSyntaxError) xmlSyntaxError.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlSyntaxError(runtime, klazz);
+ }
+ }
+ };
+
+ public static final ObjectAllocator XML_TEXT_ALLOCATOR = new ObjectAllocator() {
+ private XmlText xmlText = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlText == null) xmlText = new XmlText(runtime, klazz);
+ try {
+ XmlText clone = (XmlText) xmlText.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlText(runtime, klazz);
+ }
+ }
+ };
+
+ public static ObjectAllocator XML_XPATHCONTEXT_ALLOCATOR = new ObjectAllocator() {
+ private XmlXpathContext xmlXpathContext = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xmlXpathContext == null) xmlXpathContext = new XmlXpathContext(runtime, klazz);
+ try {
+ XmlXpathContext clone = (XmlXpathContext) xmlXpathContext.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlXpathContext(runtime, klazz);
+ }
+ }
+ };
+
+ public static ObjectAllocator XSLT_STYLESHEET_ALLOCATOR = new ObjectAllocator() {
+ private XsltStylesheet xsltStylesheet = null;
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ if (xsltStylesheet == null) xsltStylesheet = new XsltStylesheet(runtime, klazz);
+ try {
+ XsltStylesheet clone = (XsltStylesheet) xsltStylesheet.clone();
+ clone.setMetaClass(klazz);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return new XmlText(runtime, klazz);
+ }
+ }
+ };
+}
diff --git a/samples/java/clojure-type.java b/samples/java/clojure-type.java
new file mode 100644
index 0000000..4cde14a
--- /dev/null
+++ b/samples/java/clojure-type.java
@@ -0,0 +1,872 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * A Java type. This class can be used to make it easier to manipulate type and
+ * method descriptors.
+ *
+ * @author Eric Bruneton
+ * @author Chris Nokleberg
+ */
+public class Type{
+
+/**
+ * The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
+ */
+public final static int VOID = 0;
+
+/**
+ * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
+ */
+public final static int BOOLEAN = 1;
+
+/**
+ * The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
+ */
+public final static int CHAR = 2;
+
+/**
+ * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
+ */
+public final static int BYTE = 3;
+
+/**
+ * The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
+ */
+public final static int SHORT = 4;
+
+/**
+ * The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
+ */
+public final static int INT = 5;
+
+/**
+ * The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
+ */
+public final static int FLOAT = 6;
+
+/**
+ * The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
+ */
+public final static int LONG = 7;
+
+/**
+ * The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
+ */
+public final static int DOUBLE = 8;
+
+/**
+ * The sort of array reference types. See {@link #getSort getSort}.
+ */
+public final static int ARRAY = 9;
+
+/**
+ * The sort of object reference type. See {@link #getSort getSort}.
+ */
+public final static int OBJECT = 10;
+
+/**
+ * The <tt>void</tt> type.
+ */
+public final static Type VOID_TYPE = new Type(VOID);
+
+/**
+ * The <tt>boolean</tt> type.
+ */
+public final static Type BOOLEAN_TYPE = new Type(BOOLEAN);
+
+/**
+ * The <tt>char</tt> type.
+ */
+public final static Type CHAR_TYPE = new Type(CHAR);
+
+/**
+ * The <tt>byte</tt> type.
+ */
+public final static Type BYTE_TYPE = new Type(BYTE);
+
+/**
+ * The <tt>short</tt> type.
+ */
+public final static Type SHORT_TYPE = new Type(SHORT);
+
+/**
+ * The <tt>int</tt> type.
+ */
+public final static Type INT_TYPE = new Type(INT);
+
+/**
+ * The <tt>float</tt> type.
+ */
+public final static Type FLOAT_TYPE = new Type(FLOAT);
+
+/**
+ * The <tt>long</tt> type.
+ */
+public final static Type LONG_TYPE = new Type(LONG);
+
+/**
+ * The <tt>double</tt> type.
+ */
+public final static Type DOUBLE_TYPE = new Type(DOUBLE);
+
+// ------------------------------------------------------------------------
+// Fields
+// ------------------------------------------------------------------------
+
+/**
+ * The sort of this Java type.
+ */
+private final int sort;
+
+/**
+ * A buffer containing the descriptor of this Java type. This field is only
+ * used for reference types.
+ */
+private char[] buf;
+
+/**
+ * The offset of the descriptor of this Java type in {@link #buf buf}. This
+ * field is only used for reference types.
+ */
+private int off;
+
+/**
+ * The length of the descriptor of this Java type.
+ */
+private int len;
+
+// ------------------------------------------------------------------------
+// Constructors
+// ------------------------------------------------------------------------
+
+/**
+ * Constructs a primitive type.
+ *
+ * @param sort the sort of the primitive type to be constructed.
+ */
+private Type(final int sort){
+ this.sort = sort;
+ this.len = 1;
+}
+
+/**
+ * Constructs a reference type.
+ *
+ * @param sort the sort of the reference type to be constructed.
+ * @param buf a buffer containing the descriptor of the previous type.
+ * @param off the offset of this descriptor in the previous buffer.
+ * @param len the length of this descriptor.
+ */
+private Type(final int sort, final char[] buf, final int off, final int len){
+ this.sort = sort;
+ this.buf = buf;
+ this.off = off;
+ this.len = len;
+}
+
+/**
+ * Returns the Java type corresponding to the given type descriptor.
+ *
+ * @param typeDescriptor a type descriptor.
+ * @return the Java type corresponding to the given type descriptor.
+ */
+public static Type getType(final String typeDescriptor){
+ return getType(typeDescriptor.toCharArray(), 0);
+}
+
+/**
+ * Returns the Java type corresponding to the given class.
+ *
+ * @param c a class.
+ * @return the Java type corresponding to the given class.
+ */
+public static Type getType(final Class c){
+ if(c.isPrimitive())
+ {
+ if(c == Integer.TYPE)
+ {
+ return INT_TYPE;
+ }
+ else if(c == Void.TYPE)
+ {
+ return VOID_TYPE;
+ }
+ else if(c == Boolean.TYPE)
+ {
+ return BOOLEAN_TYPE;
+ }
+ else if(c == Byte.TYPE)
+ {
+ return BYTE_TYPE;
+ }
+ else if(c == Character.TYPE)
+ {
+ return CHAR_TYPE;
+ }
+ else if(c == Short.TYPE)
+ {
+ return SHORT_TYPE;
+ }
+ else if(c == Double.TYPE)
+ {
+ return DOUBLE_TYPE;
+ }
+ else if(c == Float.TYPE)
+ {
+ return FLOAT_TYPE;
+ }
+ else /* if (c == Long.TYPE) */
+ {
+ return LONG_TYPE;
+ }
+ }
+ else
+ {
+ return getType(getDescriptor(c));
+ }
+}
+
+/**
+ * Returns the {@link Type#OBJECT} type for the given internal class name.
+ * This is a shortcut method for <code>Type.getType("L"+name+";")</code>.
+ * <i>Note that opposed to {@link Type#getType(String)}, this method takes
+ * internal class names and not class descriptor.</i>
+ *
+ * @param name an internal class name.
+ * @return the the {@link Type#OBJECT} type for the given class name.
+ */
+public static Type getObjectType(String name){
+ int l = name.length();
+ char[] buf = new char[l + 2];
+ buf[0] = 'L';
+ buf[l + 1] = ';';
+ name.getChars(0, l, buf, 1);
+ return new Type(OBJECT, buf, 0, l + 2);
+}
+
+/**
+ * Returns the Java types corresponding to the argument types of the given
+ * method descriptor.
+ *
+ * @param methodDescriptor a method descriptor.
+ * @return the Java types corresponding to the argument types of the given
+ * method descriptor.
+ */
+public static Type[] getArgumentTypes(final String methodDescriptor){
+ char[] buf = methodDescriptor.toCharArray();
+ int off = 1;
+ int size = 0;
+ while(true)
+ {
+ char car = buf[off++];
+ if(car == ')')
+ {
+ break;
+ }
+ else if(car == 'L')
+ {
+ while(buf[off++] != ';')
+ {
+ }
+ ++size;
+ }
+ else if(car != '[')
+ {
+ ++size;
+ }
+ }
+ Type[] args = new Type[size];
+ off = 1;
+ size = 0;
+ while(buf[off] != ')')
+ {
+ args[size] = getType(buf, off);
+ off += args[size].len;
+ size += 1;
+ }
+ return args;
+}
+
+/**
+ * Returns the Java types corresponding to the argument types of the given
+ * method.
+ *
+ * @param method a method.
+ * @return the Java types corresponding to the argument types of the given
+ * method.
+ */
+public static Type[] getArgumentTypes(final Method method){
+ Class[] classes = method.getParameterTypes();
+ Type[] types = new Type[classes.length];
+ for(int i = classes.length - 1; i >= 0; --i)
+ {
+ types[i] = getType(classes[i]);
+ }
+ return types;
+}
+
+/**
+ * Returns the Java type corresponding to the return type of the given
+ * method descriptor.
+ *
+ * @param methodDescriptor a method descriptor.
+ * @return the Java type corresponding to the return type of the given
+ * method descriptor.
+ */
+public static Type getReturnType(final String methodDescriptor){
+ char[] buf = methodDescriptor.toCharArray();
+ return getType(buf, methodDescriptor.indexOf(')') + 1);
+}
+
+/**
+ * Returns the Java type corresponding to the return type of the given
+ * method.
+ *
+ * @param method a method.
+ * @return the Java type corresponding to the return type of the given
+ * method.
+ */
+public static Type getReturnType(final Method method){
+ return getType(method.getReturnType());
+}
+
+/**
+ * Returns the Java type corresponding to the given type descriptor.
+ *
+ * @param buf a buffer containing a type descriptor.
+ * @param off the offset of this descriptor in the previous buffer.
+ * @return the Java type corresponding to the given type descriptor.
+ */
+private static Type getType(final char[] buf, final int off){
+ int len;
+ switch(buf[off])
+ {
+ case'V':
+ return VOID_TYPE;
+ case'Z':
+ return BOOLEAN_TYPE;
+ case'C':
+ return CHAR_TYPE;
+ case'B':
+ return BYTE_TYPE;
+ case'S':
+ return SHORT_TYPE;
+ case'I':
+ return INT_TYPE;
+ case'F':
+ return FLOAT_TYPE;
+ case'J':
+ return LONG_TYPE;
+ case'D':
+ return DOUBLE_TYPE;
+ case'[':
+ len = 1;
+ while(buf[off + len] == '[')
+ {
+ ++len;
+ }
+ if(buf[off + len] == 'L')
+ {
+ ++len;
+ while(buf[off + len] != ';')
+ {
+ ++len;
+ }
+ }
+ return new Type(ARRAY, buf, off, len + 1);
+ // case 'L':
+ default:
+ len = 1;
+ while(buf[off + len] != ';')
+ {
+ ++len;
+ }
+ return new Type(OBJECT, buf, off, len + 1);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Accessors
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the sort of this Java type.
+ *
+ * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN},
+ * {@link #CHAR CHAR}, {@link #BYTE BYTE}, {@link #SHORT SHORT},
+ * {@link #INT INT}, {@link #FLOAT FLOAT}, {@link #LONG LONG},
+ * {@link #DOUBLE DOUBLE}, {@link #ARRAY ARRAY} or
+ * {@link #OBJECT OBJECT}.
+ */
+public int getSort(){
+ return sort;
+}
+
+/**
+ * Returns the number of dimensions of this array type. This method should
+ * only be used for an array type.
+ *
+ * @return the number of dimensions of this array type.
+ */
+public int getDimensions(){
+ int i = 1;
+ while(buf[off + i] == '[')
+ {
+ ++i;
+ }
+ return i;
+}
+
+/**
+ * Returns the type of the elements of this array type. This method should
+ * only be used for an array type.
+ *
+ * @return Returns the type of the elements of this array type.
+ */
+public Type getElementType(){
+ return getType(buf, off + getDimensions());
+}
+
+/**
+ * Returns the name of the class corresponding to this type.
+ *
+ * @return the fully qualified name of the class corresponding to this type.
+ */
+public String getClassName(){
+ switch(sort)
+ {
+ case VOID:
+ return "void";
+ case BOOLEAN:
+ return "boolean";
+ case CHAR:
+ return "char";
+ case BYTE:
+ return "byte";
+ case SHORT:
+ return "short";
+ case INT:
+ return "int";
+ case FLOAT:
+ return "float";
+ case LONG:
+ return "long";
+ case DOUBLE:
+ return "double";
+ case ARRAY:
+ StringBuffer b = new StringBuffer(getElementType().getClassName());
+ for(int i = getDimensions(); i > 0; --i)
+ {
+ b.append("[]");
+ }
+ return b.toString();
+ // case OBJECT:
+ default:
+ return new String(buf, off + 1, len - 2).replace('/', '.');
+ }
+}
+
+/**
+ * Returns the internal name of the class corresponding to this object type.
+ * The internal name of a class is its fully qualified name, where '.' are
+ * replaced by '/'. This method should only be used for an object type.
+ *
+ * @return the internal name of the class corresponding to this object type.
+ */
+public String getInternalName(){
+ return new String(buf, off + 1, len - 2);
+}
+
+// ------------------------------------------------------------------------
+// Conversion to type descriptors
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the descriptor corresponding to this Java type.
+ *
+ * @return the descriptor corresponding to this Java type.
+ */
+public String getDescriptor(){
+ StringBuffer buf = new StringBuffer();
+ getDescriptor(buf);
+ return buf.toString();
+}
+
+/**
+ * Returns the descriptor corresponding to the given argument and return
+ * types.
+ *
+ * @param returnType the return type of the method.
+ * @param argumentTypes the argument types of the method.
+ * @return the descriptor corresponding to the given argument and return
+ * types.
+ */
+public static String getMethodDescriptor(
+ final Type returnType,
+ final Type[] argumentTypes){
+ StringBuffer buf = new StringBuffer();
+ buf.append('(');
+ for(int i = 0; i < argumentTypes.length; ++i)
+ {
+ argumentTypes[i].getDescriptor(buf);
+ }
+ buf.append(')');
+ returnType.getDescriptor(buf);
+ return buf.toString();
+}
+
+/**
+ * Appends the descriptor corresponding to this Java type to the given
+ * string buffer.
+ *
+ * @param buf the string buffer to which the descriptor must be appended.
+ */
+private void getDescriptor(final StringBuffer buf){
+ switch(sort)
+ {
+ case VOID:
+ buf.append('V');
+ return;
+ case BOOLEAN:
+ buf.append('Z');
+ return;
+ case CHAR:
+ buf.append('C');
+ return;
+ case BYTE:
+ buf.append('B');
+ return;
+ case SHORT:
+ buf.append('S');
+ return;
+ case INT:
+ buf.append('I');
+ return;
+ case FLOAT:
+ buf.append('F');
+ return;
+ case LONG:
+ buf.append('J');
+ return;
+ case DOUBLE:
+ buf.append('D');
+ return;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ buf.append(this.buf, off, len);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Direct conversion from classes to type descriptors,
+// without intermediate Type objects
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the internal name of the given class. The internal name of a
+ * class is its fully qualified name, where '.' are replaced by '/'.
+ *
+ * @param c an object class.
+ * @return the internal name of the given class.
+ */
+public static String getInternalName(final Class c){
+ return c.getName().replace('.', '/');
+}
+
+/**
+ * Returns the descriptor corresponding to the given Java type.
+ *
+ * @param c an object class, a primitive class or an array class.
+ * @return the descriptor corresponding to the given class.
+ */
+public static String getDescriptor(final Class c){
+ StringBuffer buf = new StringBuffer();
+ getDescriptor(buf, c);
+ return buf.toString();
+}
+
+/**
+ * Returns the descriptor corresponding to the given constructor.
+ *
+ * @param c a {@link Constructor Constructor} object.
+ * @return the descriptor of the given constructor.
+ */
+public static String getConstructorDescriptor(final Constructor c){
+ Class[] parameters = c.getParameterTypes();
+ StringBuffer buf = new StringBuffer();
+ buf.append('(');
+ for(int i = 0; i < parameters.length; ++i)
+ {
+ getDescriptor(buf, parameters[i]);
+ }
+ return buf.append(")V").toString();
+}
+
+/**
+ * Returns the descriptor corresponding to the given method.
+ *
+ * @param m a {@link Method Method} object.
+ * @return the descriptor of the given method.
+ */
+public static String getMethodDescriptor(final Method m){
+ Class[] parameters = m.getParameterTypes();
+ StringBuffer buf = new StringBuffer();
+ buf.append('(');
+ for(int i = 0; i < parameters.length; ++i)
+ {
+ getDescriptor(buf, parameters[i]);
+ }
+ buf.append(')');
+ getDescriptor(buf, m.getReturnType());
+ return buf.toString();
+}
+
+/**
+ * Appends the descriptor of the given class to the given string buffer.
+ *
+ * @param buf the string buffer to which the descriptor must be appended.
+ * @param c the class whose descriptor must be computed.
+ */
+private static void getDescriptor(final StringBuffer buf, final Class c){
+ Class d = c;
+ while(true)
+ {
+ if(d.isPrimitive())
+ {
+ char car;
+ if(d == Integer.TYPE)
+ {
+ car = 'I';
+ }
+ else if(d == Void.TYPE)
+ {
+ car = 'V';
+ }
+ else if(d == Boolean.TYPE)
+ {
+ car = 'Z';
+ }
+ else if(d == Byte.TYPE)
+ {
+ car = 'B';
+ }
+ else if(d == Character.TYPE)
+ {
+ car = 'C';
+ }
+ else if(d == Short.TYPE)
+ {
+ car = 'S';
+ }
+ else if(d == Double.TYPE)
+ {
+ car = 'D';
+ }
+ else if(d == Float.TYPE)
+ {
+ car = 'F';
+ }
+ else /* if (d == Long.TYPE) */
+ {
+ car = 'J';
+ }
+ buf.append(car);
+ return;
+ }
+ else if(d.isArray())
+ {
+ buf.append('[');
+ d = d.getComponentType();
+ }
+ else
+ {
+ buf.append('L');
+ String name = d.getName();
+ int len = name.length();
+ for(int i = 0; i < len; ++i)
+ {
+ char car = name.charAt(i);
+ buf.append(car == '.' ? '/' : car);
+ }
+ buf.append(';');
+ return;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+// Corresponding size and opcodes
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the size of values of this type.
+ *
+ * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
+ * <tt>double</tt>, and 1 otherwise.
+ */
+public int getSize(){
+ return sort == LONG || sort == DOUBLE ? 2 : 1;
+}
+
+/**
+ * Returns a JVM instruction opcode adapted to this Java type.
+ *
+ * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD,
+ * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL,
+ * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
+ * @return an opcode that is similar to the given opcode, but adapted to
+ * this Java type. For example, if this type is <tt>float</tt> and
+ * <tt>opcode</tt> is IRETURN, this method returns FRETURN.
+ */
+public int getOpcode(final int opcode){
+ if(opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE)
+ {
+ switch(sort)
+ {
+ case BOOLEAN:
+ case BYTE:
+ return opcode + 5;
+ case CHAR:
+ return opcode + 6;
+ case SHORT:
+ return opcode + 7;
+ case INT:
+ return opcode;
+ case FLOAT:
+ return opcode + 2;
+ case LONG:
+ return opcode + 1;
+ case DOUBLE:
+ return opcode + 3;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ return opcode + 4;
+ }
+ }
+ else
+ {
+ switch(sort)
+ {
+ case VOID:
+ return opcode + 5;
+ case BOOLEAN:
+ case CHAR:
+ case BYTE:
+ case SHORT:
+ case INT:
+ return opcode;
+ case FLOAT:
+ return opcode + 2;
+ case LONG:
+ return opcode + 1;
+ case DOUBLE:
+ return opcode + 3;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ return opcode + 4;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+// Equals, hashCode and toString
+// ------------------------------------------------------------------------
+
+/**
+ * Tests if the given object is equal to this type.
+ *
+ * @param o the object to be compared to this type.
+ * @return <tt>true</tt> if the given object is equal to this type.
+ */
+public boolean equals(final Object o){
+ if(this == o)
+ {
+ return true;
+ }
+ if(!(o instanceof Type))
+ {
+ return false;
+ }
+ Type t = (Type) o;
+ if(sort != t.sort)
+ {
+ return false;
+ }
+ if(sort == Type.OBJECT || sort == Type.ARRAY)
+ {
+ if(len != t.len)
+ {
+ return false;
+ }
+ for(int i = off, j = t.off, end = i + len; i < end; i++, j++)
+ {
+ if(buf[i] != t.buf[j])
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Returns a hash code value for this type.
+ *
+ * @return a hash code value for this type.
+ */
+public int hashCode(){
+ int hc = 13 * sort;
+ if(sort == Type.OBJECT || sort == Type.ARRAY)
+ {
+ for(int i = off, end = i + len; i < end; i++)
+ {
+ hc = 17 * (hc + buf[i]);
+ }
+ }
+ return hc;
+}
+
+/**
+ * Returns a string representation of this type.
+ *
+ * @return the descriptor of this type.
+ */
+public String toString(){
+ return getDescriptor();
+}
+}
diff --git a/samples/java/clojure-util.java b/samples/java/clojure-util.java
new file mode 100644
index 0000000..02b5466
--- /dev/null
+++ b/samples/java/clojure-util.java
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+/* rich Apr 19, 2008 */
+
+package clojure.lang;
+
+import java.lang.ref.Reference;
+import java.math.BigInteger;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.lang.ref.SoftReference;
+import java.lang.ref.ReferenceQueue;
+
+public class Util{
+static public boolean equiv(Object k1, Object k2){
+ if(k1 == k2)
+ return true;
+ if(k1 != null)
+ {
+ if(k1 instanceof Number && k2 instanceof Number)
+ return Numbers.equal((Number)k1, (Number)k2);
+ else if(k1 instanceof IPersistentCollection || k2 instanceof IPersistentCollection)
+ return pcequiv(k1,k2);
+ return k1.equals(k2);
+ }
+ return false;
+}
+
+static public boolean equiv(long k1, long k2){
+ return k1 == k2;
+}
+
+static public boolean equiv(Object k1, long k2){
+ return equiv(k1, (Object)k2);
+}
+
+static public boolean equiv(long k1, Object k2){
+ return equiv((Object)k1, k2);
+}
+
+static public boolean equiv(double k1, double k2){
+ return k1 == k2;
+}
+
+static public boolean equiv(Object k1, double k2){
+ return equiv(k1, (Object)k2);
+}
+
+static public boolean equiv(double k1, Object k2){
+ return equiv((Object)k1, k2);
+}
+
+static public boolean equiv(boolean k1, boolean k2){
+ return k1 == k2;
+}
+
+static public boolean equiv(Object k1, boolean k2){
+ return equiv(k1, (Object)k2);
+}
+
+static public boolean equiv(boolean k1, Object k2){
+ return equiv((Object)k1, k2);
+}
+
+static public boolean equiv(char c1, char c2) {
+ return c1 == c2;
+}
+
+static public boolean pcequiv(Object k1, Object k2){
+ if(k1 instanceof IPersistentCollection)
+ return ((IPersistentCollection)k1).equiv(k2);
+ return ((IPersistentCollection)k2).equiv(k1);
+}
+
+static public boolean equals(Object k1, Object k2){
+ if(k1 == k2)
+ return true;
+ return k1 != null && k1.equals(k2);
+}
+
+static public boolean identical(Object k1, Object k2){
+ return k1 == k2;
+}
+
+static public Class classOf(Object x){
+ if(x != null)
+ return x.getClass();
+ return null;
+}
+
+static public int compare(Object k1, Object k2){
+ if(k1 == k2)
+ return 0;
+ if(k1 != null)
+ {
+ if(k2 == null)
+ return 1;
+ if(k1 instanceof Number)
+ return Numbers.compare((Number) k1, (Number) k2);
+ return ((Comparable) k1).compareTo(k2);
+ }
+ return -1;
+}
+
+static public int hash(Object o){
+ if(o == null)
+ return 0;
+ return o.hashCode();
+}
+
+static public int hasheq(Object o){
+ if(o == null)
+ return 0;
+ if(o instanceof Number)
+ return Numbers.hasheq((Number)o);
+ else if(o instanceof IHashEq)
+ return ((IHashEq)o).hasheq();
+ return o.hashCode();
+}
+
+static public int hashCombine(int seed, int hash){
+ //a la boost
+ seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ return seed;
+}
+
+static public boolean isPrimitive(Class c){
+ return c != null && c.isPrimitive() && !(c == Void.TYPE);
+}
+
+static public boolean isInteger(Object x){
+ return x instanceof Integer
+ || x instanceof Long
+ || x instanceof BigInt
+ || x instanceof BigInteger;
+}
+
+static public Object ret1(Object ret, Object nil){
+ return ret;
+}
+
+static public ISeq ret1(ISeq ret, Object nil){
+ return ret;
+}
+
+static public <K,V> void clearCache(ReferenceQueue rq, ConcurrentHashMap<K, Reference<V>> cache){
+ //cleanup any dead entries
+ if(rq.poll() != null)
+ {
+ while(rq.poll() != null)
+ ;
+ for(Map.Entry<K, Reference<V>> e : cache.entrySet())
+ {
+ Reference<V> val = e.getValue();
+ if(val != null && val.get() == null)
+ cache.remove(e.getKey(), val);
+ }
+ }
+}
+
+static public RuntimeException runtimeException(String s){
+ return new RuntimeException(s);
+}
+
+static public RuntimeException runtimeException(String s, Throwable e){
+ return new RuntimeException(s, e);
+}
+
+/**
+ * Throw even checked exceptions without being required
+ * to declare them or catch them. Suggested idiom:
+ * <p>
+ * <code>throw sneakyThrow( some exception );</code>
+ */
+static public RuntimeException sneakyThrow(Throwable t) {
+ // http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
+ if (t == null)
+ throw new NullPointerException();
+ Util.<RuntimeException>sneakyThrow0(t);
+ return null;
+}
+
+@SuppressWarnings("unchecked")
+static private <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T) t;
+}
+
+}
+
diff --git a/samples/javascript/bootstrap-modal.js b/samples/javascript/bootstrap-modal.js
new file mode 100644
index 0000000..38fd0c8
--- /dev/null
+++ b/samples/javascript/bootstrap-modal.js
@@ -0,0 +1,218 @@
+/* =========================================================
+ * bootstrap-modal.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#modals
+ * =========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* MODAL CLASS DEFINITION
+ * ====================== */
+
+ var Modal = function (content, options) {
+ this.options = options
+ this.$element = $(content)
+ .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
+ }
+
+ Modal.prototype = {
+
+ constructor: Modal
+
+ , toggle: function () {
+ return this[!this.isShown ? 'show' : 'hide']()
+ }
+
+ , show: function () {
+ var that = this
+ , e = $.Event('show')
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ $('body').addClass('modal-open')
+
+ this.isShown = true
+
+ escape.call(this)
+ backdrop.call(this, function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) //don't move modals dom position
+ }
+
+ that.$element
+ .show()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element.addClass('in')
+
+ transition ?
+ that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
+ that.$element.trigger('shown')
+
+ })
+ }
+
+ , hide: function (e) {
+ e && e.preventDefault()
+
+ var that = this
+
+ e = $.Event('hide')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ $('body').removeClass('modal-open')
+
+ escape.call(this)
+
+ this.$element.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ hideWithTransition.call(this) :
+ hideModal.call(this)
+ }
+
+ }
+
+
+ /* MODAL PRIVATE METHODS
+ * ===================== */
+
+ function hideWithTransition() {
+ var that = this
+ , timeout = setTimeout(function () {
+ that.$element.off($.support.transition.end)
+ hideModal.call(that)
+ }, 500)
+
+ this.$element.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ hideModal.call(that)
+ })
+ }
+
+ function hideModal(that) {
+ this.$element
+ .hide()
+ .trigger('hidden')
+
+ backdrop.call(this)
+ }
+
+ function backdrop(callback) {
+ var that = this
+ , animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+ .appendTo(document.body)
+
+ if (this.options.backdrop != 'static') {
+ this.$backdrop.click($.proxy(this.hide, this))
+ }
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ doAnimate ?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade')?
+ this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
+ removeBackdrop.call(this)
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+ function removeBackdrop() {
+ this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ function escape() {
+ var that = this
+ if (this.isShown && this.options.keyboard) {
+ $(document).on('keyup.dismiss.modal', function ( e ) {
+ e.which == 27 && that.hide()
+ })
+ } else if (!this.isShown) {
+ $(document).off('keyup.dismiss.modal')
+ }
+ }
+
+
+ /* MODAL PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.modal = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('modal')
+ , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option]()
+ else if (options.show) data.show()
+ })
+ }
+
+ $.fn.modal.defaults = {
+ backdrop: true
+ , keyboard: true
+ , show: true
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ /* MODAL DATA-API
+ * ============== */
+
+ $(function () {
+ $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
+
+ e.preventDefault()
+ $target.modal(option)
+ })
+ })
+
+}(window.jQuery); \ No newline at end of file
diff --git a/samples/javascript/classes-old.js b/samples/javascript/classes-old.js
new file mode 100644
index 0000000..2e4353f
--- /dev/null
+++ b/samples/javascript/classes-old.js
@@ -0,0 +1,46 @@
+(function() {
+ var Animal, Horse, Snake, sam, tom;
+ var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
+ for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
+ function ctor() { this.constructor = child; }
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor;
+ child.__super__ = parent.prototype;
+ return child;
+ };
+ Animal = (function() {
+ function Animal(name) {
+ this.name = name;
+ }
+ Animal.prototype.move = function(meters) {
+ return alert(this.name + " moved " + meters + "m.");
+ };
+ return Animal;
+ })();
+ Snake = (function() {
+ __extends(Snake, Animal);
+ function Snake() {
+ Snake.__super__.constructor.apply(this, arguments);
+ }
+ Snake.prototype.move = function() {
+ alert("Slithering...");
+ return Snake.__super__.move.call(this, 5);
+ };
+ return Snake;
+ })();
+ Horse = (function() {
+ __extends(Horse, Animal);
+ function Horse() {
+ Horse.__super__.constructor.apply(this, arguments);
+ }
+ Horse.prototype.move = function() {
+ alert("Galloping...");
+ return Horse.__super__.move.call(this, 45);
+ };
+ return Horse;
+ })();
+ sam = new Snake("Sammy the Python");
+ tom = new Horse("Tommy the Palomino");
+ sam.move();
+ tom.move();
+}).call(this);
diff --git a/samples/javascript/classes.js b/samples/javascript/classes.js
new file mode 100644
index 0000000..888e02b
--- /dev/null
+++ b/samples/javascript/classes.js
@@ -0,0 +1,69 @@
+// Generated by CoffeeScript 1.2.1
+(function() {
+ var Animal, Horse, Snake, sam, tom,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
+
+ Animal = (function() {
+
+ Animal.name = 'Animal';
+
+ function Animal(name) {
+ this.name = name;
+ }
+
+ Animal.prototype.move = function(meters) {
+ return alert(this.name + " moved " + meters + "m.");
+ };
+
+ return Animal;
+
+ })();
+
+ Snake = (function(_super) {
+
+ __extends(Snake, _super);
+
+ Snake.name = 'Snake';
+
+ function Snake() {
+ return Snake.__super__.constructor.apply(this, arguments);
+ }
+
+ Snake.prototype.move = function() {
+ alert("Slithering...");
+ return Snake.__super__.move.call(this, 5);
+ };
+
+ return Snake;
+
+ })(Animal);
+
+ Horse = (function(_super) {
+
+ __extends(Horse, _super);
+
+ Horse.name = 'Horse';
+
+ function Horse() {
+ return Horse.__super__.constructor.apply(this, arguments);
+ }
+
+ Horse.prototype.move = function() {
+ alert("Galloping...");
+ return Horse.__super__.move.call(this, 45);
+ };
+
+ return Horse;
+
+ })(Animal);
+
+ sam = new Snake("Sammy the Python");
+
+ tom = new Horse("Tommy the Palomino");
+
+ sam.move();
+
+ tom.move();
+
+}).call(this);
diff --git a/samples/javascript/dude.js b/samples/javascript/dude.js
new file mode 100644
index 0000000..89ac059
--- /dev/null
+++ b/samples/javascript/dude.js
@@ -0,0 +1 @@
+alert("dude!")
diff --git a/samples/javascript/empty.js b/samples/javascript/empty.js
new file mode 100644
index 0000000..f5e757a
--- /dev/null
+++ b/samples/javascript/empty.js
@@ -0,0 +1,3 @@
+(function() {
+
+}).call(this);
diff --git a/samples/javascript/hello.js b/samples/javascript/hello.js
new file mode 100644
index 0000000..c1c58ec
--- /dev/null
+++ b/samples/javascript/hello.js
@@ -0,0 +1,3 @@
+(function() {
+ console.log("Hello, World!");
+}).call(this);
diff --git a/samples/javascript/http.js b/samples/javascript/http.js
new file mode 100644
index 0000000..92e0004
--- /dev/null
+++ b/samples/javascript/http.js
@@ -0,0 +1,1838 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// 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.
+
+var util = require('util');
+var net = require('net');
+var stream = require('stream');
+var url = require('url');
+var EventEmitter = require('events').EventEmitter;
+var FreeList = require('freelist').FreeList;
+var HTTPParser = process.binding('http_parser').HTTPParser;
+var assert = require('assert').ok;
+var END_OF_FILE = {};
+
+
+var debug;
+if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) {
+ debug = function(x) { console.error('HTTP: %s', x); };
+} else {
+ debug = function() { };
+}
+
+// Only called in the slow case where slow means
+// that the request headers were either fragmented
+// across multiple TCP packets or too large to be
+// processed in a single run. This method is also
+// called to process trailing HTTP headers.
+function parserOnHeaders(headers, url) {
+ // Once we exceeded headers limit - stop collecting them
+ if (this.maxHeaderPairs <= 0 ||
+ this._headers.length < this.maxHeaderPairs) {
+ this._headers = this._headers.concat(headers);
+ }
+ this._url += url;
+}
+
+// info.headers and info.url are set only if .onHeaders()
+// has not been called for this request.
+//
+// info.url is not set for response parsers but that's not
+// applicable here since all our parsers are request parsers.
+function parserOnHeadersComplete(info) {
+ var parser = this;
+ var headers = info.headers;
+ var url = info.url;
+
+ if (!headers) {
+ headers = parser._headers;
+ parser._headers = [];
+ }
+
+ if (!url) {
+ url = parser._url;
+ parser._url = '';
+ }
+
+ parser.incoming = new IncomingMessage(parser.socket);
+ parser.incoming.httpVersionMajor = info.versionMajor;
+ parser.incoming.httpVersionMinor = info.versionMinor;
+ parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
+ parser.incoming.url = url;
+
+ var n = headers.length;
+
+ // If parser.maxHeaderPairs <= 0 - assume that there're no limit
+ if (parser.maxHeaderPairs > 0) {
+ n = Math.min(n, parser.maxHeaderPairs);
+ }
+
+ for (var i = 0; i < n; i += 2) {
+ var k = headers[i];
+ var v = headers[i + 1];
+ parser.incoming._addHeaderLine(k, v);
+ }
+
+
+ if (info.method) {
+ // server only
+ parser.incoming.method = info.method;
+ } else {
+ // client only
+ parser.incoming.statusCode = info.statusCode;
+ // CHECKME dead code? we're always a request parser
+ }
+
+ parser.incoming.upgrade = info.upgrade;
+
+ var skipBody = false; // response to HEAD or CONNECT
+
+ if (!info.upgrade) {
+ // For upgraded connections and CONNECT method request,
+ // we'll emit this after parser.execute
+ // so that we can capture the first part of the new protocol
+ skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
+ }
+
+ return skipBody;
+}
+
+function parserOnBody(b, start, len) {
+ var parser = this;
+ var slice = b.slice(start, start + len);
+ if (parser.incoming._paused || parser.incoming._pendings.length) {
+ parser.incoming._pendings.push(slice);
+ } else {
+ parser.incoming._emitData(slice);
+ }
+}
+
+function parserOnMessageComplete() {
+ var parser = this;
+ parser.incoming.complete = true;
+
+ // Emit any trailing headers.
+ var headers = parser._headers;
+ if (headers) {
+ for (var i = 0, n = headers.length; i < n; i += 2) {
+ var k = headers[i];
+ var v = headers[i + 1];
+ parser.incoming._addHeaderLine(k, v);
+ }
+ parser._headers = [];
+ parser._url = '';
+ }
+
+ if (!parser.incoming.upgrade) {
+ // For upgraded connections, also emit this after parser.execute
+ if (parser.incoming._paused || parser.incoming._pendings.length) {
+ parser.incoming._pendings.push(END_OF_FILE);
+ } else {
+ parser.incoming.readable = false;
+ parser.incoming._emitEnd();
+ }
+ }
+
+ if (parser.socket.readable) {
+ // force to read the next incoming message
+ parser.socket.resume();
+ }
+}
+
+
+var parsers = new FreeList('parsers', 1000, function() {
+ var parser = new HTTPParser(HTTPParser.REQUEST);
+
+ parser._headers = [];
+ parser._url = '';
+
+ // Only called in the slow case where slow means
+ // that the request headers were either fragmented
+ // across multiple TCP packets or too large to be
+ // processed in a single run. This method is also
+ // called to process trailing HTTP headers.
+ parser.onHeaders = parserOnHeaders;
+ parser.onHeadersComplete = parserOnHeadersComplete;
+ parser.onBody = parserOnBody;
+ parser.onMessageComplete = parserOnMessageComplete;
+
+ return parser;
+});
+exports.parsers = parsers;
+
+
+var CRLF = '\r\n';
+var STATUS_CODES = exports.STATUS_CODES = {
+ 100 : 'Continue',
+ 101 : 'Switching Protocols',
+ 102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
+ 200 : 'OK',
+ 201 : 'Created',
+ 202 : 'Accepted',
+ 203 : 'Non-Authoritative Information',
+ 204 : 'No Content',
+ 205 : 'Reset Content',
+ 206 : 'Partial Content',
+ 207 : 'Multi-Status', // RFC 4918
+ 300 : 'Multiple Choices',
+ 301 : 'Moved Permanently',
+ 302 : 'Moved Temporarily',
+ 303 : 'See Other',
+ 304 : 'Not Modified',
+ 305 : 'Use Proxy',
+ 307 : 'Temporary Redirect',
+ 400 : 'Bad Request',
+ 401 : 'Unauthorized',
+ 402 : 'Payment Required',
+ 403 : 'Forbidden',
+ 404 : 'Not Found',
+ 405 : 'Method Not Allowed',
+ 406 : 'Not Acceptable',
+ 407 : 'Proxy Authentication Required',
+ 408 : 'Request Time-out',
+ 409 : 'Conflict',
+ 410 : 'Gone',
+ 411 : 'Length Required',
+ 412 : 'Precondition Failed',
+ 413 : 'Request Entity Too Large',
+ 414 : 'Request-URI Too Large',
+ 415 : 'Unsupported Media Type',
+ 416 : 'Requested Range Not Satisfiable',
+ 417 : 'Expectation Failed',
+ 418 : 'I\'m a teapot', // RFC 2324
+ 422 : 'Unprocessable Entity', // RFC 4918
+ 423 : 'Locked', // RFC 4918
+ 424 : 'Failed Dependency', // RFC 4918
+ 425 : 'Unordered Collection', // RFC 4918
+ 426 : 'Upgrade Required', // RFC 2817
+ 428 : 'Precondition Required', // RFC 6585
+ 429 : 'Too Many Requests', // RFC 6585
+ 431 : 'Request Header Fields Too Large',// RFC 6585
+ 500 : 'Internal Server Error',
+ 501 : 'Not Implemented',
+ 502 : 'Bad Gateway',
+ 503 : 'Service Unavailable',
+ 504 : 'Gateway Time-out',
+ 505 : 'HTTP Version not supported',
+ 506 : 'Variant Also Negotiates', // RFC 2295
+ 507 : 'Insufficient Storage', // RFC 4918
+ 509 : 'Bandwidth Limit Exceeded',
+ 510 : 'Not Extended', // RFC 2774
+ 511 : 'Network Authentication Required' // RFC 6585
+};
+
+
+var connectionExpression = /Connection/i;
+var transferEncodingExpression = /Transfer-Encoding/i;
+var closeExpression = /close/i;
+var chunkExpression = /chunk/i;
+var contentLengthExpression = /Content-Length/i;
+var dateExpression = /Date/i;
+var expectExpression = /Expect/i;
+var continueExpression = /100-continue/i;
+
+var dateCache;
+function utcDate() {
+ if (!dateCache) {
+ var d = new Date();
+ dateCache = d.toUTCString();
+ setTimeout(function() {
+ dateCache = undefined;
+ }, 1000 - d.getMilliseconds());
+ }
+ return dateCache;
+}
+
+
+/* Abstract base class for ServerRequest and ClientResponse. */
+function IncomingMessage(socket) {
+ stream.Stream.call(this);
+
+ // TODO Remove one of these eventually.
+ this.socket = socket;
+ this.connection = socket;
+
+ this.httpVersion = null;
+ this.complete = false;
+ this.headers = {};
+ this.trailers = {};
+
+ this.readable = true;
+
+ this._paused = false;
+ this._pendings = [];
+
+ this._endEmitted = false;
+
+ // request (server) only
+ this.url = '';
+
+ this.method = null;
+
+ // response (client) only
+ this.statusCode = null;
+ this.client = this.socket;
+}
+util.inherits(IncomingMessage, stream.Stream);
+
+
+exports.IncomingMessage = IncomingMessage;
+
+
+IncomingMessage.prototype.destroy = function(error) {
+ this.socket.destroy(error);
+};
+
+
+IncomingMessage.prototype.setEncoding = function(encoding) {
+ var StringDecoder = require('string_decoder').StringDecoder; // lazy load
+ this._decoder = new StringDecoder(encoding);
+};
+
+
+IncomingMessage.prototype.pause = function() {
+ this._paused = true;
+ this.socket.pause();
+};
+
+
+IncomingMessage.prototype.resume = function() {
+ this._paused = false;
+ if (this.socket) {
+ this.socket.resume();
+ }
+
+ this._emitPending();
+};
+
+
+IncomingMessage.prototype._emitPending = function(callback) {
+ if (this._pendings.length) {
+ var self = this;
+ process.nextTick(function() {
+ while (!self._paused && self._pendings.length) {
+ var chunk = self._pendings.shift();
+ if (chunk !== END_OF_FILE) {
+ assert(Buffer.isBuffer(chunk));
+ self._emitData(chunk);
+ } else {
+ assert(self._pendings.length === 0);
+ self.readable = false;
+ self._emitEnd();
+ }
+ }
+
+ if (callback) {
+ callback();
+ }
+ });
+ } else if (callback) {
+ callback();
+ }
+};
+
+
+IncomingMessage.prototype._emitData = function(d) {
+ if (this._decoder) {
+ var string = this._decoder.write(d);
+ if (string.length) {
+ this.emit('data', string);
+ }
+ } else {
+ this.emit('data', d);
+ }
+};
+
+
+IncomingMessage.prototype._emitEnd = function() {
+ if (!this._endEmitted) {
+ this.emit('end');
+ }
+
+ this._endEmitted = true;
+};
+
+
+// Add the given (field, value) pair to the message
+//
+// Per RFC2616, section 4.2 it is acceptable to join multiple instances of the
+// same header with a ', ' if the header in question supports specification of
+// multiple values this way. If not, we declare the first instance the winner
+// and drop the second. Extended header fields (those beginning with 'x-') are
+// always joined.
+IncomingMessage.prototype._addHeaderLine = function(field, value) {
+ var dest = this.complete ? this.trailers : this.headers;
+
+ field = field.toLowerCase();
+ switch (field) {
+ // Array headers:
+ case 'set-cookie':
+ if (field in dest) {
+ dest[field].push(value);
+ } else {
+ dest[field] = [value];
+ }
+ break;
+
+ // Comma separate. Maybe make these arrays?
+ case 'accept':
+ case 'accept-charset':
+ case 'accept-encoding':
+ case 'accept-language':
+ case 'connection':
+ case 'cookie':
+ case 'pragma':
+ case 'link':
+ case 'www-authenticate':
+ case 'sec-websocket-extensions':
+ case 'sec-websocket-protocol':
+ if (field in dest) {
+ dest[field] += ', ' + value;
+ } else {
+ dest[field] = value;
+ }
+ break;
+
+
+ default:
+ if (field.slice(0, 2) == 'x-') {
+ // except for x-
+ if (field in dest) {
+ dest[field] += ', ' + value;
+ } else {
+ dest[field] = value;
+ }
+ } else {
+ // drop duplicates
+ if (!(field in dest)) dest[field] = value;
+ }
+ break;
+ }
+};
+
+
+function OutgoingMessage() {
+ stream.Stream.call(this);
+
+ this.output = [];
+ this.outputEncodings = [];
+
+ this.writable = true;
+
+ this._last = false;
+ this.chunkedEncoding = false;
+ this.shouldKeepAlive = true;
+ this.useChunkedEncodingByDefault = true;
+ this.sendDate = false;
+
+ this._hasBody = true;
+ this._trailer = '';
+
+ this.finished = false;
+}
+util.inherits(OutgoingMessage, stream.Stream);
+
+
+exports.OutgoingMessage = OutgoingMessage;
+
+
+OutgoingMessage.prototype.destroy = function(error) {
+ this.socket.destroy(error);
+};
+
+
+// This abstract either writing directly to the socket or buffering it.
+OutgoingMessage.prototype._send = function(data, encoding) {
+ // This is a shameful hack to get the headers and first body chunk onto
+ // the same packet. Future versions of Node are going to take care of
+ // this at a lower level and in a more general way.
+ if (!this._headerSent) {
+ if (typeof data === 'string') {
+ data = this._header + data;
+ } else {
+ this.output.unshift(this._header);
+ this.outputEncodings.unshift('ascii');
+ }
+ this._headerSent = true;
+ }
+ return this._writeRaw(data, encoding);
+};
+
+
+OutgoingMessage.prototype._writeRaw = function(data, encoding) {
+ if (data.length === 0) {
+ return true;
+ }
+
+ if (this.connection &&
+ this.connection._httpMessage === this &&
+ this.connection.writable) {
+ // There might be pending data in the this.output buffer.
+ while (this.output.length) {
+ if (!this.connection.writable) {
+ this._buffer(data, encoding);
+ return false;
+ }
+ var c = this.output.shift();
+ var e = this.outputEncodings.shift();
+ this.connection.write(c, e);
+ }
+
+ // Directly write to socket.
+ return this.connection.write(data, encoding);
+ } else {
+ this._buffer(data, encoding);
+ return false;
+ }
+};
+
+
+OutgoingMessage.prototype._buffer = function(data, encoding) {
+ if (data.length === 0) return;
+
+ var length = this.output.length;
+
+ if (length === 0 || typeof data != 'string') {
+ this.output.push(data);
+ this.outputEncodings.push(encoding);
+ return false;
+ }
+
+ var lastEncoding = this.outputEncodings[length - 1];
+ var lastData = this.output[length - 1];
+
+ if ((encoding && lastEncoding === encoding) ||
+ (!encoding && data.constructor === lastData.constructor)) {
+ this.output[length - 1] = lastData + data;
+ return false;
+ }
+
+ this.output.push(data);
+ this.outputEncodings.push(encoding);
+
+ return false;
+};
+
+
+OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
+ var sentConnectionHeader = false;
+ var sentContentLengthHeader = false;
+ var sentTransferEncodingHeader = false;
+ var sentDateHeader = false;
+ var sentExpect = false;
+
+ // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
+ // in the case of response it is: 'HTTP/1.1 200 OK\r\n'
+ var messageHeader = firstLine;
+ var field, value;
+ var self = this;
+
+ function store(field, value) {
+ messageHeader += field + ': ' + value + CRLF;
+
+ if (connectionExpression.test(field)) {
+ sentConnectionHeader = true;
+ if (closeExpression.test(value)) {
+ self._last = true;
+ } else {
+ self.shouldKeepAlive = true;
+ }
+
+ } else if (transferEncodingExpression.test(field)) {
+ sentTransferEncodingHeader = true;
+ if (chunkExpression.test(value)) self.chunkedEncoding = true;
+
+ } else if (contentLengthExpression.test(field)) {
+ sentContentLengthHeader = true;
+ } else if (dateExpression.test(field)) {
+ sentDateHeader = true;
+ } else if (expectExpression.test(field)) {
+ sentExpect = true;
+ }
+ }
+
+ if (headers) {
+ var keys = Object.keys(headers);
+ var isArray = (Array.isArray(headers));
+ var field, value;
+
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ if (isArray) {
+ field = headers[key][0];
+ value = headers[key][1];
+ } else {
+ field = key;
+ value = headers[key];
+ }
+
+ if (Array.isArray(value)) {
+ for (var j = 0; j < value.length; j++) {
+ store(field, value[j]);
+ }
+ } else {
+ store(field, value);
+ }
+ }
+ }
+
+ // Date header
+ if (this.sendDate == true && sentDateHeader == false) {
+ messageHeader += 'Date: ' + utcDate() + CRLF;
+ }
+
+ // keep-alive logic
+ if (sentConnectionHeader === false) {
+ var shouldSendKeepAlive = this.shouldKeepAlive &&
+ (sentContentLengthHeader ||
+ this.useChunkedEncodingByDefault ||
+ this.agent);
+ if (shouldSendKeepAlive) {
+ messageHeader += 'Connection: keep-alive\r\n';
+ } else {
+ this._last = true;
+ messageHeader += 'Connection: close\r\n';
+ }
+ }
+
+ if (sentContentLengthHeader == false && sentTransferEncodingHeader == false) {
+ if (this._hasBody) {
+ if (this.useChunkedEncodingByDefault) {
+ messageHeader += 'Transfer-Encoding: chunked\r\n';
+ this.chunkedEncoding = true;
+ } else {
+ this._last = true;
+ }
+ } else {
+ // Make sure we don't end the 0\r\n\r\n at the end of the message.
+ this.chunkedEncoding = false;
+ }
+ }
+
+ this._header = messageHeader + CRLF;
+ this._headerSent = false;
+
+ // wait until the first body chunk, or close(), is sent to flush,
+ // UNLESS we're sending Expect: 100-continue.
+ if (sentExpect) this._send('');
+};
+
+
+OutgoingMessage.prototype.setHeader = function(name, value) {
+ if (arguments.length < 2) {
+ throw new Error('`name` and `value` are required for setHeader().');
+ }
+
+ if (this._header) {
+ throw new Error('Can\'t set headers after they are sent.');
+ }
+
+ var key = name.toLowerCase();
+ this._headers = this._headers || {};
+ this._headerNames = this._headerNames || {};
+ this._headers[key] = value;
+ this._headerNames[key] = name;
+};
+
+
+OutgoingMessage.prototype.getHeader = function(name) {
+ if (arguments.length < 1) {
+ throw new Error('`name` is required for getHeader().');
+ }
+
+ if (!this._headers) return;
+
+ var key = name.toLowerCase();
+ return this._headers[key];
+};
+
+
+OutgoingMessage.prototype.removeHeader = function(name) {
+ if (arguments.length < 1) {
+ throw new Error('`name` is required for removeHeader().');
+ }
+
+ if (this._header) {
+ throw new Error('Can\'t remove headers after they are sent.');
+ }
+
+ if (!this._headers) return;
+
+ var key = name.toLowerCase();
+ delete this._headers[key];
+ delete this._headerNames[key];
+};
+
+
+OutgoingMessage.prototype._renderHeaders = function() {
+ if (this._header) {
+ throw new Error('Can\'t render headers after they are sent to the client.');
+ }
+
+ if (!this._headers) return {};
+
+ var headers = {};
+ var keys = Object.keys(this._headers);
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ headers[this._headerNames[key]] = this._headers[key];
+ }
+ return headers;
+};
+
+
+
+OutgoingMessage.prototype.write = function(chunk, encoding) {
+ if (!this._header) {
+ this._implicitHeader();
+ }
+
+ if (!this._hasBody) {
+ console.error('This type of response MUST NOT have a body. ' +
+ 'Ignoring write() calls.');
+ return true;
+ }
+
+ if (typeof chunk !== 'string' && !Buffer.isBuffer(chunk)) {
+ throw new TypeError('first argument must be a string or Buffer');
+ }
+
+ if (chunk.length === 0) return false;
+
+ var len, ret;
+ if (this.chunkedEncoding) {
+ if (typeof(chunk) === 'string') {
+ len = Buffer.byteLength(chunk, encoding);
+ chunk = len.toString(16) + CRLF + chunk + CRLF;
+ ret = this._send(chunk, encoding);
+ } else {
+ // buffer
+ len = chunk.length;
+ this._send(len.toString(16) + CRLF);
+ this._send(chunk);
+ ret = this._send(CRLF);
+ }
+ } else {
+ ret = this._send(chunk, encoding);
+ }
+
+ debug('write ret = ' + ret);
+ return ret;
+};
+
+
+OutgoingMessage.prototype.addTrailers = function(headers) {
+ this._trailer = '';
+ var keys = Object.keys(headers);
+ var isArray = (Array.isArray(headers));
+ var field, value;
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ if (isArray) {
+ field = headers[key][0];
+ value = headers[key][1];
+ } else {
+ field = key;
+ value = headers[key];
+ }
+
+ this._trailer += field + ': ' + value + CRLF;
+ }
+};
+
+
+OutgoingMessage.prototype.end = function(data, encoding) {
+ if (this.finished) {
+ return false;
+ }
+ if (!this._header) {
+ this._implicitHeader();
+ }
+
+ if (data && !this._hasBody) {
+ console.error('This type of response MUST NOT have a body. ' +
+ 'Ignoring data passed to end().');
+ data = false;
+ }
+
+ var ret;
+
+ var hot = this._headerSent === false &&
+ typeof(data) === 'string' &&
+ data.length > 0 &&
+ this.output.length === 0 &&
+ this.connection &&
+ this.connection.writable &&
+ this.connection._httpMessage === this;
+
+ if (hot) {
+ // Hot path. They're doing
+ // res.writeHead();
+ // res.end(blah);
+ // HACKY.
+
+ if (this.chunkedEncoding) {
+ var l = Buffer.byteLength(data, encoding).toString(16);
+ ret = this.connection.write(this._header + l + CRLF +
+ data + '\r\n0\r\n' +
+ this._trailer + '\r\n', encoding);
+ } else {
+ ret = this.connection.write(this._header + data, encoding);
+ }
+ this._headerSent = true;
+
+ } else if (data) {
+ // Normal body write.
+ ret = this.write(data, encoding);
+ }
+
+ if (!hot) {
+ if (this.chunkedEncoding) {
+ ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
+ } else {
+ // Force a flush, HACK.
+ ret = this._send('');
+ }
+ }
+
+ this.finished = true;
+
+ // There is the first message on the outgoing queue, and we've sent
+ // everything to the socket.
+ debug('outgoing message end.');
+ if (this.output.length === 0 && this.connection._httpMessage === this) {
+ this._finish();
+ }
+
+ return ret;
+};
+
+
+OutgoingMessage.prototype._finish = function() {
+ assert(this.connection);
+ if (this instanceof ServerResponse) {
+ DTRACE_HTTP_SERVER_RESPONSE(this.connection);
+ } else {
+ assert(this instanceof ClientRequest);
+ DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
+ }
+ this.emit('finish');
+};
+
+
+OutgoingMessage.prototype._flush = function() {
+ // This logic is probably a bit confusing. Let me explain a bit:
+ //
+ // In both HTTP servers and clients it is possible to queue up several
+ // outgoing messages. This is easiest to imagine in the case of a client.
+ // Take the following situation:
+ //
+ // req1 = client.request('GET', '/');
+ // req2 = client.request('POST', '/');
+ //
+ // When the user does
+ //
+ // req2.write('hello world\n');
+ //
+ // it's possible that the first request has not been completely flushed to
+ // the socket yet. Thus the outgoing messages need to be prepared to queue
+ // up data internally before sending it on further to the socket's queue.
+ //
+ // This function, outgoingFlush(), is called by both the Server and Client
+ // to attempt to flush any pending messages out to the socket.
+
+ if (!this.socket) return;
+
+ var ret;
+ while (this.output.length) {
+
+ if (!this.socket.writable) return; // XXX Necessary?
+
+ var data = this.output.shift();
+ var encoding = this.outputEncodings.shift();
+
+ ret = this.socket.write(data, encoding);
+ }
+
+ if (this.finished) {
+ // This is a queue to the server or client to bring in the next this.
+ this._finish();
+ } else if (ret) {
+ // This is necessary to prevent https from breaking
+ this.emit('drain');
+ }
+};
+
+
+
+
+function ServerResponse(req) {
+ OutgoingMessage.call(this);
+
+ if (req.method === 'HEAD') this._hasBody = false;
+
+ this.sendDate = true;
+
+ if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
+ this.useChunkedEncodingByDefault = false;
+ this.shouldKeepAlive = false;
+ }
+}
+util.inherits(ServerResponse, OutgoingMessage);
+
+
+exports.ServerResponse = ServerResponse;
+
+ServerResponse.prototype.statusCode = 200;
+
+function onServerResponseClose() {
+ this._httpMessage.emit('close');
+}
+
+ServerResponse.prototype.assignSocket = function(socket) {
+ assert(!socket._httpMessage);
+ socket._httpMessage = this;
+ socket.on('close', onServerResponseClose);
+ this.socket = socket;
+ this.connection = socket;
+ this._flush();
+};
+
+ServerResponse.prototype.detachSocket = function(socket) {
+ assert(socket._httpMessage == this);
+ socket.removeListener('close', onServerResponseClose);
+ socket._httpMessage = null;
+ this.socket = this.connection = null;
+};
+
+ServerResponse.prototype.writeContinue = function() {
+ this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii');
+ this._sent100 = true;
+};
+
+ServerResponse.prototype._implicitHeader = function() {
+ this.writeHead(this.statusCode);
+};
+
+ServerResponse.prototype.writeHead = function(statusCode) {
+ var reasonPhrase, headers, headerIndex;
+
+ if (typeof arguments[1] == 'string') {
+ reasonPhrase = arguments[1];
+ headerIndex = 2;
+ } else {
+ reasonPhrase = STATUS_CODES[statusCode] || 'unknown';
+ headerIndex = 1;
+ }
+ this.statusCode = statusCode;
+
+ var obj = arguments[headerIndex];
+
+ if (obj && this._headers) {
+ // Slow-case: when progressive API and header fields are passed.
+ headers = this._renderHeaders();
+
+ if (Array.isArray(obj)) {
+ // handle array case
+ // TODO: remove when array is no longer accepted
+ var field;
+ for (var i = 0, len = obj.length; i < len; ++i) {
+ field = obj[i][0];
+ if (field in headers) {
+ obj.push([field, headers[field]]);
+ }
+ }
+ headers = obj;
+
+ } else {
+ // handle object case
+ var keys = Object.keys(obj);
+ for (var i = 0; i < keys.length; i++) {
+ var k = keys[i];
+ if (k) headers[k] = obj[k];
+ }
+ }
+ } else if (this._headers) {
+ // only progressive api is used
+ headers = this._renderHeaders();
+ } else {
+ // only writeHead() called
+ headers = obj;
+ }
+
+ var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' +
+ reasonPhrase + CRLF;
+
+ if (statusCode === 204 || statusCode === 304 ||
+ (100 <= statusCode && statusCode <= 199)) {
+ // RFC 2616, 10.2.5:
+ // The 204 response MUST NOT include a message-body, and thus is always
+ // terminated by the first empty line after the header fields.
+ // RFC 2616, 10.3.5:
+ // The 304 response MUST NOT contain a message-body, and thus is always
+ // terminated by the first empty line after the header fields.
+ // RFC 2616, 10.1 Informational 1xx:
+ // This class of status code indicates a provisional response,
+ // consisting only of the Status-Line and optional headers, and is
+ // terminated by an empty line.
+ this._hasBody = false;
+ }
+
+ // don't keep alive connections where the client expects 100 Continue
+ // but we sent a final status; they may put extra bytes on the wire.
+ if (this._expect_continue && ! this._sent100) {
+ this.shouldKeepAlive = false;
+ }
+
+ this._storeHeader(statusLine, headers);
+};
+
+ServerResponse.prototype.writeHeader = function() {
+ this.writeHead.apply(this, arguments);
+};
+
+
+// New Agent code.
+
+// The largest departure from the previous implementation is that
+// an Agent instance holds connections for a variable number of host:ports.
+// Surprisingly, this is still API compatible as far as third parties are
+// concerned. The only code that really notices the difference is the
+// request object.
+
+// Another departure is that all code related to HTTP parsing is in
+// ClientRequest.onSocket(). The Agent is now *strictly*
+// concerned with managing a connection pool.
+
+function Agent(options) {
+ var self = this;
+ self.options = options || {};
+ self.requests = {};
+ self.sockets = {};
+ self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets;
+ self.on('free', function(socket, host, port, localAddress) {
+ var name = host + ':' + port;
+ if (localAddress) {
+ name += ':' + localAddress;
+ }
+
+ if (self.requests[name] && self.requests[name].length) {
+ self.requests[name].shift().onSocket(socket);
+ if (self.requests[name].length === 0) {
+ // don't leak
+ delete self.requests[name];
+ }
+ } else {
+ // If there are no pending requests just destroy the
+ // socket and it will get removed from the pool. This
+ // gets us out of timeout issues and allows us to
+ // default to Connection:keep-alive.
+ socket.destroy();
+ }
+ });
+ self.createConnection = net.createConnection;
+}
+util.inherits(Agent, EventEmitter);
+exports.Agent = Agent;
+
+Agent.defaultMaxSockets = 5;
+
+Agent.prototype.defaultPort = 80;
+Agent.prototype.addRequest = function(req, host, port, localAddress) {
+ var name = host + ':' + port;
+ if (localAddress) {
+ name += ':' + localAddress;
+ }
+ if (!this.sockets[name]) {
+ this.sockets[name] = [];
+ }
+ if (this.sockets[name].length < this.maxSockets) {
+ // If we are under maxSockets create a new one.
+ req.onSocket(this.createSocket(name, host, port, localAddress));
+ } else {
+ // We are over limit so we'll add it to the queue.
+ if (!this.requests[name]) {
+ this.requests[name] = [];
+ }
+ this.requests[name].push(req);
+ }
+};
+Agent.prototype.createSocket = function(name, host, port, localAddress) {
+ var self = this;
+ var options = util._extend({}, self.options);
+ options.port = port;
+ options.host = host;
+ options.localAddress = localAddress;
+ var s = self.createConnection(options);
+ if (!self.sockets[name]) {
+ self.sockets[name] = [];
+ }
+ this.sockets[name].push(s);
+ var onFree = function() {
+ self.emit('free', s, host, port, localAddress);
+ }
+ s.on('free', onFree);
+ var onClose = function(err) {
+ // This is the only place where sockets get removed from the Agent.
+ // If you want to remove a socket from the pool, just close it.
+ // All socket errors end in a close event anyway.
+ self.removeSocket(s, name, host, port, localAddress);
+ }
+ s.on('close', onClose);
+ var onRemove = function() {
+ // We need this function for cases like HTTP 'upgrade'
+ // (defined by WebSockets) where we need to remove a socket from the pool
+ // because it'll be locked up indefinitely
+ self.removeSocket(s, name, host, port, localAddress);
+ s.removeListener('close', onClose);
+ s.removeListener('free', onFree);
+ s.removeListener('agentRemove', onRemove);
+ }
+ s.on('agentRemove', onRemove);
+ return s;
+};
+Agent.prototype.removeSocket = function(s, name, host, port, localAddress) {
+ if (this.sockets[name]) {
+ var index = this.sockets[name].indexOf(s);
+ if (index !== -1) {
+ this.sockets[name].splice(index, 1);
+ if (this.sockets[name].length === 0) {
+ // don't leak
+ delete this.sockets[name];
+ }
+ }
+ }
+ if (this.requests[name] && this.requests[name].length) {
+ // If we have pending requests and a socket gets closed a new one
+ this.createSocket(name, host, port, localAddress).emit('free');
+ }
+};
+
+var globalAgent = new Agent();
+exports.globalAgent = globalAgent;
+
+
+function ClientRequest(options, cb) {
+ var self = this;
+ OutgoingMessage.call(self);
+
+ self.agent = options.agent === undefined ? globalAgent : options.agent;
+
+ var defaultPort = options.defaultPort || 80;
+
+ var port = options.port || defaultPort;
+ var host = options.hostname || options.host || 'localhost';
+
+ if (options.setHost === undefined) {
+ var setHost = true;
+ }
+
+ self.socketPath = options.socketPath;
+
+ var method = self.method = (options.method || 'GET').toUpperCase();
+ self.path = options.path || '/';
+ if (cb) {
+ self.once('response', cb);
+ }
+
+ if (!Array.isArray(options.headers)) {
+ if (options.headers) {
+ var keys = Object.keys(options.headers);
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ self.setHeader(key, options.headers[key]);
+ }
+ }
+ if (host && !this.getHeader('host') && setHost) {
+ var hostHeader = host;
+ if (port && +port !== defaultPort) {
+ hostHeader += ':' + port;
+ }
+ this.setHeader('Host', hostHeader);
+ }
+ }
+
+ if (options.auth && !this.getHeader('Authorization')) {
+ //basic auth
+ this.setHeader('Authorization', 'Basic ' +
+ new Buffer(options.auth).toString('base64'));
+ }
+
+ if (method === 'GET' || method === 'HEAD' || method === 'CONNECT') {
+ self.useChunkedEncodingByDefault = false;
+ } else {
+ self.useChunkedEncodingByDefault = true;
+ }
+
+ if (Array.isArray(options.headers)) {
+ self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
+ options.headers);
+ } else if (self.getHeader('expect')) {
+ self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
+ self._renderHeaders());
+ }
+ if (self.socketPath) {
+ self._last = true;
+ self.shouldKeepAlive = false;
+ if (options.createConnection) {
+ self.onSocket(options.createConnection(self.socketPath));
+ } else {
+ self.onSocket(net.createConnection(self.socketPath));
+ }
+ } else if (self.agent) {
+ // If there is an agent we should default to Connection:keep-alive.
+ self._last = false;
+ self.shouldKeepAlive = true;
+ self.agent.addRequest(self, host, port, options.localAddress);
+ } else {
+ // No agent, default to Connection:close.
+ self._last = true;
+ self.shouldKeepAlive = false;
+ if (options.createConnection) {
+ options.port = port;
+ options.host = host;
+ var conn = options.createConnection(options);
+ } else {
+ var conn = net.createConnection({
+ port: port,
+ host: host,
+ localAddress: options.localAddress
+ });
+ }
+ self.onSocket(conn);
+ }
+
+ self._deferToConnect(null, null, function() {
+ self._flush();
+ self = null;
+ });
+
+}
+util.inherits(ClientRequest, OutgoingMessage);
+
+exports.ClientRequest = ClientRequest;
+
+ClientRequest.prototype._implicitHeader = function() {
+ this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
+ this._renderHeaders());
+};
+
+ClientRequest.prototype.abort = function() {
+ if (this.socket) {
+ // in-progress
+ this.socket.destroy();
+ } else {
+ // haven't been assigned a socket yet.
+ // this could be more efficient, it could
+ // remove itself from the pending requests
+ this._deferToConnect('destroy', []);
+ }
+};
+
+
+function createHangUpError() {
+ var error = new Error('socket hang up');
+ error.code = 'ECONNRESET';
+ return error;
+}
+
+// Free the parser and also break any links that it
+// might have to any other things.
+// TODO: All parser data should be attached to a
+// single object, so that it can be easily cleaned
+// up by doing `parser.data = {}`, which should
+// be done in FreeList.free. `parsers.free(parser)`
+// should be all that is needed.
+function freeParser(parser, req) {
+ if (parser) {
+ parser._headers = [];
+ parser.onIncoming = null;
+ if (parser.socket) {
+ parser.socket.onend = null;
+ parser.socket.ondata = null;
+ parser.socket.parser = null;
+ }
+ parser.socket = null;
+ parser.incoming = null;
+ parsers.free(parser);
+ parser = null;
+ }
+ if (req) {
+ req.parser = null;
+ }
+}
+
+
+function socketCloseListener() {
+ var socket = this;
+ var parser = socket.parser;
+ var req = socket._httpMessage;
+ debug('HTTP socket close');
+ req.emit('close');
+ if (req.res && req.res.readable) {
+ // Socket closed before we emitted 'end' below.
+ req.res.emit('aborted');
+ var res = req.res;
+ req.res._emitPending(function() {
+ res._emitEnd();
+ res.emit('close');
+ res = null;
+ });
+ } else if (!req.res && !req._hadError) {
+ // This socket error fired before we started to
+ // receive a response. The error needs to
+ // fire on the request.
+ req.emit('error', createHangUpError());
+ }
+
+ if (parser) {
+ parser.finish();
+ freeParser(parser, req);
+ }
+}
+
+function socketErrorListener(err) {
+ var socket = this;
+ var parser = socket.parser;
+ var req = socket._httpMessage;
+ debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack);
+
+ if (req) {
+ req.emit('error', err);
+ // For Safety. Some additional errors might fire later on
+ // and we need to make sure we don't double-fire the error event.
+ req._hadError = true;
+ }
+
+ if (parser) {
+ parser.finish();
+ freeParser(parser, req);
+ }
+ socket.destroy();
+}
+
+function socketOnEnd() {
+ var socket = this;
+ var req = this._httpMessage;
+ var parser = this.parser;
+
+ if (!req.res) {
+ // If we don't have a response then we know that the socket
+ // ended prematurely and we need to emit an error on the request.
+ req.emit('error', createHangUpError());
+ req._hadError = true;
+ }
+ if (parser) {
+ parser.finish();
+ freeParser(parser, req);
+ }
+ socket.destroy();
+}
+
+function socketOnData(d, start, end) {
+ var socket = this;
+ var req = this._httpMessage;
+ var parser = this.parser;
+
+ var ret = parser.execute(d, start, end - start);
+ if (ret instanceof Error) {
+ debug('parse error');
+ freeParser(parser, req);
+ socket.destroy(ret);
+ } else if (parser.incoming && parser.incoming.upgrade) {
+ // Upgrade or CONNECT
+ var bytesParsed = ret;
+ var res = parser.incoming;
+ req.res = res;
+
+ socket.ondata = null;
+ socket.onend = null;
+ parser.finish();
+
+ // This is start + byteParsed
+ var bodyHead = d.slice(start + bytesParsed, end);
+
+ var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
+ if (req.listeners(eventName).length) {
+ req.upgradeOrConnect = true;
+
+ // detach the socket
+ socket.emit('agentRemove');
+ socket.removeListener('close', socketCloseListener);
+ socket.removeListener('error', socketErrorListener);
+
+ req.emit(eventName, res, socket, bodyHead);
+ req.emit('close');
+ } else {
+ // Got Upgrade header or CONNECT method, but have no handler.
+ socket.destroy();
+ }
+ freeParser(parser, req);
+ } else if (parser.incoming && parser.incoming.complete &&
+ // When the status code is 100 (Continue), the server will
+ // send a final response after this client sends a request
+ // body. So, we must not free the parser.
+ parser.incoming.statusCode !== 100) {
+ freeParser(parser, req);
+ }
+}
+
+
+function parserOnIncomingClient(res, shouldKeepAlive) {
+ var parser = this;
+ var socket = this.socket;
+ var req = socket._httpMessage;
+
+ debug('AGENT incoming response!');
+
+ if (req.res) {
+ // We already have a response object, this means the server
+ // sent a double response.
+ socket.destroy();
+ return;
+ }
+ req.res = res;
+
+ // Responses to CONNECT request is handled as Upgrade.
+ if (req.method === 'CONNECT') {
+ res.upgrade = true;
+ return true; // skip body
+ }
+
+ // Responses to HEAD requests are crazy.
+ // HEAD responses aren't allowed to have an entity-body
+ // but *can* have a content-length which actually corresponds
+ // to the content-length of the entity-body had the request
+ // been a GET.
+ var isHeadResponse = req.method == 'HEAD';
+ debug('AGENT isHeadResponse ' + isHeadResponse);
+
+ if (res.statusCode == 100) {
+ // restart the parser, as this is a continue message.
+ delete req.res; // Clear res so that we don't hit double-responses.
+ req.emit('continue');
+ return true;
+ }
+
+ if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
+ // Server MUST respond with Connection:keep-alive for us to enable it.
+ // If we've been upgraded (via WebSockets) we also shouldn't try to
+ // keep the connection open.
+ req.shouldKeepAlive = false;
+ }
+
+
+ DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
+ req.emit('response', res);
+ req.res = res;
+ res.req = req;
+
+ res.on('end', responseOnEnd);
+
+ return isHeadResponse;
+}
+
+function responseOnEnd() {
+ var res = this;
+ var req = res.req;
+ var socket = req.socket;
+
+ if (!req.shouldKeepAlive) {
+ if (socket.writable) {
+ debug('AGENT socket.destroySoon()');
+ socket.destroySoon();
+ }
+ assert(!socket.writable);
+ } else {
+ debug('AGENT socket keep-alive');
+ socket.removeListener('close', socketCloseListener);
+ socket.removeListener('error', socketErrorListener);
+ socket.emit('free');
+ }
+}
+
+ClientRequest.prototype.onSocket = function(socket) {
+ var req = this;
+
+ process.nextTick(function() {
+ var parser = parsers.alloc();
+ req.socket = socket;
+ req.connection = socket;
+ parser.reinitialize(HTTPParser.RESPONSE);
+ parser.socket = socket;
+ parser.incoming = null;
+ req.parser = parser;
+
+ parser.socket = socket;
+ socket.parser = parser;
+ parser.incoming = null;
+ socket._httpMessage = req;
+
+ // Setup "drain" propogation.
+ httpSocketSetup(socket);
+
+ // Propagate headers limit from request object to parser
+ if (typeof req.maxHeadersCount === 'number') {
+ parser.maxHeaderPairs = req.maxHeadersCount << 1;
+ } else {
+ // Set default value because parser may be reused from FreeList
+ parser.maxHeaderPairs = 2000;
+ }
+
+ socket.on('error', socketErrorListener);
+ socket.ondata = socketOnData;
+ socket.onend = socketOnEnd;
+ socket.on('close', socketCloseListener);
+ parser.onIncoming = parserOnIncomingClient;
+ req.emit('socket', socket);
+ });
+
+};
+
+ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
+ // This function is for calls that need to happen once the socket is
+ // connected and writable. It's an important promisy thing for all the socket
+ // calls that happen either now (when a socket is assigned) or
+ // in the future (when a socket gets assigned out of the pool and is
+ // eventually writable).
+ var self = this;
+ var onSocket = function() {
+ if (self.socket.writable) {
+ if (method) {
+ self.socket[method].apply(self.socket, arguments_);
+ }
+ if (cb) { cb(); }
+ } else {
+ self.socket.once('connect', function() {
+ if (method) {
+ self.socket[method].apply(self.socket, arguments_);
+ }
+ if (cb) { cb(); }
+ });
+ }
+ }
+ if (!self.socket) {
+ self.once('socket', onSocket);
+ } else {
+ onSocket();
+ }
+};
+
+ClientRequest.prototype.setTimeout = function(msecs, callback) {
+ if (callback) this.once('timeout', callback);
+
+ var self = this;
+ function emitTimeout() {
+ self.emit('timeout');
+ }
+
+ if (this.socket && this.socket.writable) {
+ this.socket.setTimeout(msecs, emitTimeout);
+ return;
+ }
+
+ if (this.socket) {
+ this.socket.once('connect', function() {
+ this.setTimeout(msecs, emitTimeout);
+ });
+ return;
+ }
+
+ this.once('socket', function(sock) {
+ this.setTimeout(msecs, emitTimeout);
+ });
+};
+
+ClientRequest.prototype.setNoDelay = function() {
+ this._deferToConnect('setNoDelay', arguments);
+};
+ClientRequest.prototype.setSocketKeepAlive = function() {
+ this._deferToConnect('setKeepAlive', arguments);
+};
+
+ClientRequest.prototype.clearTimeout = function(cb) {
+ this.setTimeout(0, cb);
+};
+
+exports.request = function(options, cb) {
+ if (typeof options === 'string') {
+ options = url.parse(options);
+ }
+
+ if (options.protocol && options.protocol !== 'http:') {
+ throw new Error('Protocol:' + options.protocol + ' not supported.');
+ }
+
+ return new ClientRequest(options, cb);
+};
+
+exports.get = function(options, cb) {
+ var req = exports.request(options, cb);
+ req.end();
+ return req;
+};
+
+
+function ondrain() {
+ if (this._httpMessage) this._httpMessage.emit('drain');
+}
+
+
+function httpSocketSetup(socket) {
+ socket.removeListener('drain', ondrain);
+ socket.on('drain', ondrain);
+}
+
+
+function Server(requestListener) {
+ if (!(this instanceof Server)) return new Server(requestListener);
+ net.Server.call(this, { allowHalfOpen: true });
+
+ if (requestListener) {
+ this.addListener('request', requestListener);
+ }
+
+ // Similar option to this. Too lazy to write my own docs.
+ // http://www.squid-cache.org/Doc/config/half_closed_clients/
+ // http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F
+ this.httpAllowHalfOpen = false;
+
+ this.addListener('connection', connectionListener);
+}
+util.inherits(Server, net.Server);
+
+
+exports.Server = Server;
+
+
+exports.createServer = function(requestListener) {
+ return new Server(requestListener);
+};
+
+
+function connectionListener(socket) {
+ var self = this;
+ var outgoing = [];
+ var incoming = [];
+
+ function abortIncoming() {
+ while (incoming.length) {
+ var req = incoming.shift();
+ req.emit('aborted');
+ req.emit('close');
+ }
+ // abort socket._httpMessage ?
+ }
+
+ function serverSocketCloseListener() {
+ debug('server socket close');
+ // mark this parser as reusable
+ freeParser(parser);
+
+ abortIncoming();
+ }
+
+ debug('SERVER new http connection');
+
+ httpSocketSetup(socket);
+
+ socket.setTimeout(2 * 60 * 1000); // 2 minute timeout
+ socket.once('timeout', function() {
+ socket.destroy();
+ });
+
+ var parser = parsers.alloc();
+ parser.reinitialize(HTTPParser.REQUEST);
+ parser.socket = socket;
+ socket.parser = parser;
+ parser.incoming = null;
+
+ // Propagate headers limit from server instance to parser
+ if (typeof this.maxHeadersCount === 'number') {
+ parser.maxHeaderPairs = this.maxHeadersCount << 1;
+ } else {
+ // Set default value because parser may be reused from FreeList
+ parser.maxHeaderPairs = 2000;
+ }
+
+ socket.addListener('error', function(e) {
+ self.emit('clientError', e);
+ });
+
+ socket.ondata = function(d, start, end) {
+ var ret = parser.execute(d, start, end - start);
+ if (ret instanceof Error) {
+ debug('parse error');
+ socket.destroy(ret);
+ } else if (parser.incoming && parser.incoming.upgrade) {
+ // Upgrade or CONNECT
+ var bytesParsed = ret;
+ var req = parser.incoming;
+
+ socket.ondata = null;
+ socket.onend = null;
+ socket.removeListener('close', serverSocketCloseListener);
+ parser.finish();
+ freeParser(parser, req);
+
+ // This is start + byteParsed
+ var bodyHead = d.slice(start + bytesParsed, end);
+
+ var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
+ if (self.listeners(eventName).length) {
+ self.emit(eventName, req, req.socket, bodyHead);
+ } else {
+ // Got upgrade header or CONNECT method, but have no handler.
+ socket.destroy();
+ }
+ }
+ };
+
+ socket.onend = function() {
+ var ret = parser.finish();
+
+ if (ret instanceof Error) {
+ debug('parse error');
+ socket.destroy(ret);
+ return;
+ }
+
+ if (!self.httpAllowHalfOpen) {
+ abortIncoming();
+ if (socket.writable) socket.end();
+ } else if (outgoing.length) {
+ outgoing[outgoing.length - 1]._last = true;
+ } else if (socket._httpMessage) {
+ socket._httpMessage._last = true;
+ } else {
+ if (socket.writable) socket.end();
+ }
+ };
+
+ socket.addListener('close', serverSocketCloseListener);
+
+ // The following callback is issued after the headers have been read on a
+ // new message. In this callback we setup the response object and pass it
+ // to the user.
+ parser.onIncoming = function(req, shouldKeepAlive) {
+ incoming.push(req);
+
+ var res = new ServerResponse(req);
+ debug('server response shouldKeepAlive: ' + shouldKeepAlive);
+ res.shouldKeepAlive = shouldKeepAlive;
+ DTRACE_HTTP_SERVER_REQUEST(req, socket);
+
+ if (socket._httpMessage) {
+ // There are already pending outgoing res, append.
+ outgoing.push(res);
+ } else {
+ res.assignSocket(socket);
+ }
+
+ // When we're finished writing the response, check if this is the last
+ // respose, if so destroy the socket.
+ res.on('finish', function() {
+ // Usually the first incoming element should be our request. it may
+ // be that in the case abortIncoming() was called that the incoming
+ // array will be empty.
+ assert(incoming.length == 0 || incoming[0] === req);
+
+ incoming.shift();
+
+ res.detachSocket(socket);
+
+ if (res._last) {
+ socket.destroySoon();
+ } else {
+ // start sending the next message
+ var m = outgoing.shift();
+ if (m) {
+ m.assignSocket(socket);
+ }
+ }
+ });
+
+ if ('expect' in req.headers &&
+ (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
+ continueExpression.test(req.headers['expect'])) {
+ res._expect_continue = true;
+ if (self.listeners('checkContinue').length) {
+ self.emit('checkContinue', req, res);
+ } else {
+ res.writeContinue();
+ self.emit('request', req, res);
+ }
+ } else {
+ self.emit('request', req, res);
+ }
+ return false; // Not a HEAD response. (Not even a response!)
+ };
+}
+exports._connectionListener = connectionListener;
+
+// Legacy Interface
+
+function Client(port, host) {
+ if (!(this instanceof Client)) return new Client(port, host);
+ host = host || 'localhost';
+ port = port || 80;
+ this.host = host;
+ this.port = port;
+ this.agent = new Agent({ host: host, port: port, maxSockets: 1 });
+}
+util.inherits(Client, EventEmitter);
+Client.prototype.request = function(method, path, headers) {
+ var self = this;
+ var options = {};
+ options.host = self.host;
+ options.port = self.port;
+ if (method[0] === '/') {
+ headers = path;
+ path = method;
+ method = 'GET';
+ }
+ options.method = method;
+ options.path = path;
+ options.headers = headers;
+ options.agent = self.agent;
+ var c = new ClientRequest(options);
+ c.on('error', function(e) {
+ self.emit('error', e);
+ });
+ // The old Client interface emitted 'end' on socket end.
+ // This doesn't map to how we want things to operate in the future
+ // but it will get removed when we remove this legacy interface.
+ c.on('socket', function(s) {
+ s.on('end', function() {
+ self.emit('end');
+ });
+ });
+ return c;
+};
+
+exports.Client = Client;
+
+// TODO http.Client can be removed in v0.9. Until then leave this message.
+module.deprecate('Client', 'It will be removed soon. Do not use it.');
+
+exports.createClient = function(port, host) {
+ return new Client(port, host);
+};
+module.deprecate('createClient', 'Use `http.request` instead.');
diff --git a/samples/javascript/intro-old.js b/samples/javascript/intro-old.js
new file mode 100644
index 0000000..f774102
--- /dev/null
+++ b/samples/javascript/intro-old.js
@@ -0,0 +1,37 @@
+(function() {
+ var cubes, list, math, num, number, opposite, race, square;
+ var __slice = Array.prototype.slice;
+ number = 42;
+ opposite = true;
+ if (opposite) {
+ number = -42;
+ }
+ square = function(x) {
+ return x * x;
+ };
+ list = [1, 2, 3, 4, 5];
+ math = {
+ root: Math.sqrt,
+ square: square,
+ cube: function(x) {
+ return x * square(x);
+ }
+ };
+ race = function() {
+ var runners, winner;
+ winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ return print(winner, runners);
+ };
+ if (typeof elvis !== "undefined" && elvis !== null) {
+ alert("I knew it!");
+ }
+ cubes = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = list.length; _i < _len; _i++) {
+ num = list[_i];
+ _results.push(math.cube(num));
+ }
+ return _results;
+ })();
+}).call(this);
diff --git a/samples/javascript/intro.js b/samples/javascript/intro.js
new file mode 100644
index 0000000..5974d26
--- /dev/null
+++ b/samples/javascript/intro.js
@@ -0,0 +1,44 @@
+// Generated by CoffeeScript 1.2.1
+(function() {
+ var cubes, list, math, num, number, opposite, race, square,
+ __slice = [].slice;
+
+ number = 42;
+
+ opposite = true;
+
+ if (opposite) number = -42;
+
+ square = function(x) {
+ return x * x;
+ };
+
+ list = [1, 2, 3, 4, 5];
+
+ math = {
+ root: Math.sqrt,
+ square: square,
+ cube: function(x) {
+ return x * square(x);
+ }
+ };
+
+ race = function() {
+ var runners, winner;
+ winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ return print(winner, runners);
+ };
+
+ if (typeof elvis !== "undefined" && elvis !== null) alert("I knew it!");
+
+ cubes = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = list.length; _i < _len; _i++) {
+ num = list[_i];
+ _results.push(math.cube(num));
+ }
+ return _results;
+ })();
+
+}).call(this);
diff --git a/samples/javascript/jquery-1.4.2.min.js b/samples/javascript/jquery-1.4.2.min.js
new file mode 100644
index 0000000..7c24308
--- /dev/null
+++ b/samples/javascript/jquery-1.4.2.min.js
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/samples/javascript/jquery-1.6.1.js b/samples/javascript/jquery-1.6.1.js
new file mode 100644
index 0000000..5d5a1d5
--- /dev/null
+++ b/samples/javascript/jquery-1.6.1.js
@@ -0,0 +1,8936 @@
+/*!
+ * jQuery JavaScript Library v1.6.1
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu May 12 15:04:36 2011 -0400
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Check for digits
+ rdigit = /\d/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.6.1",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.done( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).unbind( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery._Deferred();
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ // A crude way of determining if an object is a window
+ isWindow: function( obj ) {
+ return obj && typeof obj === "object" && "setInterval" in obj;
+ },
+
+ isNaN: function( obj ) {
+ return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return (new Function( "return " + data ))();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ // (xml & tmp used internally)
+ parseXML: function( data , xml , tmp ) {
+
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+
+ tmp = xml.documentElement;
+
+ if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+
+ if ( indexOf ) {
+ return indexOf.call( array, elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can be optionally by executed if its a function
+ access: function( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ jQuery.access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+ },
+
+ now: function() {
+ return (new Date()).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+// Expose jQuery to the global object
+return jQuery;
+
+})();
+
+
+var // Promise methods
+ promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
+ // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+ // Create a simple deferred (one callbacks list)
+ _Deferred: function() {
+ var // callbacks list
+ callbacks = [],
+ // stored [ context , args ]
+ fired,
+ // to avoid firing when already doing so
+ firing,
+ // flag to know if the deferred has been cancelled
+ cancelled,
+ // the deferred itself
+ deferred = {
+
+ // done( f1, f2, ...)
+ done: function() {
+ if ( !cancelled ) {
+ var args = arguments,
+ i,
+ length,
+ elem,
+ type,
+ _fired;
+ if ( fired ) {
+ _fired = fired;
+ fired = 0;
+ }
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ deferred.done.apply( deferred, elem );
+ } else if ( type === "function" ) {
+ callbacks.push( elem );
+ }
+ }
+ if ( _fired ) {
+ deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
+ }
+ }
+ return this;
+ },
+
+ // resolve with given context and args
+ resolveWith: function( context, args ) {
+ if ( !cancelled && !fired && !firing ) {
+ // make sure args are available (#8421)
+ args = args || [];
+ firing = 1;
+ try {
+ while( callbacks[ 0 ] ) {
+ callbacks.shift().apply( context, args );
+ }
+ }
+ finally {
+ fired = [ context, args ];
+ firing = 0;
+ }
+ }
+ return this;
+ },
+
+ // resolve with this as context and given arguments
+ resolve: function() {
+ deferred.resolveWith( this, arguments );
+ return this;
+ },
+
+ // Has this deferred been resolved?
+ isResolved: function() {
+ return !!( firing || fired );
+ },
+
+ // Cancel
+ cancel: function() {
+ cancelled = 1;
+ callbacks = [];
+ return this;
+ }
+ };
+
+ return deferred;
+ },
+
+ // Full fledged deferred (two callbacks list)
+ Deferred: function( func ) {
+ var deferred = jQuery._Deferred(),
+ failDeferred = jQuery._Deferred(),
+ promise;
+ // Add errorDeferred methods, then and promise
+ jQuery.extend( deferred, {
+ then: function( doneCallbacks, failCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks );
+ return this;
+ },
+ always: function() {
+ return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
+ },
+ fail: failDeferred.done,
+ rejectWith: failDeferred.resolveWith,
+ reject: failDeferred.resolve,
+ isRejected: failDeferred.isResolved,
+ pipe: function( fnDone, fnFail ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject );
+ } else {
+ newDefer[ action ]( returned );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ if ( promise ) {
+ return promise;
+ }
+ promise = obj = {};
+ }
+ var i = promiseMethods.length;
+ while( i-- ) {
+ obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
+ }
+ return obj;
+ }
+ });
+ // Make sure only one callback list will be used
+ deferred.done( failDeferred.cancel ).fail( deferred.cancel );
+ // Unexpose cancel
+ delete deferred.cancel;
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = arguments,
+ i = 0,
+ length = args.length,
+ count = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ // Strange bug in FF4:
+ // Values changed onto the arguments object sometimes end up as undefined values
+ // outside the $.when method. Cloning the object into a fresh array solves the issue
+ deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
+ }
+ };
+ }
+ if ( length > 1 ) {
+ for( ; i < length; i++ ) {
+ if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return deferred.promise();
+ }
+});
+
+
+
+jQuery.support = (function() {
+
+ var div = document.createElement( "div" ),
+ documentElement = document.documentElement,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ marginDiv,
+ support,
+ fragment,
+ body,
+ bodyStyle,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName( "tbody" ).length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName( "link" ).length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55$/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function click() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ div.detachEvent( "onclick", click );
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains it's value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.firstChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ div.innerHTML = "";
+
+ // Figure out if the W3C box model works as expected
+ div.style.width = div.style.paddingLeft = "1px";
+
+ // We use our own, invisible, body
+ body = document.createElement( "body" );
+ bodyStyle = {
+ visibility: "hidden",
+ width: 0,
+ height: 0,
+ border: 0,
+ margin: 0,
+ // Set background to avoid IE crashes when removing (#9028)
+ background: "none"
+ };
+ for ( i in bodyStyle ) {
+ body.style[ i ] = bodyStyle[ i ];
+ }
+ body.appendChild( div );
+ documentElement.insertBefore( body, documentElement.firstChild );
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ support.boxModel = div.offsetWidth === 2;
+
+ if ( "zoom" in div.style ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
+ }
+
+ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE < 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+ div.innerHTML = "";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ // Remove the body element we added
+ body.innerHTML = "";
+ documentElement.removeChild( body );
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ } ) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ return support;
+})();
+
+// Keep track of boxModel
+jQuery.boxModel = jQuery.support.boxModel;
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([a-z])([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ jQuery.expando ] = id = ++jQuery.uuid;
+ } else {
+ id = jQuery.expando;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+ // metadata on plain JS objects when the object is serialized using
+ // JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
+ } else {
+ cache[ id ] = jQuery.extend(cache[ id ], name);
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // Internal jQuery data is stored in a separate object inside the object's data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data
+ if ( pvt ) {
+ if ( !thisCache[ internalKey ] ) {
+ thisCache[ internalKey ] = {};
+ }
+
+ thisCache = thisCache[ internalKey ];
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
+ // not attempt to inspect the internal events object using jQuery.data, as this
+ // internal data object is undocumented and subject to change.
+ if ( name === "events" && !thisCache[name] ) {
+ return thisCache[ internalKey ] && thisCache[ internalKey ].events;
+ }
+
+ return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando, isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+ var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
+
+ if ( thisCache ) {
+ delete thisCache[ name ];
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !isEmptyDataObject(thisCache) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( pvt ) {
+ delete cache[ id ][ internalKey ];
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ var internalCache = cache[ id ][ internalKey ];
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ if ( jQuery.support.deleteExpando || cache != window ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the entire user cache at once because it's faster than
+ // iterating through each key, but we need to continue to persist internal
+ // data if it existed
+ if ( internalCache ) {
+ cache[ id ] = {};
+ // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+ // metadata on plain JS objects when the object is serialized using
+ // JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+
+ cache[ id ][ internalKey ] = internalCache;
+
+ // Otherwise, we need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ } else if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ jQuery.expando ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ } else {
+ elem[ jQuery.expando ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var data = null;
+
+ if ( typeof key === "undefined" ) {
+ if ( this.length ) {
+ data = jQuery.data( this[0] );
+
+ if ( this[0].nodeType === 1 ) {
+ var attr = this[0].attributes, name;
+ for ( var i = 0, l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( this[0], name, data[ name ] );
+ }
+ }
+ }
+ }
+
+ return data;
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ data = dataAttr( this[0], key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+
+ } else {
+ return this.each(function() {
+ var $this = jQuery( this ),
+ args = [ parts[0], value ];
+
+ $this.triggerHandler( "setData" + parts[1] + "!", args );
+ jQuery.data( this, key, value );
+ $this.triggerHandler( "changeData" + parts[1] + "!", args );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+ var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ !jQuery.isNaN( data ) ? parseFloat( data ) :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
+// property to be considered empty objects; this property always exists in
+// order to make sure JSON.stringify does not expose internal metadata
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery.data( elem, deferDataKey, undefined, true );
+ if ( defer &&
+ ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
+ ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
+ !jQuery.data( elem, markDataKey, undefined, true ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.resolve();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = (type || "fx") + "mark";
+ jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 );
+ if ( count ) {
+ jQuery.data( elem, key, count, true );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ if ( elem ) {
+ type = (type || "fx") + "queue";
+ var q = jQuery.data( elem, type, undefined, true );
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery.data( elem, type, jQuery.makeArray(data), true );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ defer;
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift("inprogress");
+ }
+
+ fn.call(elem, function() {
+ jQuery.dequeue(elem, type);
+ });
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function() {
+ var elem = this;
+ setTimeout(function() {
+ jQuery.dequeue( elem, type );
+ }, time );
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
+ count++;
+ tmp.done( resolve );
+ }
+ }
+ resolve();
+ return defer.promise();
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ rinvalidChar = /\:/,
+ formHook, boolHook;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.prop );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.addClass( value.call(this, i, self.attr("class") || "") );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ var classNames = (value || "").split( rspace );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className ) {
+ elem.className = value;
+
+ } else {
+ var className = " " + elem.className + " ",
+ setClass = elem.className;
+
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+ setClass += " " + classNames[c];
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.removeClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ var classNames = (value || "").split( rspace );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ var className = (" " + elem.className + " ").replace(rclass, " ");
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ";
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ return (elem.value || "").replace(rreturn, "");
+ }
+
+ return undefined;
+ }
+
+ var isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attrFix: {
+ // Always normalize to ensure hook usage
+ tabindex: "tabIndex"
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return undefined;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( !("getAttribute" in elem) ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ var ret, hooks,
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // Normalize the name if needed
+ name = notxml && jQuery.attrFix[ name ] || name;
+
+ hooks = jQuery.attrHooks[ name ];
+
+ if ( !hooks ) {
+ // Use boolHook for boolean attributes
+ if ( rboolean.test( name ) &&
+ (typeof value === "boolean" || value === undefined || value.toLowerCase() === name.toLowerCase()) ) {
+
+ hooks = boolHook;
+
+ // Use formHook for forms and if the name contains certain characters
+ } else if ( formHook && (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) {
+ hooks = formHook;
+ }
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return undefined;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml ) {
+ return hooks.get( elem, name );
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, name ) {
+ var propName;
+ if ( elem.nodeType === 1 ) {
+ name = jQuery.attrFix[ name ] || name;
+
+ if ( jQuery.support.getSetAttribute ) {
+ // Use removeAttribute in browsers that support it
+ elem.removeAttribute( name );
+ } else {
+ jQuery.attr( elem, name, "" );
+ elem.removeAttributeNode( elem.getAttributeNode( name ) );
+ }
+
+ // Set corresponding property to false for boolean attributes
+ if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabIndex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return undefined;
+ }
+
+ var ret, hooks,
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.propFix[ name ] || name;
+
+ hooks = jQuery.propHooks[ name ];
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return (elem[ name ] = value);
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {}
+});
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ return elem[ jQuery.propFix[ name ] || name ] ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = value;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// Use the value property for back compat
+// Use the formHook for button elements in IE6/7 (#1954)
+jQuery.attrHooks.value = {
+ get: function( elem, name ) {
+ if ( formHook && jQuery.nodeName( elem, "button" ) ) {
+ return formHook.get( elem, name );
+ }
+ return elem.value;
+ },
+ set: function( elem, value, name ) {
+ if ( formHook && jQuery.nodeName( elem, "button" ) ) {
+ return formHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !jQuery.support.getSetAttribute ) {
+
+ // propFix is more comprehensive and contains all fixes
+ jQuery.attrFix = jQuery.propFix;
+
+ // Use this for any attribute on a form in IE6/7
+ formHook = jQuery.attrHooks.name = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ // Return undefined if nodeValue is empty string
+ return ret && ret.nodeValue !== "" ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Check form objects in IE (multiple bugs related)
+ // Only use nodeValue if the attribute node exists on the form
+ var ret = elem.getAttributeNode( name );
+ if ( ret ) {
+ ret.nodeValue = value;
+ return value;
+ }
+ }
+ };
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return (elem.style.cssText = "" + value);
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ }
+ });
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
+ }
+ }
+ });
+});
+
+
+
+
+var hasOwn = Object.prototype.hasOwnProperty,
+ rnamespaces = /\.(.*)$/,
+ rformElems = /^(?:textarea|input|select)$/i,
+ rperiod = /\./g,
+ rspaces = / /g,
+ rescape = /[^\w\s.|`]/g,
+ fcleanup = function( nm ) {
+ return nm.replace(rescape, "\\$&");
+ };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function( elem, types, handler, data ) {
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ } else if ( !handler ) {
+ // Fixes bug #7229. Fix recommended by jdalton
+ return;
+ }
+
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure
+ var elemData = jQuery._data( elem );
+
+ // If no elemData is found then we must be trying to bind to one of the
+ // banned noData elements
+ if ( !elemData ) {
+ return;
+ }
+
+ var events = elemData.events,
+ eventHandle = elemData.handle;
+
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ }
+
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native events in IE.
+ eventHandle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ var type, i = 0, namespaces;
+
+ while ( (type = types[ i++ ]) ) {
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
+
+ // Namespaced event handlers
+ if ( type.indexOf(".") > -1 ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+ } else {
+ namespaces = [];
+ handleObj.namespace = "";
+ }
+
+ handleObj.type = type;
+ if ( !handleObj.guid ) {
+ handleObj.guid = handler.guid;
+ }
+
+ // Get the current list of functions bound to this event
+ var handlers = events[ type ],
+ special = jQuery.event.special[ type ] || {};
+
+ // Init the event handler queue
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers.push( handleObj );
+
+ // Keep track of which events have been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, pos ) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ }
+
+ var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ events = elemData && elemData.events;
+
+ if ( !elemData || !events ) {
+ return;
+ }
+
+ // types is actually an event object here
+ if ( types && types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Unbind all events for the element
+ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+ types = types || "";
+
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types );
+ }
+
+ return;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ while ( (type = types[ i++ ]) ) {
+ origType = type;
+ handleObj = null;
+ all = type.indexOf(".") < 0;
+ namespaces = [];
+
+ if ( !all ) {
+ // Namespaced event handlers
+ namespaces = type.split(".");
+ type = namespaces.shift();
+
+ namespace = new RegExp("(^|\\.)" +
+ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ eventType = events[ type ];
+
+ if ( !eventType ) {
+ continue;
+ }
+
+ if ( !handler ) {
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ jQuery.event.remove( elem, origType, handleObj.handler, j );
+ eventType.splice( j--, 1 );
+ }
+ }
+
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+
+ for ( j = pos || 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( handler.guid === handleObj.guid ) {
+ // remove the given handler for the given type
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ if ( pos == null ) {
+ eventType.splice( j--, 1 );
+ }
+
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+
+ if ( pos != null ) {
+ break;
+ }
+ }
+ }
+
+ // remove generic event handler if no more handlers exist
+ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ ret = null;
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ var handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ delete elemData.events;
+ delete elemData.handle;
+
+ if ( jQuery.isEmptyObject( elemData ) ) {
+ jQuery.removeData( elem, undefined, true );
+ }
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ exclusive;
+
+ if ( type.indexOf("!") >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf(".") >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join(".");
+ event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
+
+ // triggerHandler() and global events don't bubble or run the default action
+ if ( onlyHandlers || !elem ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ jQuery.each( jQuery.cache, function() {
+ // internalKey variable is just used to make it easier to find
+ // and potentially change this stuff later; currently it just
+ // points to jQuery.expando
+ var internalKey = jQuery.expando,
+ internalCache = this[ internalKey ];
+ if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
+ jQuery.event.trigger( event, data, internalCache.handle.elem );
+ }
+ });
+ return;
+ }
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ var cur = elem,
+ // IE doesn't like method names with a colon (#3533, #8272)
+ ontype = type.indexOf(":") < 0 ? "on" + type : "";
+
+ // Fire event on the current element, then bubble up the DOM tree
+ do {
+ var handle = jQuery._data( cur, "handle" );
+
+ event.currentTarget = cur;
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Trigger an inline bound script
+ if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
+ event.result = false;
+ event.preventDefault();
+ }
+
+ // Bubble up to document, then to window
+ cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
+ } while ( cur && !event.isPropagationStopped() );
+
+ // If nobody prevented the default action, do it now
+ if ( !event.isDefaultPrevented() ) {
+ var old,
+ special = jQuery.event.special[ type ] || {};
+
+ if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction)() check here because IE6/7 fails that test.
+ // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
+ try {
+ if ( ontype && elem[ type ] ) {
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ }
+ } catch ( ieError ) {}
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+
+ jQuery.event.triggered = undefined;
+ }
+ }
+
+ return event.result;
+ },
+
+ handle: function( event ) {
+ event = jQuery.event.fix( event || window.event );
+ // Snapshot the handlers list since a called handler may add/remove events.
+ var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
+ run_all = !event.exclusive && !event.namespace,
+ args = Array.prototype.slice.call( arguments, 0 );
+
+ // Use the fix-ed Event rather than the (read-only) native event
+ args[0] = event;
+ event.currentTarget = this;
+
+ for ( var j = 0, l = handlers.length; j < l; j++ ) {
+ var handleObj = handlers[ j ];
+
+ // Triggered event must 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event.
+ if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handleObj.handler;
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ var ret = handleObj.handler.apply( this, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+ return event.result;
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ) {
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target ) {
+ // Fixes #1925 where srcElement might not be defined either
+ event.target = event.srcElement || document;
+ }
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement ) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var eventDocument = event.target.ownerDocument || document,
+ doc = eventDocument.documentElement,
+ body = eventDocument.body;
+
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
+ event.which = event.charCode != null ? event.charCode : event.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button !== undefined ) {
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+ }
+
+ return event;
+ },
+
+ // Deprecated, use jQuery.guid instead
+ guid: 1E8,
+
+ // Deprecated, use jQuery.proxy instead
+ proxy: jQuery.proxy,
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady,
+ teardown: jQuery.noop
+ },
+
+ live: {
+ add: function( handleObj ) {
+ jQuery.event.add( this,
+ liveConvert( handleObj.origType, handleObj.selector ),
+ jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
+ },
+
+ remove: function( handleObj ) {
+ jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
+ }
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ }
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !this.preventDefault ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+
+ // set the correct event type
+ event.type = event.data;
+
+ // Firefox sometimes assigns relatedTarget a XUL element
+ // which we cannot access the parentNode property of
+ try {
+
+ // Chrome does something similar, the parentNode property
+ // can be accessed but is null.
+ if ( parent && parent !== document && !parent.parentNode ) {
+ return;
+ }
+
+ // Traverse up the tree
+ while ( parent && parent !== this ) {
+ parent = parent.parentNode;
+ }
+
+ if ( parent !== this ) {
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+
+ // assuming we've left the element since we most likely mousedover a xul element
+ } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+ event.type = event.data;
+ jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ setup: function( data ) {
+ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+ },
+ teardown: function( data ) {
+ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ }
+ };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function( data, namespaces ) {
+ if ( !jQuery.nodeName( this, "form" ) ) {
+ jQuery.event.add(this, "click.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+ trigger( "submit", this, arguments );
+ }
+ });
+
+ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+ trigger( "submit", this, arguments );
+ }
+ });
+
+ } else {
+ return false;
+ }
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialSubmit" );
+ }
+ };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+ var changeFilters,
+
+ getVal = function( elem ) {
+ var type = elem.type, val = elem.value;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ val = elem.checked;
+
+ } else if ( type === "select-multiple" ) {
+ val = elem.selectedIndex > -1 ?
+ jQuery.map( elem.options, function( elem ) {
+ return elem.selected;
+ }).join("-") :
+ "";
+
+ } else if ( jQuery.nodeName( elem, "select" ) ) {
+ val = elem.selectedIndex;
+ }
+
+ return val;
+ },
+
+ testChange = function testChange( e ) {
+ var elem = e.target, data, val;
+
+ if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
+ return;
+ }
+
+ data = jQuery._data( elem, "_change_data" );
+ val = getVal(elem);
+
+ // the current data will be also retrieved by beforeactivate
+ if ( e.type !== "focusout" || elem.type !== "radio" ) {
+ jQuery._data( elem, "_change_data", val );
+ }
+
+ if ( data === undefined || val === data ) {
+ return;
+ }
+
+ if ( data != null || val ) {
+ e.type = "change";
+ e.liveFired = undefined;
+ jQuery.event.trigger( e, arguments[1], elem );
+ }
+ };
+
+ jQuery.event.special.change = {
+ filters: {
+ focusout: testChange,
+
+ beforedeactivate: testChange,
+
+ click: function( e ) {
+ var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+
+ if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
+ testChange.call( this, e );
+ }
+ },
+
+ // Change has to be called before submit
+ // Keydown will be called before keypress, which is used in submit-event delegation
+ keydown: function( e ) {
+ var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+
+ if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
+ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+ type === "select-multiple" ) {
+ testChange.call( this, e );
+ }
+ },
+
+ // Beforeactivate happens also before the previous element is blurred
+ // with this event you can't trigger a change event, but you can store
+ // information
+ beforeactivate: function( e ) {
+ var elem = e.target;
+ jQuery._data( elem, "_change_data", getVal(elem) );
+ }
+ },
+
+ setup: function( data, namespaces ) {
+ if ( this.type === "file" ) {
+ return false;
+ }
+
+ for ( var type in changeFilters ) {
+ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ }
+
+ return rformElems.test( this.nodeName );
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialChange" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+
+ changeFilters = jQuery.event.special.change.filters;
+
+ // Handle when the input is .focus()'d
+ changeFilters.focus = changeFilters.beforeactivate;
+}
+
+function trigger( type, elem, args ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ // Don't pass args or remember liveFired; they apply to the donor event.
+ var event = jQuery.extend( {}, args[ 0 ] );
+ event.type = type;
+ event.originalEvent = {};
+ event.liveFired = undefined;
+ jQuery.event.handle.call( elem, event );
+ if ( event.isDefaultPrevented() ) {
+ args[ 0 ].preventDefault();
+ }
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0;
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+
+ function handler( donor ) {
+ // Donor event is always a native one; fix it and switch its type.
+ // Let focusin/out handler cancel the donor focus/blur event.
+ var e = jQuery.event.fix( donor );
+ e.type = fix;
+ e.originalEvent = {};
+ jQuery.event.trigger( e, null, e.target );
+ if ( e.isDefaultPrevented() ) {
+ donor.preventDefault();
+ }
+ }
+ });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+ jQuery.fn[ name ] = function( type, data, fn ) {
+ var handler;
+
+ // Handle object literals
+ if ( typeof type === "object" ) {
+ for ( var key in type ) {
+ this[ name ](key, data, type[key], fn);
+ }
+ return this;
+ }
+
+ if ( arguments.length === 2 || data === false ) {
+ fn = data;
+ data = undefined;
+ }
+
+ if ( name === "one" ) {
+ handler = function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ };
+ handler.guid = fn.guid || jQuery.guid++;
+ } else {
+ handler = fn;
+ }
+
+ if ( type === "unload" && name !== "one" ) {
+ this.one( type, data, fn );
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.add( this[i], type, handler, data );
+ }
+ }
+
+ return this;
+ };
+});
+
+jQuery.fn.extend({
+ unbind: function( type, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" && !type.preventDefault ) {
+ for ( var key in type ) {
+ this.unbind(key, type[key]);
+ }
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.remove( this[i], type, fn );
+ }
+ }
+
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.live( types, data, fn, selector );
+ },
+
+ undelegate: function( selector, types, fn ) {
+ if ( arguments.length === 0 ) {
+ return this.unbind( "live" );
+
+ } else {
+ return this.die( types, null, fn, selector );
+ }
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+var liveMap = {
+ focus: "focusin",
+ blur: "focusout",
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+ var type, i = 0, match, namespaces, preType,
+ selector = origSelector || this.selector,
+ context = origSelector ? this : jQuery( this.context );
+
+ if ( typeof types === "object" && !types.preventDefault ) {
+ for ( var key in types ) {
+ context[ name ]( key, data, types[key], selector );
+ }
+
+ return this;
+ }
+
+ if ( name === "die" && !types &&
+ origSelector && origSelector.charAt(0) === "." ) {
+
+ context.unbind( origSelector );
+
+ return this;
+ }
+
+ if ( data === false || jQuery.isFunction( data ) ) {
+ fn = data || returnFalse;
+ data = undefined;
+ }
+
+ types = (types || "").split(" ");
+
+ while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
+ if ( type === "hover" ) {
+ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+ continue;
+ }
+
+ preType = type;
+
+ if ( liveMap[ type ] ) {
+ types.push( liveMap[ type ] + namespaces );
+ type = type + namespaces;
+
+ } else {
+ type = (liveMap[ type ] || type) + namespaces;
+ }
+
+ if ( name === "live" ) {
+ // bind live handler
+ for ( var j = 0, l = context.length; j < l; j++ ) {
+ jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+ }
+
+ } else {
+ // unbind live handler
+ context.unbind( "live." + liveConvert( type, selector ), fn );
+ }
+ }
+
+ return this;
+ };
+});
+
+function liveHandler( event ) {
+ var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
+ elems = [],
+ selectors = [],
+ events = jQuery._data( this, "events" );
+
+ // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
+ if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
+ return;
+ }
+
+ if ( event.namespace ) {
+ namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
+
+ } else {
+ live.splice( j--, 1 );
+ }
+ }
+
+ match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+ for ( i = 0, l = match.length; i < l; i++ ) {
+ close = match[i];
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
+ elem = close.elem;
+ related = null;
+
+ // Those two events require additional checking
+ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+ event.type = handleObj.preType;
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+
+ // Make sure not to accidentally match a child element with the same selector
+ if ( related && jQuery.contains( elem, related ) ) {
+ related = elem;
+ }
+ }
+
+ if ( !related || related !== elem ) {
+ elems.push({ elem: elem, handleObj: handleObj, level: close.level });
+ }
+ }
+ }
+ }
+
+ for ( i = 0, l = elems.length; i < l; i++ ) {
+ match = elems[i];
+
+ if ( maxLevel && match.level > maxLevel ) {
+ break;
+ }
+
+ event.currentTarget = match.elem;
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ ret = match.handleObj.origHandler.apply( match.elem, arguments );
+
+ if ( ret === false || event.isPropagationStopped() ) {
+ maxLevel = match.level;
+
+ if ( ret === false ) {
+ stop = false;
+ }
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+
+ return stop;
+}
+
+function liveConvert( type, selector ) {
+ return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.bind( name, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var match,
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var found, item,
+ filter = Expr.filter[ type ],
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+ },
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+ },
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ },
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ var first = match[2],
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+Sizzle.getText = function( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += Sizzle.getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+};
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.POS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && ( typeof selector === "string" ?
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array
+ if ( jQuery.isArray( selectors ) ) {
+ var match, selector,
+ matches = {},
+ level = 1;
+
+ if ( cur && selectors.length ) {
+ for ( i = 0, l = selectors.length; i < l; i++ ) {
+ selector = selectors[i];
+
+ if ( !matches[ selector ] ) {
+ matches[ selector ] = POS.test( selector ) ?
+ jQuery( selector, context || this.context ) :
+ selector;
+ }
+ }
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( selector in matches ) {
+ match = matches[ selector ];
+
+ if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
+ ret.push({ selector: selector, elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ if ( !elem || typeof elem === "string" ) {
+ return jQuery.inArray( this[0],
+ // If it receives a string, the selector is used
+ // If it receives nothing, the siblings are used
+ elem ? jQuery( elem ) : this.parent().children() );
+ }
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until ),
+ // The variable 'args' was introduced in
+ // https://github.com/jquery/jquery/commit/52a0238
+ // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
+ // http://code.google.com/p/v8/issues/detail?id=1050
+ args = slice.call(arguments);
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, args.join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return (elem === qualifier) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ });
+}
+
+
+
+
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery( this );
+
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function() {
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery(arguments[0]);
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery(arguments[0]).toArray() );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+ value = value.replace(rxhtmlTag, "<$1></$2>");
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery( this );
+
+ self.html( value.call(this, i, self.html()) );
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ // Make sure that we do not leak memory by inadvertently discarding
+ // the original fragment (which might have attached data) instead of
+ // using it; in addition, use the original fragment object for the last
+ // item instead of first because it can end up being emptied incorrectly
+ // in certain situations (Bug #8070).
+ // Fragments from the fragment cache must always be cloned and never used
+ // in place.
+ results.cacheable || (l > 1 && i < lastIndex) ?
+ jQuery.clone( fragment, true, true ) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando,
+ oldData = jQuery.data( src ),
+ curData = jQuery.data( dest, oldData );
+
+ // Switch to use the internal data object, if it exists, for the next
+ // stage of data copying
+ if ( (oldData = oldData[ internalKey ]) ) {
+ var events = oldData.events;
+ curData = curData[ internalKey ] = jQuery.extend({}, oldData);
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( var type in events ) {
+ for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
+ }
+ }
+ }
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 fail to clone children inside object elements that use
+ // the proprietary classid attribute value (rather than the type
+ // attribute) to identify the type of content to display
+ if ( nodeName === "object" ) {
+ dest.outerHTML = src.outerHTML;
+
+ } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+ if ( src.checked ) {
+ dest.defaultChecked = dest.checked = src.checked;
+ }
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults,
+ doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+ args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+ cacheable = true;
+
+ cacheresults = jQuery.fragments[ args[0] ];
+ if ( cacheresults && cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( "getElementsByTagName" in elem ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( "querySelectorAll" in elem ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ fixDefaultChecked( elem );
+ } else if ( elem.getElementsByTagName ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var clone = elem.cloneNode(true),
+ srcElements,
+ destElements,
+ i;
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName
+ // instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var checkScriptType;
+
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ var ret = [], j;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+ }
+
+ // Resets defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ var len;
+ if ( !jQuery.support.appendChecked ) {
+ if ( elem[0] && typeof (len = elem.length) === "number" ) {
+ for ( j = 0; j < len; j++ ) {
+ findInputs( elem[j] );
+ }
+ } else {
+ findInputs( elem );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ checkScriptType = function( elem ) {
+ return !elem.type || rscriptType.test( elem.type );
+ };
+ for ( i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType );
+
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ] && cache[ id ][ internalKey ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+
+ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+ if ( data.handle ) {
+ data.handle.elem = null;
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ rdashAlpha = /-([a-z])/ig,
+ // fixed for IE9, see #8346
+ rupper = /([A-Z]|^ms)/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+ rrelNum = /^[+\-]=/,
+ rrelNumFilter = /[^+\-\.\de]+/g,
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+ curCSS,
+
+ getComputedStyle,
+ currentStyle,
+
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn.css = function( name, value ) {
+ // Setting 'undefined' is a no-op
+ if ( arguments.length === 2 && value === undefined ) {
+ return this;
+ }
+
+ return jQuery.access( this, name, value, true, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ });
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity", "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "zIndex": true,
+ "fontWeight": true,
+ "opacity": true,
+ "zoom": true,
+ "lineHeight": true,
+ "widows": true,
+ "orphans": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( type === "number" && isNaN( value ) || value == null ) {
+ return;
+ }
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && rrelNum.test( value ) ) {
+ value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) );
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ var ret, hooks;
+
+ // Make sure that we're working with the right name
+ name = jQuery.camelCase( name );
+ hooks = jQuery.cssHooks[ name ];
+ name = jQuery.cssProps[ name ] || name;
+
+ // cssFloat needs a special treatment
+ if ( name === "cssFloat" ) {
+ name = "float";
+ }
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ },
+
+ camelCase: function( string ) {
+ return string.replace( rdashAlpha, fcamelCase );
+ }
+});
+
+// DEPRECATED, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+jQuery.each(["height", "width"], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ var val;
+
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ val = getWH( elem, name, extra );
+
+ } else {
+ jQuery.swap( elem, cssShow, function() {
+ val = getWH( elem, name, extra );
+ });
+ }
+
+ if ( val <= 0 ) {
+ val = curCSS( elem, name, name );
+
+ if ( val === "0px" && currentStyle ) {
+ val = currentStyle( elem, name, name );
+ }
+
+ if ( val != null ) {
+ // Should return "auto" instead of 0, use 0 for
+ // temporary backwards-compat
+ return val === "" || val === "auto" ? "0px" : val;
+ }
+ }
+
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+
+ // Should return "auto" instead of 0, use 0 for
+ // temporary backwards-compat
+ return val === "" || val === "auto" ? "0px" : val;
+ }
+
+ return typeof val === "string" ? val : val + "px";
+ }
+ },
+
+ set: function( elem, value ) {
+ if ( rnumpx.test( value ) ) {
+ // ignore negative width and height values #1599
+ value = parseFloat(value);
+
+ if ( value >= 0 ) {
+ return value + "px";
+ }
+
+ } else {
+ return value;
+ }
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle;
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ var opacity = jQuery.isNaN( value ) ?
+ "" :
+ "alpha(opacity=" + value * 100 + ")",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+jQuery(function() {
+ // This hook cannot be added until DOM ready because the support test
+ // for it is not run until after DOM ready
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ var ret;
+ jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ ret = curCSS( elem, "margin-right", "marginRight" );
+ } else {
+ ret = elem.style.marginRight;
+ }
+ });
+ return ret;
+ }
+ };
+ }
+});
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, name ) {
+ var ret, defaultView, computedStyle;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( !(defaultView = elem.ownerDocument.defaultView) ) {
+ return undefined;
+ }
+
+ if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
+ style = elem.style;
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+ // Remember the original values
+ left = style.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : (ret || 0);
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWH( elem, name, extra ) {
+ var which = name === "width" ? cssWidth : cssHeight,
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" ) {
+ return val;
+ }
+
+ jQuery.each( which, function() {
+ if ( !extra ) {
+ val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
+ }
+
+ if ( extra === "margin" ) {
+ val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;
+
+ } else {
+ val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
+ }
+ });
+
+ return val;
+}
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rspacesAjax = /\s+/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts;
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ if ( jQuery.isFunction( func ) ) {
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+ i = 0,
+ length = dataTypes.length,
+ dataType,
+ list,
+ placeBefore;
+
+ // For each dataType in the dataTypeExpression
+ for(; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters ),
+ selection;
+
+ for(; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf( " " );
+ if ( off >= 0 ) {
+ var selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ // Complete callback (responseText is used internally)
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
+ // If successful, inject the HTML into all the matched elements
+ if ( jqXHR.isResolved() ) {
+ // #4825: Get the actual response in case
+ // a dataFilter is present in ajaxSettings
+ jqXHR.done(function( r ) {
+ responseText = r;
+ });
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [ responseText, status, jqXHR ] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.bind( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function ( target, settings ) {
+ if ( !settings ) {
+ // Only one parameter, we extend ajaxSettings
+ settings = target;
+ target = jQuery.extend( true, jQuery.ajaxSettings, settings );
+ } else {
+ // target was provided, we extend into it
+ jQuery.extend( true, target, jQuery.ajaxSettings, settings );
+ }
+ // Flatten fields we don't want deep extended
+ for( var field in { context: 1, url: 1 } ) {
+ if ( field in settings ) {
+ target[ field ] = settings[ field ];
+ } else if( field in jQuery.ajaxSettings ) {
+ target[ field ] = jQuery.ajaxSettings[ field ];
+ }
+ }
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": "*/*"
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery._Deferred(),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // The jqXHR state
+ state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || "abort";
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, statusText, responses, headers ) {
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status ? 4 : 0;
+
+ var isSuccess,
+ success,
+ error,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+ lastModified,
+ etag;
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ }
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ try {
+ success = ajaxConvert( s, response );
+ statusText = "success";
+ isSuccess = true;
+ } catch(e) {
+ // We have a parsererror
+ statusText = "parsererror";
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = statusText;
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.done;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+
+ // Determine if a cross-domain request is in order
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefiler, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already
+ jqXHR.abort();
+ return false;
+
+ }
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( status < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ jQuery.error( e );
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : value;
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj === "object" ) {
+ // Serialize object item.
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields,
+ ct,
+ type,
+ finalDataType,
+ firstDataType;
+
+ // Fill responseXXX fields
+ for( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ var dataTypes = s.dataTypes,
+ converters = {},
+ i,
+ key,
+ length = dataTypes.length,
+ tmp,
+ // Current and previous dataTypes
+ current = dataTypes[ 0 ],
+ prev,
+ // Conversion expression
+ conversion,
+ // Conversion function
+ conv,
+ // Conversion functions (transitive conversion)
+ conv1,
+ conv2;
+
+ // For each dataType in the chain
+ for( i = 1; i < length; i++ ) {
+
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for( key in s.converters ) {
+ if( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
+ // Get the dataTypes
+ prev = current;
+ current = dataTypes[ i ];
+
+ // If current is auto dataType, update it to prev
+ if( current === "*" ) {
+ current = prev;
+ // If no auto and dataTypes are actually different
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Get the converter
+ conversion = prev + " " + current;
+ conv = converters[ conversion ] || converters[ "* " + current ];
+
+ // If there is no direct converter, search transitively
+ if ( !conv ) {
+ conv2 = undefined;
+ for( conv1 in converters ) {
+ tmp = conv1.split( " " );
+ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+ conv2 = converters[ tmp[1] + " " + current ];
+ if ( conv2 ) {
+ conv1 = converters[ conv1 ];
+ if ( conv1 === true ) {
+ conv = conv2;
+ } else if ( conv2 === true ) {
+ conv = conv1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ // If we found no converter, dispatch an error
+ if ( !( conv || conv2 ) ) {
+ jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
+ }
+ // If found converter is not an equivalence
+ if ( conv !== true ) {
+ // Convert with 1 or 2 converters accordingly
+ response = conv ? conv( response ) : conv2( conv1(response) );
+ }
+ }
+ }
+ return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+ jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ return jQuery.expando + "_" + ( jsc++ );
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var inspectData = s.contentType === "application/x-www-form-urlencoded" &&
+ ( typeof s.data === "string" );
+
+ if ( s.dataTypes[ 0 ] === "jsonp" ||
+ s.jsonp !== false && ( jsre.test( s.url ) ||
+ inspectData && jsre.test( s.data ) ) ) {
+
+ var responseContainer,
+ jsonpCallback = s.jsonpCallback =
+ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+ previous = window[ jsonpCallback ],
+ url = s.url,
+ data = s.data,
+ replace = "$1" + jsonpCallback + "$2";
+
+ if ( s.jsonp !== false ) {
+ url = url.replace( jsre, replace );
+ if ( s.url === url ) {
+ if ( inspectData ) {
+ data = data.replace( jsre, replace );
+ }
+ if ( s.data === data ) {
+ // Add callback manually
+ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
+ }
+ }
+ }
+
+ s.url = url;
+ s.data = data;
+
+ // Install callback
+ window[ jsonpCallback ] = function( response ) {
+ responseContainer = [ response ];
+ };
+
+ // Clean-up function
+ jqXHR.always(function() {
+ // Set callback back to previous value
+ window[ jsonpCallback ] = previous;
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( previous ) ) {
+ window[ jsonpCallback ]( responseContainer[ 0 ] );
+ }
+ });
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( jsonpCallback + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0,
+ xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var xhr = s.xhr(),
+ handle,
+ i;
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occured
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+ responses.text = xhr.responseText;
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ // if we're in sync mode or it's in cache
+ // and has been retrieved directly (IE6 & IE7)
+ // we need to manually fire the callback
+ if ( !s.async || xhr.readyState === 4 ) {
+ callback();
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+
+
+
+
+var elemdisplay = {},
+ iframe, iframeDoc,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ],
+ fxNow,
+ requestAnimationFrame = window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame;
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[i];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
+ jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[i];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery._data(elem, "olddisplay") || "";
+ }
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ if ( this[i].style ) {
+ var display = jQuery.css( this[i], "display" );
+
+ if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
+ jQuery._data( this[i], "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ if ( this[i].style ) {
+ this[i].style.display = "none";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
+ }
+
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
+ return this[ optall.queue === false ? "each" : "queue" ](function() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ if ( optall.queue === false ) {
+ jQuery._mark( this );
+ }
+
+ var opt = jQuery.extend( {}, optall ),
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ name, val, p,
+ display, e,
+ parts, start, end, unit;
+
+ // will store per property easing and be used to determine when an animation is complete
+ opt.animatedProperties = {};
+
+ for ( p in prop ) {
+
+ // property name normalization
+ name = jQuery.camelCase( p );
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ }
+
+ val = prop[ name ];
+
+ // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
+ if ( jQuery.isArray( val ) ) {
+ opt.animatedProperties[ name ] = val[ 1 ];
+ val = prop[ name ] = val[ 0 ];
+ } else {
+ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
+ }
+
+ if ( val === "hide" && hidden || val === "show" && !hidden ) {
+ return opt.complete.call( this );
+ }
+
+ if ( isElement && ( name === "height" || name === "width" ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height
+ // animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+ if ( !jQuery.support.inlineBlockNeedsLayout ) {
+ this.style.display = "inline-block";
+
+ } else {
+ display = defaultDisplay( this.nodeName );
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( display === "inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.display = "inline";
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ for ( p in prop ) {
+ e = new jQuery.fx( this, opt, p );
+ val = prop[ p ];
+
+ if ( rfxtypes.test(val) ) {
+ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]();
+
+ } else {
+ parts = rfxnum.exec( val );
+ start = e.cur();
+
+ if ( parts ) {
+ end = parseFloat( parts[2] );
+ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, p, (end || 1) + unit);
+ start = ((end || 1) / e.cur()) * start;
+ jQuery.style( this, p, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ }
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function( clearQueue, gotoEnd ) {
+ if ( clearQueue ) {
+ this.queue([]);
+ }
+
+ this.each(function() {
+ var timers = jQuery.timers,
+ i = timers.length;
+ // clear marker counters if we know they won't be
+ if ( !gotoEnd ) {
+ jQuery._unmark( true, this );
+ }
+ while ( i-- ) {
+ if ( timers[i].elem === this ) {
+ if (gotoEnd) {
+ // force the next step to be the last
+ timers[i](true);
+ }
+
+ timers.splice(i, 1);
+ }
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if ( !gotoEnd ) {
+ this.dequeue();
+ }
+
+ return this;
+ }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout( clearFxNow, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+ fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function( noUnmark ) {
+ if ( opt.queue !== false ) {
+ jQuery.dequeue( this );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
+ }
+
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ options.orig = options.orig || {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var parsed,
+ r = jQuery.css( this.elem, this.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx,
+ raf;
+
+ this.startTime = fxNow || createFxNow();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ function t( gotoEnd ) {
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ // Use requestAnimationFrame instead of setInterval if available
+ if ( requestAnimationFrame ) {
+ timerId = 1;
+ raf = function() {
+ // When timerId gets set to null at any point, this stops
+ if ( timerId ) {
+ requestAnimationFrame( raf );
+ fx.tick();
+ }
+ };
+ requestAnimationFrame( raf );
+ } else {
+ timerId = setInterval( fx.tick, fx.interval );
+ }
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var t = fxNow || createFxNow(),
+ done = true,
+ elem = this.elem,
+ options = this.options,
+ i, n;
+
+ if ( gotoEnd || t >= options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ options.animatedProperties[ this.prop ] = true;
+
+ for ( i in options.animatedProperties ) {
+ if ( options.animatedProperties[i] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+ jQuery.each( [ "", "X", "Y" ], function (index, value) {
+ elem.style[ "overflow" + value ] = options.overflow[index];
+ });
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide ) {
+ jQuery(elem).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show ) {
+ for ( var p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[p] );
+ }
+ }
+
+ // Execute the complete function
+ options.complete.call( elem );
+ }
+
+ return false;
+
+ } else {
+ // classical easing cannot be used with an Infinity duration
+ if ( options.duration == Infinity ) {
+ this.now = t;
+ } else {
+ n = t - this.startTime;
+ this.state = n / options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration );
+ this.now = this.start + ((this.end - this.start) * this.pos);
+ }
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) {
+ if ( !timers[i]() ) {
+ timers.splice(i--, 1);
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+ if ( !elemdisplay[ nodeName ] ) {
+
+ var elem = jQuery( "<" + nodeName + ">" ).appendTo( "body" ),
+ display = elem.css( "display" );
+
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // No iframe to use yet, so create it
+ if ( !iframe ) {
+ iframe = document.createElement( "iframe" );
+ iframe.frameBorder = iframe.width = iframe.height = 0;
+ }
+
+ document.body.appendChild( iframe );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake html
+ // document to it, Webkit & Firefox won't allow reusing the iframe document
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( "<!doctype><html><body></body></html>" );
+ }
+
+ elem = iframeDoc.createElement( nodeName );
+
+ iframeDoc.body.appendChild( elem );
+
+ display = jQuery.css( elem, "display" );
+
+ document.body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0], box;
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ var doc = elem.ownerDocument,
+ docElem = doc.documentElement;
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow(doc),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
+ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ jQuery.offset.initialize();
+
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ doc = elem.ownerDocument,
+ docElem = doc.documentElement,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+ initialize: function() {
+ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
+ html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+ container.innerHTML = html;
+ body.insertBefore( container, body.firstChild );
+ innerDiv = container.firstChild;
+ checkDiv = innerDiv.firstChild;
+ td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ checkDiv.style.position = "fixed";
+ checkDiv.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+ checkDiv.style.position = checkDiv.style.top = "";
+
+ innerDiv.style.overflow = "hidden";
+ innerDiv.style.position = "relative";
+
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+ body.removeChild( container );
+ jQuery.offset.initialize = jQuery.noop;
+ },
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ jQuery.offset.initialize();
+
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if (options.top != null) {
+ props.top = (options.top - curOffset.top) + curTop;
+ }
+ if (options.left != null) {
+ props.left = (options.left - curOffset.left) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function( val ) {
+ var elem, win;
+
+ if ( val === undefined ) {
+ elem = this[ 0 ];
+
+ if ( !elem ) {
+ return null;
+ }
+
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery( win ).scrollLeft(),
+ i ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function() {
+ return this[0] ?
+ parseFloat( jQuery.css( this[0], type, "padding" ) ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function( margin ) {
+ return this[0] ?
+ parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ if ( jQuery.isWindow( elem ) ) {
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+ var docElemProp = elem.document.documentElement[ "client" + name ];
+ return elem.document.compatMode === "CSS1Compat" && docElemProp ||
+ elem.document.body[ "client" + name ] || docElemProp;
+
+ // Get document width or height
+ } else if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ return Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" + name],
+ elem.body["offset" + name], elem.documentElement["offset" + name]
+ );
+
+ // Get or set width or height on the element
+ } else if ( size === undefined ) {
+ var orig = jQuery.css( elem, type ),
+ ret = parseFloat( orig );
+
+ return jQuery.isNaN( ret ) ? orig : ret;
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ } else {
+ return this.css( type, typeof size === "string" ? size : size + "px" );
+ }
+ };
+
+});
+
+
+window.jQuery = window.$ = jQuery;
+})(window);
diff --git a/samples/javascript/jquery-1.6.1.min.js b/samples/javascript/jquery-1.6.1.min.js
new file mode 100644
index 0000000..b2ac174
--- /dev/null
+++ b/samples/javascript/jquery-1.6.1.min.js
@@ -0,0 +1,18 @@
+/*!
+ * jQuery JavaScript Library v1.6.1
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu May 12 15:04:36 2011 -0400
+ */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!cj[a]){var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),c.body.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write("<!doctype><html><body></body></html>");b=cl.createElement(a),cl.body.appendChild(b),d=f.css(b,"display"),c.body.removeChild(ck)}cj[a]=d}return cj[a]}function cu(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function ct(){cq=b}function cs(){setTimeout(ct,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bF.test(a)?d(a,e):b_(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bU,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bQ),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bD(a,b,c){var d=b==="width"?bx:by,e=b==="width"?a.offsetWidth:a.offsetHeight;if(c==="border")return e;f.each(d,function(){c||(e-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?e+=parseFloat(f.css(a,"margin"+this))||0:e-=parseFloat(f.css(a,"border"+this+"Width"))||0});return e}function bn(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bm(a){f.nodeName(a,"input")?bl(a):a.getElementsByTagName&&f.grep(a.getElementsByTagName("input"),bl)}function bl(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bk(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bj(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bi(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bh(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function X(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(S.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function W(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function O(a,b){return(a&&a!=="*"?a+".":"")+b.replace(A,"`").replace(B,"&")}function N(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(y,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function L(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function F(){return!0}function E(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function H(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(H,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=d.userAgent,x,y,z,A=Object.prototype.toString,B=Object.prototype.hasOwnProperty,C=Array.prototype.push,D=Array.prototype.slice,E=String.prototype.trim,F=Array.prototype.indexOf,G={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.1",length:0,size:function(){return this.length},toArray:function(){return D.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?C.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(D.apply(this,arguments),"slice",D.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:C,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;y.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!y){y=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",z,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",z),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&H()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):G[A.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!B.call(a,"constructor")&&!B.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||B.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:E?function(a){return a==null?"":E.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?C.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(F)return F.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=D.call(arguments,2),g=function(){return a.apply(c,f.concat(D.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){G["[object "+b+"]"]=b.toLowerCase()}),x=e.uaMatch(w),x.browser&&(e.browser[x.browser]=!0,e.browser.version=x.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?z=function(){c.removeEventListener("DOMContentLoaded",z,!1),e.ready()}:c.attachEvent&&(z=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",z),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g](h)}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};f=c.createElement("select"),g=f.appendChild(c.createElement("option")),h=a.getElementsByTagName("input")[0],j={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},h.checked=!0,j.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,j.optDisabled=!g.disabled;try{delete a.test}catch(s){j.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function b(){j.noCloneEvent=!1,a.detachEvent("onclick",b)}),a.cloneNode(!0).fireEvent("onclick")),h=c.createElement("input"),h.value="t",h.setAttribute("type","radio"),j.radioValue=h.value==="t",h.setAttribute("checked","checked"),a.appendChild(h),k=c.createDocumentFragment(),k.appendChild(a.firstChild),j.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",l=c.createElement("body"),m={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"};for(q in m)l.style[q]=m[q];l.appendChild(a),b.insertBefore(l,b.firstChild),j.appendChecked=h.checked,j.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,j.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",j.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",n=a.getElementsByTagName("td"),r=n[0].offsetHeight===0,n[0].style.display="",n[1].style.display="none",j.reliableHiddenOffsets=r&&n[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(i=c.createElement("div"),i.style.width="0",i.style.marginRight="0",a.appendChild(i),j.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(i,null)||{marginRight:0}).marginRight,10)||0)===0),l.innerHTML="",b.removeChild(l);if(a.attachEvent)for(q in{submit:1,change:1,focusin:1})p="on"+q,r=p in a,r||(a.setAttribute(p,"return;"),r=typeof a[p]=="function"),j[q+"Bubbles"]=r;return j}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u=/\:/,v,w;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.addClass(a.call(this,b,c.attr("class")||""))});if(a&&typeof a=="string"){var b=(a||"").split(o);for(var c=0,d=this.length;c<d;c++){var e=this[c];if(e.nodeType===1)if(!e.className)e.className=a;else{var g=" "+e.className+" ",h=e.className;for(var i=0,j=b.length;i<j;i++)g.indexOf(" "+b[i]+" ")<0&&(h+=" "+b[i]);e.className=f.trim(h)}}}return this},removeClass:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.removeClass(a.call(this,b,c.attr("class")))});if(a&&typeof a=="string"||a===b){var c=(a||"").split(o);for(var d=0,e=this.length;d<e;d++){var g=this[d];if(g.nodeType===1&&g.className)if(a){var h=(" "+g.className+" ").replace(n," ");for(var i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){var d=f(this);d.toggleClass(a.call(this,c,d.attr("class"),b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;return(e.value||"").replace(p,"")}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);c=j&&f.attrFix[c]||c,i=f.attrHooks[c],i||(!t.test(c)||typeof d!="boolean"&&d!==b&&d.toLowerCase()!==c.toLowerCase()?v&&(f.nodeName(a,"form")||u.test(c))&&(i=v):i=w);if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j)return i.get(a,c);h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);c=i&&f.propFix[c]||c,h=f.propHooks[c];return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return a[f.propFix[c]||c]?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=b),a.setAttribute(c,c.toLowerCase()));return c}},f.attrHooks.value={get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return a.value},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=Object.prototype.hasOwnProperty,y=/\.(.*)$/,z=/^(?:textarea|input|select)$/i,A=/\./g,B=/ /g,C=/[^\w\s.|`]/g,D=function(a){return a.replace(C,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=E;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=E);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),D).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem
+)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,O(a.origType,a.selector),f.extend({},a,{handler:N,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,O(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?F:E):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=F;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=F;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=F,this.stopPropagation()},isDefaultPrevented:E,isPropagationStopped:E,isImmediatePropagationStopped:E};var G=function(a){var b=a.relatedTarget;a.type=a.data;try{if(b&&b!==c&&!b.parentNode)return;while(b&&b!==this)b=b.parentNode;b!==this&&f.event.handle.apply(this,arguments)}catch(d){}},H=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?H:G,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?H:G)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&L("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&L("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var I,J=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},K=function(c){var d=c.target,e,g;if(!!z.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=J(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:K,beforedeactivate:K,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&K.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&K.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",J(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in I)f.event.add(this,c+".specialChange",I[c]);return z.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return z.test(this.nodeName)}},I=f.event.special.change.filters,I.focus=I.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var M={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||E,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=y.exec(h),k="",j&&(k=j[0],h=h.replace(y,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,M[h]?(a.push(M[h]+k),h=h+k):h=(M[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+O(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+O(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var P=/Until$/,Q=/^(?:parents|prevUntil|prevAll)/,R=/,/,S=/^.[^:#\[\.,]*$/,T=Array.prototype.slice,U=f.expr.match.POS,V={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(X(this,a,!1),"not",a)},filter:function(a){return this.pushStack(X(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=U.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=U.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(W(c[0])||W(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=T.call(arguments);P.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!V[a]?f.unique(e):e,(this.length>1||R.test(d))&&Q.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y=/ jQuery\d+="(?:\d+|null)"/g,Z=/^\s+/,$=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,_=/<([\w:]+)/,ba=/<tbody/i,bb=/<|&#?\w+;/,bc=/<(?:script|object|embed|option|style)/i,bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Y,""):null;if(typeof a=="string"&&!bc.test(a)&&(f.support.leadingWhitespace||!Z.test(a))&&!bg[(_.exec(a)||["",""])[1].toLowerCase()]){a=a.replace($,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bh(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bn)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i=b&&b[0]?b[0].ownerDocument||b[0]:c;a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!bc.test(a[0])&&(f.support.checkClone||!bd.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bj(a,d),e=bk(a),g=bk(d);for(h=0;e[h];++h)bj(e[h],g[h])}if(b){bi(a,d);if(c){e=bk(a),g=bk(d);for(h=0;e[h];++h)bi(e[h],g[h])}}return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||
+b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bb.test(k))k=b.createTextNode(k);else{k=k.replace($,"<$1></$2>");var l=(_.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=ba.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Z.test(k)&&o.insertBefore(b.createTextNode(Z.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bm(k[i]);else bm(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bo=/alpha\([^)]*\)/i,bp=/opacity=([^)]*)/,bq=/-([a-z])/ig,br=/([A-Z]|^ms)/g,bs=/^-?\d+(?:px)?$/i,bt=/^-?\d/,bu=/^[+\-]=/,bv=/[^+\-\.\de]+/g,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB,bC=function(a,b){return b.toUpperCase()};f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0,widows:!0,orphans:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bu.test(d)&&(d=+d.replace(bv,"")+parseFloat(f.css(a,c))),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bq,bC)}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){a.offsetWidth!==0?e=bD(a,b,d):f.swap(a,bw,function(){e=bD(a,b,d)});if(e<=0){e=bz(a,b,b),e==="0px"&&bB&&(e=bB(a,b,b));if(e!=null)return e===""||e==="auto"?"0px":e}if(e<0||e==null){e=a.style[b];return e===""||e==="auto"?"0px":e}return typeof e=="string"?e:e+"px"}},set:function(a,b){if(!bs.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bp.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bo.test(g)?g.replace(bo,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,c){var d,e,g;c=c.replace(br,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bs.test(d)&&bt.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bE=/%20/g,bF=/\[\]$/,bG=/\r?\n/g,bH=/#.*$/,bI=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bJ=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bK=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bL=/^(?:GET|HEAD)$/,bM=/^\/\//,bN=/\?/,bO=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bP=/^(?:select|textarea)/i,bQ=/\s+/,bR=/([?&])_=[^&]*/,bS=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bT=f.fn.load,bU={},bV={},bW,bX;try{bW=e.href}catch(bY){bW=c.createElement("a"),bW.href="",bW=bW.href}bX=bS.exec(bW.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bT)return bT.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bO,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bP.test(this.nodeName)||bJ.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bG,"\r\n")}}):{name:b.name,value:c.replace(bG,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bW,isLocal:bK.test(bX[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bZ(bU),ajaxTransport:bZ(bV),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?ca(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=cb(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bI.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bH,"").replace(bM,bX[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bQ),d.crossDomain==null&&(r=bS.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bX[1]&&r[2]==bX[2]&&(r[3]||(r[1]==="http:"?80:443))==(bX[3]||(bX[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bU,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bL.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bN.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bR,"$1_="+x);d.url=y+(y===d.url?(bN.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bV,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bE,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq,cr=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cv(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cm.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=cn.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this),f.isFunction(d.old)&&d.old.call(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function h(a){return d.step(a)}var d=this,e=f.fx,g;this.startTime=cq||cs(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,h.elem=this.elem,h()&&f.timers.push(h)&&!co&&(cr?(co=1,g=function(){co&&(cr(g),e.tick())},cr(g)):co=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cq||cs(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){return this[0]?parseFloat(f.css(this[0],d,"padding")):null},f.fn["outer"+c]=function(a){return this[0]?parseFloat(f.css(this[0],d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file
diff --git a/samples/javascript/jquery-1.7.2.js b/samples/javascript/jquery-1.7.2.js
new file mode 100644
index 0000000..3774ff9
--- /dev/null
+++ b/samples/javascript/jquery-1.7.2.js
@@ -0,0 +1,9404 @@
+/*!
+ * jQuery JavaScript Library v1.7.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Mar 21 12:46:34 2012 -0700
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Matches dashed string for camelizing
+ rdashAlpha = /-([a-z]|[0-9])/ig,
+ rmsPrefix = /^-ms-/,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return ( letter + "" ).toUpperCase();
+ },
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = ( context ? context.ownerDocument || context : document );
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.7.2",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.add( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ i = +i;
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.fireWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).off( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery.Callbacks( "once memory" );
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+ var xml, tmp;
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array, i ) {
+ var len;
+
+ if ( array ) {
+ if ( indexOf ) {
+ return indexOf.call( array, elem, i );
+ }
+
+ len = array.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in array && array[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+ var exec,
+ bulk = key == null,
+ i = 0,
+ length = elems.length;
+
+ // Sets many values
+ if ( key && typeof key === "object" ) {
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+ }
+ chainable = 1;
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = pass === undefined && jQuery.isFunction( value );
+
+ if ( bulk ) {
+ // Bulk operations only iterate when executing function values
+ if ( exec ) {
+ exec = fn;
+ fn = function( elem, key, value ) {
+ return exec.call( jQuery( elem ), value );
+ };
+
+ // Otherwise they run against the entire set
+ } else {
+ fn.call( elems, value );
+ fn = null;
+ }
+ }
+
+ if ( fn ) {
+ for (; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+ }
+
+ chainable = 1;
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+return jQuery;
+
+})();
+
+
+// String to Object flags format cache
+var flagsCache = {};
+
+// Convert String-formatted flags into Object-formatted ones and store in cache
+function createFlags( flags ) {
+ var object = flagsCache[ flags ] = {},
+ i, length;
+ flags = flags.split( /\s+/ );
+ for ( i = 0, length = flags.length; i < length; i++ ) {
+ object[ flags[i] ] = true;
+ }
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * flags: an optional list of space-separated flags that will change how
+ * the callback list behaves
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible flags:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( flags ) {
+
+ // Convert flags from String-formatted to Object-formatted
+ // (we check in cache first)
+ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+
+ var // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = [],
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Add one or several callbacks to the list
+ add = function( args ) {
+ var i,
+ length,
+ elem,
+ type,
+ actual;
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ // Inspect recursively
+ add( elem );
+ } else if ( type === "function" ) {
+ // Add if not in unique mode and callback is not in
+ if ( !flags.unique || !self.has( elem ) ) {
+ list.push( elem );
+ }
+ }
+ }
+ },
+ // Fire callbacks
+ fire = function( context, args ) {
+ args = args || [];
+ memory = !flags.memory || [ context, args ];
+ fired = true;
+ firing = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
+ memory = true; // Mark as halted
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( !flags.once ) {
+ if ( stack && stack.length ) {
+ memory = stack.shift();
+ self.fireWith( memory[ 0 ], memory[ 1 ] );
+ }
+ } else if ( memory === true ) {
+ self.disable();
+ } else {
+ list = [];
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ var length = list.length;
+ add( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away, unless previous
+ // firing was halted (stopOnFalse)
+ } else if ( memory && memory !== true ) {
+ firingStart = length;
+ fire( memory[ 0 ], memory[ 1 ] );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ var args = arguments,
+ argIndex = 0,
+ argLength = args.length;
+ for ( ; argIndex < argLength ; argIndex++ ) {
+ for ( var i = 0; i < list.length; i++ ) {
+ if ( args[ argIndex ] === list[ i ] ) {
+ // Handle firingIndex and firingLength
+ if ( firing ) {
+ if ( i <= firingLength ) {
+ firingLength--;
+ if ( i <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ // Remove the element
+ list.splice( i--, 1 );
+ // If we have some unicity property then
+ // we only need to do this once
+ if ( flags.unique ) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ if ( list ) {
+ var i = 0,
+ length = list.length;
+ for ( ; i < length; i++ ) {
+ if ( fn === list[ i ] ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory || memory === true ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( stack ) {
+ if ( firing ) {
+ if ( !flags.once ) {
+ stack.push( [ context, args ] );
+ }
+ } else if ( !( flags.once && memory ) ) {
+ fire( context, args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+
+
+
+
+var // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var doneList = jQuery.Callbacks( "once memory" ),
+ failList = jQuery.Callbacks( "once memory" ),
+ progressList = jQuery.Callbacks( "memory" ),
+ state = "pending",
+ lists = {
+ resolve: doneList,
+ reject: failList,
+ notify: progressList
+ },
+ promise = {
+ done: doneList.add,
+ fail: failList.add,
+ progress: progressList.add,
+
+ state: function() {
+ return state;
+ },
+
+ // Deprecated
+ isResolved: doneList.fired,
+ isRejected: failList.fired,
+
+ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
+ return this;
+ },
+ always: function() {
+ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+ return this;
+ },
+ pipe: function( fnDone, fnFail, fnProgress ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ],
+ progress: [ fnProgress, "notify" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ obj = promise;
+ } else {
+ for ( var key in promise ) {
+ obj[ key ] = promise[ key ];
+ }
+ }
+ return obj;
+ }
+ },
+ deferred = promise.promise({}),
+ key;
+
+ for ( key in lists ) {
+ deferred[ key ] = lists[ key ].fire;
+ deferred[ key + "With" ] = lists[ key ].fireWith;
+ }
+
+ // Handle state
+ deferred.done( function() {
+ state = "resolved";
+ }, failList.disable, progressList.lock ).fail( function() {
+ state = "rejected";
+ }, doneList.disable, progressList.lock );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = sliceDeferred.call( arguments, 0 ),
+ i = 0,
+ length = args.length,
+ pValues = new Array( length ),
+ count = length,
+ pCount = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred(),
+ promise = deferred.promise();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ deferred.resolveWith( deferred, args );
+ }
+ };
+ }
+ function progressFunc( i ) {
+ return function( value ) {
+ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ deferred.notifyWith( promise, pValues );
+ };
+ }
+ if ( length > 1 ) {
+ for ( ; i < length; i++ ) {
+ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return promise;
+ }
+});
+
+
+
+
+jQuery.support = (function() {
+
+ var support,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ fragment,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported,
+ div = document.createElement( "div" ),
+ documentElement = document.documentElement;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Tests for enctype support on a form(#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true,
+ pixelMargin: true
+ };
+
+ // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
+ jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains its value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.lastChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ fragment.removeChild( input );
+ fragment.appendChild( div );
+
+ // Technique from Juriy Zaytsev
+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for ( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ }) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ fragment.removeChild( div );
+
+ // Null elements to avoid leaks in IE
+ fragment = select = opt = div = input = null;
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, outer, inner, table, td, offsetSupport,
+ marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
+ paddingMarginBorderVisibility, paddingMarginBorder,
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ conMarginTop = 1;
+ paddingMarginBorder = "padding:0;margin:0;border:";
+ positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
+ paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
+ style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
+ html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" +
+ "<table " + style + "' cellpadding='0' cellspacing='0'>" +
+ "<tr><td></td></tr></table>";
+
+ container = document.createElement("div");
+ container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( window.getComputedStyle ) {
+ div.innerHTML = "";
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.style.width = "2px";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.innerHTML = "";
+ div.style.width = div.style.padding = "1px";
+ div.style.border = 0;
+ div.style.overflow = "hidden";
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "block";
+ div.style.overflow = "visible";
+ div.innerHTML = "<div style='width:5px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+ }
+
+ div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
+ div.innerHTML = html;
+
+ outer = div.firstChild;
+ inner = outer.firstChild;
+ td = outer.nextSibling.firstChild.firstChild;
+
+ offsetSupport = {
+ doesNotAddBorder: ( inner.offsetTop !== 5 ),
+ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
+ };
+
+ inner.style.position = "fixed";
+ inner.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
+ inner.style.position = inner.style.top = "";
+
+ outer.style.overflow = "hidden";
+ outer.style.position = "relative";
+
+ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
+ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
+
+ if ( window.getComputedStyle ) {
+ div.style.marginTop = "1%";
+ support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
+ }
+
+ if ( typeof container.style.zoom !== "undefined" ) {
+ container.style.zoom = 1;
+ }
+
+ body.removeChild( container );
+ marginDiv = div = container = null;
+
+ jQuery.extend( support, offsetSupport );
+ });
+
+ return support;
+})();
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var privateCache, thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
+ isEvents = name === "events";
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = ++jQuery.uuid;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ privateCache = thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Users should not attempt to inspect the internal events object using jQuery.data,
+ // it is undocumented and subject to change. But does anyone listen? No.
+ if ( isEvents && !thisCache[ name ] ) {
+ return privateCache.events;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i, l,
+
+ // Reference to internal data cache key
+ internalKey = jQuery.expando,
+
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ internalKey ] : internalKey;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split( " " );
+ }
+ }
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ // Ensure that `cache` is not a window object #10080
+ if ( jQuery.support.deleteExpando || !cache.setInterval ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the cache and need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ internalKey ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+ } else {
+ elem[ internalKey ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var parts, part, attr, name, l,
+ elem = this[0],
+ i = 0,
+ data = null;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attr = elem.attributes;
+ for ( l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ parts = key.split( ".", 2 );
+ parts[1] = parts[1] ? "." + parts[1] : "";
+ part = parts[1] + "!";
+
+ return jQuery.access( this, function( value ) {
+
+ if ( value === undefined ) {
+ data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && elem ) {
+ data = jQuery.data( elem, key );
+ data = dataAttr( elem, key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ }
+
+ parts[1] = value;
+ this.each(function() {
+ var self = jQuery( this );
+
+ self.triggerHandler( "setData" + part, parts );
+ jQuery.data( this, key, value );
+ self.triggerHandler( "changeData" + part, parts );
+ });
+ }, null, value, arguments.length > 1, null, false );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ jQuery.isNumeric( data ) ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery._data( elem, deferDataKey );
+ if ( defer &&
+ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
+ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery._data( elem, queueDataKey ) &&
+ !jQuery._data( elem, markDataKey ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.fire();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = ( type || "fx" ) + "mark";
+ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
+ if ( count ) {
+ jQuery._data( elem, key, count );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ var q;
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ q = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ hooks = {};
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ jQuery._data( elem, type + ".run", hooks );
+ fn.call( elem, function() {
+ jQuery.dequeue( elem, type );
+ }, hooks );
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue " + type + ".run", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
+ count++;
+ tmp.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( object );
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ nodeHook, boolHook, fixSpecified;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classNames, i, l, elem, className, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ classNames = ( value || "" ).split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ className = (" " + elem.className + " ").replace( rclass, " " );
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[ c ] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, i, max, option,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ i = one ? index : 0;
+ max = one ? index + 1 : options.length;
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === "undefined" ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, l, isBool,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+ attrNames = value.toLowerCase().split( rspace );
+ l = attrNames.length;
+
+ for ( ; i < l; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
+ isBool = rboolean.test( name );
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ // Do not do this for boolean attributes (see #10870)
+ if ( !isBool ) {
+ jQuery.attr( elem, name, "" );
+ }
+ elem.removeAttribute( getSetAttribute ? name : propName );
+
+ // Set corresponding property to false for boolean attributes
+ if ( isBool && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ // Use the value property for back compat
+ // Use the nodeHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true,
+ coords: true
+ };
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ ret = document.createAttribute( name );
+ elem.setAttributeNode( ret );
+ }
+ return ( ret.nodeValue = value + "" );
+ }
+ };
+
+ // Apply the nodeHook to tabindex
+ jQuery.attrHooks.tabindex.set = nodeHook.set;
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = "" + value );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+
+
+
+
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
+ rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
+ quickParse = function( selector ) {
+ var quick = rquickIs.exec( selector );
+ if ( quick ) {
+ // 0 1 2 3
+ // [ _, tag, id, class ]
+ quick[1] = ( quick[1] || "" ).toLowerCase();
+ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
+ }
+ return quick;
+ },
+ quickIs = function( elem, m ) {
+ var attrs = elem.attributes || {};
+ return (
+ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
+ (!m[2] || (attrs.id || {}).value === m[2]) &&
+ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
+ );
+ },
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+ };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, quick, handlers, special;
+
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+ eventHandle = elemData.handle;
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
+
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ quick: selector && quickParse( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ t, tns, type, origType, namespaces, origCount,
+ j, events, special, handle, eventType, handleObj;
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
+
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, [ "events", "handle" ], true );
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "!" ) >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf( "." ) >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.isTrigger = true;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+ // Handle a global trigger
+ if ( !elem ) {
+
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+ }
+ }
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ old = null;
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old && old === elem.ownerDocument ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event || window.event );
+
+ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ delegateCount = handlers.delegateCount,
+ args = [].slice.call( arguments, 0 ),
+ run_all = !event.exclusive && !event.namespace,
+ special = jQuery.event.special[ event.type ] || {},
+ handlerQueue = [],
+ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && !(event.button && event.type === "click") ) {
+
+ // Pregenerate a single jQuery object for reuse with .is()
+ jqcur = jQuery(this);
+ jqcur.context = this.ownerDocument || this;
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+ // Don't process events on disabled elements (#6911, #8165)
+ if ( cur.disabled !== true ) {
+ selMatch = {};
+ matches = [];
+ jqcur[0] = cur;
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = (
+ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
+ );
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = jQuery.Event( originalEvent );
+
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
+ if ( event.metaKey === undefined ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady
+ },
+
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector,
+ ret;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !form._submit_attached ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ form._submit_attached = true;
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ jQuery.event.simulate( "change", this, event, true );
+ }
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ elem._change_attached = true;
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) { // && selector != null
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ var handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( var type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ expando = "sizcache" + (Math.random() + '').replace('.', ''),
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rReturn = /\r\n/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context, seed );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set, seed );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set, i, len, match, type, left;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ type, found, item, filter, left,
+ i, pass,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ filter = Expr.filter[ type ];
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ pass = not ^ found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+ var i, node,
+ nodeType = elem.nodeType,
+ ret = "";
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( rReturn, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( i = 0; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ if ( node.nodeType !== 8 ) {
+ ret += getText( node );
+ }
+ }
+ }
+ return ret;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+ },
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+ },
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ },
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var first, last,
+ doneName, parent, cache,
+ count, diff,
+ type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ first = match[2];
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ doneName = match[0];
+ parent = elem.parentNode;
+
+ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+ count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent[ expando ] = doneName;
+ }
+
+ diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Sizzle.attr ?
+ Sizzle.attr( elem, name ) :
+ Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ !type && Sizzle.attr ?
+ result != null :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+// Expose origPOS
+// "global" as in regardless of relation to brackets/parens
+Expr.match.globalPOS = origPOS;
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context, seed ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet, seed );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.globalPOS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ POS.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array (deprecated as of jQuery 1.7)
+ if ( jQuery.isArray( selectors ) ) {
+ var level = 1;
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( i = 0; i < selectors.length; i++ ) {
+
+ if ( jQuery( cur ).is( selectors[ i ] ) ) {
+ ret.push({ selector: selectors[ i ], elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call( arguments ).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+
+
+
+
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style)/i,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ },
+ safeFragment = createSafeFragment( document );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( value ) {
+ return jQuery.access( this, function( value ) {
+ return value === undefined ?
+ jQuery.text( this ) :
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+ }, null, value, arguments.length );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery.clean(arguments) );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ return jQuery.access( this, function( value ) {
+ var elem = this[0] || {},
+ i = 0,
+ l = this.length;
+
+ if ( value === undefined ) {
+ return elem.nodeType === 1 ?
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
+ null;
+ }
+
+
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+ try {
+ for (; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ elem = this[i] || {};
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName( "*" ) );
+ elem.innerHTML = value;
+ }
+ }
+
+ elem = 0;
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {}
+ }
+
+ if ( elem ) {
+ this.empty().append( value );
+ }
+ }, null, value, arguments.length );
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ // Make sure that we do not leak memory by inadvertently discarding
+ // the original fragment (which might have attached data) instead of
+ // using it; in addition, use the original fragment object for the last
+ // item instead of first because it can end up being emptied incorrectly
+ // in certain situations (Bug #8070).
+ // Fragments from the fragment cache must always be cloned and never used
+ // in place.
+ results.cacheable || ( l > 1 && i < lastIndex ) ?
+ jQuery.clone( fragment, true, true ) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, function( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ type: "GET",
+ global: false,
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ });
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type, events[ type ][ i ] );
+ }
+ }
+ }
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 fail to clone children inside object elements that use
+ // the proprietary classid attribute value (rather than the type
+ // attribute) to identify the type of content to display
+ if ( nodeName === "object" ) {
+ dest.outerHTML = src.outerHTML;
+
+ } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+ if ( src.checked ) {
+ dest.defaultChecked = dest.checked = src.checked;
+ }
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+
+ // IE blanks contents when cloning scripts
+ } else if ( nodeName === "script" && dest.text !== src.text ) {
+ dest.text = src.text;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+
+ // Clear flags for bubbling special change/submit events, they must
+ // be reattached when the newly cloned events are first activated
+ dest.removeAttribute( "_submit_attached" );
+ dest.removeAttribute( "_change_attached" );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults, doc,
+ first = args[ 0 ];
+
+ // nodes may contain either an explicit document object,
+ // a jQuery collection or context object.
+ // If nodes[0] contains a valid object to assign to doc
+ if ( nodes && nodes[0] ) {
+ doc = nodes[0].ownerDocument || nodes[0];
+ }
+
+ // Ensure that an attr object doesn't incorrectly stand in as a document object
+ // Chrome and Firefox seem to allow this to occur and will throw exception
+ // Fixes #8950
+ if ( !doc.createDocumentFragment ) {
+ doc = document;
+ }
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+ if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
+ first.charAt(0) === "<" && !rnocache.test( first ) &&
+ (jQuery.support.checkClone || !rchecked.test( first )) &&
+ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+ cacheable = true;
+
+ cacheresults = jQuery.fragments[ first ];
+ if ( cacheresults && cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ first ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = ( i > 0 ? this.clone(true) : this ).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+ var nodeName = ( elem.nodeName || "" ).toLowerCase();
+ if ( nodeName === "input" ) {
+ fixDefaultChecked( elem );
+ // Skip scripts, get other children
+ } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+}
+
+// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
+function shimCloneNode( elem ) {
+ var div = document.createElement( "div" );
+ safeFragment.appendChild( div );
+
+ div.innerHTML = elem.outerHTML;
+ return div.firstChild;
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var srcElements,
+ destElements,
+ i,
+ // IE<=8 does not properly clone detached, unknown element nodes
+ clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ?
+ elem.cloneNode( true ) :
+ shimCloneNode( elem );
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ // Ensure that the destination node is not null; Fixes #9587
+ if ( destElements[i] ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ srcElements = destElements = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var checkScriptType, script, j,
+ ret = [];
+
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div"),
+ safeChildNodes = safeFragment.childNodes,
+ remove;
+
+ // Append wrapper element to unknown element safe doc fragment
+ if ( context === document ) {
+ // Use the fragment we've already created for this document
+ safeFragment.appendChild( div );
+ } else {
+ // Use a fragment created with the owner document
+ createSafeFragment( context ).appendChild( div );
+ }
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+
+ // Clear elements from DocumentFragment (safeFragment or otherwise)
+ // to avoid hoarding elements. Fixes #11356
+ if ( div ) {
+ div.parentNode.removeChild( div );
+
+ // Guard against -1 index exceptions in FF3.6
+ if ( safeChildNodes.length > 0 ) {
+ remove = safeChildNodes[ safeChildNodes.length - 1 ];
+
+ if ( remove && remove.parentNode ) {
+ remove.parentNode.removeChild( remove );
+ }
+ }
+ }
+ }
+ }
+
+ // Resets defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ var len;
+ if ( !jQuery.support.appendChecked ) {
+ if ( elem[0] && typeof (len = elem.length) === "number" ) {
+ for ( j = 0; j < len; j++ ) {
+ findInputs( elem[j] );
+ }
+ } else {
+ findInputs( elem );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ checkScriptType = function( elem ) {
+ return !elem.type || rscriptType.test( elem.type );
+ };
+ for ( i = 0; ret[i]; i++ ) {
+ script = ret[i];
+ if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
+ scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );
+
+ } else {
+ if ( script.nodeType === 1 ) {
+ var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );
+
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ }
+ fragment.appendChild( script );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id,
+ cache = jQuery.cache,
+ special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+
+ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+ if ( data.handle ) {
+ data.handle.elem = null;
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ // fixed for IE9, see #8346
+ rupper = /([A-Z]|^ms)/g,
+ rnum = /^[\-+]?(?:\d*\.)?\d+$/i,
+ rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
+ rrelNum = /^([\-+])=([\-+.\de]+)/,
+ rmargin = /^margin/,
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+
+ // order is important!
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+
+ curCSS,
+
+ getComputedStyle,
+ currentStyle;
+
+jQuery.fn.css = function( name, value ) {
+ return jQuery.access( this, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( value == null || type === "number" && isNaN( value ) ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ var ret, hooks;
+
+ // Make sure that we're working with the right name
+ name = jQuery.camelCase( name );
+ hooks = jQuery.cssHooks[ name ];
+ name = jQuery.cssProps[ name ] || name;
+
+ // cssFloat needs a special treatment
+ if ( name === "cssFloat" ) {
+ name = "float";
+ }
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {},
+ ret, name;
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
+});
+
+// DEPRECATED in 1.3, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, name ) {
+ var ret, defaultView, computedStyle, width,
+ style = elem.style;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( (defaultView = elem.ownerDocument.defaultView) &&
+ (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ // A tribute to the "awesome hack by Dean Edwards"
+ // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins
+ // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) {
+ width = style.width;
+ style.width = ret;
+ ret = computedStyle.width;
+ style.width = width;
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left, rsLeft, uncomputed,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ style = elem.style;
+
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret == null && style && (uncomputed = style[ name ]) ) {
+ ret = uncomputed;
+ }
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( rnumnonpx.test( ret ) ) {
+
+ // Remember the original values
+ left = style.left;
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : ret;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWidthOrHeight( elem, name, extra ) {
+
+ // Start with offset property
+ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ i = name === "width" ? 1 : 0,
+ len = 4;
+
+ if ( val > 0 ) {
+ if ( extra !== "border" ) {
+ for ( ; i < len; i += 2 ) {
+ if ( !extra ) {
+ val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0;
+ } else {
+ val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ }
+ }
+
+ return val + "px";
+ }
+
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+ }
+
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+
+ // Add padding, border, margin
+ if ( extra ) {
+ for ( ; i < len; i += 2 ) {
+ val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ if ( extra !== "padding" ) {
+ val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0;
+ }
+ }
+ }
+
+ return val + "px";
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ return getWidthOrHeight( elem, name, extra );
+ } else {
+ return jQuery.swap( elem, cssShow, function() {
+ return getWidthOrHeight( elem, name, extra );
+ });
+ }
+ }
+ },
+
+ set: function( elem, value ) {
+ return rnum.test( value ) ?
+ value + "px" :
+ value;
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle,
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
+
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
+ // style.removeAttribute is IE Only, but so apparently is this code path...
+ style.removeAttribute( "filter" );
+
+ // if there there is no filter style applied in a css rule, we are done
+ if ( currentStyle && !currentStyle.filter ) {
+ return;
+ }
+ }
+
+ // otherwise, set new filter values
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+jQuery(function() {
+ // This hook cannot be added until DOM ready because the support test
+ // for it is not run until after DOM ready
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ return jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ return curCSS( elem, "margin-right" );
+ } else {
+ return elem.style.marginRight;
+ }
+ });
+ }
+ };
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+ margin: "",
+ padding: "",
+ border: "Width"
+}, function( prefix, suffix ) {
+
+ jQuery.cssHooks[ prefix + suffix ] = {
+ expand: function( value ) {
+ var i,
+
+ // assumes a single number if not a string
+ parts = typeof value === "string" ? value.split(" ") : [ value ],
+ expanded = {};
+
+ for ( i = 0; i < 4; i++ ) {
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+ }
+
+ return expanded;
+ }
+ };
+});
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rspacesAjax = /\s+/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts,
+
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ allTypes = ["*/"] + ["*"];
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ if ( jQuery.isFunction( func ) ) {
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+ i = 0,
+ length = dataTypes.length,
+ dataType,
+ list,
+ placeBefore;
+
+ // For each dataType in the dataTypeExpression
+ for ( ; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters ),
+ selection;
+
+ for ( ; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+ var key, deep,
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
+ for ( key in src ) {
+ if ( src[ key ] !== undefined ) {
+ ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+ }
+ }
+ if ( deep ) {
+ jQuery.extend( true, target, deep );
+ }
+}
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf( " " );
+ if ( off >= 0 ) {
+ var selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ // Complete callback (responseText is used internally)
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
+ // If successful, inject the HTML into all the matched elements
+ if ( jqXHR.isResolved() ) {
+ // #4825: Get the actual response in case
+ // a dataFilter is present in ajaxSettings
+ jqXHR.done(function( r ) {
+ responseText = r;
+ });
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [ responseText, status, jqXHR ] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.on( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ if ( settings ) {
+ // Building a settings object
+ ajaxExtend( target, jQuery.ajaxSettings );
+ } else {
+ // Extending ajaxSettings
+ settings = target;
+ target = jQuery.ajaxSettings;
+ }
+ ajaxExtend( target, settings );
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": allTypes
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ context: true,
+ url: true
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks( "once memory" ),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // The jqXHR state
+ state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || "abort";
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, nativeStatusText, responses, headers ) {
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ var isSuccess,
+ success,
+ error,
+ statusText = nativeStatusText,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+ lastModified,
+ etag;
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ }
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ try {
+ success = ajaxConvert( s, response );
+ statusText = "success";
+ isSuccess = true;
+ } catch(e) {
+ // We have a parsererror
+ statusText = "parsererror";
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if ( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = "" + ( nativeStatusText || statusText );
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.add;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for ( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+
+ // Determine if a cross-domain request is in order
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefilter, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already
+ jqXHR.abort();
+ return false;
+
+ }
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : value;
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields,
+ ct,
+ type,
+ finalDataType,
+ firstDataType;
+
+ // Fill responseXXX fields
+ for ( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ var dataTypes = s.dataTypes,
+ converters = {},
+ i,
+ key,
+ length = dataTypes.length,
+ tmp,
+ // Current and previous dataTypes
+ current = dataTypes[ 0 ],
+ prev,
+ // Conversion expression
+ conversion,
+ // Conversion function
+ conv,
+ // Conversion functions (transitive conversion)
+ conv1,
+ conv2;
+
+ // For each dataType in the chain
+ for ( i = 1; i < length; i++ ) {
+
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for ( key in s.converters ) {
+ if ( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
+ // Get the dataTypes
+ prev = current;
+ current = dataTypes[ i ];
+
+ // If current is auto dataType, update it to prev
+ if ( current === "*" ) {
+ current = prev;
+ // If no auto and dataTypes are actually different
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Get the converter
+ conversion = prev + " " + current;
+ conv = converters[ conversion ] || converters[ "* " + current ];
+
+ // If there is no direct converter, search transitively
+ if ( !conv ) {
+ conv2 = undefined;
+ for ( conv1 in converters ) {
+ tmp = conv1.split( " " );
+ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+ conv2 = converters[ tmp[1] + " " + current ];
+ if ( conv2 ) {
+ conv1 = converters[ conv1 ];
+ if ( conv1 === true ) {
+ conv = conv2;
+ } else if ( conv2 === true ) {
+ conv = conv1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ // If we found no converter, dispatch an error
+ if ( !( conv || conv2 ) ) {
+ jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
+ }
+ // If found converter is not an equivalence
+ if ( conv !== true ) {
+ // Convert with 1 or 2 converters accordingly
+ response = conv ? conv( response ) : conv2( conv1(response) );
+ }
+ }
+ }
+ return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+ jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ return jQuery.expando + "_" + ( jsc++ );
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType );
+
+ if ( s.dataTypes[ 0 ] === "jsonp" ||
+ s.jsonp !== false && ( jsre.test( s.url ) ||
+ inspectData && jsre.test( s.data ) ) ) {
+
+ var responseContainer,
+ jsonpCallback = s.jsonpCallback =
+ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+ previous = window[ jsonpCallback ],
+ url = s.url,
+ data = s.data,
+ replace = "$1" + jsonpCallback + "$2";
+
+ if ( s.jsonp !== false ) {
+ url = url.replace( jsre, replace );
+ if ( s.url === url ) {
+ if ( inspectData ) {
+ data = data.replace( jsre, replace );
+ }
+ if ( s.data === data ) {
+ // Add callback manually
+ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
+ }
+ }
+ }
+
+ s.url = url;
+ s.data = data;
+
+ // Install callback
+ window[ jsonpCallback ] = function( response ) {
+ responseContainer = [ response ];
+ };
+
+ // Clean-up function
+ jqXHR.always(function() {
+ // Set callback back to previous value
+ window[ jsonpCallback ] = previous;
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( previous ) ) {
+ window[ jsonpCallback ]( responseContainer[ 0 ] );
+ }
+ });
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( jsonpCallback + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0,
+ xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var xhr = s.xhr(),
+ handle,
+ i;
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occured
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+
+ // When requesting binary data, IE6-9 will throw an exception
+ // on any attempt to access responseText (#11426)
+ try {
+ responses.text = xhr.responseText;
+ } catch( _ ) {
+ }
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ // if we're in sync mode or it's in cache
+ // and has been retrieved directly (IE6 & IE7)
+ // we need to manually fire the callback
+ if ( !s.async || xhr.readyState === 4 ) {
+ callback();
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+
+
+
+
+var elemdisplay = {},
+ iframe, iframeDoc,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ],
+ fxNow;
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback );
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( (display === "" && jQuery.css(elem, "display") === "none") ||
+ !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
+ }
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ var elem, display,
+ i = 0,
+ j = this.length;
+
+ for ( ; i < j; i++ ) {
+ elem = this[i];
+ if ( elem.style ) {
+ display = jQuery.css( elem, "display" );
+
+ if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ if ( this[i].style ) {
+ this[i].style.display = "none";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed( speed, easing, callback );
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
+ }
+
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
+ function doAnimation() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ if ( optall.queue === false ) {
+ jQuery._mark( this );
+ }
+
+ var opt = jQuery.extend( {}, optall ),
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ name, val, p, e, hooks, replace,
+ parts, start, end, unit,
+ method;
+
+ // will store per property easing and be used to determine when an animation is complete
+ opt.animatedProperties = {};
+
+ // first pass over propertys to expand / normalize
+ for ( p in prop ) {
+ name = jQuery.camelCase( p );
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ }
+
+ if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
+ replace = hooks.expand( prop[ name ] );
+ delete prop[ name ];
+
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'p' from above because we have the correct "name"
+ for ( p in replace ) {
+ if ( ! ( p in prop ) ) {
+ prop[ p ] = replace[ p ];
+ }
+ }
+ }
+ }
+
+ for ( name in prop ) {
+ val = prop[ name ];
+ // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
+ if ( jQuery.isArray( val ) ) {
+ opt.animatedProperties[ name ] = val[ 1 ];
+ val = prop[ name ] = val[ 0 ];
+ } else {
+ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
+ }
+
+ if ( val === "hide" && hidden || val === "show" && !hidden ) {
+ return opt.complete.call( this );
+ }
+
+ if ( isElement && ( name === "height" || name === "width" ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ for ( p in prop ) {
+ e = new jQuery.fx( this, opt, p );
+ val = prop[ p ];
+
+ if ( rfxtypes.test( val ) ) {
+
+ // Tracks whether to show or hide based on private
+ // data attached to the element
+ method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
+ if ( method ) {
+ jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
+ e[ method ]();
+ } else {
+ e[ val ]();
+ }
+
+ } else {
+ parts = rfxnum.exec( val );
+ start = e.cur();
+
+ if ( parts ) {
+ end = parseFloat( parts[2] );
+ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, p, (end || 1) + unit);
+ start = ( (end || 1) / e.cur() ) * start;
+ jQuery.style( this, p, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ }
+
+ // For JS strict compliance
+ return true;
+ }
+
+ return optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+
+ stop: function( type, clearQueue, gotoEnd ) {
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var index,
+ hadTimers = false,
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
+ // clear marker counters if we know they won't be
+ if ( !gotoEnd ) {
+ jQuery._unmark( true, this );
+ }
+
+ function stopQueue( elem, data, index ) {
+ var hooks = data[ index ];
+ jQuery.removeData( elem, index, true );
+ hooks.stop( gotoEnd );
+ }
+
+ if ( type == null ) {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
+ stopQueue( this, data, index );
+ }
+ }
+ } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
+ stopQueue( this, data, index );
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ if ( gotoEnd ) {
+
+ // force the next step to be the last
+ timers[ index ]( true );
+ } else {
+ timers[ index ].saveState();
+ }
+ hadTimers = true;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( !( gotoEnd && hadTimers ) ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout( clearFxNow, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+ fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx( "show", 1 ),
+ slideUp: genFx( "hide", 1 ),
+ slideToggle: genFx( "toggle", 1 ),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function( noUnmark ) {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ options.orig = options.orig || {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var parsed,
+ r = jQuery.css( this.elem, this.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx;
+
+ this.startTime = fxNow || createFxNow();
+ this.end = to;
+ this.now = this.start = from;
+ this.pos = this.state = 0;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
+
+ function t( gotoEnd ) {
+ return self.step( gotoEnd );
+ }
+
+ t.queue = this.options.queue;
+ t.elem = this.elem;
+ t.saveState = function() {
+ if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
+ if ( self.options.hide ) {
+ jQuery._data( self.elem, "fxshow" + self.prop, self.start );
+ } else if ( self.options.show ) {
+ jQuery._data( self.elem, "fxshow" + self.prop, self.end );
+ }
+ }
+ };
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval( fx.tick, fx.interval );
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
+
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any flash of content
+ if ( dataShow !== undefined ) {
+ // This show is picking up where a previous hide or show left off
+ this.custom( this.cur(), dataShow );
+ } else {
+ this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
+ }
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom( this.cur(), 0 );
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var p, n, complete,
+ t = fxNow || createFxNow(),
+ done = true,
+ elem = this.elem,
+ options = this.options;
+
+ if ( gotoEnd || t >= options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ options.animatedProperties[ this.prop ] = true;
+
+ for ( p in options.animatedProperties ) {
+ if ( options.animatedProperties[ p ] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+ jQuery.each( [ "", "X", "Y" ], function( index, value ) {
+ elem.style[ "overflow" + value ] = options.overflow[ index ];
+ });
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide ) {
+ jQuery( elem ).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show ) {
+ for ( p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[ p ] );
+ jQuery.removeData( elem, "fxshow" + p, true );
+ // Toggle data is no longer needed
+ jQuery.removeData( elem, "toggle" + p, true );
+ }
+ }
+
+ // Execute the complete function
+ // in the event that the complete function throws an exception
+ // we must ensure it won't be called twice. #5684
+
+ complete = options.complete;
+ if ( complete ) {
+
+ options.complete = false;
+ complete.call( elem );
+ }
+ }
+
+ return false;
+
+ } else {
+ // classical easing cannot be used with an Infinity duration
+ if ( options.duration == Infinity ) {
+ this.now = t;
+ } else {
+ n = t - this.startTime;
+ this.state = n / options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
+ this.now = this.start + ( (this.end - this.start) * this.pos );
+ }
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+// Ensure props that can't be negative don't go there on undershoot easing
+jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
+ // exclude marginTop, marginLeft, marginBottom and marginRight from this list
+ if ( prop.indexOf( "margin" ) ) {
+ jQuery.fx.step[ prop ] = function( fx ) {
+ jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
+ };
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+ if ( !elemdisplay[ nodeName ] ) {
+
+ var body = document.body,
+ elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
+ display = elem.css( "display" );
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // No iframe to use yet, so create it
+ if ( !iframe ) {
+ iframe = document.createElement( "iframe" );
+ iframe.frameBorder = iframe.width = iframe.height = 0;
+ }
+
+ body.appendChild( iframe );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
+ iframeDoc.close();
+ }
+
+ elem = iframeDoc.createElement( nodeName );
+
+ iframeDoc.body.appendChild( elem );
+
+ display = jQuery.css( elem, "display" );
+ body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var getOffset,
+ rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ getOffset = function( elem, doc, docElem, box ) {
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow( doc ),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
+ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ getOffset = function( elem, doc, docElem ) {
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.fn.offset = function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var elem = this[0],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return null;
+ }
+
+ if ( elem === doc.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ return getOffset( elem, doc, doc.documentElement );
+};
+
+jQuery.offset = {
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return jQuery.access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ var clientProp = "client" + name,
+ scrollProp = "scroll" + name,
+ offsetProp = "offset" + name;
+
+ // innerHeight and innerWidth
+ jQuery.fn[ "inner" + name ] = function() {
+ var elem = this[0];
+ return elem ?
+ elem.style ?
+ parseFloat( jQuery.css( elem, type, "padding" ) ) :
+ this[ type ]() :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn[ "outer" + name ] = function( margin ) {
+ var elem = this[0];
+ return elem ?
+ elem.style ?
+ parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
+ this[ type ]() :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( value ) {
+ return jQuery.access( this, function( elem, type, value ) {
+ var doc, docElemProp, orig, ret;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+ doc = elem.document;
+ docElemProp = doc.documentElement[ clientProp ];
+ return jQuery.support.boxModel && docElemProp ||
+ doc.body && doc.body[ clientProp ] || docElemProp;
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ doc = elem.documentElement;
+
+ // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height]
+ // so we can't use max, as it'll choose the incorrect offset[Width/Height]
+ // instead we use the correct client[Width/Height]
+ // support:IE6
+ if ( doc[ clientProp ] >= doc[ scrollProp ] ) {
+ return doc[ clientProp ];
+ }
+
+ return Math.max(
+ elem.body[ scrollProp ], doc[ scrollProp ],
+ elem.body[ offsetProp ], doc[ offsetProp ]
+ );
+ }
+
+ // Get width or height on the element
+ if ( value === undefined ) {
+ orig = jQuery.css( elem, type );
+ ret = parseFloat( orig );
+ return jQuery.isNumeric( ret ) ? ret : orig;
+ }
+
+ // Set the width or height on the element
+ jQuery( elem ).css( type, value );
+ }, type, value, arguments.length, null );
+ };
+});
+
+
+
+
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+
+
+})( window );
diff --git a/samples/javascript/json2_backbone.js b/samples/javascript/json2_backbone.js
new file mode 100644
index 0000000..ccf1225
--- /dev/null
+++ b/samples/javascript/json2_backbone.js
@@ -0,0 +1,1152 @@
+// Inline json2
+var JSON;if(!JSON){JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==="string"){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());
+
+// Backbone.js 0.5.0
+// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
+// Backbone may be freely distributed under the MIT license.
+// For all details and documentation:
+// http://documentcloud.github.com/backbone
+
+(function(){
+
+ // Initial Setup
+ // -------------
+
+ // Save a reference to the global object.
+ var root = this;
+
+ // Save the previous value of the `Backbone` variable.
+ var previousBackbone = root.Backbone;
+
+ // The top-level namespace. All public Backbone classes and modules will
+ // be attached to this. Exported for both CommonJS and the browser.
+ var Backbone;
+ if (typeof exports !== 'undefined') {
+ Backbone = exports;
+ } else {
+ Backbone = root.Backbone = {};
+ }
+
+ // Current version of the library. Keep in sync with `package.json`.
+ Backbone.VERSION = '0.5.0';
+
+ // Require Underscore, if we're on the server, and it's not already present.
+ var _ = root._;
+ if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
+
+ // For Backbone's purposes, jQuery or Zepto owns the `$` variable.
+ var $ = root.jQuery || root.Zepto;
+
+ // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+ // to its previous owner. Returns a reference to this Backbone object.
+ Backbone.noConflict = function() {
+ root.Backbone = previousBackbone;
+ return this;
+ };
+
+ // Turn on `emulateHTTP` to use support legacy HTTP servers. Setting this option will
+ // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
+ // `X-Http-Method-Override` header.
+ Backbone.emulateHTTP = false;
+
+ // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+ // `application/json` requests ... will encode the body as
+ // `application/x-www-form-urlencoded` instead and will send the model in a
+ // form param named `model`.
+ Backbone.emulateJSON = false;
+
+ // Backbone.Events
+ // -----------------
+
+ // A module that can be mixed in to *any object* in order to provide it with
+ // custom events. You may `bind` or `unbind` a callback function to an event;
+ // `trigger`-ing an event fires all callbacks in succession.
+ //
+ // var object = {};
+ // _.extend(object, Backbone.Events);
+ // object.bind('expand', function(){ alert('expanded'); });
+ // object.trigger('expand');
+ //
+ Backbone.Events = {
+
+ // Bind an event, specified by a string name, `ev`, to a `callback` function.
+ // Passing `"all"` will bind the callback to all events fired.
+ bind : function(ev, callback) {
+ var calls = this._callbacks || (this._callbacks = {});
+ var list = calls[ev] || (calls[ev] = []);
+ list.push(callback);
+ return this;
+ },
+
+ // Remove one or many callbacks. If `callback` is null, removes all
+ // callbacks for the event. If `ev` is null, removes all bound callbacks
+ // for all events.
+ unbind : function(ev, callback) {
+ var calls;
+ if (!ev) {
+ this._callbacks = {};
+ } else if (calls = this._callbacks) {
+ if (!callback) {
+ calls[ev] = [];
+ } else {
+ var list = calls[ev];
+ if (!list) return this;
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (callback === list[i]) {
+ list[i] = null;
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ // Trigger an event, firing all bound callbacks. Callbacks are passed the
+ // same arguments as `trigger` is, apart from the event name.
+ // Listening for `"all"` passes the true event name as the first argument.
+ trigger : function(eventName) {
+ var list, calls, ev, callback, args;
+ var both = 2;
+ if (!(calls = this._callbacks)) return this;
+ while (both--) {
+ ev = both ? eventName : 'all';
+ if (list = calls[ev]) {
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (!(callback = list[i])) {
+ list.splice(i, 1); i--; l--;
+ } else {
+ args = both ? Array.prototype.slice.call(arguments, 1) : arguments;
+ callback.apply(this, args);
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ };
+
+ // Backbone.Model
+ // --------------
+
+ // Create a new model, with defined attributes. A client id (`cid`)
+ // is automatically generated and assigned for you.
+ Backbone.Model = function(attributes, options) {
+ var defaults;
+ attributes || (attributes = {});
+ if (defaults = this.defaults) {
+ if (_.isFunction(defaults)) defaults = defaults();
+ attributes = _.extend({}, defaults, attributes);
+ }
+ this.attributes = {};
+ this._escapedAttributes = {};
+ this.cid = _.uniqueId('c');
+ this.set(attributes, {silent : true});
+ this._changed = false;
+ this._previousAttributes = _.clone(this.attributes);
+ if (options && options.collection) this.collection = options.collection;
+ this.initialize.apply(this, arguments);
+ };
+
+ // Attach all inheritable methods to the Model prototype.
+ _.extend(Backbone.Model.prototype, Backbone.Events, {
+
+ // A snapshot of the model's previous attributes, taken immediately
+ // after the last `"change"` event was fired.
+ _previousAttributes : null,
+
+ // Has the item been changed since the last `"change"` event?
+ _changed : false,
+
+ // The default name for the JSON `id` attribute is `"id"`. MongoDB and
+ // CouchDB users may want to set this to `"_id"`.
+ idAttribute : 'id',
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize : function(){},
+
+ // Return a copy of the model's `attributes` object.
+ toJSON : function() {
+ return _.clone(this.attributes);
+ },
+
+ // Get the value of an attribute.
+ get : function(attr) {
+ return this.attributes[attr];
+ },
+
+ // Get the HTML-escaped value of an attribute.
+ escape : function(attr) {
+ var html;
+ if (html = this._escapedAttributes[attr]) return html;
+ var val = this.attributes[attr];
+ return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val);
+ },
+
+ // Returns `true` if the attribute contains a value that is not null
+ // or undefined.
+ has : function(attr) {
+ return this.attributes[attr] != null;
+ },
+
+ // Set a hash of model attributes on the object, firing `"change"` unless you
+ // choose to silence it.
+ set : function(attrs, options) {
+
+ // Extract attributes and options.
+ options || (options = {});
+ if (!attrs) return this;
+ if (attrs.attributes) attrs = attrs.attributes;
+ var now = this.attributes, escaped = this._escapedAttributes;
+
+ // Run validation.
+ if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
+
+ // Check for changes of `id`.
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
+
+ // We're about to start triggering change events.
+ var alreadyChanging = this._changing;
+ this._changing = true;
+
+ // Update attributes.
+ for (var attr in attrs) {
+ var val = attrs[attr];
+ if (!_.isEqual(now[attr], val)) {
+ now[attr] = val;
+ delete escaped[attr];
+ this._changed = true;
+ if (!options.silent) this.trigger('change:' + attr, this, val, options);
+ }
+ }
+
+ // Fire the `"change"` event, if the model has been changed.
+ if (!alreadyChanging && !options.silent && this._changed) this.change(options);
+ this._changing = false;
+ return this;
+ },
+
+ // Remove an attribute from the model, firing `"change"` unless you choose
+ // to silence it. `unset` is a noop if the attribute doesn't exist.
+ unset : function(attr, options) {
+ if (!(attr in this.attributes)) return this;
+ options || (options = {});
+ var value = this.attributes[attr];
+
+ // Run validation.
+ var validObj = {};
+ validObj[attr] = void 0;
+ if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
+
+ // Remove the attribute.
+ delete this.attributes[attr];
+ delete this._escapedAttributes[attr];
+ if (attr == this.idAttribute) delete this.id;
+ this._changed = true;
+ if (!options.silent) {
+ this.trigger('change:' + attr, this, void 0, options);
+ this.change(options);
+ }
+ return this;
+ },
+
+ // Clear all attributes on the model, firing `"change"` unless you choose
+ // to silence it.
+ clear : function(options) {
+ options || (options = {});
+ var attr;
+ var old = this.attributes;
+
+ // Run validation.
+ var validObj = {};
+ for (attr in old) validObj[attr] = void 0;
+ if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
+
+ this.attributes = {};
+ this._escapedAttributes = {};
+ this._changed = true;
+ if (!options.silent) {
+ for (attr in old) {
+ this.trigger('change:' + attr, this, void 0, options);
+ }
+ this.change(options);
+ }
+ return this;
+ },
+
+ // Fetch the model from the server. If the server's representation of the
+ // model differs from its current attributes, they will be overriden,
+ // triggering a `"change"` event.
+ fetch : function(options) {
+ options || (options = {});
+ var model = this;
+ var success = options.success;
+ options.success = function(resp, status, xhr) {
+ if (!model.set(model.parse(resp, xhr), options)) return false;
+ if (success) success(model, resp);
+ };
+ options.error = wrapError(options.error, model, options);
+ return (this.sync || Backbone.sync).call(this, 'read', this, options);
+ },
+
+ // Set a hash of model attributes, and sync the model to the server.
+ // If the server returns an attributes hash that differs, the model's
+ // state will be `set` again.
+ save : function(attrs, options) {
+ options || (options = {});
+ if (attrs && !this.set(attrs, options)) return false;
+ var model = this;
+ var success = options.success;
+ options.success = function(resp, status, xhr) {
+ if (!model.set(model.parse(resp, xhr), options)) return false;
+ if (success) success(model, resp, xhr);
+ };
+ options.error = wrapError(options.error, model, options);
+ var method = this.isNew() ? 'create' : 'update';
+ return (this.sync || Backbone.sync).call(this, method, this, options);
+ },
+
+ // Destroy this model on the server if it was already persisted. Upon success, the model is removed
+ // from its collection, if it has one.
+ destroy : function(options) {
+ options || (options = {});
+ if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
+ var model = this;
+ var success = options.success;
+ options.success = function(resp) {
+ model.trigger('destroy', model, model.collection, options);
+ if (success) success(model, resp);
+ };
+ options.error = wrapError(options.error, model, options);
+ return (this.sync || Backbone.sync).call(this, 'delete', this, options);
+ },
+
+ // Default URL for the model's representation on the server -- if you're
+ // using Backbone's restful methods, override this to change the endpoint
+ // that will be called.
+ url : function() {
+ var base = getUrl(this.collection) || this.urlRoot || urlError();
+ if (this.isNew()) return base;
+ return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
+ },
+
+ // **parse** converts a response into the hash of attributes to be `set` on
+ // the model. The default implementation is just to pass the response along.
+ parse : function(resp, xhr) {
+ return resp;
+ },
+
+ // Create a new model with identical attributes to this one.
+ clone : function() {
+ return new this.constructor(this);
+ },
+
+ // A model is new if it has never been saved to the server, and lacks an id.
+ isNew : function() {
+ return this.id == null;
+ },
+
+ // Call this method to manually fire a `change` event for this model.
+ // Calling this will cause all objects observing the model to update.
+ change : function(options) {
+ this.trigger('change', this, options);
+ this._previousAttributes = _.clone(this.attributes);
+ this._changed = false;
+ },
+
+ // Determine if the model has changed since the last `"change"` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged : function(attr) {
+ if (attr) return this._previousAttributes[attr] != this.attributes[attr];
+ return this._changed;
+ },
+
+ // Return an object containing all the attributes that have changed, or false
+ // if there are no changed attributes. Useful for determining what parts of a
+ // view need to be updated and/or what attributes need to be persisted to
+ // the server.
+ changedAttributes : function(now) {
+ now || (now = this.attributes);
+ var old = this._previousAttributes;
+ var changed = false;
+ for (var attr in now) {
+ if (!_.isEqual(old[attr], now[attr])) {
+ changed = changed || {};
+ changed[attr] = now[attr];
+ }
+ }
+ return changed;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous : function(attr) {
+ if (!attr || !this._previousAttributes) return null;
+ return this._previousAttributes[attr];
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes : function() {
+ return _.clone(this._previousAttributes);
+ },
+
+ // Run validation against a set of incoming attributes, returning `true`
+ // if all is well. If a specific `error` callback has been passed,
+ // call that instead of firing the general `"error"` event.
+ _performValidation : function(attrs, options) {
+ var error = this.validate(attrs);
+ if (error) {
+ if (options.error) {
+ options.error(this, error, options);
+ } else {
+ this.trigger('error', this, error, options);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ });
+
+ // Backbone.Collection
+ // -------------------
+
+ // Provides a standard collection class for our sets of models, ordered
+ // or unordered. If a `comparator` is specified, the Collection will maintain
+ // its models in sort order, as they're added and removed.
+ Backbone.Collection = function(models, options) {
+ options || (options = {});
+ if (options.comparator) this.comparator = options.comparator;
+ _.bindAll(this, '_onModelEvent', '_removeReference');
+ this._reset();
+ if (models) this.reset(models, {silent: true});
+ this.initialize.apply(this, arguments);
+ };
+
+ // Define the Collection's inheritable methods.
+ _.extend(Backbone.Collection.prototype, Backbone.Events, {
+
+ // The default model for a collection is just a **Backbone.Model**.
+ // This should be overridden in most cases.
+ model : Backbone.Model,
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize : function(){},
+
+ // The JSON representation of a Collection is an array of the
+ // models' attributes.
+ toJSON : function() {
+ return this.map(function(model){ return model.toJSON(); });
+ },
+
+ // Add a model, or list of models to the set. Pass **silent** to avoid
+ // firing the `added` event for every new model.
+ add : function(models, options) {
+ if (_.isArray(models)) {
+ for (var i = 0, l = models.length; i < l; i++) {
+ this._add(models[i], options);
+ }
+ } else {
+ this._add(models, options);
+ }
+ return this;
+ },
+
+ // Remove a model, or a list of models from the set. Pass silent to avoid
+ // firing the `removed` event for every model removed.
+ remove : function(models, options) {
+ if (_.isArray(models)) {
+ for (var i = 0, l = models.length; i < l; i++) {
+ this._remove(models[i], options);
+ }
+ } else {
+ this._remove(models, options);
+ }
+ return this;
+ },
+
+ // Get a model from the set by id.
+ get : function(id) {
+ if (id == null) return null;
+ return this._byId[id.id != null ? id.id : id];
+ },
+
+ // Get a model from the set by client id.
+ getByCid : function(cid) {
+ return cid && this._byCid[cid.cid || cid];
+ },
+
+ // Get the model at the given index.
+ at: function(index) {
+ return this.models[index];
+ },
+
+ // Force the collection to re-sort itself. You don't need to call this under normal
+ // circumstances, as the set will maintain sort order as each item is added.
+ sort : function(options) {
+ options || (options = {});
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+ this.models = this.sortBy(this.comparator);
+ if (!options.silent) this.trigger('reset', this, options);
+ return this;
+ },
+
+ // Pluck an attribute from each model in the collection.
+ pluck : function(attr) {
+ return _.map(this.models, function(model){ return model.get(attr); });
+ },
+
+ // When you have more items than you want to add or remove individually,
+ // you can reset the entire set with a new list of models, without firing
+ // any `added` or `removed` events. Fires `reset` when finished.
+ reset : function(models, options) {
+ models || (models = []);
+ options || (options = {});
+ this.each(this._removeReference);
+ this._reset();
+ this.add(models, {silent: true});
+ if (!options.silent) this.trigger('reset', this, options);
+ return this;
+ },
+
+ // Fetch the default set of models for this collection, resetting the
+ // collection when they arrive. If `add: true` is passed, appends the
+ // models to the collection instead of resetting.
+ fetch : function(options) {
+ options || (options = {});
+ var collection = this;
+ var success = options.success;
+ options.success = function(resp, status, xhr) {
+ collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
+ if (success) success(collection, resp);
+ };
+ options.error = wrapError(options.error, collection, options);
+ return (this.sync || Backbone.sync).call(this, 'read', this, options);
+ },
+
+ // Create a new instance of a model in this collection. After the model
+ // has been created on the server, it will be added to the collection.
+ // Returns the model, or 'false' if validation on a new model fails.
+ create : function(model, options) {
+ var coll = this;
+ options || (options = {});
+ model = this._prepareModel(model, options);
+ if (!model) return false;
+ var success = options.success;
+ options.success = function(nextModel, resp, xhr) {
+ coll.add(nextModel, options);
+ if (success) success(nextModel, resp, xhr);
+ };
+ model.save(null, options);
+ return model;
+ },
+
+ // **parse** converts a response into a list of models to be added to the
+ // collection. The default implementation is just to pass it through.
+ parse : function(resp, xhr) {
+ return resp;
+ },
+
+ // Proxy to _'s chain. Can't be proxied the same way the rest of the
+ // underscore methods are proxied because it relies on the underscore
+ // constructor.
+ chain: function () {
+ return _(this.models).chain();
+ },
+
+ // Reset all internal state. Called when the collection is reset.
+ _reset : function(options) {
+ this.length = 0;
+ this.models = [];
+ this._byId = {};
+ this._byCid = {};
+ },
+
+ // Prepare a model to be added to this collection
+ _prepareModel: function(model, options) {
+ if (!(model instanceof Backbone.Model)) {
+ var attrs = model;
+ model = new this.model(attrs, {collection: this});
+ if (model.validate && !model._performValidation(attrs, options)) model = false;
+ } else if (!model.collection) {
+ model.collection = this;
+ }
+ return model;
+ },
+
+ // Internal implementation of adding a single model to the set, updating
+ // hash indexes for `id` and `cid` lookups.
+ // Returns the model, or 'false' if validation on a new model fails.
+ _add : function(model, options) {
+ options || (options = {});
+ model = this._prepareModel(model, options);
+ if (!model) return false;
+ var already = this.getByCid(model) || this.get(model);
+ if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
+ this._byId[model.id] = model;
+ this._byCid[model.cid] = model;
+ var index = options.at != null ? options.at :
+ this.comparator ? this.sortedIndex(model, this.comparator) :
+ this.length;
+ this.models.splice(index, 0, model);
+ model.bind('all', this._onModelEvent);
+ this.length++;
+ if (!options.silent) model.trigger('add', model, this, options);
+ return model;
+ },
+
+ // Internal implementation of removing a single model from the set, updating
+ // hash indexes for `id` and `cid` lookups.
+ _remove : function(model, options) {
+ options || (options = {});
+ model = this.getByCid(model) || this.get(model);
+ if (!model) return null;
+ delete this._byId[model.id];
+ delete this._byCid[model.cid];
+ this.models.splice(this.indexOf(model), 1);
+ this.length--;
+ if (!options.silent) model.trigger('remove', model, this, options);
+ this._removeReference(model);
+ return model;
+ },
+
+ // Internal method to remove a model's ties to a collection.
+ _removeReference : function(model) {
+ if (this == model.collection) {
+ delete model.collection;
+ }
+ model.unbind('all', this._onModelEvent);
+ },
+
+ // Internal method called every time a model in the set fires an event.
+ // Sets need to update their indexes when models change ids. All other
+ // events simply proxy through. "add" and "remove" events that originate
+ // in other collections are ignored.
+ _onModelEvent : function(ev, model, collection, options) {
+ if ((ev == 'add' || ev == 'remove') && collection != this) return;
+ if (ev == 'destroy') {
+ this._remove(model, options);
+ }
+ if (model && ev === 'change:' + model.idAttribute) {
+ delete this._byId[model.previous(model.idAttribute)];
+ this._byId[model.id] = model;
+ }
+ this.trigger.apply(this, arguments);
+ }
+
+ });
+
+ // Underscore methods that we want to implement on the Collection.
+ var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
+ 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
+ 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
+ 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty'];
+
+ // Mix in each Underscore method as a proxy to `Collection#models`.
+ _.each(methods, function(method) {
+ Backbone.Collection.prototype[method] = function() {
+ return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
+ };
+ });
+
+ // Backbone.Router
+ // -------------------
+
+ // Routers map faux-URLs to actions, and fire events when routes are
+ // matched. Creating a new one sets its `routes` hash, if not set statically.
+ Backbone.Router = function(options) {
+ options || (options = {});
+ if (options.routes) this.routes = options.routes;
+ this._bindRoutes();
+ this.initialize.apply(this, arguments);
+ };
+
+ // Cached regular expressions for matching named param parts and splatted
+ // parts of route strings.
+ var namedParam = /:([\w\d]+)/g;
+ var splatParam = /\*([\w\d]+)/g;
+ var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;
+
+ // Set up all inheritable **Backbone.Router** properties and methods.
+ _.extend(Backbone.Router.prototype, Backbone.Events, {
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize : function(){},
+
+ // Manually bind a single named route to a callback. For example:
+ //
+ // this.route('search/:query/p:num', 'search', function(query, num) {
+ // ...
+ // });
+ //
+ route : function(route, name, callback) {
+ Backbone.history || (Backbone.history = new Backbone.History);
+ if (!_.isRegExp(route)) route = this._routeToRegExp(route);
+ Backbone.history.route(route, _.bind(function(fragment) {
+ var args = this._extractParameters(route, fragment);
+ callback.apply(this, args);
+ this.trigger.apply(this, ['route:' + name].concat(args));
+ }, this));
+ },
+
+ // Simple proxy to `Backbone.history` to save a fragment into the history.
+ navigate : function(fragment, triggerRoute) {
+ Backbone.history.navigate(fragment, triggerRoute);
+ },
+
+ // Bind all defined routes to `Backbone.history`. We have to reverse the
+ // order of the routes here to support behavior where the most general
+ // routes can be defined at the bottom of the route map.
+ _bindRoutes : function() {
+ if (!this.routes) return;
+ var routes = [];
+ for (var route in this.routes) {
+ routes.unshift([route, this.routes[route]]);
+ }
+ for (var i = 0, l = routes.length; i < l; i++) {
+ this.route(routes[i][0], routes[i][1], this[routes[i][1]]);
+ }
+ },
+
+ // Convert a route string into a regular expression, suitable for matching
+ // against the current location hash.
+ _routeToRegExp : function(route) {
+ route = route.replace(escapeRegExp, "\\$&")
+ .replace(namedParam, "([^\/]*)")
+ .replace(splatParam, "(.*?)");
+ return new RegExp('^' + route + '$');
+ },
+
+ // Given a route, and a URL fragment that it matches, return the array of
+ // extracted parameters.
+ _extractParameters : function(route, fragment) {
+ return route.exec(fragment).slice(1);
+ }
+
+ });
+
+ // Backbone.History
+ // ----------------
+
+ // Handles cross-browser history management, based on URL fragments. If the
+ // browser does not support `onhashchange`, falls back to polling.
+ Backbone.History = function() {
+ this.handlers = [];
+ _.bindAll(this, 'checkUrl');
+ };
+
+ // Cached regex for cleaning hashes.
+ var hashStrip = /^#*!?/;
+
+ // Cached regex for detecting MSIE.
+ var isExplorer = /msie [\w.]+/;
+
+ // Has the history handling already been started?
+ var historyStarted = false;
+
+ // Set up all inheritable **Backbone.History** properties and methods.
+ _.extend(Backbone.History.prototype, {
+
+ // The default interval to poll for hash changes, if necessary, is
+ // twenty times a second.
+ interval: 50,
+
+ // Get the cross-browser normalized URL fragment, either from the URL,
+ // the hash, or the override.
+ getFragment : function(fragment, forcePushState) {
+ if (fragment == null) {
+ if (this._hasPushState || forcePushState) {
+ fragment = window.location.pathname;
+ var search = window.location.search;
+ if (search) fragment += search;
+ if (fragment.indexOf(this.options.root) == 0) fragment = fragment.substr(this.options.root.length);
+ } else {
+ fragment = window.location.hash;
+ }
+ }
+ return fragment.replace(hashStrip, '');
+ },
+
+ // Start the hash change handling, returning `true` if the current URL matches
+ // an existing route, and `false` otherwise.
+ start : function(options) {
+
+ // Figure out the initial configuration. Do we need an iframe?
+ // Is pushState desired ... is it available?
+ if (historyStarted) throw new Error("Backbone.history has already been started");
+ this.options = _.extend({}, {root: '/'}, this.options, options);
+ this._wantsPushState = !!this.options.pushState;
+ this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState);
+ var fragment = this.getFragment();
+ var docMode = document.documentMode;
+ var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
+ if (oldIE) {
+ this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
+ this.navigate(fragment);
+ }
+
+ // Depending on whether we're using pushState or hashes, and whether
+ // 'onhashchange' is supported, determine how we check the URL state.
+ if (this._hasPushState) {
+ $(window).bind('popstate', this.checkUrl);
+ } else if ('onhashchange' in window && !oldIE) {
+ $(window).bind('hashchange', this.checkUrl);
+ } else {
+ setInterval(this.checkUrl, this.interval);
+ }
+
+ // Determine if we need to change the base url, for a pushState link
+ // opened by a non-pushState browser.
+ this.fragment = fragment;
+ historyStarted = true;
+ var loc = window.location;
+ var atRoot = loc.pathname == this.options.root;
+ if (this._wantsPushState && !this._hasPushState && !atRoot) {
+ this.fragment = this.getFragment(null, true);
+ window.location = this.options.root + '#' + this.fragment;
+ } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
+ this.fragment = loc.hash.replace(hashStrip, '');
+ window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
+ }
+ return this.loadUrl();
+ },
+
+ // Add a route to be tested when the fragment changes. Routes added later may
+ // override previous routes.
+ route : function(route, callback) {
+ this.handlers.unshift({route : route, callback : callback});
+ },
+
+ // Checks the current URL to see if it has changed, and if it has,
+ // calls `loadUrl`, normalizing across the hidden iframe.
+ checkUrl : function(e) {
+ var current = this.getFragment();
+ if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash);
+ if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false;
+ if (this.iframe) this.navigate(current);
+ this.loadUrl() || this.loadUrl(window.location.hash);
+ },
+
+ // Attempt to load the current URL fragment. If a route succeeds with a
+ // match, returns `true`. If no defined routes matches the fragment,
+ // returns `false`.
+ loadUrl : function(fragmentOverride) {
+ var fragment = this.fragment = this.getFragment(fragmentOverride);
+ var matched = _.any(this.handlers, function(handler) {
+ if (handler.route.test(fragment)) {
+ handler.callback(fragment);
+ return true;
+ }
+ });
+ return matched;
+ },
+
+ // Save a fragment into the hash history. You are responsible for properly
+ // URL-encoding the fragment in advance. This does not trigger
+ // a `hashchange` event.
+ navigate : function(fragment, triggerRoute) {
+ var frag = (fragment || '').replace(hashStrip, '');
+ if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return;
+ if (this._hasPushState) {
+ var loc = window.location;
+ if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
+ this.fragment = frag;
+ window.history.pushState({}, document.title, loc.protocol + '//' + loc.host + frag);
+ } else {
+ window.location.hash = this.fragment = frag;
+ if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) {
+ this.iframe.document.open().close();
+ this.iframe.location.hash = frag;
+ }
+ }
+ if (triggerRoute) this.loadUrl(fragment);
+ }
+
+ });
+
+ // Backbone.View
+ // -------------
+
+ // Creating a Backbone.View creates its initial element outside of the DOM,
+ // if an existing element is not provided...
+ Backbone.View = function(options) {
+ this.cid = _.uniqueId('view');
+ this._configure(options || {});
+ this._ensureElement();
+ this.delegateEvents();
+ this.initialize.apply(this, arguments);
+ };
+
+ // Element lookup, scoped to DOM elements within the current view.
+ // This should be prefered to global lookups, if you're dealing with
+ // a specific view.
+ var selectorDelegate = function(selector) {
+ return $(selector, this.el);
+ };
+
+ // Cached regex to split keys for `delegate`.
+ var eventSplitter = /^(\S+)\s*(.*)$/;
+
+ // List of view options to be merged as properties.
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
+
+ // Set up all inheritable **Backbone.View** properties and methods.
+ _.extend(Backbone.View.prototype, Backbone.Events, {
+
+ // The default `tagName` of a View's element is `"div"`.
+ tagName : 'div',
+
+ // Attach the `selectorDelegate` function as the `$` property.
+ $ : selectorDelegate,
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize : function(){},
+
+ // **render** is the core function that your view should override, in order
+ // to populate its element (`this.el`), with the appropriate HTML. The
+ // convention is for **render** to always return `this`.
+ render : function() {
+ return this;
+ },
+
+ // Remove this view from the DOM. Note that the view isn't present in the
+ // DOM by default, so calling this method may be a no-op.
+ remove : function() {
+ $(this.el).remove();
+ return this;
+ },
+
+ // For small amounts of DOM Elements, where a full-blown template isn't
+ // needed, use **make** to manufacture elements, one at a time.
+ //
+ // var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
+ //
+ make : function(tagName, attributes, content) {
+ var el = document.createElement(tagName);
+ if (attributes) $(el).attr(attributes);
+ if (content) $(el).html(content);
+ return el;
+ },
+
+ // Set callbacks, where `this.callbacks` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save'
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ // This only works for delegate-able events: not `focus`, `blur`, and
+ // not `change`, `submit`, and `reset` in Internet Explorer.
+ delegateEvents : function(events) {
+ if (!(events || (events = this.events))) return;
+ $(this.el).unbind('.delegateEvents' + this.cid);
+ for (var key in events) {
+ var method = this[events[key]];
+ if (!method) throw new Error('Event "' + events[key] + '" does not exist');
+ var match = key.match(eventSplitter);
+ var eventName = match[1], selector = match[2];
+ method = _.bind(method, this);
+ eventName += '.delegateEvents' + this.cid;
+ if (selector === '') {
+ $(this.el).bind(eventName, method);
+ } else {
+ $(this.el).delegate(selector, eventName, method);
+ }
+ }
+ },
+
+ // Performs the initial configuration of a View with a set of options.
+ // Keys with special meaning *(model, collection, id, className)*, are
+ // attached directly to the view.
+ _configure : function(options) {
+ if (this.options) options = _.extend({}, this.options, options);
+ for (var i = 0, l = viewOptions.length; i < l; i++) {
+ var attr = viewOptions[i];
+ if (options[attr]) this[attr] = options[attr];
+ }
+ this.options = options;
+ },
+
+ // Ensure that the View has a DOM element to render into.
+ // If `this.el` is a string, pass it through `$()`, take the first
+ // matching element, and re-assign it to `el`. Otherwise, create
+ // an element from the `id`, `className` and `tagName` proeprties.
+ _ensureElement : function() {
+ if (!this.el) {
+ var attrs = this.attributes || {};
+ if (this.id) attrs.id = this.id;
+ if (this.className) attrs['class'] = this.className;
+ this.el = this.make(this.tagName, attrs);
+ } else if (_.isString(this.el)) {
+ this.el = $(this.el).get(0);
+ }
+ }
+
+ });
+
+ // The self-propagating extend function that Backbone classes use.
+ var extend = function (protoProps, classProps) {
+ var child = inherits(this, protoProps, classProps);
+ child.extend = this.extend;
+ return child;
+ };
+
+ // Set up inheritance for the model, collection, and view.
+ Backbone.Model.extend = Backbone.Collection.extend =
+ Backbone.Router.extend = Backbone.View.extend = extend;
+
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+ var methodMap = {
+ 'create': 'POST',
+ 'update': 'PUT',
+ 'delete': 'DELETE',
+ 'read' : 'GET'
+ };
+
+ // Backbone.sync
+ // -------------
+
+ // Override this function to change the manner in which Backbone persists
+ // models to the server. You will be passed the type of request, and the
+ // model in question. By default, uses makes a RESTful Ajax request
+ // to the model's `url()`. Some possible customizations could be:
+ //
+ // * Use `setTimeout` to batch rapid-fire updates into a single request.
+ // * Send up the models as XML instead of JSON.
+ // * Persist models via WebSockets instead of Ajax.
+ //
+ // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
+ // as `POST`, with a `_method` parameter containing the true HTTP method,
+ // as well as all requests with the body as `application/x-www-form-urlencoded` instead of
+ // `application/json` with the model in a param named `model`.
+ // Useful when interfacing with server-side languages like **PHP** that make
+ // it difficult to read the body of `PUT` requests.
+ Backbone.sync = function(method, model, options) {
+ var type = methodMap[method];
+
+ // Default JSON-request options.
+ var params = _.extend({
+ type: type,
+ dataType: 'json',
+ processData: false
+ }, options);
+
+ // Ensure that we have a URL.
+ if (!params.url) {
+ params.url = getUrl(model) || urlError();
+ }
+
+ // Ensure that we have the appropriate request data.
+ if (!params.data && model && (method == 'create' || method == 'update')) {
+ params.contentType = 'application/json';
+ params.data = JSON.stringify(model.toJSON());
+ }
+
+ // For older servers, emulate JSON by encoding the request into an HTML-form.
+ if (Backbone.emulateJSON) {
+ params.contentType = 'application/x-www-form-urlencoded';
+ params.processData = true;
+ params.data = params.data ? {model : params.data} : {};
+ }
+
+ // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
+ // And an `X-HTTP-Method-Override` header.
+ if (Backbone.emulateHTTP) {
+ if (type === 'PUT' || type === 'DELETE') {
+ if (Backbone.emulateJSON) params.data._method = type;
+ params.type = 'POST';
+ params.beforeSend = function(xhr) {
+ xhr.setRequestHeader('X-HTTP-Method-Override', type);
+ };
+ }
+ }
+
+ // Make the request.
+ return $.ajax(params);
+ };
+
+ // Helpers
+ // -------
+
+ // Shared empty constructor function to aid in prototype-chain creation.
+ var ctor = function(){};
+
+ // Helper function to correctly set up the prototype chain, for subclasses.
+ // Similar to `goog.inherits`, but uses a hash of prototype properties and
+ // class properties to be extended.
+ var inherits = function(parent, protoProps, staticProps) {
+ var child;
+
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call `super()`.
+ if (protoProps && protoProps.hasOwnProperty('constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
+
+ // Inherit class (static) properties from parent.
+ _.extend(child, parent);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
+
+ // Add static properties to the constructor function, if supplied.
+ if (staticProps) _.extend(child, staticProps);
+
+ // Correctly set child's `prototype.constructor`.
+ child.prototype.constructor = child;
+
+ // Set a convenience property in case the parent's prototype is needed later.
+ child.__super__ = parent.prototype;
+
+ return child;
+ };
+
+ // Helper function to get a URL from a Model or Collection as a property
+ // or as a function.
+ var getUrl = function(object) {
+ if (!(object && object.url)) return null;
+ return _.isFunction(object.url) ? object.url() : object.url;
+ };
+
+ // Throw an error when a URL is needed, and none is supplied.
+ var urlError = function() {
+ throw new Error('A "url" property or function must be specified');
+ };
+
+ // Wrap an optional error callback with a fallback error event.
+ var wrapError = function(onError, model, options) {
+ return function(resp) {
+ if (onError) {
+ onError(model, resp, options);
+ } else {
+ model.trigger('error', model, resp, options);
+ }
+ };
+ };
+
+ // Helper function to escape a string for HTML rendering.
+ var escapeHTML = function(string) {
+ return string.replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27').replace(/\//g,'&#x2F;');
+ };
+
+}).call(this); \ No newline at end of file
diff --git a/samples/javascript/modernizr.js b/samples/javascript/modernizr.js
new file mode 100644
index 0000000..5a72b47
--- /dev/null
+++ b/samples/javascript/modernizr.js
@@ -0,0 +1,1295 @@
+/*!
+ * Modernizr v2.6pre
+ * www.modernizr.com
+ *
+ * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
+ * Available under the BSD and MIT licenses: www.modernizr.com/license/
+ */
+
+/*
+ * Modernizr tests which native CSS3 and HTML5 features are available in
+ * the current UA and makes the results available to you in two ways:
+ * as properties on a global Modernizr object, and as classes on the
+ * <html> element. This information allows you to progressively enhance
+ * your pages with a granular level of control over the experience.
+ *
+ * Modernizr has an optional (not included) conditional resource loader
+ * called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
+ * To get a build that includes Modernizr.load(), as well as choosing
+ * which tests to include, go to www.modernizr.com/download/
+ *
+ * Authors Faruk Ates, Paul Irish, Alex Sexton
+ * Contributors Ryan Seddon, Ben Alman
+ */
+
+window.Modernizr = (function( window, document, undefined ) {
+
+ var version = '2.5.3',
+
+ Modernizr = {},
+
+ /*>>cssclasses*/
+ // option for enabling the HTML classes to be added
+ enableClasses = true,
+ /*>>cssclasses*/
+
+ docElement = document.documentElement,
+
+ /**
+ * Create our "modernizr" element that we do most feature tests on.
+ */
+ mod = 'modernizr',
+ modElem = document.createElement(mod),
+ mStyle = modElem.style,
+
+ /**
+ * Create the input element for various Web Forms feature tests.
+ */
+ inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,
+
+ /*>>smile*/
+ smile = ':)',
+ /*>>smile*/
+
+ toString = {}.toString,
+
+ // TODO :: make the prefixes more granular
+ /*>>prefixes*/
+ // List of property values to set for css tests. See ticket #21
+ prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
+ /*>>prefixes*/
+
+ /*>>domprefixes*/
+ // Following spec is to expose vendor-specific style properties as:
+ // elem.style.WebkitBorderRadius
+ // and the following would be incorrect:
+ // elem.style.webkitBorderRadius
+
+ // Webkit ghosts their properties in lowercase but Opera & Moz do not.
+ // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
+ // erik.eae.net/archives/2008/03/10/21.48.10/
+
+ // More here: github.com/Modernizr/Modernizr/issues/issue/21
+ omPrefixes = 'Webkit Moz O ms',
+
+ cssomPrefixes = omPrefixes.split(' '),
+
+ domPrefixes = omPrefixes.toLowerCase().split(' '),
+ /*>>domprefixes*/
+
+ /*>>ns*/
+ ns = {'svg': 'http://www.w3.org/2000/svg'},
+ /*>>ns*/
+
+ tests = {},
+ inputs = {},
+ attrs = {},
+
+ classes = [],
+
+ slice = classes.slice,
+
+ featureName, // used in testing loop
+
+
+ /*>>teststyles*/
+ // Inject element with style element and some CSS rules
+ injectElementWithStyles = function( rule, callback, nodes, testnames ) {
+
+ var style, ret, node,
+ div = document.createElement('div'),
+ // After page load injecting a fake body doesn't work so check if body exists
+ body = document.body,
+ // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.
+ fakeBody = body ? body : document.createElement('body');
+
+ if ( parseInt(nodes, 10) ) {
+ // In order not to give false positives we create a node for each test
+ // This also allows the method to scale for unspecified uses
+ while ( nodes-- ) {
+ node = document.createElement('div');
+ node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
+ div.appendChild(node);
+ }
+ }
+
+ // <style> elements in IE6-9 are considered 'NoScope' elements and therefore will be removed
+ // when injected with innerHTML. To get around this you need to prepend the 'NoScope' element
+ // with a 'scoped' element, in our case the soft-hyphen entity as it won't mess with our measurements.
+ // msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx
+ // Documents served as xml will throw if using &shy; so use xml friendly encoded version. See issue #277
+ style = ['&#173;','<style>', rule, '</style>'].join('');
+ div.id = mod;
+ // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
+ // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
+ (body ? div : fakeBody).innerHTML += style;
+ fakeBody.appendChild(div);
+ if ( !body ) {
+ //avoid crashing IE8, if background image is used
+ fakeBody.style.background = "";
+ docElement.appendChild(fakeBody);
+ }
+
+ ret = callback(div, rule);
+ // If this is done after page load we don't want to remove the body so check if body exists
+ !body ? fakeBody.parentNode.removeChild(fakeBody) : div.parentNode.removeChild(div);
+
+ return !!ret;
+
+ },
+ /*>>teststyles*/
+
+ /*>>mq*/
+ // adapted from matchMedia polyfill
+ // by Scott Jehl and Paul Irish
+ // gist.github.com/786768
+ testMediaQuery = function( mq ) {
+
+ var matchMedia = window.matchMedia || window.msMatchMedia;
+ if ( matchMedia ) {
+ return matchMedia(mq).matches;
+ }
+
+ var bool;
+
+ injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
+ bool = (window.getComputedStyle ?
+ getComputedStyle(node, null) :
+ node.currentStyle)['position'] == 'absolute';
+ });
+
+ return bool;
+
+ },
+ /*>>mq*/
+
+
+ /*>>hasevent*/
+ //
+ // isEventSupported determines if a given element supports the given event
+ // kangax.github.com/iseventsupported/
+ //
+ // The following results are known incorrects:
+ // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative
+ // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333
+ // ...
+ isEventSupported = (function() {
+
+ var TAGNAMES = {
+ 'select': 'input', 'change': 'input',
+ 'submit': 'form', 'reset': 'form',
+ 'error': 'img', 'load': 'img', 'abort': 'img'
+ };
+
+ function isEventSupported( eventName, element ) {
+
+ element = element || document.createElement(TAGNAMES[eventName] || 'div');
+ eventName = 'on' + eventName;
+
+ // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
+ var isSupported = eventName in element;
+
+ if ( !isSupported ) {
+ // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
+ if ( !element.setAttribute ) {
+ element = document.createElement('div');
+ }
+ if ( element.setAttribute && element.removeAttribute ) {
+ element.setAttribute(eventName, '');
+ isSupported = is(element[eventName], 'function');
+
+ // If property was created, "remove it" (by setting value to `undefined`)
+ if ( !is(element[eventName], 'undefined') ) {
+ element[eventName] = undefined;
+ }
+ element.removeAttribute(eventName);
+ }
+ }
+
+ element = null;
+ return isSupported;
+ }
+ return isEventSupported;
+ })(),
+ /*>>hasevent*/
+
+ // TODO :: Add flag for hasownprop ? didn't last time
+
+ // hasOwnProperty shim by kangax needed for Safari 2.0 support
+ _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty;
+
+ if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
+ hasOwnProperty = function (object, property) {
+ return _hasOwnProperty.call(object, property);
+ };
+ }
+ else {
+ hasOwnProperty = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
+ return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
+ };
+ }
+
+ // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
+ // es5.github.com/#x15.3.4.5
+
+ if (!Function.prototype.bind) {
+ Function.prototype.bind = function bind(that) {
+
+ var target = this;
+ if (typeof target != "function") throw new TypeError();
+
+ var
+ args = slice.call(arguments, 1),
+ bound = function () {
+
+ if (this instanceof bound) {
+
+ var F = function(){};
+ F.prototype = target.prototype;
+ var self = new F();
+ var result = target.apply(self, args.concat(slice.call(arguments)));
+
+ if (Object(result) === result) return result;
+ return self;
+
+ } else {
+ return target.apply( that, args.concat(slice.call(arguments)));
+ }
+ };
+ return bound;
+ };
+ }
+
+ /**
+ * setCss applies given styles to the Modernizr DOM node.
+ */
+ function setCss( str ) {
+ mStyle.cssText = str;
+ }
+
+ /**
+ * setCssAll extrapolates all vendor-specific css strings.
+ */
+ function setCssAll( str1, str2 ) {
+ return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
+ }
+
+ /**
+ * is returns a boolean for if typeof obj is exactly type.
+ */
+ function is( obj, type ) {
+ return typeof obj === type;
+ }
+
+ /**
+ * contains returns a boolean for if substr is found within str.
+ */
+ function contains( str, substr ) {
+ return !!~('' + str).indexOf(substr);
+ }
+
+ /*>>testprop*/
+
+ // testProps is a generic CSS / DOM property test.
+
+ // In testing support for a given CSS property, it's legit to test:
+ // `elem.style[styleName] !== undefined`
+ // If the property is supported it will return an empty string,
+ // if unsupported it will return undefined.
+
+ // We'll take advantage of this quick test and skip setting a style
+ // on our modernizr element, but instead just testing undefined vs
+ // empty string.
+
+ function testProps( props, prefixed ) {
+ for ( var i in props ) {
+ if ( mStyle[ props[i] ] !== undefined ) {
+ return prefixed == 'pfx' ? props[i] : true;
+ }
+ }
+ return false;
+ }
+ /*>>testprop*/
+
+ // TODO :: add testDOMProps
+ /**
+ * testDOMProps is a generic DOM property test; if a browser supports
+ * a certain property, it won't return undefined for it.
+ */
+ function testDOMProps( props, obj, elem ) {
+ for ( var i in props ) {
+ var item = obj[props[i]];
+ if ( item !== undefined) {
+
+ // return the property name as a string
+ if (elem === false) return props[i];
+
+ // let's bind a function
+ if (is(item, 'function')){
+ // default to autobind unless override
+ return item.bind(elem || obj);
+ }
+
+ // return the unbound function or obj or value
+ return item;
+ }
+ }
+ return false;
+ }
+
+ /*>>testallprops*/
+ /**
+ * testPropsAll tests a list of DOM properties we want to check against.
+ * We specify literally ALL possible (known and/or likely) properties on
+ * the element including the non-vendor prefixed one, for forward-
+ * compatibility.
+ */
+ function testPropsAll( prop, prefixed, elem ) {
+
+ var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
+ props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
+
+ // did they call .prefixed('boxSizing') or are we just testing a prop?
+ if(is(prefixed, "string") || is(prefixed, "undefined")) {
+ return testProps(props, prefixed);
+
+ // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
+ } else {
+ props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
+ return testDOMProps(props, prefixed, elem);
+ }
+ }
+ /*>>testallprops*/
+
+
+ /**
+ * Tests
+ * -----
+ */
+
+ // The *new* flexbox
+ // dev.w3.org/csswg/css3-flexbox
+
+ tests['flexbox'] = function() {
+ return testPropsAll('flexOrder');
+ };
+
+ // The *old* flexbox
+ // www.w3.org/TR/2009/WD-css3-flexbox-20090723/
+
+ tests['flexboxlegacy'] = function() {
+ return testPropsAll('boxDirection');
+ };
+
+ // On the S60 and BB Storm, getContext exists, but always returns undefined
+ // so we actually have to call getContext() to verify
+ // github.com/Modernizr/Modernizr/issues/issue/97/
+
+ tests['canvas'] = function() {
+ var elem = document.createElement('canvas');
+ return !!(elem.getContext && elem.getContext('2d'));
+ };
+
+ tests['canvastext'] = function() {
+ return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
+ };
+
+ // webk.it/70117 is tracking a legit WebGL feature detect proposal
+
+ // We do a soft detect which may false positive in order to avoid
+ // an expensive context creation: bugzil.la/732441
+
+ tests['webgl'] = function() {
+ return !!window.WebGLRenderingContext;
+ };
+
+ /*
+ * The Modernizr.touch test only indicates if the browser supports
+ * touch events, which does not necessarily reflect a touchscreen
+ * device, as evidenced by tablets running Windows 7 or, alas,
+ * the Palm Pre / WebOS (touch) phones.
+ *
+ * Additionally, Chrome (desktop) used to lie about its support on this,
+ * but that has since been rectified: crbug.com/36415
+ *
+ * We also test for Firefox 4 Multitouch Support.
+ *
+ * For more info, see: modernizr.github.com/Modernizr/touch.html
+ */
+
+ tests['touch'] = function() {
+ var bool;
+
+ if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
+ bool = true;
+ } else {
+ injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
+ bool = node.offsetTop === 9;
+ });
+ }
+
+ return bool;
+ };
+
+
+ // geolocation is often considered a trivial feature detect...
+ // Turns out, it's quite tricky to get right:
+ //
+ // Using !!navigator.geolocation does two things we don't want. It:
+ // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
+ // 2. Disables page caching in WebKit: webk.it/43956
+ //
+ // Meanwhile, in Firefox < 8, an about:config setting could expose
+ // a false positive that would throw an exception: bugzil.la/688158
+
+ tests['geolocation'] = function() {
+ return 'geolocation' in navigator;
+ };
+
+
+ tests['postmessage'] = function() {
+ return !!window.postMessage;
+ };
+
+
+ // Chrome incognito mode used to throw an exception when using openDatabase
+ // It doesn't anymore.
+ tests['websqldatabase'] = function() {
+ return !!window.openDatabase;
+ };
+
+ // Vendors had inconsistent prefixing with the experimental Indexed DB:
+ // - Webkit's implementation is accessible through webkitIndexedDB
+ // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
+ // For speed, we don't test the legacy (and beta-only) indexedDB
+ tests['indexedDB'] = function() {
+ return !!testPropsAll("indexedDB", window);
+ };
+
+ // documentMode logic from YUI to filter out IE8 Compat Mode
+ // which false positives.
+ tests['hashchange'] = function() {
+ return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
+ };
+
+ // Per 1.6:
+ // This used to be Modernizr.historymanagement but the longer
+ // name has been deprecated in favor of a shorter and property-matching one.
+ // The old API is still available in 1.6, but as of 2.0 will throw a warning,
+ // and in the first release thereafter disappear entirely.
+ tests['history'] = function() {
+ return !!(window.history && history.pushState);
+ };
+
+ tests['draganddrop'] = function() {
+ var div = document.createElement('div');
+ return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
+ };
+
+ // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10
+ // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.
+ // FF10 still uses prefixes, so check for it until then.
+ // for more ESR info, see: http://www.mozilla.org/en-US/firefox/organizations/faq/
+ tests['websockets'] = function() {
+ return 'WebSocket' in window || 'MozWebSocket' in window;
+ };
+
+
+ // css-tricks.com/rgba-browser-support/
+ tests['rgba'] = function() {
+ // Set an rgba() color and check the returned value
+
+ setCss('background-color:rgba(150,255,150,.5)');
+
+ return contains(mStyle.backgroundColor, 'rgba');
+ };
+
+ tests['hsla'] = function() {
+ // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
+ // except IE9 who retains it as hsla
+
+ setCss('background-color:hsla(120,40%,100%,.5)');
+
+ return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
+ };
+
+ tests['multiplebgs'] = function() {
+ // Setting multiple images AND a color on the background shorthand property
+ // and then querying the style.background property value for the number of
+ // occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
+
+ setCss('background:url(https://),url(https://),red url(https://)');
+
+ // If the UA supports multiple backgrounds, there should be three occurrences
+ // of the string "url(" in the return value for elemStyle.background
+
+ return /(url\s*\(.*?){3}/.test(mStyle.background);
+ };
+
+
+
+ // this will false positive in Opera Mini
+ // github.com/Modernizr/Modernizr/issues/396
+
+ tests['backgroundsize'] = function() {
+ return testPropsAll('backgroundSize');
+ };
+
+ tests['borderimage'] = function() {
+ return testPropsAll('borderImage');
+ };
+
+
+ // Super comprehensive table about all the unique implementations of
+ // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance
+
+ tests['borderradius'] = function() {
+ return testPropsAll('borderRadius');
+ };
+
+ // WebOS unfortunately false positives on this test.
+ tests['boxshadow'] = function() {
+ return testPropsAll('boxShadow');
+ };
+
+ // FF3.0 will false positive on this test
+ tests['textshadow'] = function() {
+ return document.createElement('div').style.textShadow === '';
+ };
+
+
+ tests['opacity'] = function() {
+ // Browsers that actually have CSS Opacity implemented have done so
+ // according to spec, which means their return values are within the
+ // range of [0.0,1.0] - including the leading zero.
+
+ setCssAll('opacity:.55');
+
+ // The non-literal . in this regex is intentional:
+ // German Chrome returns this value as 0,55
+ // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
+ return /^0.55$/.test(mStyle.opacity);
+ };
+
+
+ // Note, Android < 4 will pass this test, but can only animate
+ // a single property at a time
+ // daneden.me/2011/12/putting-up-with-androids-bullshit/
+ tests['cssanimations'] = function() {
+ return testPropsAll('animationName');
+ };
+
+
+ tests['csscolumns'] = function() {
+ return testPropsAll('columnCount');
+ };
+
+
+ tests['cssgradients'] = function() {
+ /**
+ * For CSS Gradients syntax, please see:
+ * webkit.org/blog/175/introducing-css-gradients/
+ * developer.mozilla.org/en/CSS/-moz-linear-gradient
+ * developer.mozilla.org/en/CSS/-moz-radial-gradient
+ * dev.w3.org/csswg/css3-images/#gradients-
+ */
+
+ var str1 = 'background-image:',
+ str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
+ str3 = 'linear-gradient(left top,#9f9, white);';
+
+ setCss(
+ // legacy webkit syntax (FIXME: remove when syntax not in use anymore)
+ (str1 + '-webkit- '.split(' ').join(str2 + str1)
+ // standard syntax // trailing 'background-image:'
+ + prefixes.join(str3 + str1)).slice(0, -str1.length)
+ );
+
+ return contains(mStyle.backgroundImage, 'gradient');
+ };
+
+
+ tests['cssreflections'] = function() {
+ return testPropsAll('boxReflect');
+ };
+
+
+ tests['csstransforms'] = function() {
+ return !!testPropsAll('transform');
+ };
+
+
+ tests['csstransforms3d'] = function() {
+
+ var ret = !!testPropsAll('perspective');
+
+ // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
+ // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
+ // some conditions. As a result, Webkit typically recognizes the syntax but
+ // will sometimes throw a false positive, thus we must do a more thorough check:
+ if ( ret && 'webkitPerspective' in docElement.style ) {
+
+ // Webkit allows this media query to succeed only if the feature is enabled.
+ // `@media (transform-3d),(-webkit-transform-3d){ ... }`
+ injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
+ ret = node.offsetLeft === 9 && node.offsetHeight === 3;
+ });
+ }
+ return ret;
+ };
+
+
+ tests['csstransitions'] = function() {
+ return testPropsAll('transition');
+ };
+
+
+ /*>>fontface*/
+ // @font-face detection routine by Diego Perini
+ // javascript.nwbox.com/CSSSupport/
+
+ // false positives:
+ // WebOS github.com/Modernizr/Modernizr/issues/342
+ // WP7 github.com/Modernizr/Modernizr/issues/538
+ tests['fontface'] = function() {
+ var bool;
+
+ injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
+ var style = document.styleSheets[document.styleSheets.length - 1],
+ cssText = style ? (style.cssRules && style.cssRules[0] ? style.cssRules[0].cssText : style.cssText || '') : '';
+
+ bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
+ });
+
+ return bool;
+ };
+ /*>>fontface*/
+
+ // CSS generated content detection
+ tests['generatedcontent'] = function() {
+ var bool;
+
+ injectElementWithStyles(['#modernizr:after{content:"',smile,'";visibility:hidden}'].join(''), function( node ) {
+ bool = node.offsetHeight >= 1;
+ });
+
+ return bool;
+ };
+
+
+
+ // These tests evaluate support of the video/audio elements, as well as
+ // testing what types of content they support.
+ //
+ // We're using the Boolean constructor here, so that we can extend the value
+ // e.g. Modernizr.video // true
+ // Modernizr.video.ogg // 'probably'
+ //
+ // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
+ // thx to NielsLeenheer and zcorpan
+
+ // Note: in some older browsers, "no" was a return value instead of empty string.
+ // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
+ // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
+
+ tests['video'] = function() {
+ var elem = document.createElement('video'),
+ bool = false;
+
+ // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
+ try {
+ if ( bool = !!elem.canPlayType ) {
+ bool = new Boolean(bool);
+ bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,'');
+
+ // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
+ bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
+
+ bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
+ }
+
+ } catch(e) { }
+
+ return bool;
+ };
+
+ tests['audio'] = function() {
+ var elem = document.createElement('audio'),
+ bool = false;
+
+ try {
+ if ( bool = !!elem.canPlayType ) {
+ bool = new Boolean(bool);
+ bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
+ bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,'');
+
+ // Mimetypes accepted:
+ // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
+ // bit.ly/iphoneoscodecs
+ bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,'');
+ bool.m4a = ( elem.canPlayType('audio/x-m4a;') ||
+ elem.canPlayType('audio/aac;')) .replace(/^no$/,'');
+ }
+ } catch(e) { }
+
+ return bool;
+ };
+
+
+ // In FF4, if disabled, window.localStorage should === null.
+
+ // Normally, we could not test that directly and need to do a
+ // `('localStorage' in window) && ` test first because otherwise Firefox will
+ // throw bugzil.la/365772 if cookies are disabled
+
+ // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
+ // will throw the exception:
+ // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
+ // Peculiarly, getItem and removeItem calls do not throw.
+
+ // Because we are forced to try/catch this, we'll go aggressive.
+
+ // Just FWIW: IE8 Compat mode supports these features completely:
+ // www.quirksmode.org/dom/html5.html
+ // But IE8 doesn't support either with local files
+
+ tests['localstorage'] = function() {
+ try {
+ localStorage.setItem(mod, mod);
+ localStorage.removeItem(mod);
+ return true;
+ } catch(e) {
+ return false;
+ }
+ };
+
+ tests['sessionstorage'] = function() {
+ try {
+ sessionStorage.setItem(mod, mod);
+ sessionStorage.removeItem(mod);
+ return true;
+ } catch(e) {
+ return false;
+ }
+ };
+
+
+ tests['webworkers'] = function() {
+ return !!window.Worker;
+ };
+
+
+ tests['applicationcache'] = function() {
+ return !!window.applicationCache;
+ };
+
+
+ // Thanks to Erik Dahlstrom
+ tests['svg'] = function() {
+ return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
+ };
+
+ // specifically for SVG inline in HTML, not within XHTML
+ // test page: paulirish.com/demo/inline-svg
+ tests['inlinesvg'] = function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<svg/>';
+ return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
+ };
+
+ // SVG SMIL animation
+ tests['smil'] = function() {
+ return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
+ };
+
+ // This test is only for clip paths in SVG proper, not clip paths on HTML content
+ // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg
+
+ // However read the comments to dig into applying SVG clippaths to HTML content here:
+ // github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491
+ tests['svgclippaths'] = function() {
+ return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
+ };
+
+ /*>>webforms*/
+ // input features and input types go directly onto the ret object, bypassing the tests loop.
+ // Hold this guy to execute in a moment.
+ function webforms() {
+ /*>>input*/
+ // Run through HTML5's new input attributes to see if the UA understands any.
+ // We're using f which is the <input> element created early on
+ // Mike Taylr has created a comprehensive resource for testing these attributes
+ // when applied to all input types:
+ // miketaylr.com/code/input-type-attr.html
+ // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
+
+ // Only input placeholder is tested while textarea's placeholder is not.
+ // Currently Safari 4 and Opera 11 have support only for the input placeholder
+ // Both tests are available in feature-detects/forms-placeholder.js
+ Modernizr['input'] = (function( props ) {
+ for ( var i = 0, len = props.length; i < len; i++ ) {
+ attrs[ props[i] ] = !!(props[i] in inputElem);
+ }
+ if (attrs.list){
+ // safari false positive's on datalist: webk.it/74252
+ // see also github.com/Modernizr/Modernizr/issues/146
+ attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
+ }
+ return attrs;
+ })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
+ /*>>input*/
+
+ /*>>inputtypes*/
+ // Run through HTML5's new input types to see if the UA understands any.
+ // This is put behind the tests runloop because it doesn't return a
+ // true/false like all the other tests; instead, it returns an object
+ // containing each input type with its corresponding true/false value
+
+ // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
+ Modernizr['inputtypes'] = (function(props) {
+
+ for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
+
+ inputElem.setAttribute('type', inputElemType = props[i]);
+ bool = inputElem.type !== 'text';
+
+ // We first check to see if the type we give it sticks..
+ // If the type does, we feed it a textual value, which shouldn't be valid.
+ // If the value doesn't stick, we know there's input sanitization which infers a custom UI
+ if ( bool ) {
+
+ inputElem.value = smile;
+ inputElem.style.cssText = 'position:absolute;visibility:hidden;';
+
+ if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
+
+ docElement.appendChild(inputElem);
+ defaultView = document.defaultView;
+
+ // Safari 2-4 allows the smiley as a value, despite making a slider
+ bool = defaultView.getComputedStyle &&
+ defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
+ // Mobile android web browser has false positive, so must
+ // check the height to see if the widget is actually there.
+ (inputElem.offsetHeight !== 0);
+
+ docElement.removeChild(inputElem);
+
+ } else if ( /^(search|tel)$/.test(inputElemType) ){
+ // Spec doesn't define any special parsing or detectable UI
+ // behaviors so we pass these through as true
+
+ // Interestingly, opera fails the earlier test, so it doesn't
+ // even make it here.
+
+ } else if ( /^(url|email)$/.test(inputElemType) ) {
+ // Real url and email support comes with prebaked validation.
+ bool = inputElem.checkValidity && inputElem.checkValidity() === false;
+
+ } else {
+ // If the upgraded input compontent rejects the :) text, we got a winner
+ bool = inputElem.value != smile;
+ }
+ }
+
+ inputs[ props[i] ] = !!bool;
+ }
+ return inputs;
+ })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
+ /*>>inputtypes*/
+ }
+ /*>>webforms*/
+
+
+ // End of test definitions
+ // -----------------------
+
+
+
+ // Run through all tests and detect their support in the current UA.
+ // todo: hypothetically we could be doing an array of tests and use a basic loop here.
+ for ( var feature in tests ) {
+ if ( hasOwnProperty(tests, feature) ) {
+ // run the test, throw the return value into the Modernizr,
+ // then based on that boolean, define an appropriate className
+ // and push it into an array of classes we'll join later.
+ featureName = feature.toLowerCase();
+ Modernizr[featureName] = tests[feature]();
+
+ classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
+ }
+ }
+
+ /*>>webforms*/
+ // input tests need to run.
+ Modernizr.input || webforms();
+ /*>>webforms*/
+
+
+ /*>>addtest*/
+ /**
+ * addTest allows the user to define their own feature tests
+ * the result will be added onto the Modernizr object,
+ * as well as an appropriate className set on the html element
+ *
+ * @param feature - String naming the feature
+ * @param test - Function returning true if feature is supported, false if not
+ */
+ Modernizr.addTest = function ( feature, test ) {
+ if ( typeof feature == 'object' ) {
+ for ( var key in feature ) {
+ if ( hasOwnProperty( feature, key ) ) {
+ Modernizr.addTest( key, feature[ key ] );
+ }
+ }
+ } else {
+
+ feature = feature.toLowerCase();
+
+ if ( Modernizr[feature] !== undefined ) {
+ // we're going to quit if you're trying to overwrite an existing test
+ // if we were to allow it, we'd do this:
+ // var re = new RegExp("\\b(no-)?" + feature + "\\b");
+ // docElement.className = docElement.className.replace( re, '' );
+ // but, no rly, stuff 'em.
+ return Modernizr;
+ }
+
+ test = typeof test == 'function' ? test() : test;
+
+ if (enableClasses) {
+ docElement.className += ' ' + (test ? '' : 'no-') + feature;
+ }
+ Modernizr[feature] = test;
+
+ }
+
+ return Modernizr; // allow chaining.
+ };
+ /*>>addtest*/
+
+
+ // Reset modElem.cssText to nothing to reduce memory footprint.
+ setCss('');
+ modElem = inputElem = null;
+
+ /*>>shiv*/
+ /*! HTML5 Shiv v3.5 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */
+ ;(function(window, document) {
+
+ /** Preset options */
+ var options = window.html5 || {};
+
+ /** Used to skip problem elements */
+ var reSkip = /^<|^(?:button|form|map|select|textarea|object|iframe|option|optgroup)$/i;
+
+ /** Not all elements can be cloned in IE (this list can be shortend) **/
+ var saveClones = /^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i;
+
+ /** Detect whether the browser supports default html5 styles */
+ var supportsHtml5Styles;
+
+ /** Detect whether the browser supports unknown elements */
+ var supportsUnknownElements;
+
+ (function() {
+ var a = document.createElement('a');
+
+ a.innerHTML = '<xyz></xyz>';
+
+ //if the hidden property is implemented we can assume, that the browser supports HTML5 Styles | this fails in Chrome 8
+ supportsHtml5Styles = ('hidden' in a);
+ //if we are part of Modernizr, we do an additional test to solve the Chrome 8 fail
+ if(supportsHtml5Styles && typeof injectElementWithStyles == 'function'){
+ injectElementWithStyles('#modernizr{}', function(node){
+ node.hidden = true;
+ supportsHtml5Styles = (window.getComputedStyle ?
+ getComputedStyle(node, null) :
+ node.currentStyle).display == 'none';
+ });
+ }
+
+ supportsUnknownElements = a.childNodes.length == 1 || (function() {
+ // assign a false positive if unable to shiv
+ try {
+ (document.createElement)('a');
+ } catch(e) {
+ return true;
+ }
+ var frag = document.createDocumentFragment();
+ return (
+ typeof frag.cloneNode == 'undefined' ||
+ typeof frag.createDocumentFragment == 'undefined' ||
+ typeof frag.createElement == 'undefined'
+ );
+ }());
+
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a style sheet with the given CSS text and adds it to the document.
+ * @private
+ * @param {Document} ownerDocument The document.
+ * @param {String} cssText The CSS text.
+ * @returns {StyleSheet} The style element.
+ */
+ function addStyleSheet(ownerDocument, cssText) {
+ var p = ownerDocument.createElement('p'),
+ parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
+
+ p.innerHTML = 'x<style>' + cssText + '</style>';
+ return parent.insertBefore(p.lastChild, parent.firstChild);
+ }
+
+ /**
+ * Returns the value of `html5.elements` as an array.
+ * @private
+ * @returns {Array} An array of shived element node names.
+ */
+ function getElements() {
+ var elements = html5.elements;
+ return typeof elements == 'string' ? elements.split(' ') : elements;
+ }
+
+ /**
+ * Shivs the `createElement` and `createDocumentFragment` methods of the document.
+ * @private
+ * @param {Document|DocumentFragment} ownerDocument The document.
+ */
+ function shivMethods(ownerDocument) {
+ var cache = {},
+ docCreateElement = ownerDocument.createElement,
+ docCreateFragment = ownerDocument.createDocumentFragment,
+ frag = docCreateFragment();
+
+ ownerDocument.createElement = function(nodeName) {
+ //abort shiv
+ if(!html5.shivMethods){
+ return docCreateElement(nodeName);
+ }
+
+ var node;
+
+ if(cache[nodeName]){
+ node = cache[nodeName].cloneNode();
+ } else if(saveClones.test(nodeName)){
+ node = (cache[nodeName] = docCreateElement(nodeName)).cloneNode();
+ } else {
+ node = docCreateElement(nodeName);
+ }
+
+ // Avoid adding some elements to fragments in IE < 9 because
+ // * Attributes like `name` or `type` cannot be set/changed once an element
+ // is inserted into a document/fragment
+ // * Link elements with `src` attributes that are inaccessible, as with
+ // a 403 response, will cause the tab/window to crash
+ // * Script elements appended to fragments will execute when their `src`
+ // or `text` property is set
+ return node.canHaveChildren && !reSkip.test(nodeName) ? frag.appendChild(node) : node;
+ };
+
+ ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
+ 'var n=f.cloneNode(),c=n.createElement;' +
+ 'h.shivMethods&&(' +
+ // unroll the `createElement` calls
+ getElements().join().replace(/\w+/g, function(nodeName) {
+ docCreateElement(nodeName);
+ frag.createElement(nodeName);
+ return 'c("' + nodeName + '")';
+ }) +
+ ');return n}'
+ )(html5, frag);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Shivs the given document.
+ * @memberOf html5
+ * @param {Document} ownerDocument The document to shiv.
+ * @returns {Document} The shived document.
+ */
+ function shivDocument(ownerDocument) {
+ var shived;
+ if (ownerDocument.documentShived) {
+ return ownerDocument;
+ }
+ if (html5.shivCSS && !supportsHtml5Styles) {
+ shived = !!addStyleSheet(ownerDocument,
+ // corrects block display not defined in IE6/7/8/9
+ 'article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +
+ // corrects audio display not defined in IE6/7/8/9
+ 'audio{display:none}' +
+ // corrects canvas and video display not defined in IE6/7/8/9
+ 'canvas,video{display:inline-block;*display:inline;*zoom:1}' +
+ // corrects 'hidden' attribute and audio[controls] display not present in IE7/8/9
+ '[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}' +
+ // adds styling not present in IE6/7/8/9
+ 'mark{background:#FF0;color:#000}'
+ );
+ }
+ if (!supportsUnknownElements) {
+ shived = !shivMethods(ownerDocument);
+ }
+ if (shived) {
+ ownerDocument.documentShived = shived;
+ }
+ return ownerDocument;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The `html5` object is exposed so that more elements can be shived and
+ * existing shiving can be detected on iframes.
+ * @type Object
+ * @example
+ *
+ * // options can be changed before the script is included
+ * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
+ */
+ var html5 = {
+
+ /**
+ * An array or space separated string of node names of the elements to shiv.
+ * @memberOf html5
+ * @type Array|String
+ */
+ 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',
+
+ /**
+ * A flag to indicate that the HTML5 style sheet should be inserted.
+ * @memberOf html5
+ * @type Boolean
+ */
+ 'shivCSS': !(options.shivCSS === false),
+
+ /**
+ * A flag to indicate that the document's `createElement` and `createDocumentFragment`
+ * methods should be overwritten.
+ * @memberOf html5
+ * @type Boolean
+ */
+ 'shivMethods': !(options.shivMethods === false),
+
+ /**
+ * A string to describe the type of `html5` object ("default" or "default print").
+ * @memberOf html5
+ * @type String
+ */
+ 'type': 'default',
+
+ // shivs the document according to the specified `html5` object options
+ 'shivDocument': shivDocument
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose html5
+ window.html5 = html5;
+
+ // shiv the document
+ shivDocument(document);
+
+ }(this, document));
+ /*>>shiv*/
+
+ // Assign private properties to the return object with prefix
+ Modernizr._version = version;
+
+ // expose these for the plugin API. Look in the source for how to join() them against your input
+ /*>>prefixes*/
+ Modernizr._prefixes = prefixes;
+ /*>>prefixes*/
+ /*>>domprefixes*/
+ Modernizr._domPrefixes = domPrefixes;
+ Modernizr._cssomPrefixes = cssomPrefixes;
+ /*>>domprefixes*/
+
+ /*>>mq*/
+ // Modernizr.mq tests a given media query, live against the current state of the window
+ // A few important notes:
+ // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
+ // * A max-width or orientation query will be evaluated against the current state, which may change later.
+ // * You must specify values. Eg. If you are testing support for the min-width media query use:
+ // Modernizr.mq('(min-width:0)')
+ // usage:
+ // Modernizr.mq('only screen and (max-width:768)')
+ Modernizr.mq = testMediaQuery;
+ /*>>mq*/
+
+ /*>>hasevent*/
+ // Modernizr.hasEvent() detects support for a given event, with an optional element to test on
+ // Modernizr.hasEvent('gesturestart', elem)
+ Modernizr.hasEvent = isEventSupported;
+ /*>>hasevent*/
+
+ /*>>testprop*/
+ // Modernizr.testProp() investigates whether a given style property is recognized
+ // Note that the property names must be provided in the camelCase variant.
+ // Modernizr.testProp('pointerEvents')
+ Modernizr.testProp = function(prop){
+ return testProps([prop]);
+ };
+ /*>>testprop*/
+
+ /*>>testallprops*/
+ // Modernizr.testAllProps() investigates whether a given style property,
+ // or any of its vendor-prefixed variants, is recognized
+ // Note that the property names must be provided in the camelCase variant.
+ // Modernizr.testAllProps('boxSizing')
+ Modernizr.testAllProps = testPropsAll;
+ /*>>testallprops*/
+
+
+ /*>>teststyles*/
+ // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
+ // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
+ Modernizr.testStyles = injectElementWithStyles;
+ /*>>teststyles*/
+
+
+ /*>>prefixed*/
+ // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
+ // Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
+
+ // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
+ // Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
+ //
+ // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
+
+ // If you're trying to ascertain which transition end event to bind to, you might do something like...
+ //
+ // var transEndEventNames = {
+ // 'WebkitTransition' : 'webkitTransitionEnd',
+ // 'MozTransition' : 'transitionend',
+ // 'OTransition' : 'oTransitionEnd',
+ // 'msTransition' : 'MSTransitionEnd',
+ // 'transition' : 'transitionend'
+ // },
+ // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
+
+ Modernizr.prefixed = function(prop, obj, elem){
+ if(!obj) {
+ return testPropsAll(prop, 'pfx');
+ } else {
+ // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
+ return testPropsAll(prop, obj, elem);
+ }
+ };
+ /*>>prefixed*/
+
+
+ /*>>cssclasses*/
+ // Remove "no-js" class from <html> element, if it exists:
+ docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
+
+ // Add the new classes to the <html> element.
+ (enableClasses ? ' js ' + classes.join(' ') : '');
+ /*>>cssclasses*/
+
+ return Modernizr;
+
+})(this, this.document);
diff --git a/samples/javascript/parser.js b/samples/javascript/parser.js
new file mode 100644
index 0000000..d695036
--- /dev/null
+++ b/samples/javascript/parser.js
@@ -0,0 +1,2963 @@
+PEG.parser = (function(){
+ /*
+ * Generated by PEG.js 0.7.0.
+ *
+ * http://pegjs.majda.cz/
+ */
+
+ function quote(s) {
+ /*
+ * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a
+ * string literal except for the closing quote character, backslash,
+ * carriage return, line separator, paragraph separator, and line feed.
+ * Any character may appear in the form of an escape sequence.
+ *
+ * For portability, we also escape escape all control and non-ASCII
+ * characters. Note that "\0" and "\v" escape sequences are not used
+ * because JSHint does not like the first and IE the second.
+ */
+ return '"' + s
+ .replace(/\\/g, '\\\\') // backslash
+ .replace(/"/g, '\\"') // closing quote character
+ .replace(/\x08/g, '\\b') // backspace
+ .replace(/\t/g, '\\t') // horizontal tab
+ .replace(/\n/g, '\\n') // line feed
+ .replace(/\f/g, '\\f') // form feed
+ .replace(/\r/g, '\\r') // carriage return
+ .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape)
+ + '"';
+ }
+
+ var result = {
+ /*
+ * Parses the input with a generated parser. If the parsing is successfull,
+ * returns a value explicitly or implicitly specified by the grammar from
+ * which the parser was generated (see |PEG.buildParser|). If the parsing is
+ * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.
+ */
+ parse: function(input, startRule) {
+ var parseFunctions = {
+ "grammar": parse_grammar,
+ "initializer": parse_initializer,
+ "rule": parse_rule,
+ "choice": parse_choice,
+ "sequence": parse_sequence,
+ "labeled": parse_labeled,
+ "prefixed": parse_prefixed,
+ "suffixed": parse_suffixed,
+ "primary": parse_primary,
+ "action": parse_action,
+ "braced": parse_braced,
+ "nonBraceCharacters": parse_nonBraceCharacters,
+ "nonBraceCharacter": parse_nonBraceCharacter,
+ "equals": parse_equals,
+ "colon": parse_colon,
+ "semicolon": parse_semicolon,
+ "slash": parse_slash,
+ "and": parse_and,
+ "not": parse_not,
+ "question": parse_question,
+ "star": parse_star,
+ "plus": parse_plus,
+ "lparen": parse_lparen,
+ "rparen": parse_rparen,
+ "dot": parse_dot,
+ "identifier": parse_identifier,
+ "literal": parse_literal,
+ "string": parse_string,
+ "doubleQuotedString": parse_doubleQuotedString,
+ "doubleQuotedCharacter": parse_doubleQuotedCharacter,
+ "simpleDoubleQuotedCharacter": parse_simpleDoubleQuotedCharacter,
+ "singleQuotedString": parse_singleQuotedString,
+ "singleQuotedCharacter": parse_singleQuotedCharacter,
+ "simpleSingleQuotedCharacter": parse_simpleSingleQuotedCharacter,
+ "class": parse_class,
+ "classCharacterRange": parse_classCharacterRange,
+ "classCharacter": parse_classCharacter,
+ "bracketDelimitedCharacter": parse_bracketDelimitedCharacter,
+ "simpleBracketDelimitedCharacter": parse_simpleBracketDelimitedCharacter,
+ "simpleEscapeSequence": parse_simpleEscapeSequence,
+ "zeroEscapeSequence": parse_zeroEscapeSequence,
+ "hexEscapeSequence": parse_hexEscapeSequence,
+ "unicodeEscapeSequence": parse_unicodeEscapeSequence,
+ "eolEscapeSequence": parse_eolEscapeSequence,
+ "digit": parse_digit,
+ "hexDigit": parse_hexDigit,
+ "letter": parse_letter,
+ "lowerCaseLetter": parse_lowerCaseLetter,
+ "upperCaseLetter": parse_upperCaseLetter,
+ "__": parse___,
+ "comment": parse_comment,
+ "singleLineComment": parse_singleLineComment,
+ "multiLineComment": parse_multiLineComment,
+ "eol": parse_eol,
+ "eolChar": parse_eolChar,
+ "whitespace": parse_whitespace
+ };
+
+ if (startRule !== undefined) {
+ if (parseFunctions[startRule] === undefined) {
+ throw new Error("Invalid rule name: " + quote(startRule) + ".");
+ }
+ } else {
+ startRule = "grammar";
+ }
+
+ var pos = 0;
+ var reportFailures = 0;
+ var rightmostFailuresPos = 0;
+ var rightmostFailuresExpected = [];
+
+ function padLeft(input, padding, length) {
+ var result = input;
+
+ var padLength = length - input.length;
+ for (var i = 0; i < padLength; i++) {
+ result = padding + result;
+ }
+
+ return result;
+ }
+
+ function escape(ch) {
+ var charCode = ch.charCodeAt(0);
+ var escapeChar;
+ var length;
+
+ if (charCode <= 0xFF) {
+ escapeChar = 'x';
+ length = 2;
+ } else {
+ escapeChar = 'u';
+ length = 4;
+ }
+
+ return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
+ }
+
+ function matchFailed(failure) {
+ if (pos < rightmostFailuresPos) {
+ return;
+ }
+
+ if (pos > rightmostFailuresPos) {
+ rightmostFailuresPos = pos;
+ rightmostFailuresExpected = [];
+ }
+
+ rightmostFailuresExpected.push(failure);
+ }
+
+ function parse_grammar() {
+ var result0, result1, result2, result3;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse___();
+ if (result0 !== null) {
+ result1 = parse_initializer();
+ result1 = result1 !== null ? result1 : "";
+ if (result1 !== null) {
+ result3 = parse_rule();
+ if (result3 !== null) {
+ result2 = [];
+ while (result3 !== null) {
+ result2.push(result3);
+ result3 = parse_rule();
+ }
+ } else {
+ result2 = null;
+ }
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, initializer, rules) {
+ return {
+ type: "grammar",
+ initializer: initializer !== "" ? initializer : null,
+ rules: rules,
+ startRule: rules[0].name
+ };
+ })(pos0, result0[1], result0[2]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_initializer() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_action();
+ if (result0 !== null) {
+ result1 = parse_semicolon();
+ result1 = result1 !== null ? result1 : "";
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, code) {
+ return {
+ type: "initializer",
+ code: code
+ };
+ })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_rule() {
+ var result0, result1, result2, result3, result4;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_identifier();
+ if (result0 !== null) {
+ result1 = parse_string();
+ result1 = result1 !== null ? result1 : "";
+ if (result1 !== null) {
+ result2 = parse_equals();
+ if (result2 !== null) {
+ result3 = parse_choice();
+ if (result3 !== null) {
+ result4 = parse_semicolon();
+ result4 = result4 !== null ? result4 : "";
+ if (result4 !== null) {
+ result0 = [result0, result1, result2, result3, result4];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, name, displayName, expression) {
+ return {
+ type: "rule",
+ name: name,
+ displayName: displayName !== "" ? displayName : null,
+ expression: expression
+ };
+ })(pos0, result0[0], result0[1], result0[3]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_choice() {
+ var result0, result1, result2, result3;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_sequence();
+ if (result0 !== null) {
+ result1 = [];
+ pos2 = pos;
+ result2 = parse_slash();
+ if (result2 !== null) {
+ result3 = parse_sequence();
+ if (result3 !== null) {
+ result2 = [result2, result3];
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ while (result2 !== null) {
+ result1.push(result2);
+ pos2 = pos;
+ result2 = parse_slash();
+ if (result2 !== null) {
+ result3 = parse_sequence();
+ if (result3 !== null) {
+ result2 = [result2, result3];
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, head, tail) {
+ if (tail.length > 0) {
+ var alternatives = [head].concat(map(
+ tail,
+ function(element) { return element[1]; }
+ ));
+ return {
+ type: "choice",
+ alternatives: alternatives
+ };
+ } else {
+ return head;
+ }
+ })(pos0, result0[0], result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_sequence() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = [];
+ result1 = parse_labeled();
+ while (result1 !== null) {
+ result0.push(result1);
+ result1 = parse_labeled();
+ }
+ if (result0 !== null) {
+ result1 = parse_action();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, elements, code) {
+ var expression = elements.length !== 1
+ ? {
+ type: "sequence",
+ elements: elements
+ }
+ : elements[0];
+ return {
+ type: "action",
+ expression: expression,
+ code: code
+ };
+ })(pos0, result0[0], result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ pos0 = pos;
+ result0 = [];
+ result1 = parse_labeled();
+ while (result1 !== null) {
+ result0.push(result1);
+ result1 = parse_labeled();
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, elements) {
+ return elements.length !== 1
+ ? {
+ type: "sequence",
+ elements: elements
+ }
+ : elements[0];
+ })(pos0, result0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ }
+ return result0;
+ }
+
+ function parse_labeled() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_identifier();
+ if (result0 !== null) {
+ result1 = parse_colon();
+ if (result1 !== null) {
+ result2 = parse_prefixed();
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, label, expression) {
+ return {
+ type: "labeled",
+ label: label,
+ expression: expression
+ };
+ })(pos0, result0[0], result0[2]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ result0 = parse_prefixed();
+ }
+ return result0;
+ }
+
+ function parse_prefixed() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_and();
+ if (result0 !== null) {
+ result1 = parse_action();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, code) {
+ return {
+ type: "semantic_and",
+ code: code
+ };
+ })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_and();
+ if (result0 !== null) {
+ result1 = parse_suffixed();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, expression) {
+ return {
+ type: "simple_and",
+ expression: expression
+ };
+ })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_not();
+ if (result0 !== null) {
+ result1 = parse_action();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, code) {
+ return {
+ type: "semantic_not",
+ code: code
+ };
+ })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_not();
+ if (result0 !== null) {
+ result1 = parse_suffixed();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, expression) {
+ return {
+ type: "simple_not",
+ expression: expression
+ };
+ })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ result0 = parse_suffixed();
+ }
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_suffixed() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_primary();
+ if (result0 !== null) {
+ result1 = parse_question();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, expression) {
+ return {
+ type: "optional",
+ expression: expression
+ };
+ })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_primary();
+ if (result0 !== null) {
+ result1 = parse_star();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, expression) {
+ return {
+ type: "zero_or_more",
+ expression: expression
+ };
+ })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_primary();
+ if (result0 !== null) {
+ result1 = parse_plus();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, expression) {
+ return {
+ type: "one_or_more",
+ expression: expression
+ };
+ })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ result0 = parse_primary();
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_primary() {
+ var result0, result1, result2;
+ var pos0, pos1, pos2, pos3;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_identifier();
+ if (result0 !== null) {
+ pos2 = pos;
+ reportFailures++;
+ pos3 = pos;
+ result1 = parse_string();
+ result1 = result1 !== null ? result1 : "";
+ if (result1 !== null) {
+ result2 = parse_equals();
+ if (result2 !== null) {
+ result1 = [result1, result2];
+ } else {
+ result1 = null;
+ pos = pos3;
+ }
+ } else {
+ result1 = null;
+ pos = pos3;
+ }
+ reportFailures--;
+ if (result1 === null) {
+ result1 = "";
+ } else {
+ result1 = null;
+ pos = pos2;
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, name) {
+ return {
+ type: "rule_ref",
+ name: name
+ };
+ })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ result0 = parse_literal();
+ if (result0 === null) {
+ pos0 = pos;
+ result0 = parse_dot();
+ if (result0 !== null) {
+ result0 = (function(offset) { return { type: "any" }; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ if (result0 === null) {
+ result0 = parse_class();
+ if (result0 === null) {
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_lparen();
+ if (result0 !== null) {
+ result1 = parse_choice();
+ if (result1 !== null) {
+ result2 = parse_rparen();
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, expression) { return expression; })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ }
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_action() {
+ var result0, result1;
+ var pos0, pos1;
+
+ reportFailures++;
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_braced();
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, braced) { return braced.substr(1, braced.length - 2); })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("action");
+ }
+ return result0;
+ }
+
+ function parse_braced() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 123) {
+ result0 = "{";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"{\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = [];
+ result2 = parse_braced();
+ if (result2 === null) {
+ result2 = parse_nonBraceCharacters();
+ }
+ while (result2 !== null) {
+ result1.push(result2);
+ result2 = parse_braced();
+ if (result2 === null) {
+ result2 = parse_nonBraceCharacters();
+ }
+ }
+ if (result1 !== null) {
+ if (input.charCodeAt(pos) === 125) {
+ result2 = "}";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"}\"");
+ }
+ }
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, parts) {
+ return "{" + parts.join("") + "}";
+ })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_nonBraceCharacters() {
+ var result0, result1;
+ var pos0;
+
+ pos0 = pos;
+ result1 = parse_nonBraceCharacter();
+ if (result1 !== null) {
+ result0 = [];
+ while (result1 !== null) {
+ result0.push(result1);
+ result1 = parse_nonBraceCharacter();
+ }
+ } else {
+ result0 = null;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, chars) { return chars.join(""); })(pos0, result0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_nonBraceCharacter() {
+ var result0;
+
+ if (/^[^{}]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[^{}]");
+ }
+ }
+ return result0;
+ }
+
+ function parse_equals() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 61) {
+ result0 = "=";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"=\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "="; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_colon() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 58) {
+ result0 = ":";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\":\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return ":"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_semicolon() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 59) {
+ result0 = ";";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\";\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return ";"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_slash() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 47) {
+ result0 = "/";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"/\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "/"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_and() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 38) {
+ result0 = "&";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"&\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "&"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_not() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 33) {
+ result0 = "!";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"!\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "!"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_question() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 63) {
+ result0 = "?";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"?\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "?"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_star() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 42) {
+ result0 = "*";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"*\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "*"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_plus() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 43) {
+ result0 = "+";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"+\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "+"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_lparen() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 40) {
+ result0 = "(";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"(\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "("; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_rparen() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 41) {
+ result0 = ")";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\")\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return ")"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_dot() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 46) {
+ result0 = ".";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\".\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "."; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_identifier() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ reportFailures++;
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_letter();
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 95) {
+ result0 = "_";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"_\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 36) {
+ result0 = "$";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"$\"");
+ }
+ }
+ }
+ }
+ if (result0 !== null) {
+ result1 = [];
+ result2 = parse_letter();
+ if (result2 === null) {
+ result2 = parse_digit();
+ if (result2 === null) {
+ if (input.charCodeAt(pos) === 95) {
+ result2 = "_";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"_\"");
+ }
+ }
+ if (result2 === null) {
+ if (input.charCodeAt(pos) === 36) {
+ result2 = "$";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"$\"");
+ }
+ }
+ }
+ }
+ }
+ while (result2 !== null) {
+ result1.push(result2);
+ result2 = parse_letter();
+ if (result2 === null) {
+ result2 = parse_digit();
+ if (result2 === null) {
+ if (input.charCodeAt(pos) === 95) {
+ result2 = "_";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"_\"");
+ }
+ }
+ if (result2 === null) {
+ if (input.charCodeAt(pos) === 36) {
+ result2 = "$";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"$\"");
+ }
+ }
+ }
+ }
+ }
+ }
+ if (result1 !== null) {
+ result2 = parse___();
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, head, tail) {
+ return head + tail.join("");
+ })(pos0, result0[0], result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("identifier");
+ }
+ return result0;
+ }
+
+ function parse_literal() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ reportFailures++;
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_doubleQuotedString();
+ if (result0 === null) {
+ result0 = parse_singleQuotedString();
+ }
+ if (result0 !== null) {
+ if (input.charCodeAt(pos) === 105) {
+ result1 = "i";
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"i\"");
+ }
+ }
+ result1 = result1 !== null ? result1 : "";
+ if (result1 !== null) {
+ result2 = parse___();
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, value, flags) {
+ return {
+ type: "literal",
+ value: value,
+ ignoreCase: flags === "i"
+ };
+ })(pos0, result0[0], result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("literal");
+ }
+ return result0;
+ }
+
+ function parse_string() {
+ var result0, result1;
+ var pos0, pos1;
+
+ reportFailures++;
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_doubleQuotedString();
+ if (result0 === null) {
+ result0 = parse_singleQuotedString();
+ }
+ if (result0 !== null) {
+ result1 = parse___();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, string) { return string; })(pos0, result0[0]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("string");
+ }
+ return result0;
+ }
+
+ function parse_doubleQuotedString() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 34) {
+ result0 = "\"";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\"\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = [];
+ result2 = parse_doubleQuotedCharacter();
+ while (result2 !== null) {
+ result1.push(result2);
+ result2 = parse_doubleQuotedCharacter();
+ }
+ if (result1 !== null) {
+ if (input.charCodeAt(pos) === 34) {
+ result2 = "\"";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\"\"");
+ }
+ }
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, chars) { return chars.join(""); })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_doubleQuotedCharacter() {
+ var result0;
+
+ result0 = parse_simpleDoubleQuotedCharacter();
+ if (result0 === null) {
+ result0 = parse_simpleEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_zeroEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_hexEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_unicodeEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_eolEscapeSequence();
+ }
+ }
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_simpleDoubleQuotedCharacter() {
+ var result0, result1;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ if (input.charCodeAt(pos) === 34) {
+ result0 = "\"";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\"\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 92) {
+ result0 = "\\";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\\"");
+ }
+ }
+ if (result0 === null) {
+ result0 = parse_eolChar();
+ }
+ }
+ reportFailures--;
+ if (result0 === null) {
+ result0 = "";
+ } else {
+ result0 = null;
+ pos = pos2;
+ }
+ if (result0 !== null) {
+ if (input.length > pos) {
+ result1 = input.charAt(pos);
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, char_) { return char_; })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_singleQuotedString() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 39) {
+ result0 = "'";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"'\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = [];
+ result2 = parse_singleQuotedCharacter();
+ while (result2 !== null) {
+ result1.push(result2);
+ result2 = parse_singleQuotedCharacter();
+ }
+ if (result1 !== null) {
+ if (input.charCodeAt(pos) === 39) {
+ result2 = "'";
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"'\"");
+ }
+ }
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, chars) { return chars.join(""); })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_singleQuotedCharacter() {
+ var result0;
+
+ result0 = parse_simpleSingleQuotedCharacter();
+ if (result0 === null) {
+ result0 = parse_simpleEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_zeroEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_hexEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_unicodeEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_eolEscapeSequence();
+ }
+ }
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_simpleSingleQuotedCharacter() {
+ var result0, result1;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ if (input.charCodeAt(pos) === 39) {
+ result0 = "'";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"'\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 92) {
+ result0 = "\\";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\\"");
+ }
+ }
+ if (result0 === null) {
+ result0 = parse_eolChar();
+ }
+ }
+ reportFailures--;
+ if (result0 === null) {
+ result0 = "";
+ } else {
+ result0 = null;
+ pos = pos2;
+ }
+ if (result0 !== null) {
+ if (input.length > pos) {
+ result1 = input.charAt(pos);
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, char_) { return char_; })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_class() {
+ var result0, result1, result2, result3, result4, result5;
+ var pos0, pos1;
+
+ reportFailures++;
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 91) {
+ result0 = "[";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"[\"");
+ }
+ }
+ if (result0 !== null) {
+ if (input.charCodeAt(pos) === 94) {
+ result1 = "^";
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"^\"");
+ }
+ }
+ result1 = result1 !== null ? result1 : "";
+ if (result1 !== null) {
+ result2 = [];
+ result3 = parse_classCharacterRange();
+ if (result3 === null) {
+ result3 = parse_classCharacter();
+ }
+ while (result3 !== null) {
+ result2.push(result3);
+ result3 = parse_classCharacterRange();
+ if (result3 === null) {
+ result3 = parse_classCharacter();
+ }
+ }
+ if (result2 !== null) {
+ if (input.charCodeAt(pos) === 93) {
+ result3 = "]";
+ pos++;
+ } else {
+ result3 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"]\"");
+ }
+ }
+ if (result3 !== null) {
+ if (input.charCodeAt(pos) === 105) {
+ result4 = "i";
+ pos++;
+ } else {
+ result4 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"i\"");
+ }
+ }
+ result4 = result4 !== null ? result4 : "";
+ if (result4 !== null) {
+ result5 = parse___();
+ if (result5 !== null) {
+ result0 = [result0, result1, result2, result3, result4, result5];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, inverted, parts, flags) {
+ var partsConverted = map(parts, function(part) { return part.data; });
+ var rawText = "["
+ + inverted
+ + map(parts, function(part) { return part.rawText; }).join("")
+ + "]"
+ + flags;
+
+ return {
+ type: "class",
+ parts: partsConverted,
+ // FIXME: Get the raw text from the input directly.
+ rawText: rawText,
+ inverted: inverted === "^",
+ ignoreCase: flags === "i"
+ };
+ })(pos0, result0[1], result0[2], result0[4]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("character class");
+ }
+ return result0;
+ }
+
+ function parse_classCharacterRange() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ result0 = parse_classCharacter();
+ if (result0 !== null) {
+ if (input.charCodeAt(pos) === 45) {
+ result1 = "-";
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"-\"");
+ }
+ }
+ if (result1 !== null) {
+ result2 = parse_classCharacter();
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, begin, end) {
+ if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) {
+ throw new this.SyntaxError(
+ "Invalid character range: " + begin.rawText + "-" + end.rawText + "."
+ );
+ }
+
+ return {
+ data: [begin.data, end.data],
+ // FIXME: Get the raw text from the input directly.
+ rawText: begin.rawText + "-" + end.rawText
+ };
+ })(pos0, result0[0], result0[2]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_classCharacter() {
+ var result0;
+ var pos0;
+
+ pos0 = pos;
+ result0 = parse_bracketDelimitedCharacter();
+ if (result0 !== null) {
+ result0 = (function(offset, char_) {
+ return {
+ data: char_,
+ // FIXME: Get the raw text from the input directly.
+ rawText: quoteForRegexpClass(char_)
+ };
+ })(pos0, result0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_bracketDelimitedCharacter() {
+ var result0;
+
+ result0 = parse_simpleBracketDelimitedCharacter();
+ if (result0 === null) {
+ result0 = parse_simpleEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_zeroEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_hexEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_unicodeEscapeSequence();
+ if (result0 === null) {
+ result0 = parse_eolEscapeSequence();
+ }
+ }
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_simpleBracketDelimitedCharacter() {
+ var result0, result1;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ if (input.charCodeAt(pos) === 93) {
+ result0 = "]";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"]\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 92) {
+ result0 = "\\";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\\"");
+ }
+ }
+ if (result0 === null) {
+ result0 = parse_eolChar();
+ }
+ }
+ reportFailures--;
+ if (result0 === null) {
+ result0 = "";
+ } else {
+ result0 = null;
+ pos = pos2;
+ }
+ if (result0 !== null) {
+ if (input.length > pos) {
+ result1 = input.charAt(pos);
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, char_) { return char_; })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_simpleEscapeSequence() {
+ var result0, result1, result2;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 92) {
+ result0 = "\\";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\\"");
+ }
+ }
+ if (result0 !== null) {
+ pos2 = pos;
+ reportFailures++;
+ result1 = parse_digit();
+ if (result1 === null) {
+ if (input.charCodeAt(pos) === 120) {
+ result1 = "x";
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"x\"");
+ }
+ }
+ if (result1 === null) {
+ if (input.charCodeAt(pos) === 117) {
+ result1 = "u";
+ pos++;
+ } else {
+ result1 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"u\"");
+ }
+ }
+ if (result1 === null) {
+ result1 = parse_eolChar();
+ }
+ }
+ }
+ reportFailures--;
+ if (result1 === null) {
+ result1 = "";
+ } else {
+ result1 = null;
+ pos = pos2;
+ }
+ if (result1 !== null) {
+ if (input.length > pos) {
+ result2 = input.charAt(pos);
+ pos++;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, char_) {
+ return char_
+ .replace("b", "\b")
+ .replace("f", "\f")
+ .replace("n", "\n")
+ .replace("r", "\r")
+ .replace("t", "\t")
+ .replace("v", "\x0B"); // IE does not recognize "\v".
+ })(pos0, result0[2]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_zeroEscapeSequence() {
+ var result0, result1;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.substr(pos, 2) === "\\0") {
+ result0 = "\\0";
+ pos += 2;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\0\"");
+ }
+ }
+ if (result0 !== null) {
+ pos2 = pos;
+ reportFailures++;
+ result1 = parse_digit();
+ reportFailures--;
+ if (result1 === null) {
+ result1 = "";
+ } else {
+ result1 = null;
+ pos = pos2;
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset) { return "\x00"; })(pos0);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_hexEscapeSequence() {
+ var result0, result1, result2;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.substr(pos, 2) === "\\x") {
+ result0 = "\\x";
+ pos += 2;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\x\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse_hexDigit();
+ if (result1 !== null) {
+ result2 = parse_hexDigit();
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, h1, h2) {
+ return String.fromCharCode(parseInt(h1 + h2, 16));
+ })(pos0, result0[1], result0[2]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_unicodeEscapeSequence() {
+ var result0, result1, result2, result3, result4;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.substr(pos, 2) === "\\u") {
+ result0 = "\\u";
+ pos += 2;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\u\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse_hexDigit();
+ if (result1 !== null) {
+ result2 = parse_hexDigit();
+ if (result2 !== null) {
+ result3 = parse_hexDigit();
+ if (result3 !== null) {
+ result4 = parse_hexDigit();
+ if (result4 !== null) {
+ result0 = [result0, result1, result2, result3, result4];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, h1, h2, h3, h4) {
+ return String.fromCharCode(parseInt(h1 + h2 + h3 + h4, 16));
+ })(pos0, result0[1], result0[2], result0[3], result0[4]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_eolEscapeSequence() {
+ var result0, result1;
+ var pos0, pos1;
+
+ pos0 = pos;
+ pos1 = pos;
+ if (input.charCodeAt(pos) === 92) {
+ result0 = "\\";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\\\\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = parse_eol();
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ } else {
+ result0 = null;
+ pos = pos1;
+ }
+ if (result0 !== null) {
+ result0 = (function(offset, eol) { return eol; })(pos0, result0[1]);
+ }
+ if (result0 === null) {
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_digit() {
+ var result0;
+
+ if (/^[0-9]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[0-9]");
+ }
+ }
+ return result0;
+ }
+
+ function parse_hexDigit() {
+ var result0;
+
+ if (/^[0-9a-fA-F]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[0-9a-fA-F]");
+ }
+ }
+ return result0;
+ }
+
+ function parse_letter() {
+ var result0;
+
+ result0 = parse_lowerCaseLetter();
+ if (result0 === null) {
+ result0 = parse_upperCaseLetter();
+ }
+ return result0;
+ }
+
+ function parse_lowerCaseLetter() {
+ var result0;
+
+ if (/^[a-z]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[a-z]");
+ }
+ }
+ return result0;
+ }
+
+ function parse_upperCaseLetter() {
+ var result0;
+
+ if (/^[A-Z]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[A-Z]");
+ }
+ }
+ return result0;
+ }
+
+ function parse___() {
+ var result0, result1;
+
+ result0 = [];
+ result1 = parse_whitespace();
+ if (result1 === null) {
+ result1 = parse_eol();
+ if (result1 === null) {
+ result1 = parse_comment();
+ }
+ }
+ while (result1 !== null) {
+ result0.push(result1);
+ result1 = parse_whitespace();
+ if (result1 === null) {
+ result1 = parse_eol();
+ if (result1 === null) {
+ result1 = parse_comment();
+ }
+ }
+ }
+ return result0;
+ }
+
+ function parse_comment() {
+ var result0;
+
+ reportFailures++;
+ result0 = parse_singleLineComment();
+ if (result0 === null) {
+ result0 = parse_multiLineComment();
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("comment");
+ }
+ return result0;
+ }
+
+ function parse_singleLineComment() {
+ var result0, result1, result2, result3;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ if (input.substr(pos, 2) === "//") {
+ result0 = "//";
+ pos += 2;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"//\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = [];
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ result2 = parse_eolChar();
+ reportFailures--;
+ if (result2 === null) {
+ result2 = "";
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ if (result2 !== null) {
+ if (input.length > pos) {
+ result3 = input.charAt(pos);
+ pos++;
+ } else {
+ result3 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result3 !== null) {
+ result2 = [result2, result3];
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ while (result2 !== null) {
+ result1.push(result2);
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ result2 = parse_eolChar();
+ reportFailures--;
+ if (result2 === null) {
+ result2 = "";
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ if (result2 !== null) {
+ if (input.length > pos) {
+ result3 = input.charAt(pos);
+ pos++;
+ } else {
+ result3 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result3 !== null) {
+ result2 = [result2, result3];
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ }
+ if (result1 !== null) {
+ result0 = [result0, result1];
+ } else {
+ result0 = null;
+ pos = pos0;
+ }
+ } else {
+ result0 = null;
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_multiLineComment() {
+ var result0, result1, result2, result3;
+ var pos0, pos1, pos2;
+
+ pos0 = pos;
+ if (input.substr(pos, 2) === "/*") {
+ result0 = "/*";
+ pos += 2;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"/*\"");
+ }
+ }
+ if (result0 !== null) {
+ result1 = [];
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ if (input.substr(pos, 2) === "*/") {
+ result2 = "*/";
+ pos += 2;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"*/\"");
+ }
+ }
+ reportFailures--;
+ if (result2 === null) {
+ result2 = "";
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ if (result2 !== null) {
+ if (input.length > pos) {
+ result3 = input.charAt(pos);
+ pos++;
+ } else {
+ result3 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result3 !== null) {
+ result2 = [result2, result3];
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ while (result2 !== null) {
+ result1.push(result2);
+ pos1 = pos;
+ pos2 = pos;
+ reportFailures++;
+ if (input.substr(pos, 2) === "*/") {
+ result2 = "*/";
+ pos += 2;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"*/\"");
+ }
+ }
+ reportFailures--;
+ if (result2 === null) {
+ result2 = "";
+ } else {
+ result2 = null;
+ pos = pos2;
+ }
+ if (result2 !== null) {
+ if (input.length > pos) {
+ result3 = input.charAt(pos);
+ pos++;
+ } else {
+ result3 = null;
+ if (reportFailures === 0) {
+ matchFailed("any character");
+ }
+ }
+ if (result3 !== null) {
+ result2 = [result2, result3];
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ } else {
+ result2 = null;
+ pos = pos1;
+ }
+ }
+ if (result1 !== null) {
+ if (input.substr(pos, 2) === "*/") {
+ result2 = "*/";
+ pos += 2;
+ } else {
+ result2 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"*/\"");
+ }
+ }
+ if (result2 !== null) {
+ result0 = [result0, result1, result2];
+ } else {
+ result0 = null;
+ pos = pos0;
+ }
+ } else {
+ result0 = null;
+ pos = pos0;
+ }
+ } else {
+ result0 = null;
+ pos = pos0;
+ }
+ return result0;
+ }
+
+ function parse_eol() {
+ var result0;
+
+ reportFailures++;
+ if (input.charCodeAt(pos) === 10) {
+ result0 = "\n";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\n\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.substr(pos, 2) === "\r\n") {
+ result0 = "\r\n";
+ pos += 2;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\r\\n\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 13) {
+ result0 = "\r";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\r\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 8232) {
+ result0 = "\u2028";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\u2028\"");
+ }
+ }
+ if (result0 === null) {
+ if (input.charCodeAt(pos) === 8233) {
+ result0 = "\u2029";
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("\"\\u2029\"");
+ }
+ }
+ }
+ }
+ }
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("end of line");
+ }
+ return result0;
+ }
+
+ function parse_eolChar() {
+ var result0;
+
+ if (/^[\n\r\u2028\u2029]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[\\n\\r\\u2028\\u2029]");
+ }
+ }
+ return result0;
+ }
+
+ function parse_whitespace() {
+ var result0;
+
+ reportFailures++;
+ if (/^[ \t\x0B\f\xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/.test(input.charAt(pos))) {
+ result0 = input.charAt(pos);
+ pos++;
+ } else {
+ result0 = null;
+ if (reportFailures === 0) {
+ matchFailed("[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]");
+ }
+ }
+ reportFailures--;
+ if (reportFailures === 0 && result0 === null) {
+ matchFailed("whitespace");
+ }
+ return result0;
+ }
+
+
+ function cleanupExpected(expected) {
+ expected.sort();
+
+ var lastExpected = null;
+ var cleanExpected = [];
+ for (var i = 0; i < expected.length; i++) {
+ if (expected[i] !== lastExpected) {
+ cleanExpected.push(expected[i]);
+ lastExpected = expected[i];
+ }
+ }
+ return cleanExpected;
+ }
+
+ function computeErrorPosition() {
+ /*
+ * The first idea was to use |String.split| to break the input up to the
+ * error position along newlines and derive the line and column from
+ * there. However IE's |split| implementation is so broken that it was
+ * enough to prevent it.
+ */
+
+ var line = 1;
+ var column = 1;
+ var seenCR = false;
+
+ for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) {
+ var ch = input.charAt(i);
+ if (ch === "\n") {
+ if (!seenCR) { line++; }
+ column = 1;
+ seenCR = false;
+ } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
+ line++;
+ column = 1;
+ seenCR = true;
+ } else {
+ column++;
+ seenCR = false;
+ }
+ }
+
+ return { line: line, column: column };
+ }
+
+
+ var result = parseFunctions[startRule]();
+
+ /*
+ * The parser is now in one of the following three states:
+ *
+ * 1. The parser successfully parsed the whole input.
+ *
+ * - |result !== null|
+ * - |pos === input.length|
+ * - |rightmostFailuresExpected| may or may not contain something
+ *
+ * 2. The parser successfully parsed only a part of the input.
+ *
+ * - |result !== null|
+ * - |pos < input.length|
+ * - |rightmostFailuresExpected| may or may not contain something
+ *
+ * 3. The parser did not successfully parse any part of the input.
+ *
+ * - |result === null|
+ * - |pos === 0|
+ * - |rightmostFailuresExpected| contains at least one failure
+ *
+ * All code following this comment (including called functions) must
+ * handle these states.
+ */
+ if (result === null || pos !== input.length) {
+ var offset = Math.max(pos, rightmostFailuresPos);
+ var found = offset < input.length ? input.charAt(offset) : null;
+ var errorPosition = computeErrorPosition();
+
+ throw new this.SyntaxError(
+ cleanupExpected(rightmostFailuresExpected),
+ found,
+ offset,
+ errorPosition.line,
+ errorPosition.column
+ );
+ }
+
+ return result;
+ },
+
+ /* Returns the parser source code. */
+ toSource: function() { return this._source; }
+ };
+
+ /* Thrown when a parser encounters a syntax error. */
+
+ result.SyntaxError = function(expected, found, offset, line, column) {
+ function buildMessage(expected, found) {
+ var expectedHumanized, foundHumanized;
+
+ switch (expected.length) {
+ case 0:
+ expectedHumanized = "end of input";
+ break;
+ case 1:
+ expectedHumanized = expected[0];
+ break;
+ default:
+ expectedHumanized = expected.slice(0, expected.length - 1).join(", ")
+ + " or "
+ + expected[expected.length - 1];
+ }
+
+ foundHumanized = found ? quote(found) : "end of input";
+
+ return "Expected " + expectedHumanized + " but " + foundHumanized + " found.";
+ }
+
+ this.name = "SyntaxError";
+ this.expected = expected;
+ this.found = found;
+ this.message = buildMessage(expected, found);
+ this.offset = offset;
+ this.line = line;
+ this.column = column;
+ };
+
+ result.SyntaxError.prototype = Error.prototype;
+
+ return result;
+})();
diff --git a/samples/javascript/script.js b/samples/javascript/script.js
new file mode 100755
index 0000000..a399ce3
--- /dev/null
+++ b/samples/javascript/script.js
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+console.log("Node")
diff --git a/samples/javascript/steelseries-min.js b/samples/javascript/steelseries-min.js
new file mode 100644
index 0000000..7b05bc8
--- /dev/null
+++ b/samples/javascript/steelseries-min.js
@@ -0,0 +1,24 @@
+/*!
+ * Name : steelseries.js
+ * Authors : Gerrit Grunwald, Mark Crossley
+ * Last modified : 09.05.2012
+ * Revision : 0.11.3
+ *
+ * Copyright (c) 2011, Gerrit Grunwald, Mark Crossley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * # Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * # Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+var steelseries=function(){function f(n,t){var i="#"===n.charAt(0)?n.substring(1,7):n,f=parseInt(i.substring(0,2),16),u=parseInt(i.substring(2,4),16),r=parseInt(i.substring(4,6),16);return this.color="rgba("+f+","+u+","+r+","+t+")",this}function ui(t,i,r,u){var f=1/255,y=t.getRed(),v=t.getGreen(),a=t.getBlue(),b=t.getAlpha(),w=i.getRed()-t.getRed(),p=i.getGreen()-t.getGreen(),s=i.getBlue()-t.getBlue(),o=i.getAlpha()*f-t.getAlpha()*f,e=w/r,l=p/r,c=s/r,h=o/r;return new n((y+e*u).toFixed(0),(v+l*u).toFixed(0),(a+c*u).toFixed(0),b+h*u)}function vr(n,t,i){return{start:n,stop:t,color:i}}function c(n,t){var u=Math.floor(Math.log10(n)),r=n/Math.pow(10,u),i;return i=t?1.5>r?1:3>r?2:7>r?5:10:1<r?2<r?5<r?10:5:2:1,i*Math.pow(10,u)}function s(n,t,i,r,u,f){var e=t+r,o=i+u;n.beginPath(),n.moveTo(t+f,i),n.lineTo(e-f,i),n.quadraticCurveTo(e,i,e,i+f),n.lineTo(e,i+u-f),n.quadraticCurveTo(e,o,e-f,o),n.lineTo(t+f,o),n.quadraticCurveTo(t,o,t,o-f),n.lineTo(t,i+f),n.quadraticCurveTo(t,i,t+f,i),n.closePath(),n.stroke()}function t(n,t){var i=r.createElement("canvas");return i.width=n,i.height=t,i}function ii(n,t,i){var u=r.createElement("canvas");return u.width=n,u.height=t,i(u.getContext("2d")),u}function ft(n){var t,i=ii(1,1,function(t){t.fillStyle=n,t.beginPath(),t.rect(0,0,1,1),t.fill()});return t=i.getContext("2d").getImageData(0,0,2,2).data,[t[0],t[1],t[2],t[3]]}function wt(t){var o,e,f,s,u,r=ft(t),i=new n(r[0],r[1],r[2],r[3]);return o=si(i,.32),e=si(i,.62),f=ti(i,.84),s=ti(i,.94),u=ti(i,1),new b(o,e,i,f,s,u)}function ar(n,t,i){var e,r,f,o,s,u;n/=255,t/=255,i/=255,r=Math.max(n,t,i),e=Math.min(n,t,i),s=(r+e)/2;if(r===e)f=o=0;else{u=r-e,o=s>.5?u/(2-r-e):u/(r+e);switch(r){case n:f=(t-i)/u+(t<i?6:0);break;case t:f=(i-n)/u+2;break;case i:f=(n-t)/u+4}f/=6}return[f,o,s]}function vt(n,t,i){var f,u,r,c=Math.floor(n*6),h=n*6-c,e=i*(1-t),o=i*(1-h*t),s=i*(1-(1-h)*t);switch(c%6){case 0:f=i,u=s,r=e;break;case 1:f=o,u=i,r=e;break;case 2:f=e,u=i,r=s;break;case 3:f=e,u=o,r=i;break;case 4:f=s,u=e,r=i;break;case 5:f=i,u=e,r=o}return[Math.floor(f*255),Math.floor(u*255),Math.floor(r*255)]}function st(n,t,i){var e,r,u,o,s,f;n=n/255,t=t/255,i=i/255,r=Math.max(n,t,i),e=Math.min(n,t,i),s=r,f=r-e,o=r===0?0:f/r;if(r===e)u=0;else{switch(r){case n:u=(t-i)/f+(t<i?6:0);break;case t:u=(i-n)/f+2;break;case i:u=(n-t)/f+4}u/=6}return[u,o,s]}function k(n,t){return n<0?0:n>t?t:n}function si(t,i){var f=Math.floor(t.getRed()*(1-i)),u=Math.floor(t.getGreen()*(1-i)),r=Math.floor(t.getBlue()*(1-i));return f=k(f,255),u=k(u,255),r=k(r,255),new n(f,u,r,t.getAlpha())}function ti(t,i){var f=Math.round(t.getRed()*(1+i)),u=Math.round(t.getGreen()*(1+i)),r=Math.round(t.getBlue()*(1+i));return f=k(f,255),u=k(u,255),r=k(r,255),new n(f,u,r,t.getAlpha())}function or(n,t,i){var r,u;if(i<=t)throw"Rotary bounds are of negative or zero size";return r=i-t,u=Math.floor((n-t)/r),n-u*r}function ni(n,t){return or(t-n,-180,180)}var r=document,tt="LCDMono2Ultra,sans-serif",ei=function(n,i){i=i||{};var ot=undefined===i.gaugeType?steelseries.GaugeType.TYPE4:i.gaugeType,f=undefined===i.size?200:i.size,h=undefined===i.minValue?0:i.minValue,b=undefined===i.maxValue?h+100:i.maxValue,bf=undefined===i.niceScale?!0:i.niceScale,ti=undefined===i.threshold?(b-h)/2:i.threshold,ii=undefined===i.section?null:i.section,ui=undefined===i.area?null:i.area,lu=undefined===i.titleString?"":i.titleString,au=undefined===i.unitString?"":i.unitString,hu=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,wu=undefined===i.frameVisible?!0:i.frameVisible,bt=undefined===i.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:i.backgroundColor,bu=undefined===i.backgroundVisible?!0:i.backgroundVisible,pt=undefined===i.pointerType?steelseries.PointerType.TYPE1:i.pointerType,ar=undefined===i.pointerColor?steelseries.ColorDef.RED:i.pointerColor,ee=undefined===i.knobType?steelseries.KnobType.STANDARD_KNOB:i.knobType,fi=undefined===i.knobStyle?steelseries.KnobStyle.SILVER:i.knobStyle,ei=undefined===i.lcdColor?steelseries.LcdColor.STANDARD:i.lcdColor,vr=undefined===i.lcdVisible?!0:i.lcdVisible,eu=undefined===i.lcdDecimals?2:i.lcdDecimals,ye=undefined===i.digitalFont?!1:i.digitalFont,pe=undefined===i.fractionalScaleDecimals?1:i.fractionalScaleDecimals,br=undefined===i.ledColor?steelseries.LedColor.RED_LED:i.ledColor,ru=undefined===i.ledVisible?!0:i.ledVisible,vf=undefined===i.thresholdVisible?!0:i.thresholdVisible,kr=undefined===i.minMeasuredValueVisible?!1:i.minMeasuredValueVisible,dr=undefined===i.maxMeasuredValueVisible?!1:i.maxMeasuredValueVisible,cf=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,af=undefined===i.foregroundVisible?!0:i.foregroundVisible,oe=undefined===i.labelNumberFormat?steelseries.LabelNumberFormat.STANDARD:i.labelNumberFormat,yr=undefined===i.playAlarm?!1:i.playAlarm,uf=undefined===i.alarmSound?!1:i.alarmSound,fe=undefined===i.customLayer?null:i.customLayer,le=undefined===i.tickLabelOrientation?ot===steelseries.GaugeType.TYPE1?steelseries.TickLabelOrientation.TANGENT:steelseries.TickLabelOrientation.NORMAL:i.tickLabelOrientation,wr=undefined===i.trendVisible?!1:i.trendVisible,hr=undefined===i.trendColors?[steelseries.LedColor.RED_LED,steelseries.LedColor.GREEN_LED,steelseries.LedColor.CYAN_LED]:i.trendColors,sr=undefined===i.useOdometer?!1:i.useOdometer,wt=undefined===i.odometerParams?{}:i.odometerParams,wf=undefined===i.odometerUseValue?!1:i.odometerUseValue,ki;yr&&uf!==!1&&(ki=r.createElement("audio"),ki.setAttribute("src",uf),ki.setAttribute("preload","auto"));var l=h,or=h,yf=this,ri=b,ni=h,ht=!1,ef=0,we,of=steelseries.TrendState.OFF,lr=f*.06,cr=f*.29,er=f*.36,ci,et,gi,at,lt,rr=et+(l-h)*lt,u=r.getElementById(n).getContext("2d");u.save(),u.clearRect(0,0,u.canvas.width,u.canvas.height),u.canvas.width=f,u.canvas.height=f;var s=f,k=f,g=s/2,it=k/2,pf=.6*s,ne=.4*k,pr=Math.floor(s/10),ce=pr+"px sans-serif",ae=pr+"px "+tt,hf=k*.13,bi=s*.4,sf=(s-bi)/2,rf=k*.57,tf,ve=k*.61,st=Math.PI/2,ue=Math.PI/180,ff=!1,ir=h,nr=b,ai=b-h,yt=nr-ir,fr=0,vt=0,oi=10,lf=10,re=function(){bf?(ai=c(b-h,!1),vt=c(ai/(lf-1),!0),ir=Math.floor(h/vt)*vt,nr=Math.ceil(b/vt)*vt,fr=c(vt/(oi-1),!0),h=ir,b=nr,yt=b-h):(ai=b-h,ir=h,nr=b,yt=ai,vt=c(ai/(lf-1),!0),fr=c(vt/(oi-1),!0));switch(ot.type){case"type1":ci=0,et=Math.PI,gi=st,at=st,lt=at/yt;break;case"type2":ci=0,et=Math.PI,gi=st,at=Math.PI,lt=at/yt;break;case"type3":ci=0,et=st,gi=0,at=1.5*Math.PI,lt=at/yt;break;case"type4":default:ci=60*ue,et=st+ci/2,gi=0,at=2*Math.PI-ci,lt=at/yt}rr=et+(l-h)*lt},wi=t(f,f),nf=wi.getContext("2d"),di=t(f,f),ut=di.getContext("2d"),fu,hi=t(f*.093457,f*.093457),uu=hi.getContext("2d"),gt=t(f*.093457,f*.093457),nu=gt.getContext("2d"),si=gt,iu=t(Math.ceil(f*.028037),Math.ceil(f*.028037)),se=iu.getContext("2d"),gr=t(Math.ceil(f*.028037),Math.ceil(f*.028037)),he=gr.getContext("2d"),vi=t(f,f),tu=vi.getContext("2d"),yi=t(f,f),ou=yi.getContext("2d"),pi=t(f,f),kt=pi.getContext("2d"),li=t(f,f),pu=li.getContext("2d"),gu,du,ku,yu,cu,tr,su;sr&&vr&&(tr=t(10,10),su=tr.getContext("2d"));var kf=function(n){u.save(),u.textAlign="right",u.strokeStyle=ei.textColor,u.fillStyle=ei.textColor,(ei===steelseries.LcdColor.STANDARD||ei===steelseries.LcdColor.STANDARD_GREEN)&&(u.shadowColor="gray",u.shadowOffsetX=s*.007,u.shadowOffsetY=s*.007,u.shadowBlur=s*.007),u.font=ye?ae:ce,u.fillText(n.toFixed(eu),sf+bi-bi*.05,rf+hf*.5+pr*.38,bi*.9),u.restore()},te=function(n){n.save(),"type1"===ot.type&&n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,fi),s*.523364,k*.130841),("type1"===ot.type||"type2"===ot.type)&&n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,fi),s*.130841,k*.514018),("type2"===ot.type||"type3"===ot.type)&&n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,fi),s*.831775,k*.514018),"type3"===ot.type&&n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,fi),s*.523364,k*.831775),"type4"===ot.type&&(n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,fi),s*.336448,k*.803738),n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,fi),s*.626168,k*.803738)),n.restore()},ie=function(){var t=r.createElement("canvas"),n,i;return t.width=Math.ceil(f*.046728),t.height=Math.ceil(t.width*.9),n=t.getContext("2d"),n.save(),i=n.createLinearGradient(0,.1,0,t.height*.9),i.addColorStop(0,"#520000"),i.addColorStop(.3,"#fc1d00"),i.addColorStop(.59,"#fc1d00"),i.addColorStop(1,"#520000"),n.fillStyle=i,n.beginPath(),n.moveTo(t.width*.5,.1),n.lineTo(t.width*.9,t.height*.9),n.lineTo(t.width*.1,t.height*.9),n.lineTo(t.width*.5,.1),n.closePath(),n.fill(),n.strokeStyle="#FFFFFF",n.stroke(),n.restore(),t},vu=function(n,t,i,r,u){t<h?t=h:t>b&&(t=b),i<h?i=h:i>b&&(i=b);if(t>=i)return;n.save(),n.strokeStyle=r,n.fillStyle=r,n.lineWidth=s*.035;var f=at/yt*t-at/yt*h,e=f+(i-t)/(yt/at);n.translate(g,it),n.rotate(et),n.beginPath(),u?(n.moveTo(0,0),n.arc(0,0,s*.365-n.lineWidth/2,f,e,!1)):n.arc(0,0,s*.365,f,e,!1),u?(n.moveTo(0,0),n.fill()):n.stroke(),n.translate(-g,-it),n.restore()},df=function(n,t){var y,v,a,c;bt.labelColor.setAlpha(1),n.save(),n.textAlign="center",n.textBaseline="middle",y=Math.ceil(s*.04),n.font=y+"px sans-serif",n.strokeStyle=bt.labelColor.getRgbaColor(),n.fillStyle=bt.labelColor.getRgbaColor(),n.translate(g,it),n.rotate(et);var o=et,i=lt*fr,r,e=h,u=oi-1,l=s*.38,w=s*.35,d=s*.355,k=s*.36,p=s*.3,f=s*.1;for((ot.type==="type1"||ot.type==="type2")&&(f=s*.035),v=oi/2,a=parseFloat(b.toFixed(2)),c=h;parseFloat(c.toFixed(2))<=a;c+=fr){r=i+st,u++;if(u===oi){n.lineWidth=1.5,n.beginPath(),n.moveTo(l,0),n.lineTo(w,0),n.closePath(),n.stroke(),n.save(),n.translate(p,0);switch(le.type){case"horizontal":r=-o;break;case"tangent":r=o<=st+Math.PI?Math.PI:0;break;case"normal":default:r=st}n.rotate(r);switch(t.format){case"fractional":n.fillText(e.toFixed(pe),0,0,f);break;case"scientific":n.fillText(e.toPrecision(2),0,0,f);break;case"standard":default:n.fillText(e.toFixed(0),0,0,f)}n.translate(-p,0),n.restore(),e+=vt,u=0,n.rotate(i),o+=i;continue}0==oi%2&&u===v?(n.lineWidth=1,n.beginPath(),n.moveTo(l,0),n.lineTo(d,0),n.closePath(),n.stroke()):(n.lineWidth=.5,n.beginPath(),n.moveTo(l,0),n.lineTo(k,0),n.closePath(),n.stroke()),n.rotate(i),o+=i}n.translate(-g,-it),n.restore()},ft=function(n){var t,i,u;n=n||{};var c=undefined===n.frame?!1:n.frame,r=undefined===n.background?!1:n.background,e=undefined===n.led?!1:n.led,tt=undefined===n.pointer?!1:n.pointer,b=undefined===n.foreground?!1:n.foreground,ft=undefined===n.trend?!1:n.trend,ht=undefined===n.odo?!1:n.odo;ff=!0,re(),c&&wu&&v(nf,hu,g,it,s,k),r&&bu&&(w(ut,bt,g,it,s,k),rt(ut,fe,g,it,s,k)),e&&(uu.drawImage(o(Math.ceil(f*.093457),1,br),0,0),nu.drawImage(o(Math.ceil(f*.093457),0,br),0,0)),kr&&se.drawImage(a(Math.ceil(f*.028037),steelseries.ColorDef.BLUE.dark.getRgbaColor(),!0,!0),0,0),dr&&he.drawImage(a(Math.ceil(f*.028037),steelseries.ColorDef.RED.medium.getRgbaColor(),!0),0,0);if(r&&bu){te(ut);if(null!==ii&&0<ii.length){t=ii.length;do t--,vu(ut,ii[t].start,ii[t].stop,ii[t].color,!1);while(0<t)}if(null!==ui&&0<ui.length){i=ui.length;do i--,vu(ut,ui[i].start,ui[i].stop,ui[i].color,!0);while(0<i)}df(ut,oe),ct(ut,s,k,lu,au,bt,!0,!0)}r&&vf&&(ut.save(),ut.translate(g,it),ut.rotate(et+(ti-h)*lt+st),ut.translate(-g,-it),ut.drawImage(ie(),s*.475,k*.13),ut.translate(g,it),ut.restore()),r&&vr&&(sr&&ht?(cu=new steelseries.Odometer("",{_context:su,height:f*.075,decimals:wt.decimals,digits:wt.digits===undefined?5:wt.digits,valueForeColor:wt.valueForeColor,valueBackColor:wt.valueBackColor,decimalForeColor:wt.decimalForeColor,decimalBackColor:wt.decimalBackColor,font:wt.font,value:l}),tf=(s-tr.width)/2):sr||(fu=p(bi,hf,ei),ut.drawImage(fu,sf,rf))),tt&&(nt(tu,s,pt,ar,bt.labelColor,!1),nt(ou,s,pt,ar,bt.labelColor,!0)),b&&af&&(u=pt.type==="type15"||pt.type==="type16"?!1:!0,y(pu,cf,s,k,u,ee,fi,ot)),ft&&wr&&(gu=d(lr,steelseries.TrendState.UP,hr),du=d(lr,steelseries.TrendState.STEADY,hr),ku=d(lr,steelseries.TrendState.DOWN,hr),yu=d(lr,steelseries.TrendState.OFF,hr))},dt=function(n){n=n||{};var u=undefined===n.frame?!1:n.frame,e=undefined===n.background?!1:n.background,r=undefined===n.led?!1:n.led,t=undefined===n.pointer?!1:n.pointer,i=undefined===n.foreground?!1:n.foreground;u&&(wi.width=f,wi.height=f,nf=wi.getContext("2d")),e&&(di.width=f,di.height=f,ut=di.getContext("2d")),r&&(hi.width=Math.ceil(f*.093457),hi.height=Math.ceil(f*.093457),uu=hi.getContext("2d"),gt.width=Math.ceil(f*.093457),gt.height=Math.ceil(f*.093457),nu=gt.getContext("2d"),si=gt),t&&(vi.width=f,vi.height=f,tu=vi.getContext("2d"),yi.width=f,yi.height=f,ou=yi.getContext("2d"),pi.width=f,pi.height=f,kt=pi.getContext("2d")),i&&(li.width=f,li.height=f,pu=li.getContext("2d"))},gf=function(){ru&&(si=si===hi?gt:hi,yf.repaint())},ur=function(n){n?ef=setInterval(gf,1e3):clearInterval(ef)};return this.setValue=function(n){var t=n<h?h:n>b?b:n;l!==t&&(l=t,l>ni&&(ni=l),l<ri&&(ri=l),l<ti||ht?l<ti&&(ht=!1,ur(ht),yr&&ki.pause()):(ht=!0,ur(ht),yr&&ki.play()),this.repaint())},this.getValue=function(){return l},this.setOdoValue=function(n){var t=n<0?0:n;or!==t&&(or=t,this.repaint())},this.getOdoValue=function(){return or},this.setValueAnimated=function(n){var r=n<h?h:n>b?b:n,t,i;l!==r&&(undefined!==t&&t.playing&&t.stop(),t=new Tween({},"",Tween.regularEaseInOut,l,r,1),i=this,t.onMotionChanged=function(n){l=n.target._pos,l<ti||ht?l<ti&&(ht=!1,ur(ht)):(ht=!0,ur(ht)),l>ni&&(ni=l),l<ri&&(ri=l),i.repaint()},t.start())},this.resetMinMeasuredValue=function(){ri=l,this.repaint()},this.resetMaxMeasuredValue=function(){ni=l,this.repaint()},this.setMinMeasuredValueVisible=function(n){kr=n,this.repaint()},this.setMaxMeasuredValueVisible=function(n){dr=n,this.repaint()},this.setMaxMeasuredValue=function(n){var t=n<h?h:n>b?b:n;ni=t,this.repaint()},this.setMinMeasuredValue=function(n){var t=n<h?h:n>b?b:n;ri=t,this.repaint()},this.setTitleString=function(n){lu=n,ft({background:!0}),this.repaint()},this.setUnitString=function(n){au=n,ft({background:!0}),this.repaint()},this.setMinValue=function(n){h=n,ft({frame:!0,background:!0}),this.repaint()},this.getMinValue=function(){return h},this.setMaxValue=function(n){b=n,ft({frame:!0,background:!0}),this.repaint()},this.getMaxValue=function(){return b},this.setThreshold=function(n){var t=n<h?h:n>b?b:n;ti=t,ft({background:!0}),this.repaint()},this.setArea=function(n){ui=n,dt({foreground:!0}),ft({background:!0,foreground:!0}),this.repaint()},this.setSection=function(n){ii=n,dt({foreground:!0}),ft({background:!0,foreground:!0}),this.repaint()},this.setThresholdVisible=function(n){vf=n,this.repaint()},this.setLcdDecimals=function(n){eu=n,this.repaint()},this.setFrameDesign=function(n){dt({frame:!0}),hu=n,ft({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){dt({background:!0,pointer:pt.type==="type2"||pt.type==="type13"?!0:!1}),bt=n,ft({background:!0,pointer:pt.type==="type2"||pt.type==="type13"?!0:!1}),this.repaint()},this.setForegroundType=function(n){dt({foreground:!0}),cf=n,ft({foreground:!0}),this.repaint()},this.setPointerType=function(n){dt({pointer:!0,foreground:!0}),pt=n,ft({pointer:!0,foreground:!0}),this.repaint()},this.setPointerColor=function(n){dt({pointer:!0}),ar=n,ft({pointer:!0}),this.repaint()},this.setLedColor=function(n){dt({led:!0}),br=n,ft({led:!0}),this.repaint()},this.setLcdColor=function(n){ei=n,ft({background:!0}),this.repaint()},this.setTrend=function(n){of=n,this.repaint()},this.setTrendVisible=function(n){wr=n,this.repaint()},this.repaint=function(){ff||ft({frame:!0,background:!0,led:!0,pointer:!0,trend:!0,foreground:!0,odo:!0}),u.clearRect(0,0,u.canvas.width,u.canvas.height),wu&&u.drawImage(wi,0,0),u.drawImage(di,0,0),vr&&(sr?(cu.setValue(wf?l:or),u.drawImage(tr,tf,ve)):kf(l)),ru&&(l<ti&&(ht=!1,si=gt),u.drawImage(si,pf,ne));if(wr)switch(of.state){case"up":u.drawImage(gu,cr,er);break;case"steady":u.drawImage(du,cr,er);break;case"down":u.drawImage(ku,cr,er);break;case"off":u.drawImage(yu,cr,er)}kr&&(u.save(),u.translate(g,it),u.rotate(et+st+(ri-h)*lt),u.translate(-g,-it),u.drawImage(iu,u.canvas.width*.4865,u.canvas.height*.105),u.restore()),dr&&(u.save(),u.translate(g,it),u.rotate(et+st+(ni-h)*lt),u.translate(-g,-it),u.drawImage(gr,u.canvas.width*.4865,u.canvas.height*.105),u.restore()),rr=et+st+(l-h)*lt;var n=s*.006;kt.clearRect(0,0,s,k),kt.save(),kt.translate(g,it),kt.rotate(rr),kt.translate(-g,-it),kt.drawImage(yi,0,0),kt.restore(),u.drawImage(pi,0,0,s,k,n,n,s+n,k+n),u.save(),u.translate(g,it),u.rotate(rr),u.translate(-g,-it),u.drawImage(vi,0,0),u.restore(),af&&u.drawImage(li,0,0)},this.repaint(),this},wi=function(n,i){var yi;i=i||{};var dt=undefined===i.gaugeType?steelseries.GaugeType.TYPE4:i.gaugeType,s=undefined===i.size?200:i.size,f=undefined===i.minValue?0:i.minValue,h=undefined===i.maxValue?f+100:i.maxValue,cf=undefined===i.niceScale?!0:i.niceScale,fi=undefined===i.threshold?(h-f)/2:i.threshold,bt=undefined===i.section?null:i.section,uu=undefined===i.useSectionColors?!1:i.useSectionColors,su=undefined===i.titleString?"":i.titleString,lu=undefined===i.unitString?"":i.unitString,bu=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,yu=undefined===i.frameVisible?!0:i.frameVisible,ei=undefined===i.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:i.backgroundColor,ou=undefined===i.backgroundVisible?!0:i.backgroundVisible,ur=undefined===i.valueColor?steelseries.ColorDef.RED:i.valueColor,ni=undefined===i.lcdColor?steelseries.LcdColor.STANDARD:i.lcdColor,au=undefined===i.lcdVisible?!0:i.lcdVisible,of=undefined===i.lcdDecimals?2:i.lcdDecimals,nf=undefined===i.digitalFont?!1:i.digitalFont,tf=undefined===i.fractionalScaleDecimals?1:i.fractionalScaleDecimals,wf=undefined===i.customLayer?null:i.customLayer,sr=undefined===i.ledColor?steelseries.LedColor.RED_LED:i.ledColor,ku=undefined===i.ledVisible?!0:i.ledVisible,pf=undefined===i.labelNumberFormat?steelseries.LabelNumberFormat.STANDARD:i.labelNumberFormat,fu=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,cu=undefined===i.foregroundVisible?!0:i.foregroundVisible,er=undefined===i.playAlarm?!1:i.playAlarm,eu=undefined===i.alarmSound?!1:i.alarmSound,vi=undefined===i.valueGradient?null:i.valueGradient,bf=undefined===i.useValueGradient?!1:i.useValueGradient,df=undefined===i.tickLabelOrientation?dt===steelseries.GaugeType.TYPE1?steelseries.TickLabelOrientation.TANGENT:steelseries.TickLabelOrientation.NORMAL:i.tickLabelOrientation,ar=undefined===i.trendVisible?!1:i.trendVisible,gi=undefined===i.trendColors?[steelseries.LedColor.RED_LED,steelseries.LedColor.GREEN_LED,steelseries.LedColor.CYAN_LED]:i.trendColors;er&&eu!==!1&&(yi=r.createElement("audio"),yi.setAttribute("src",eu),yi.setAttribute("preload","auto"));var it=f,et=h-f,ot=!1,hu=0,ti,kf=this,ft,b,ui,vt,l,lt,st,ii=[],wi=!1,lr=!1,u=r.getElementById(n).getContext("2d");u.clearRect(0,0,u.canvas.width,u.canvas.height),u.canvas.width=s,u.canvas.height=s;var e=s,ut=s,k=e/2,g=ut/2,cr=Math.floor(e/10),hf=cr+"px sans-serif",lf=cr+"px "+tt,or=ut*.13,oi=e*.4,du=(e-oi)/2,gu=ut/2-or/2,nt=Math.PI/2,vf=e*.116822,af=e*.485981,pt=Math.ceil(s*.093457),vu=e*.53,wu=ut*.61,kt=Math.PI/180,pu=steelseries.TrendState.OFF,bi=s*.06,ki=s*.38,rr=s*.57;switch(dt.type){case"type1":ft=0,b=Math.PI,ui=0,vt=nt,l=nt,lt=l/Math.PI*180,st=l/et;break;case"type2":ft=0,b=Math.PI,ui=0,vt=nt,l=Math.PI,lt=l/Math.PI*180,st=l/et;break;case"type3":ft=0,b=nt,ui=-nt,vt=0,l=1.5*Math.PI,lt=l/Math.PI*180,st=l/et;break;case"type4":default:ft=Math.PI/3,b=nt+ft/2,ui=Math.PI/-3,vt=0,l=2*Math.PI-ft,lt=l/Math.PI*180,st=l/et}var hi=t(s,s),pr=hi.getContext("2d"),ai=t(s,s),at=ai.getContext("2d"),ru,li=t(Math.ceil(s*.060747),Math.ceil(s*.023364)),hr=li.getContext("2d"),ri=t(pt,pt),tu=ri.getContext("2d"),yt=t(pt,pt),gr=yt.getContext("2d"),gt=yt,yf,si=t(s,s),wr=si.getContext("2d"),br,kr,iu,dr,nu=!1,nr=f,tr=h,ci=h-f;et=tr-nr;var di=0,ht=0,fr=10,vr=10,ef=function(){cf?(ci=c(h-f,!1),ht=c(ci/(vr-1),!0),nr=Math.floor(f/ht)*ht,tr=Math.ceil(h/ht)*ht,di=c(ht/(fr-1),!0),f=nr,h=tr,et=h-f):(ci=h-f,nr=f,tr=h,et=ci,ht=c(ci/(vr-1),!0),di=c(ht/(fr-1),!0));switch(dt.type){case"type1":ft=0,b=Math.PI,vt=nt,l=nt,st=l/et;break;case"type2":ft=0,b=Math.PI,vt=nt,l=Math.PI,st=l/et;break;case"type3":ft=0,b=nt,vt=0,l=1.5*Math.PI,st=l/et;break;case"type4":default:ft=60*kt,b=nt+ft/2,vt=0,l=2*Math.PI-ft,st=l/et}angle=b+(it-f)*st},a=function(n){var t;n=n||{};var c=undefined===n.frame?!1:n.frame,i=undefined===n.background?!1:n.background,u=undefined===n.led?!1:n.led,l=undefined===n.value?!1:n.value,s=undefined===n.foreground?!1:n.foreground,r=undefined===n.trend?!1:n.trend;nu=!0,ef(),c&&yu&&v(pr,bu,k,g,e,ut),i&&ou&&(w(at,ei,k,g,e,ut),rt(at,wf,k,g,e,ut)),u&&(tu.drawImage(o(pt,1,sr),0,0),gr.drawImage(o(pt,0,sr),0,0),yf=at.getImageData(vu,wu,pt,pt)),i&&sf(at),i&&ou&&(uf(at,pf),ct(at,e,ut,su,lu,ei,!0,!0)),i&&au&&(ru=p(oi,or,ni),at.drawImage(ru,du,gu)),wi=!1;if(uu&&null!==bt&&0<bt.length){wi=!0,t=bt.length,ii=[];do t--,ii.push({start:(bt[t].start+Math.abs(f))/(h-f)*lt,stop:(bt[t].stop+Math.abs(f))/(h-f)*lt,color:wt(bt[t].color)});while(0<t)}lr=!1,bf&&vi!==null&&(wi=!1,lr=!0),l&&yr(hr,ur),s&&cu&&y(wr,fu,e,ut,!1,dt),r&&ar&&(br=d(bi,steelseries.TrendState.UP,gi),kr=d(bi,steelseries.TrendState.STEADY,gi),iu=d(bi,steelseries.TrendState.DOWN,gi),dr=d(bi,steelseries.TrendState.OFF,gi))},pi=function(n){n=n||{};var u=undefined===n.frame?!1:n.frame,f=undefined===n.background?!1:n.background,r=undefined===n.led?!1:n.led,t=undefined===n.value?!1:n.value,i=undefined===n.foreground?!1:n.foreground;u&&(hi.width=s,hi.height=s,pr=hi.getContext("2d")),f&&(ai.width=s,ai.height=s,at=ai.getContext("2d")),t&&(li.width=Math.ceil(s*.060747),li.height=Math.ceil(s*.023364),hr=li.getContext("2d")),r&&(ri.width=Math.ceil(s*.093457),ri.height=Math.ceil(s*.093457),tu=ri.getContext("2d"),yt.width=Math.ceil(s*.093457),yt.height=Math.ceil(s*.093457),gr=yt.getContext("2d"),gt=yt),i&&(si.width=s,si.height=s,wr=si.getContext("2d"))},sf=function(n){var t,r,i;n.save(),n.save(),n.lineWidth=s*.085,n.beginPath(),n.translate(k,g),n.rotate(b-4*kt),n.translate(-k,-g),n.arc(k,g,e*.35514,0,l+8*kt,!1),n.rotate(-b),t=n.createLinearGradient(0,.107476*ut,0,.897195*ut),t.addColorStop(0,"#000000"),t.addColorStop(.22,"#333333"),t.addColorStop(.76,"#333333"),t.addColorStop(1,"#cccccc"),n.strokeStyle=t,n.stroke(),n.restore(),n.save(),n.lineWidth=s*.075,n.beginPath(),n.translate(k,g),n.rotate(b-4*kt),n.translate(-k,-g),n.arc(k,g,e*.35514,0,l+8*kt,!1),n.rotate(-b),r=n.createLinearGradient(0,.112149*ut,0,.892523*ut),r.addColorStop(0,"#111111"),r.addColorStop(1,"#333333"),n.strokeStyle=r,n.stroke(),n.restore();var f=(e*.116822+e*.060747)/2,o=(e*.485981+e*.023364)/2,u=n.createRadialGradient(f,o,0,f,o,.030373*e);for(u.addColorStop(0,"#3c3c3c"),u.addColorStop(1,"#323232"),i=0,i=0;i<=lt;i+=5)n.save(),n.translate(k,g),n.rotate(i*kt+ui),n.translate(-k,-g),n.beginPath(),n.rect(e*.116822,e*.485981,e*.060747,e*.023364),n.closePath(),n.fillStyle=u,n.fill(),n.restore();n.restore()},yr=function(n,t){n.save(),n.beginPath(),n.rect(0,0,n.canvas.width,n.canvas.height),n.closePath();var r=n.canvas.width/2,f=n.canvas.height/2,i=u.createRadialGradient(r,f,0,r,f,n.canvas.width/2);i.addColorStop(0,t.light.getRgbaColor()),i.addColorStop(1,t.dark.getRgbaColor()),n.fillStyle=i,n.fill(),n.restore()},rf=function(n){u.save(),u.textAlign="right",u.strokeStyle=ni.textColor,u.fillStyle=ni.textColor,(ni===steelseries.LcdColor.STANDARD||ni===steelseries.LcdColor.STANDARD_GREEN)&&(u.shadowColor="gray",u.shadowOffsetX=e*.007,u.shadowOffsetY=e*.007,u.shadowBlur=e*.007),u.font=nf?lf:hf,u.fillText(n.toFixed(of),du+oi-oi*.05,gu+or*.5+cr*.38,oi*.9),u.restore()},uf=function(n,t){var a,v,c;ei.labelColor.setAlpha(1),n.save(),n.textAlign="center",n.textBaseline="middle",a=Math.ceil(e*.04),n.font=a+"px sans-serif",n.strokeStyle=ei.labelColor.getRgbaColor(),n.fillStyle=ei.labelColor.getRgbaColor(),n.translate(k,g),n.rotate(b);var s=b,i=st*di,r,u=f,l=fr-1,y=e*.28,o=e*.1;for((dt.type==="type1"||dt.type==="type2")&&(o=e*.0375),v=parseFloat(h.toFixed(2)),c=f;parseFloat(c.toFixed(2))<=v;c+=di){r=+i+nt,l++;if(l===fr){n.save(),n.translate(y,0);switch(df.type){case"horizontal":r=-s;break;case"tangent":r=s<=nt+Math.PI?Math.PI:0;break;case"normal":default:r=nt}n.rotate(r);switch(t.format){case"fractional":n.fillText(u.toFixed(tf),0,0,o);break;case"scientific":n.fillText(u.toPrecision(2),0,0,o);break;case"standard":default:n.fillText(u.toFixed(0),0,0,o)}n.translate(-y,0),n.restore(),u+=ht,l=0,n.rotate(i),s+=i;continue}n.rotate(i),s+=i}n.translate(-k,-g),n.restore()},ir=function(n){n?hu=setInterval(ff,1e3):clearInterval(hu)},ff=function(){ku&&(gt=gt===ri?yt:ri,kf.repaint())};return this.setValue=function(n){var t=n<f?f:n>h?h:n;it!==t&&(it=t,it<fi||ot?it<fi&&(ot=!1,ir(ot),er&&yi.pause()):(ot=!0,ir(ot),er&&yi.play()),this.repaint())},this.getValue=function(){return it},this.setValueAnimated=function(n){var i=n<f?f:n>h?h:n,t;it!==i&&(undefined!==ti&&ti.playing&&ti.stop(),ti=new Tween({},"",Tween.regularEaseInOut,it,i,1),t=this,ti.onMotionChanged=function(n){it=n.target._pos,it<fi||ot?it<fi&&(ot=!1,ir(ot)):(ot=!0,ir(ot)),t.repaint()},ti.start())},this.setFrameDesign=function(n){pi({frame:!0}),bu=n,a({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){pi({background:!0,led:!0}),ei=n,a({background:!0,led:!0}),this.repaint()},this.setForegroundType=function(n){pi({foreground:!0}),fu=n,a({foreground:!0}),this.repaint()},this.setValueColor=function(n){pi({value:!0}),ur=n,a({value:!0}),this.repaint()},this.setLedColor=function(n){pi({led:!0}),sr=n,a({led:!0}),this.repaint()},this.setLcdColor=function(n){ni=n,a({background:!0}),this.repaint()},this.setSection=function(n){bt=n,a(),this.repaint()},this.setSectionActive=function(n){uu=n,a(),this.repaint()},this.setGradient=function(n){vi=n,a(),this.repaint()},this.setGradientActive=function(n){useGradient=n,a(),this.repaint()},this.setMinValue=function(n){f=n,a({background:!0}),this.repaint()},this.getMinValue=function(){return f},this.setMaxValue=function(n){h=n,a({background:!0}),this.repaint()},this.getMaxValue=function(){return h},this.setThreshold=function(n){var t=n<f?f:n>h?h:n;fi=t,a({background:!0}),this.repaint()},this.setTitleString=function(n){su=n,a({background:!0}),this.repaint()},this.setUnitString=function(n){lu=n,a({background:!0}),this.repaint()},this.setTrend=function(n){pu=n,this.repaint()},this.setTrendVisible=function(n){ar=n,this.repaint()},this.repaint=function(){var o,i,e,n,t;for(nu||a({frame:!0,background:!0,led:!0,value:!0,trend:!0,foreground:!0}),u.clearRect(0,0,u.canvas.width,u.canvas.height),yu&&u.drawImage(hi,0,0),u.drawImage(ai,0,0),o=(it+Math.abs(f))/(h-f)*lt,e=ur,n=0;n<=o;n+=5){i=ur;if(lr){var s=f+n/lt*(h-f),c=vi.getEnd()-vi.getStart(),r=s/c;r=Math.max(Math.min(r,1),0),i=wt(vi.getColorAt(r).getRgbaColor())}else if(wi)for(t=0;t<ii.length;t++)if(n>=ii[t].start&&n<ii[t].stop){i=ii[t].color;break}e.medium.getHexColor()!==i.medium.getHexColor()&&(yr(hr,i),e=i),u.save(),u.translate(k,g),u.rotate(n*kt+ui),u.translate(-k,-g),u.drawImage(li,vf,af),u.restore()}au&&rf(it),ku&&(it<fi&&(ot=!1,gt=yt),u.drawImage(gt,vu,wu));if(ar)switch(pu.state){case"up":u.drawImage(br,ki,rr);break;case"steady":u.drawImage(kr,ki,rr);break;case"down":u.drawImage(iu,ki,rr);break;case"off":u.drawImage(dr,ki,rr)}cu&&u.drawImage(si,0,0)},this.repaint(),this},bi=function(n,i){var hi;i=i||{};var ft=undefined===i.orientation?steelseries.Orientation.NORTH:i.orientation,u=undefined===i.size?200:i.size,l=undefined===i.minValue?0:i.minValue,d=undefined===i.maxValue?l+100:i.maxValue,bu=undefined===i.niceScale?!0:i.niceScale,pt=undefined===i.threshold?(d-l)/2:i.threshold,gt=undefined===i.section?null:i.section,ni=undefined===i.area?null:i.area,gr=undefined===i.titleString?"":i.titleString,uu=undefined===i.unitString?"":i.unitString,iu=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,ru=undefined===i.frameVisible?!0:i.frameVisible,it=undefined===i.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:i.backgroundColor,yr=undefined===i.backgroundVisible?!0:i.backgroundVisible,ut=undefined===i.pointerType?steelseries.PointerType.TYPE1:i.pointerType,tr=undefined===i.pointerColor?steelseries.ColorDef.RED:i.pointerColor,du=undefined===i.knobType?steelseries.KnobType.STANDARD_KNOB:i.knobType,ci=undefined===i.knobStyle?steelseries.KnobStyle.SILVER:i.knobStyle,ki=undefined===i.ledColor?steelseries.LedColor.RED_LED:i.ledColor,tu=undefined===i.ledVisible?!0:i.ledVisible,vr=undefined===i.thresholdVisible?!0:i.thresholdVisible,gi=undefined===i.minMeasuredValueVisible?!1:i.minMeasuredValueVisible,di=undefined===i.maxMeasuredValueVisible?!1:i.maxMeasuredValueVisible,wr=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,dr=undefined===i.foregroundVisible?!0:i.foregroundVisible,eu=undefined===i.labelNumberFormat?steelseries.LabelNumberFormat.STANDARD:i.labelNumberFormat,nr=undefined===i.playAlarm?!1:i.playAlarm,br=undefined===i.alarmSound?!1:i.alarmSound;nr&&br!==!1&&(hi=r.createElement("audio"),hi.setAttribute("src",br),hi.setAttribute("preload","auto"));var kr=steelseries.GaugeType.TYPE5,wu=this,b=l,vt=d,wt=l,tt=!1,nu=0,dt,ct=Math.PI/2,gu=Math.PI/180,pi=l,wi=d,bi=d-l,at=wi-pi,yi=0,ot=0,oi=10,au=10,lu=0,rt=1.25*Math.PI,vu=1.25*Math.PI,kt=ct,ht=kt/at,pr=!1,ai=rt+(b-l)*ht,f=r.getElementById(n).getContext("2d");f.clearRect(0,0,f.canvas.width,f.canvas.height),f.canvas.width=u,f.canvas.height=u;var h=u,k=u,s=h/2,g=k*.733644,pu=.455*h,yu=.51*k,ku=function(){bu?(bi=c(d-l,!1),ot=c(bi/(au-1),!0),pi=Math.floor(l/ot)*ot,wi=Math.ceil(d/ot)*ot,yi=c(ot/(oi-1),!0),l=pi,d=wi,at=d-l):(bi=d-l,pi=l,wi=d,at=bi,yi=1,ot=10),lu=0,rt=1.25*Math.PI,vu=1.25*Math.PI,kt=ct,ht=kt/at,ai=rt+(b-l)*ht},ui=t(u,u),fr=ui.getContext("2d"),ri=t(u,u),p=ri.getContext("2d"),ii=t(u*.093457,u*.093457),cr=ii.getContext("2d"),st=t(u*.093457,u*.093457),ar=st.getContext("2d"),bt=st,hr=t(Math.ceil(u*.028037),Math.ceil(u*.028037)),or=hr.getContext("2d"),er=t(Math.ceil(u*.028037),Math.ceil(u*.028037)),rr=er.getContext("2d"),fi=t(u,u),sr=fi.getContext("2d"),li=t(u,u),lr=li.getContext("2d"),yt=t(u,u),et=yt.getContext("2d"),si=t(u,u),ir=si.getContext("2d"),cu=function(n){"type5"===kr.type&&(n.save(),ft.type==="west"?(n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,ci),h*.44,k*.8),n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,ci),h*.44,k*.16)):(n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,ci),h*.2-k*.037383,k*.446666),n.drawImage(e(Math.ceil(k*.037383),steelseries.KnobType.STANDARD_KNOB,ci),h*.8,k*.446666)),n.restore())},hu=function(){var t=r.createElement("canvas"),n,i;return t.width=Math.ceil(u*.046728),t.height=Math.ceil(t.width*.9),n=t.getContext("2d"),n.save(),i=n.createLinearGradient(0,.1,0,t.height*.9),i.addColorStop(0,"#520000"),i.addColorStop(.3,"#fc1d00"),i.addColorStop(.59,"#fc1d00"),i.addColorStop(1,"#520000"),n.fillStyle=i,n.beginPath(),n.moveTo(t.width*.5,.1),n.lineTo(t.width*.9,t.height*.9),n.lineTo(t.width*.1,t.height*.9),n.lineTo(t.width*.5,.1),n.closePath(),n.fill(),n.strokeStyle="#FFFFFF",n.stroke(),n.restore(),t},ur=function(n,t,i,r,u){n.save(),n.strokeStyle=r,n.fillStyle=r,n.lineWidth=h*.035;var f=kt/at*t-kt/at*l,e=f+(i-t)/(at/kt);n.translate(s,g),n.rotate(rt),n.beginPath(),u?(n.moveTo(0,0),n.arc(0,0,h*.365-n.lineWidth/2,f,e,!1)):n.arc(0,0,h*.365,f,e,!1),u?(n.moveTo(0,0),n.fill()):n.stroke(),n.translate(-s,-g),n.restore()},ou=function(n){var r,i,t;n.save(),n.textAlign="left",n.textBaseline="middle",n.strokeStyle=it.labelColor.getRgbaColor(),n.fillStyle=it.labelColor.getRgbaColor(),r=h,ei||vertical||(r=k),n.font=.046728*h+"px sans-serif",i=n.measureText(gr).width,n.fillText(gr,(h-i)/2,k*.4,h*.3),t=n.measureText(uu).width,n.fillText(uu,(h-t)/2,k*.47,h*.2),n.restore()},su=function(n,t){var a;it.labelColor.setAlpha(1),n.save(),steelseries.Orientation.WEST===ft&&(n.translate(s,s),n.rotate(-Math.PI/2),n.translate(-s,-s)),n.textAlign="center",n.textBaseline="middle",a=Math.ceil(h*.04),n.font=a+"px sans-serif",n.strokeStyle=it.labelColor.getRgbaColor(),n.fillStyle=it.labelColor.getRgbaColor(),n.translate(s,g),n.rotate(rt);for(var o=ht*yi,v,i=l,r=oi-1,e=h*.44,k=h*.41,b=h*.415,p=h*.42,c=h*.48,u=h*.0375,w=oi/2,y=parseFloat(d.toFixed(2)),f=l;parseFloat(f.toFixed(2))<=y;f+=yi){v=+o+ct,r++;if(r===oi){n.lineWidth=1.5,n.beginPath(),n.moveTo(e,0),n.lineTo(k,0),n.closePath(),n.stroke(),n.save(),n.translate(c,0),n.rotate(v);switch(t.format){case"fractional":n.fillText(i.toFixed(2),0,0,u);break;case"scientific":n.fillText(i.toPrecision(2),0,0,u);break;case"standard":default:n.fillText(i.toFixed(0),0,0,u)}n.translate(-c,0),n.restore(),i+=ot,r=0,n.rotate(o);continue}0==oi%2&&r===w?(n.lineWidth=1,n.beginPath(),n.moveTo(e,0),n.lineTo(b,0),n.closePath(),n.stroke()):(n.lineWidth=.5,n.beginPath(),n.moveTo(e,0),n.lineTo(p,0),n.closePath(),n.stroke()),n.rotate(o)}n.translate(-s,-g),n.restore()},lt=function(n){var t,i,r;n=n||{};var e=undefined===n.frame?!1:n.frame,f=undefined===n.background?!1:n.background,d=undefined===n.led?!1:n.led,c=undefined===n.pointer?!1:n.pointer,b=undefined===n.foreground?!1:n.foreground;pr=!0,ku(),e&&ru&&v(fr,iu,s,u/2,h,k),f&&yr&&w(p,it,s,u/2,h,k),d&&(cr.drawImage(o(Math.ceil(u*.093457),1,ki),0,0),ar.drawImage(o(Math.ceil(u*.093457),0,ki),0,0)),gi&&(or.drawImage(a(Math.ceil(u*.028037),steelseries.ColorDef.BLUE.dark.getRgbaColor(),!0,!0),0,0),or.restore()),di&&(rr.drawImage(a(Math.ceil(u*.028037),steelseries.ColorDef.RED.medium.getRgbaColor(),!0),0,0),rr.restore());if(f&&yr){cu(p);if(null!==gt&&0<gt.length){p.save(),steelseries.Orientation.WEST===ft&&(p.translate(s,s),p.rotate(-Math.PI/2),p.translate(-s,-s)),t=gt.length;do t--,ur(p,gt[t].start,gt[t].stop,gt[t].color,!1);while(0<t);p.restore()}if(null!==ni&&0<ni.length){steelseries.Orientation.WEST===ft&&(p.translate(s,s),p.rotate(-Math.PI/2),p.translate(-s,-s)),i=ni.length;do i--,ur(p,ni[i].start,ni[i].stop,ni[i].color,!0);while(0<i);p.restore()}su(p,eu),ou(p)}vr&&(p.save(),steelseries.Orientation.WEST===ft&&(p.translate(s,s),p.rotate(-Math.PI/2),p.translate(-s,-s)),p.translate(s,g),p.rotate(rt+(pt-l)*ht+ct),p.translate(-s,-g),p.drawImage(hu(),h*.475,k*.32),p.restore()),c&&(nt(sr,h*1.17,ut,tr,it.labelColor,!1),nt(lr,h*1.17,ut,tr,it.labelColor,!0)),b&&dr&&(r=ut.type==="type15"||ut.type==="type16"?!1:!0,y(ir,wr,h,k,r,du,ci,kr,ft))},ti=function(n){n=n||{};var f=undefined===n.frame?!1:n.frame,e=undefined===n.background?!1:n.background,r=undefined===n.led?!1:n.led,t=undefined===n.pointer?!1:n.pointer,i=undefined===n.foreground?!1:n.foreground;f&&(ui.width=u,ui.height=u,fr=ui.getContext("2d")),e&&(ri.width=u,ri.height=u,p=ri.getContext("2d")),r&&(ii.width=Math.ceil(u*.093457),ii.height=Math.ceil(u*.093457),cr=ii.getContext("2d"),st.width=Math.ceil(u*.093457),st.height=Math.ceil(u*.093457),ar=st.getContext("2d"),bt=st),t&&(fi.width=u,fi.height=u,sr=fi.getContext("2d"),li.width=u,li.height=u,lr=li.getContext("2d"),yt.width=u,yt.height=u,et=yt.getContext("2d")),i&&(si.width=u,si.height=u,ir=si.getContext("2d"))},vi=function(n){n?nu=setInterval(fu,1e3):clearInterval(nu)},fu=function(){tu&&(bt=bt===ii?st:ii,wu.repaint())};return this.setValue=function(n){var t=n<l?l:n>d?d:n;b!==t&&(b=t,b>wt&&(wt=b),b<vt&&(vt=b),b<pt||tt?b<pt&&(tt=!1,vi(tt),nr&&hi.pause()):(tt=!0,vi(tt),nr&&hi.play()),this.repaint())},this.getValue=function(){return b},this.setValueAnimated=function(n){var i=n<l?l:n>d?d:n,t;b!==i&&(undefined!==dt&&dt.playing&&dt.stop(),dt=new Tween({},"",Tween.regularEaseInOut,b,i,1),t=this,dt.onMotionChanged=function(n){b=n.target._pos,b<pt||tt?b<pt&&(tt=!1,vi(tt)):(tt=!0,vi(tt)),b>wt&&(wt=b),b<vt&&(vt=b),t.repaint()},dt.start())},this.resetMinMeasuredValue=function(){vt=b,this.repaint()},this.resetMaxMeasuredValue=function(){wt=b,this.repaint()},this.setMinMeasuredValueVisible=function(n){gi=n,this.repaint()},this.setMaxMeasuredValueVisible=function(n){di=n,this.repaint()},this.setThresholdVisible=function(n){vr=n,this.repaint()},this.setFrameDesign=function(n){ti({frame:!0}),iu=n,lt({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){ti({background:!0,pointer:ut.type==="type2"||ut.type==="type13"?!0:!1}),it=n,lt({background:!0,pointer:ut.type==="type2"||ut.type==="type13"?!0:!1}),this.repaint()},this.setForegroundType=function(n){ti({foreground:!0}),wr=n,lt({foreground:!0}),this.repaint()},this.setPointerType=function(n){ti({pointer:!0,foreground:!0}),ut=n,lt({pointer:!0,foreground:!0}),this.repaint()},this.setPointerColor=function(n){ti({pointer:!0}),tr=n,lt({pointer:!0}),this.repaint()},this.setLedColor=function(n){ti({led:!0}),ki=n,lt({led:!0}),this.repaint()},this.repaint=function(){pr||lt({frame:!0,background:!0,led:!0,pointer:!0,foreground:!0}),f.clearRect(0,0,f.canvas.width,f.canvas.height),f.save(),ru&&f.drawImage(ui,0,0),f.drawImage(ri,0,0),tu&&(b<pt&&(tt=!1,bt=st),f.drawImage(bt,pu,yu)),steelseries.Orientation.WEST===ft&&(f.translate(s,s),f.rotate(-Math.PI/2),f.translate(-s,-s)),gi&&(f.save(),f.translate(s,g),f.rotate(rt+ct+(vt-l)*ht),f.translate(-s,-g),f.drawImage(hr,f.canvas.width*.4865,f.canvas.height*.27),f.restore()),di&&(f.save(),f.translate(s,g),f.rotate(rt+ct+(wt-l)*ht),f.translate(-s,-g),f.drawImage(er,f.canvas.width*.4865,f.canvas.height*.27),f.restore()),ai=rt+ct+(b-l)*ht;var n=h*.006,t=h*1.17/2;et.clearRect(0,0,h,k),et.save(),et.translate(s,g),et.rotate(ai),et.translate(-t,-t),et.drawImage(li,0,0),et.restore(),steelseries.Orientation.NORTH===ft?f.drawImage(yt,0,0,h,k,n,n,h+n,k+n):f.drawImage(yt,0,0,h,k,-n,n,h-n,k+n),f.save(),f.translate(s,g),f.rotate(ai),f.translate(-t,-t),f.drawImage(fi,0,0),f.restore(),dr&&(f.save(),steelseries.Orientation.WEST===ft&&(f.translate(s,s),f.rotate(Math.PI/2),f.translate(-s,-s)),f.drawImage(si,0,0),f.restore()),f.restore()},this.repaint(),this},yi=function(n,i){var fi;i=i||{};var l=undefined===i.gaugeType?steelseries.GaugeType.TYPE1:i.gaugeType,b=undefined===i.width?140:i.width,d=undefined===i.height?320:i.height,e=undefined===i.minValue?0:i.minValue,s=undefined===i.maxValue?e+100:i.maxValue,gr=undefined===i.niceScale?!0:i.niceScale,bt=undefined===i.threshold?(s-e)/2:i.threshold,gi=undefined===i.titleString?"":i.titleString,wi=undefined===i.unitString?"":i.unitString,sr=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,or=undefined===i.frameVisible?!0:i.frameVisible,w=undefined===i.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:i.backgroundColor,lr=undefined===i.backgroundVisible?!0:i.backgroundVisible,wt=undefined===i.valueColor?steelseries.ColorDef.RED:i.valueColor,lt=undefined===i.lcdColor?steelseries.LcdColor.STANDARD:i.lcdColor,hi=undefined===i.lcdVisible?!0:i.lcdVisible,fr=undefined===i.lcdDecimals?2:i.lcdDecimals,eu=undefined===i.digitalFont?!1:i.digitalFont,ti=undefined===i.ledColor?steelseries.LedColor.RED_LED:i.ledColor,er=undefined===i.ledVisible?!0:i.ledVisible,ar=undefined===i.thresholdVisible?!0:i.thresholdVisible,tr=undefined===i.minMeasuredValueVisible?!1:i.minMeasuredValueVisible,di=undefined===i.maxMeasuredValueVisible?!1:i.maxMeasuredValueVisible,fu=undefined===i.labelNumberFormat?steelseries.LabelNumberFormat.STANDARD:i.labelNumberFormat,kr=undefined===i.foregroundVisible?!0:i.foregroundVisible,ki=undefined===i.playAlarm?!1:i.playAlarm,vr=undefined===i.alarmSound?!1:i.alarmSound;ki&&vr!==!1&&(fi=r.createElement("audio"),fi.setAttribute("src",vr),fi.setAttribute("preload","auto"));var hu=this,v=e,at=s,ft=e;l.type!=="type1"&&l.type!=="type2"&&(l=steelseries.GaugeType.TYPE1);var yt,vt=!1,br=0,y=r.getElementById(n).getContext("2d");y.clearRect(0,0,y.canvas.width,y.canvas.height),y.canvas.width=b,y.canvas.height=d;var f=b,u=d,h=b<=d,bi,rr,it=Math.round((h?d:b)*.05),nt=Math.round((h?b:d)*.05),ir,nr;h?(bi=f/2-it/2,rr=(l.type==="type1"?.053:.038)*u,ir=Math.floor(u/22)+"px sans-serif",nr=Math.floor(u/22)+"px "+tt):(bi=.89*f,rr=u/2-it/2,ir=Math.floor(u/10)+"px sans-serif",nr=Math.floor(u/10)+"px "+tt);var pr=!1,ci=e,si=s,ei=s-e,yr=si-ci,oi=0,ut=0,ui=10,uu=10,ru=function(){gr?(ei=c(s-e,!1),ut=c(ei/(uu-1),!0),ci=Math.floor(e/ut)*ut,si=Math.ceil(s/ut)*ut,oi=c(ut/(ui-1),!0),e=ci,s=si,yr=s-e):(ei=s-e,ci=e,si=s,yr=ei,oi=1,ut=10)},ii=t(b,d),wr=ii.getContext("2d"),ri=t(b,d),k=ri.getContext("2d"),li,kt=t(it,it),vi=kt.getContext("2d"),rt=t(it,it),yi=rt.getContext("2d"),pt=rt,gt=t(nt,nt),cr=gt.getContext("2d"),dt=t(nt,nt),hr=dt.getContext("2d"),ni=t(b,d),pi=ni.getContext("2d"),cu=function(n,t,i){n.save(),n.textAlign="right",n.textBaseline="middle",n.strokeStyle=lt.textColor,n.fillStyle=lt.textColor,(lt===steelseries.LcdColor.STANDARD||lt===steelseries.LcdColor.STANDARD_GREEN)&&(n.shadowColor="gray",i?(n.shadowOffsetX=u*.003,n.shadowOffsetY=u*.003,n.shadowBlur=u*.004):(n.shadowOffsetX=u*.007,n.shadowOffsetY=u*.007,n.shadowBlur=u*.009));var o,e,r;n.font=eu?nr:ir,i?(o=(f-f*.571428)/2+f*.571428-2,e=u*.88+1+(u*.055-2)/2,r=f*.7-2):(o=f*.695+f*.18-2,e=u*.22+1+(u*.15-2)/2,r=u*.22-2),n.fillText(t.toFixed(fr),o,e,r),n.restore()},ou=function(n){var i=r.createElement("canvas"),t,u;return i.height=i.width=nt,t=i.getContext("2d"),t.save(),u=t.createLinearGradient(0,.1,0,i.height*.9),u.addColorStop(0,"#520000"),u.addColorStop(.3,"#fc1d00"),u.addColorStop(.59,"#fc1d00"),u.addColorStop(1,"#520000"),t.fillStyle=u,n?(t.beginPath(),t.moveTo(.1,i.height*.5),t.lineTo(i.width*.9,.1),t.lineTo(i.width*.9,i.height*.9),t.closePath()):(t.beginPath(),t.moveTo(.1,.1),t.lineTo(i.width*.9,.1),t.lineTo(i.width*.5,i.height*.9),t.closePath()),t.fill(),t.strokeStyle="#FFFFFF",t.stroke(),t.restore(),i},su=function(n,t,i){var o,k;w.labelColor.setAlpha(1),n.save(),n.textBaseline="middle",o=f*.1,n.strokeStyle=w.labelColor.getRgbaColor(),n.fillStyle=w.labelColor.getRgbaColor();var r=e,y=ui-1,h,nt,v,c,a=1,g,it,tt,b,p,d;for(i?(g=.34*f,it=.36*f,tt=.33*f,b=.36*f,p=.32*f,d=.36*f,n.textAlign="right",h=0,nt=u*.12864,v=0,c=l.type==="type1"?u*.856796-u*.12864:u*.7475-u*.12864,a=c/(s-e)):(g=.65*u,it=.63*u,tt=.66*u,b=.63*u,p=.67*u,d=.63*u,n.textAlign="center",nt=0,l.type==="type1"?(h=f*.142857,v=f*.871012-h):(h=f*.19857,v=f*.82-h),c=0,a=v/(s-e)),k=e,tickCounter=0;k<=s;k+=oi,tickCounter+=oi){currentPos=i?nt+c-tickCounter*a:h+tickCounter*a,y++;if(y===ui){n.lineWidth=1.5,ai(n,p,d,currentPos,i);if(i)switch(t.format){case"fractional":n.fillText(r.toFixed(2),f*.28,currentPos,o);break;case"scientific":n.fillText(r.toPrecision(2),f*.28,currentPos,o);break;case"standard":default:n.fillText(r.toFixed(0),f*.28,currentPos,o)}else switch(t.format){case"fractional":n.fillText(r.toFixed(2),currentPos,u*.73,o);break;case"scientific":n.fillText(r.toPrecision(2),currentPos,u*.73,o);break;case"standard":default:n.fillText(r.toFixed(0),currentPos,u*.73,o)}r+=ut,y=0;continue}0==ui%2&&y===ui/2?(n.lineWidth=1,ai(n,tt,b,currentPos,i)):(n.lineWidth=.5,ai(n,g,it,currentPos,i))}n.restore()},ai=function(n,t,i,r,u){u?(n.beginPath(),n.moveTo(t,r),n.lineTo(i,r),n.closePath(),n.stroke()):(n.beginPath(),n.moveTo(r,t),n.lineTo(r,i),n.closePath(),n.stroke())},g=function(n){n=n||{};var b=undefined===n.frame?!1:n.frame,t=undefined===n.background?!1:n.background,y=undefined===n.led?!1:n.led,v=undefined===n.foreground?!1:n.foreground,i,r,c;pr=!0,ru(),b&&or&&ht(wr,sr,f,u,h),t&&lr&&ot(k,w,f,u,h),t&&l.type==="type2"&&dr(k),y&&(h?(vi.drawImage(o(it,1,ti),0,0),yi.drawImage(o(it,0,ti),0,0)):(vi.drawImage(o(it,1,ti),0,0),yi.drawImage(o(it,0,ti),0,0))),tr&&(h?cr.drawImage(a(nt,steelseries.ColorDef.BLUE.dark.getRgbaColor(),!1,h),0,0):cr.drawImage(a(nt,steelseries.ColorDef.BLUE.dark.getRgbaColor(),!1,h),0,0)),di&&(h?hr.drawImage(a(nt,steelseries.ColorDef.RED.medium.getRgbaColor(),!1,h),0,0):hr.drawImage(a(nt,steelseries.ColorDef.RED.medium.getRgbaColor(),!1,h),0,0)),t&&lr&&(su(k,fu,h),h?ct(k,f,u,gi,wi,w,h,null,hi,l):ct(k,f,u,gi,wi,w,h,null,hi,l)),t&&ar&&(k.save(),h?(i=l.type==="type1"?.856796:.7475,r=i-.12864,c=u*i-u*r*(bt/(s-e)),k.translate(f*.365,c-nt/2)):(i=l.type==="type1"?.871012:.82,r=i-(l.type==="type1"?.142857:.19857),c=f*r*bt/(s-e),k.translate(f*(l.type==="type1"?.142857:.19857)-nt/2+c,u*.58)),k.drawImage(ou(h),0,0),k.restore()),t&&hi&&(h?(li=p(f*.571428,u*.055,lt),k.drawImage(li,(f-f*.571428)/2,u*.88)):(li=p(f*.18,u*.15,lt),k.drawImage(li,f*.695,u*.22))),v&&l.type==="type2"&&nu(pi),v&&kr&&et(pi,f,u,h,!1)},st=function(n){n=n||{};var r=undefined===n.frame?!1:n.frame,u=undefined===n.background?!1:n.background,t=undefined===n.led?!1:n.led,i=undefined===n.foreground?!1:n.foreground;r&&(ii.width=b,ii.height=d,wr=ii.getContext("2d")),u&&(ri.width=b,ri.height=d,k=ri.getContext("2d")),t&&(kt.width=Math.ceil(b*.093457),kt.height=Math.ceil(d*.093457),vi=kt.getContext("2d"),rt.width=Math.ceil(b*.093457),rt.height=Math.ceil(d*.093457),yi=rt.getContext("2d"),pt=rt),i&&(ni.width=b,ni.height=d,pi=ni.getContext("2d"))},ur=function(n){n?br=setInterval(iu,1e3):clearInterval(br)},iu=function(){er&&(pt=pt===kt?rt:kt,hu.repaint())},tu=function(n,t,i){var u,o,r=w.labelColor,f,y,nt,c,a,tt,g,ct,ht,st,lt,yt,pt,at,vt,it,k,et,ft,d,p,b,ut,rt,ot;h?(u=i*.12864,o=l.type==="type1"?i*.856796:i*.7475,f=o-u,y=f*(v-e)/(s-e),nt=o-y,ct=0,ht=u,st=0,lt=o):(l.type==="type1"?(u=t*.871012,o=t*.142857):(u=t*.82,o=t*.19857),f=u-o,y=f*(v-e)/(s-e),nt=o,ct=u,ht=0,st=o,lt=0),l.type==="type1"&&(d=w===steelseries.BackgroundColor.CARBON||w===steelseries.BackgroundColor.PUNCHED_SHEET||w===steelseries.BackgroundColor.STAINLESS||w===steelseries.BackgroundColor.BRUSHED_STAINLESS||w===steelseries.BackgroundColor.TURNED?.3:0,p=n.createLinearGradient(ct,ht,st,lt),r.setAlpha(.05+d),p.addColorStop(0,r.getRgbaColor()),r.setAlpha(.15+d),p.addColorStop(.48,r.getRgbaColor()),r.setAlpha(.15+d),p.addColorStop(.49,r.getRgbaColor()),r.setAlpha(.05+d),p.addColorStop(1,r.getRgbaColor()),n.fillStyle=p,h?n.fillRect(t*.435714,u,t*.142857,f):n.fillRect(t*.142857,i*.435714,f,i*.142857),h?(yt=0,pt=u,at=0,vt=u+f):(yt=t*.142857+f,pt=0,at=t*.142857,vt=0),b=n.createLinearGradient(yt,pt,at,vt),r.setAlpha(.3+d),b.addColorStop(0,r.getRgbaColor()),r.setAlpha(.69),b.addColorStop(.48,r.getRgbaColor()),r.setAlpha(.7),b.addColorStop(.49,r.getRgbaColor()),r.setAlpha(.4),b.addColorStop(1,r.getRgbaColor()),n.fillStyle=b,h?(n.fillRect(t*.435714,u,t*.007142,f),n.fillRect(t*.571428,u,t*.007142,f)):(n.fillRect(t*.142857,i*.435714,f,i*.007142),n.fillRect(t*.142857,i*.571428,f,i*.007142))),h?l.type==="type1"?(c=t*.45,a=0,tt=t*.45+t*.114285,g=0):(c=t/2-i*.0486/2,a=0,tt=c+i*.053,g=0):l.type==="type1"?(c=0,a=i*.45,tt=0,g=i*.45+i*.114285):(c=0,a=i/2-t*.025,tt=0,g=a+t*.053),ut=n.createLinearGradient(c,a,tt,g),ut.addColorStop(0,wt.medium.getRgbaColor()),ut.addColorStop(1,wt.light.getRgbaColor()),n.fillStyle=ut,rt=l.type==="type1"?0:h?i*.05:t*.05,h?n.fillRect(c,nt,tt-c,y+rt):n.fillRect(nt-rt,a,y+rt,g-a),l.type==="type1"&&(h?(it=t*.45,k=0,et=it+t*.05,ft=0):(it=0,k=i*.45,et=0,ft=k+i*.05),ot=n.createLinearGradient(it,k,et,ft),ot.addColorStop(0,"rgba(255, 255, 255, 0.7)"),ot.addColorStop(.98,"rgba(255, 255, 255, 0.0)"),n.fillStyle=ot,h?n.fillRect(it,nt,et,y):n.fillRect(nt,k,y,ft-k))},nu=function(n){var t=h?u:f,i;n.save(),h?n.translate(f/2,0):(n.translate(f/2,u/2),n.rotate(Math.PI*.5),n.translate(0,-f/2+f*.05)),n.beginPath(),n.moveTo(-.049*t,.825*t),n.bezierCurveTo(-.049*t,.7975*t,-.0264*t,.775*t,.0013*t,.775*t),n.bezierCurveTo(.0264*t,.775*t,.049*t,.7975*t,.049*t,.825*t),n.bezierCurveTo(.049*t,.85*t,.0264*t,.8725*t,.0013*t,.8725*t),n.bezierCurveTo(-.0264*t,.8725*t,-.049*t,.85*t,-.049*t,.825*t),n.closePath(),i=n.createRadialGradient(0*t,.825*t,0,0*t,.825*t,.049*t),i.addColorStop(0,wt.medium.getRgbaColor()),i.addColorStop(.3,wt.medium.getRgbaColor()),i.addColorStop(1,wt.light.getRgbaColor()),n.fillStyle=i,n.fill(),n.beginPath(),h?(n.moveTo(-.0365*t,.8075*t),n.bezierCurveTo(-.0365*t,.7925*t,-.0214*t,.7875*t,-.0214*t,.7825*t),n.bezierCurveTo(.0189*t,.785*t,.0365*t,.7925*t,.0365*t,.8075*t),n.bezierCurveTo(.0365*t,.8175*t,.0214*t,.815*t,.0013*t,.8125*t),n.bezierCurveTo(-.0189*t,.8125*t,-.0365*t,.8175*t,-.0365*t,.8075*t),i=n.createRadialGradient(0,.8*t,0,0,.8*t,.0377*t)):(n.beginPath(),n.moveTo(-.0214*t,.86*t),n.bezierCurveTo(-.0365*t,.86*t,-.0415*t,.845*t,-.0465*t,.825*t),n.bezierCurveTo(-.0465*t,.805*t,-.0365*t,.7875*t,-.0214*t,.7875*t),n.bezierCurveTo(-.0113*t,.7875*t,-.0163*t,.8025*t,-.0163*t,.8225*t),n.bezierCurveTo(-.0163*t,.8425*t,-.0113*t,.86*t,-.0214*t,.86*t),i=n.createRadialGradient(-.03*t,.8225*t,0,-.03*t,.8225*t,.0377*t)),i.addColorStop(0,"rgba(255, 255, 255, 0.55)"),i.addColorStop(1,"rgba(255, 255, 255, 0.05)"),n.fillStyle=i,n.closePath(),n.fill(),n.beginPath(),n.moveTo(-.0214*t,.115*t),n.bezierCurveTo(-.0214*t,.1075*t,-.0163*t,.1025*t,-.0113*t,.1025*t),n.bezierCurveTo(-.0113*t,.1025*t,-.0113*t,.1025*t,-.0113*t,.1025*t),n.bezierCurveTo(-.0038*t,.1025*t,.0013*t,.1075*t,.0013*t,.115*t),n.bezierCurveTo(.0013*t,.115*t,.0013*t,.76*t,.0013*t,.76*t),n.bezierCurveTo(.0013*t,.7675*t,-.0038*t,.7725*t,-.0113*t,.7725*t),n.bezierCurveTo(-.0113*t,.7725*t,-.0113*t,.7725*t,-.0113*t,.7725*t),n.bezierCurveTo(-.0163*t,.7725*t,-.0214*t,.7675*t,-.0214*t,.76*t),n.bezierCurveTo(-.0214*t,.76*t,-.0214*t,.115*t,-.0214*t,.115*t),n.closePath(),i=n.createLinearGradient(-.0189*t,0,.0013*t,0),i.addColorStop(0,"rgba(255, 255, 255, 0.1)"),i.addColorStop(.34,"rgba(255, 255, 255, 0.5)"),i.addColorStop(1,"rgba(255, 255, 255, 0.1)"),n.fillStyle=i,n.fill(),n.restore()},dr=function(n){var t=h?u:f,i;n.save(),h?n.translate(f/2,0):(n.translate(f/2,u/2),n.rotate(Math.PI*.5),n.translate(0,-f/2+f*.05)),n.beginPath(),n.moveTo(-.0516*t,.825*t),n.bezierCurveTo(-.0516*t,.8525*t,-.0289*t,.875*t,.0013*t,.875*t),n.bezierCurveTo(.0289*t,.875*t,.0516*t,.8525*t,.0516*t,.825*t),n.bezierCurveTo(.0516*t,.8075*t,.044*t,.7925*t,.0314*t,.7825*t),n.bezierCurveTo(.0314*t,.7825*t,.0314*t,.12*t,.0314*t,.12*t),n.bezierCurveTo(.0314*t,.1025*t,.0189*t,.0875*t,.0013*t,.0875*t),n.bezierCurveTo(-.0163*t,.0875*t,-.0289*t,.1025*t,-.0289*t,.12*t),n.bezierCurveTo(-.0289*t,.12*t,-.0289*t,.7825*t,-.0289*t,.7825*t),n.bezierCurveTo(-.0415*t,.79*t,-.0516*t,.805*t,-.0516*t,.825*t),n.closePath(),i=n.createLinearGradient(-.0163*t,0,.0289*t,0),i.addColorStop(0,"rgba(226, 226, 226, 0.5)"),i.addColorStop(.5,"rgba(226, 226, 226, 0.2)"),i.addColorStop(1,"rgba(226, 226, 226, 0.5)"),n.fillStyle=i,n.fill(),n.lineWidth=1,n.strokeStyle="rgba(153, 153, 153, 0.5)",n.stroke(),n.restore()};return this.setValue=function(n){var t=n<e?e:n>s?s:n;v!==t&&(v=t,v>ft&&(ft=v),v<at&&(at=v),v<bt||vt?v<bt&&(vt=!1,ur(vt),ki&&fi.pause()):(vt=!0,ur(vt),ki&&fi.play()),this.repaint())},this.getValue=function(){return v},this.setValueAnimated=function(n){var i=n<e?e:n>s?s:n,t;v!==i&&(undefined!==yt&&yt.playing&&yt.stop(),yt=new Tween({},"",Tween.regularEaseInOut,v,i,1),t=this,yt.onMotionChanged=function(n){t.setValue(n.target._pos)},yt.start())},this.resetMinMeasuredValue=function(){at=v,this.repaint()},this.resetMaxMeasuredValue=function(){ft=v,this.repaint()},this.setMinMeasuredValueVisible=function(n){tr=n,this.repaint()},this.setMaxMeasuredValueVisible=function(n){di=n,this.repaint()},this.setThresholdVisible=function(n){ar=n,this.repaint()},this.setLcdDecimals=function(n){fr=n,this.repaint()},this.setFrameDesign=function(n){st({frame:!0}),sr=n,g({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){st({background:!0}),w=n,g({background:!0}),this.repaint()},this.setValueColor=function(n){st({foreground:!0}),wt=n,g({foreground:!0}),this.repaint()},this.setLedColor=function(n){st({led:!0}),ti=n,g({led:!0}),this.repaint()},this.setLcdColor=function(n){st({background:!0}),lt=n,g({background:!0}),this.repaint()},this.setMaxMeasuredValue=function(n){var t=n<e?e:n>s?s:n;ft=t,this.repaint()},this.setMinMeasuredValue=function(n){var t=n<e?e:n>s?s:n;at=t,this.repaint()},this.setTitleString=function(n){gi=n,g({background:!0}),this.repaint()},this.setUnitString=function(n){wi=n,g({background:!0}),this.repaint()},this.setMinValue=function(n){st({background:!0}),e=n,mminMeasuredValue<e&&(at=e),v<e&&(v=e),g({background:!0}),this.repaint()},this.getMinValue=function(){return e},this.setMaxValue=function(n){st({background:!0}),s=n,ft>s&&(ft=s),v>s&&(v=s),g({background:!0}),this.repaint()},this.getMaxValue=function(){return s},this.setThreshold=function(n){var t=n<e?e:n>s?s:n;bt=t,g({background:!0}),this.repaint()},this.repaint=function(){pr||g({frame:!0,background:!0,led:!0,foreground:!0}),y.clearRect(0,0,y.canvas.width,y.canvas.height),or&&y.drawImage(ii,0,0),y.drawImage(ri,0,0),hi&&cu(y,v,h),er&&(v<bt&&(vt=!1,pt=rt),y.drawImage(pt,bi,rr));var t,n,i,o,r;tr&&(h?(n=l.type==="type1"?.856796:.7475,i=n-.12864,t=u*n-u*i*(at/(s-e)),o=f*.34-gt.width,r=t-gt.height/2):(n=l.type==="type1"?.871012:.82,i=n-(l.type==="type1"?.142857:.19857),t=f*i*at/(s-e),o=f*(l.type==="type1"?.142857:.19857)-gt.height/2+t,r=u*.65),y.drawImage(gt,o,r)),di&&(h?(t=u*n-u*i*(ft/(s-e)),o=f*.34-dt.width,r=t-dt.height/2):(n=l.type==="type1"?.871012:.8,i=n-(l.type==="type1"?.14857:.19857),t=f*i*ft/(s-e),o=f*(l.type==="type1"?.142857:.19857)-dt.height/2+t,r=u*.65),y.drawImage(dt,o,r)),y.save(),tu(y,f,u),y.restore(),(kr||l.type==="type2")&&y.drawImage(ni,0,0)},this.repaint(),this},kr=function(n,i){var oi,li,d;i=i||{};var y=undefined===i.width?140:i.width,w=undefined===i.height?320:i.height,u=undefined===i.minValue?0:i.minValue,s=undefined===i.maxValue?u+100:i.maxValue,bt=undefined===i.section?null:i.section,lu=undefined===i.useSectionColors?!1:i.useSectionColors,su=undefined===i.niceScale?!0:i.niceScale,st=undefined===i.threshold?(s-u)/2:i.threshold,ar=undefined===i.titleString?"":i.titleString,rr=undefined===i.unitString?"":i.unitString,dr=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,kr=undefined===i.frameVisible?!0:i.frameVisible,k=undefined===i.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:i.backgroundColor,or=undefined===i.backgroundVisible?!0:i.backgroundVisible,ei=undefined===i.valueColor?steelseries.ColorDef.RED:i.valueColor,kt=undefined===i.lcdColor?steelseries.LcdColor.STANDARD:i.lcdColor,gi=undefined===i.lcdVisible?!0:i.lcdVisible,pr=undefined===i.lcdDecimals?2:i.lcdDecimals,wu=undefined===i.digitalFont?!1:i.digitalFont,si=undefined===i.ledColor?steelseries.LedColor.RED_LED:i.ledColor,br=undefined===i.ledVisible?!0:i.ledVisible,wr=undefined===i.thresholdVisible?!0:i.thresholdVisible,lr=undefined===i.minMeasuredValueVisible?!1:i.minMeasuredValueVisible,fr=undefined===i.maxMeasuredValueVisible?!1:i.maxMeasuredValueVisible,bu=undefined===i.labelNumberFormat?steelseries.LabelNumberFormat.STANDARD:i.labelNumberFormat,fu=undefined===i.foregroundVisible?!0:i.foregroundVisible,er=undefined===i.playAlarm?!1:i.playAlarm,iu=undefined===i.alarmSound?!1:i.alarmSound,yt=undefined===i.valueGradient?null:i.valueGradient,pu=undefined===i.useValueGradient?!1:i.useValueGradient;er&&iu!==!1&&(oi=r.createElement("audio"),oi.setAttribute("src",iu),oi.setAttribute("preload","auto"));var ku=this,l=u,at=s,lt=u,dt,it=!1,ri=!1,ki=!1,ft=[],ru=0,v=r.getElementById(n).getContext("2d");v.clearRect(0,0,v.canvas.width,v.canvas.height),v.canvas.width=y,v.canvas.height=w;var f=v.canvas.width,e=v.canvas.height,h=y<=w,ur,sr,ut=Math.round((h?w:y)*.05),rt=Math.round((h?y:w)*.05),hr,cr;h?(ur=f/2-ut/2,sr=.053*e,hr=Math.floor(e/22)+"px sans-serif",cr=Math.floor(e/22)+"px "+tt):(ur=.89*f,sr=e/1.95-ut/2,hr=Math.floor(e/10)+"px sans-serif",cr=Math.floor(e/10)+"px "+tt);var tu=!1,pi=u,wi=s,bi=s-u,ou=wi-pi,ai=0,vt=0,ti=10,hu=10,yu=function(){su?(bi=c(s-u,!1),vt=c(bi/(hu-1),!0),pi=Math.floor(u/vt)*vt,wi=Math.ceil(s/vt)*vt,ai=c(vt/(ti-1),!0),u=pi,s=wi,ou=s-u):(bi=s-u,pi=u,wi=s,ou=bi,ai=1,vt=10)},fi=t(y,w),uu=fi.getContext("2d"),hi=t(y,w),nt=hi.getContext("2d"),di,g=r.createElement("canvas");h?(g.width=f*.121428,g.height=e*.012135):(g.width=f*.012135,g.height=e*.121428),li=g.getContext("2d"),d=r.createElement("canvas"),h?(d.width=f*.121428,d.height=e*.012135):(d.width=f*.012135,d.height=e*.121428);var eu=d.getContext("2d"),ni=t(ut,ut),nr=ni.getContext("2d"),pt=t(ut,ut),tr=pt.getContext("2d"),gt=pt,ci=t(rt,rt),gr=ci.getContext("2d"),ii=t(rt,rt),nu=ii.getContext("2d"),ui=t(y,w),yr=ui.getContext("2d"),nf=function(n,t,i){n.save(),n.textAlign="right",n.textBaseline="middle",n.strokeStyle=kt.textColor,n.fillStyle=kt.textColor,(kt===steelseries.LcdColor.STANDARD||kt===steelseries.LcdColor.STANDARD_GREEN)&&(n.shadowColor="gray",i?(n.shadowOffsetX=f*.007,n.shadowOffsetY=f*.007,n.shadowBlur=f*.009):(n.shadowOffsetX=e*.007,n.shadowOffsetY=e*.007,n.shadowBlur=e*.009));var o,u,r;n.font=wu?cr:hr,i?(o=(f-f*.571428)/2+1+f*.571428-2,u=e*.88+1+(e*.055-2)/2,r=f*.7-2):(o=f*.695+f*.18-2,u=e*.22+1+(e*.15-2)/2,r=e*.22-2),n.fillText(t.toFixed(pr),o,u,r),n.restore()},gu=function(n){var i=r.createElement("canvas"),t,u;return i.height=i.width=rt,t=i.getContext("2d"),t.save(),u=t.createLinearGradient(0,.1,0,i.height*.9),u.addColorStop(0,"#520000"),u.addColorStop(.3,"#fc1d00"),u.addColorStop(.59,"#fc1d00"),u.addColorStop(1,"#520000"),t.fillStyle=u,n?(t.beginPath(),t.moveTo(.1,i.height*.5),t.lineTo(i.width*.9,.1),t.lineTo(i.width*.9,i.height*.9),t.closePath()):(t.beginPath(),t.moveTo(.1,.1),t.lineTo(i.width*.9,.1),t.lineTo(i.width*.5,i.height*.9),t.closePath()),t.fill(),t.strokeStyle="#FFFFFF",t.stroke(),t.restore(),i},du=function(n,t,i){var o,y;k.labelColor.setAlpha(1),n.save(),n.textBaseline="middle",o=f*.1,n.strokeStyle=k.labelColor.getRgbaColor(),n.fillStyle=k.labelColor.getRgbaColor();var r=u,l=ti-1,d,b,g,c,h=1,tt,nt,a,v,w,p;for(i?(tt=.34*f,nt=.36*f,a=.33*f,v=.36*f,w=.32*f,p=.36*f,n.textAlign="right",d=0,b=e*.12864,g=0,c=e*.856796-e*.12864,h=c/(s-u)):(tt=.65*e,nt=.63*e,a=.66*e,v=.63*e,w=.67*e,p=.63*e,n.textAlign="center",d=f*.142857,b=0,g=f*.871012-f*.142857,c=0,h=g/(s-u)),y=u,tickCounter=0;y<=s;y+=ai,tickCounter+=ai){currentPos=i?b+c-tickCounter*h:d+tickCounter*h,l++;if(l===ti){n.lineWidth=1.5,ir(n,w,p,currentPos,i);if(i)switch(t.format){case"fractional":n.fillText(r.toFixed(2),f*.28,currentPos,o);break;case"scientific":n.fillText(r.toPrecision(2),f*.28,currentPos,o);break;case"standard":default:n.fillText(r.toFixed(0),f*.28,currentPos,o)}else switch(t.format){case"fractional":n.fillText(r.toFixed(2),currentPos,e*.73,o);break;case"scientific":n.fillText(r.toPrecision(2),currentPos,e*.73,o);break;case"standard":default:n.fillText(r.toFixed(0),currentPos,e*.73,o)}r+=vt,l=0;continue}0==ti%2&&l===ti/2?(n.lineWidth=1,ir(n,a,v,currentPos,i)):(n.lineWidth=.5,ir(n,tt,nt,currentPos,i))}n.restore()},ir=function(n,t,i,r,u){u?(n.beginPath(),n.moveTo(t,r),n.lineTo(i,r),n.closePath(),n.stroke()):(n.beginPath(),n.moveTo(r,t),n.lineTo(r,i),n.closePath(),n.stroke())},b=function(n){var i,t,r,l,c,v;n=n||{};var b=undefined===n.frame?!1:n.frame,y=undefined===n.background?!1:n.background,g=undefined===n.led?!1:n.led,d=undefined===n.foreground?!1:n.foreground,w=undefined===n.bargraphled?!1:n.bargraphled;tu=!0,yu(),b&&kr&&ht(uu,dr,f,e,h),y&&or&&ot(nt,k,f,e,h),g&&(h?(nr.drawImage(o(ut,1,si),0,0),tr.drawImage(o(ut,0,si),0,0)):(nr.drawImage(o(ut,1,si),0,0),tr.drawImage(o(ut,0,si),0,0))),lr&&(h?gr.drawImage(a(rt,steelseries.ColorDef.BLUE.dark.getRgbaColor(),!1,h),0,0):gr.drawImage(a(rt,steelseries.ColorDef.BLUE.dark.getRgbaColor(),!1,h),0,0)),fr&&(h?nu.drawImage(a(rt,steelseries.ColorDef.RED.medium.getRgbaColor(),!1,h),0,0):nu.drawImage(a(rt,steelseries.ColorDef.RED.medium.getRgbaColor(),!1,h),0,0)),y&&or&&(du(nt,bu,h),wr&&(nt.save(),h?(i=e*.856796-e*.728155*(st/(s-u)),nt.translate(f*.365,i-rt/2)):(i=(f*.856796-f*.12864)*st/(s-u),nt.translate(f*.142857-rt/2+i,e*.58)),nt.drawImage(gu(h),0,0),nt.restore()),h?ct(nt,f,e,ar,rr,k,h,null,gi):ct(nt,f,e,ar,rr,k,h,null,gi)),y&&gi&&(h?(di=p(f*.571428,e*.055,kt),nt.drawImage(di,(f-f*.571428)/2,e*.88)):(di=p(f*.18,e*.15,kt),nt.drawImage(di,f*.695,e*.22))),w&&(cu(eu),vr(li,ei)),ri=!1;if(null!==bt&&0<bt.length){ri=!0,t=bt.length,h?(r=e*.12864,l=e*.856796,c=l-r,v=0):(r=f*.856796,l=f*.12864,c=r-l,v=f*.012135/2),ft=[];do t--,ft.push({start:(bt[t].start+Math.abs(u))/(s-u)*c-v,stop:(bt[t].stop+Math.abs(u))/(s-u)*c-v,color:wt(bt[t].color)});while(0<t)}ki=!1,pu&&yt!==null&&(ri=!1,ki=!0),d&&fu&&et(yr,f,e,h,!1)},yi=function(n){n=n||{};var u=undefined===n.frame?!1:n.frame,f=undefined===n.background?!1:n.background,r=undefined===n.led?!1:n.led,t=undefined===n.bargraphled?!1:n.bargraphled,i=undefined===n.foreground?!1:n.foreground;u&&(fi.width=y,fi.height=w,uu=fi.getContext("2d")),f&&(hi.width=y,hi.height=w,nt=hi.getContext("2d")),t&&(h?(g.width=y*.121428,g.height=w*.012135):(g.width=y*.012135,g.height=w*.121428),li=g.getContext("2d"),h?(d.width=y*.121428,d.height=w*.012135):(d.width=y*.012135,d.height=w*.121428),eu=d.getContext("2d")),r&&(ni.width=Math.ceil(y*.093457),ni.height=Math.ceil(w*.093457),nr=ni.getContext("2d"),pt.width=Math.ceil(y*.093457),pt.height=Math.ceil(w*.093457),tr=pt.getContext("2d"),gt=pt),i&&(ui.width=y,ui.height=w,yr=ui.getContext("2d"))},vi=function(n){n?ru=setInterval(au,1e3):clearInterval(ru)},au=function(){br&&(gt=gt===ni?pt:ni,ku.repaint())},vu=function(n,t,i){var v,ot,f=k.labelColor,r,bt,ti,ut,et,vt,gt,ni,pt,dt,kt,lt,ct,w,y,rt,it,p,nt,b,tt,ii,ui,ht,at,e,o,a,st,c;h?(v=i*.12864,ot=i*.856796,r=ot-v,bt=r*(l-u)/(s-u),ti=v+r-bt,ut=0,et=v,vt=0,gt=v+r*1.014):(v=t*.856796,ot=t*.12864,r=v-ot,bt=r*(l-u)/(s-u),ti=ot,ut=t*.13,et=i*.435714,vt=ut+r*1.035,gt=et),y=k===steelseries.BackgroundColor.CARBON||k===steelseries.BackgroundColor.PUNCHED_SHEET||k===steelseries.BackgroundColor.STAINLESS||k===steelseries.BackgroundColor.BRUSHED_STAINLESS||k===steelseries.BackgroundColor.TURNED?.3:0,rt=n.createLinearGradient(ut,et,vt,gt),f.setAlpha(.047058+y),rt.addColorStop(0,f.getRgbaColor()),f.setAlpha(.145098+y),rt.addColorStop(.48,f.getRgbaColor()),f.setAlpha(.149019+y),rt.addColorStop(.49,f.getRgbaColor()),f.setAlpha(.047058+y),rt.addColorStop(1,f.getRgbaColor()),n.fillStyle=rt,h?n.fillRect(t*.435714,v,t*.15,r*1.014):n.fillRect(ut,et,r*1.035,i*.152857),h?(ni=0,pt=v,dt=0,kt=v+r*1.014):(ni=ut,pt=0,dt=vt,kt=0),it=n.createLinearGradient(ni,pt,dt,kt),f.setAlpha(.298039+y),it.addColorStop(0,f.getRgbaColor()),f.setAlpha(.686274+y),it.addColorStop(.48,f.getRgbaColor()),f.setAlpha(.698039+y),it.addColorStop(.49,f.getRgbaColor()),f.setAlpha(.4+y),it.addColorStop(1,f.getRgbaColor()),n.fillStyle=it,h?(n.fillRect(t*.435714,v,t*.007142,r*1.014),n.fillRect(t*.571428,v,t*.007142,r*1.014)):(n.fillRect(t*.13,i*.435714,r*1.035,i*.007142),n.fillRect(t*.13,i*.571428,r*1.035,i*.007142)),h?(p=t*.45,nt=i*.851941,b=t*.121428,tt=i*.012135,ii=(p+b)/2,ui=(nt+tt)/2):(p=t*.142857,nt=i*.45,b=t*.012135,tt=i*.121428,ii=(p+b)/2,ui=(nt+tt)/2),st=ei;if(h){for(at=(s+Math.abs(u))/(s-u)*r,o=0;o<=at;o+=tt+1)n.translate(0,-o),n.drawImage(d,p,nt),n.translate(0,o);for(ht=(l+Math.abs(u))/(s-u)*r,o=0;o<=ht;o+=tt+1){a=ei;if(ki)lt=u+o/r*(s-u),ct=yt.getEnd()-yt.getStart(),w=lt/ct,w=Math.max(Math.min(w,1),0),a=wt(yt.getColorAt(w).getRgbaColor());else if(ri)for(c=0;c<ft.length;c++)if(o>=ft[c].start&&o<ft[c].stop){a=ft[c].color;break}st.medium.getHexColor()!==a.medium.getHexColor()&&(vr(li,a),st=a),n.translate(0,-o),n.drawImage(g,p,nt),n.translate(0,o)}}else{for(at=(s+Math.abs(u))/(s-u)*r,e=-(b/2);e<=at;e+=b+1)n.translate(e,0),n.drawImage(d,p,nt),n.translate(-e,0);for(ht=(l+Math.abs(u))/(s-u)*r,e=-(b/2);e<=ht;e+=b+1){a=ei;if(ki)lt=u+e/r*(s-u),ct=yt.getEnd()-yt.getStart(),w=lt/ct,w=Math.max(Math.min(w,1),0),a=wt(yt.getColorAt(w).getRgbaColor());else if(ri)for(c=0;c<ft.length;c++)if(e>=ft[c].start&&e<ft[c].stop){a=ft[c].color;break}st.medium.getHexColor()!==a.medium.getHexColor()&&(vr(li,a),st=a),n.translate(e,0),n.drawImage(g,p,nt),n.translate(-e,0)}}},cu=function(n){n.save(),n.beginPath(),n.rect(0,0,n.canvas.width,n.canvas.height),n.closePath();var i=n.canvas.width/2,r=n.canvas.height/2,t=v.createRadialGradient(i,r,0,i,r,n.canvas.width/2);t.addColorStop(0,"#3c3c3c"),t.addColorStop(1,"#323232"),n.fillStyle=t,n.fill(),n.restore()},vr=function(n,t){var r,f,u,i;n.save(),n.beginPath(),n.rect(0,0,n.canvas.width,n.canvas.height),n.closePath(),r=n.canvas.width/2,f=n.canvas.height/2,u=h?n.canvas.width/2:n.canvas.height/2,i=v.createRadialGradient(r,f,0,r,f,u),i.addColorStop(0,t.light.getRgbaColor()),i.addColorStop(1,t.dark.getRgbaColor()),n.fillStyle=i,n.fill(),n.restore()};return this.setValue=function(n){var t=n<u?u:n>s?s:n;l!==t&&(l=t,l>lt&&(lt=l),l<at&&(at=l),l<st||it?l<st&&(it=!1,vi(it),er&&oi.pause()):(it=!0,vi(it),er&&oi.play()),this.repaint())},this.getValue=function(){return l},this.setValueAnimated=function(n){var i=n<u?u:n>s?s:n,t;l!==i&&(undefined!==dt&&dt.playing&&dt.stop(),dt=new Tween({},"",Tween.regularEaseInOut,l,i,1),t=this,dt.onMotionChanged=function(n){l=n.target._pos,l<st||it?l<st&&(it=!1,vi(it)):(it=!0,vi(it)),l>lt&&(lt=l),l<at&&(at=l),t.repaint()},dt.start())},this.resetMinMeasuredValue=function(){at=l,this.repaint()},this.resetMaxMeasuredValue=function(){lt=l,this.repaint()},this.setMinMeasuredValueVisible=function(n){lr=n,this.repaint()},this.setMaxMeasuredValueVisible=function(n){fr=n,this.repaint()},this.setThresholdVisible=function(n){wr=n,this.repaint()},this.setLcdDecimals=function(n){pr=n,this.repaint()},this.setFrameDesign=function(n){yi({frame:!0}),dr=n,b({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){yi({background:!0}),k=n,b({background:!0}),this.repaint()},this.setValueColor=function(n){yi({bargraphled:!0}),ei=n,b({bargraphled:!0}),this.repaint()},this.setLedColor=function(n){yi({led:!0}),si=n,b({led:!0}),this.repaint()},this.setLcdColor=function(n){kt=n,b({background:!0}),this.repaint()},this.setSection=function(n){bt=n,b(),this.repaint()},this.setSectionActive=function(n){lu=n,b(),this.repaint()},this.setGradient=function(n){yt=n,b(),this.repaint()},this.setGradientActive=function(n){useGradient=n,b(),this.repaint()},this.setMaxMeasuredValue=function(n){var t=n<u?u:n>s?s:n;lt!==t&&(lt=t,this.repaint())},this.setMinMeasuredValue=function(n){var t=n<u?u:n>s?s:n;at!==t&&(at=t,this.repaint())},this.setTitleString=function(n){ar=n,b({background:!0}),this.repaint()},this.setUnitString=function(n){rr=n,b({background:!0}),this.repaint()},this.setMinValue=function(n){u=n,b({background:!0,foreground:!0,pointer:!0}),this.repaint()},this.getMinValue=function(){return u},this.setMaxValue=function(n){s!==n&&(s=n,b({background:!0,foreground:!0,pointer:!0}),this.repaint())},this.getMaxValue=function(){return s},this.setThreshold=function(n){var t=n<u?u:n>s?s:n;st!==t&&(st=t,b({background:!0}),this.repaint())},this.repaint=function(){tu||b({frame:!0,background:!0,led:!0,pointer:!0,foreground:!0,bargraphled:!0}),v.clearRect(0,0,v.canvas.width,v.canvas.height),kr&&v.drawImage(fi,0,0),or&&v.drawImage(hi,0,0),gi&&nf(v,l,h),br&&(l<st&&(it=!1,gt=pt),v.drawImage(gt,ur,sr));var n,i,t;lr&&(h?(n=e*.856796-e*.728155*(at/(s-u)),i=f*.34-ci.width,t=n-ci.height/2):(n=(f*.856796-f*.12864)*at/(s-u),i=f*.142857-ci.height/2+n,t=e*.65),v.drawImage(ci,i,t)),fr&&(h?(n=e*.856796-e*.728155*(lt/(s-u)),i=f*.34-ii.width,t=n-ii.height/2):(n=(f*.856796-f*.12864)*lt/(s-u),i=f*.142857-ii.height/2+n,t=e*.65),v.drawImage(ii,i,t)),v.save(),vu(v,f,e),v.restore(),fu&&v.drawImage(ui,0,0)},this.repaint(),this},dr=function(n,i){i=i||{};var b=undefined===i.width?128:i.width,y=undefined===i.height?48:i.height,w=undefined===i.lcdColor?steelseries.LcdColor.STANDARD:i.lcdColor,pt=undefined===i.lcdDecimals?2:i.lcdDecimals,lt=undefined===i.unitString?"":i.unitString,at=undefined===i.unitStringVisible?!1:i.unitStringVisible,ht=undefined===i.digitalFont?!1:i.digitalFont,bt=undefined===i.valuesNumeric?!0:i.valuesNumeric,a=undefined===i.value?0:i.value,ct=undefined===i.autoScroll?!1:i.autoScroll,f=undefined===i.section?null:i.section,c=!1,e=0,g,wt=this,u=r.getElementById(n).getContext("2d");u.save(),u.clearRect(0,0,u.canvas.width,u.canvas.height),u.canvas.width=b,u.canvas.height=y;var o=b,h=y,l=0,v=Math.floor(h/1.5),ot=v+"px sans-serif",it=v+"px "+tt,nt=!1,rt,et=[],ut=[],kt=function(n,t){var r,i;u.save(),u.textAlign="right",u.strokeStyle=t,u.fillStyle=t,u.beginPath(),u.rect(2,0,o-4,h),u.closePath(),u.clip(),u.font=ht?it:ot,bt?(r=0,l=0,at&&(u.font=Math.floor(h/2.5)+"px sans-serif",r=u.measureText(lt).width),u.font=ht?it:ot,i=n.toFixed(pt),l=u.measureText(i).width,u.fillText(i,o-r-4-e,h*.5+v*.38),at&&(u.font=Math.floor(h/2.5)+"px sans-serif",u.fillText(lt,o-2-e,h*.5+v*.38))):(l=u.measureText(n).width,ct&&l>o-4?c||(e=o-l-o*.2,c=!0,clearTimeout(g),g=setTimeout(d,200)):ct&&l<=o-4&&(e=0,c=!1),u.fillText(n,o-2-e,h*.5+v*.38)),u.restore()},dt=function(n,i,r,u){var it=t(n,i),f=it.getContext("2d");f.save();var pt=0,d=0,kt=n,et=i,rt=Math.min(n,i)*.095,h=f.createLinearGradient(0,d,0,d+et);h.addColorStop(0,"#4c4c4c"),h.addColorStop(.08,"#666666"),h.addColorStop(.92,"#666666"),h.addColorStop(1,"#e6e6e6"),f.fillStyle=h,s(f,pt,d,kt,et,rt),f.fill(),f.restore(),f.save();var nt=ft(r),e=st(nt[0],nt[1],nt[2]),g=ft(u.gradientStartColor),ht=st(g[0],g[1],g[2]),tt=ft(u.gradientFraction1Color),lt=st(tt[0],tt[1],tt[2]),a=ft(u.gradientFraction2Color),ct=st(a[0],a[1],a[2]),v=ft(u.gradientFraction3Color),wt=st(v[0],v[1],v[2]),c=ft(u.gradientStopColor),yt=st(c[0],c[1],c[2]),l=vt(e[0],e[1],ht[2]-.31),y=vt(e[0],e[1],lt[2]-.31),k=vt(e[0],e[1],ct[2]-.31),p=vt(e[0],e[1],wt[2]-.31),w=vt(e[0],e[1],yt[2]-.31),bt=1,b=1,ot=n-2,ut=i-2,at=rt-1,o=f.createLinearGradient(0,b,0,b+ut);return o.addColorStop(0,"rgb("+l[0]+", "+l[1]+", "+l[2]+")"),o.addColorStop(.03,"rgb("+y[0]+","+y[1]+","+y[2]+")"),o.addColorStop(.49,"rgb("+k[0]+","+k[1]+","+k[2]+")"),o.addColorStop(.5,"rgb("+p[0]+","+p[1]+","+p[2]+")"),o.addColorStop(1,"rgb("+w[0]+","+w[1]+","+w[2]+")"),f.fillStyle=o,s(f,bt,b,ot,ut,at),f.fill(),f.restore(),it},yt=function(n){var i=ft(n),r=st(i[0],i[1],i[2]),t=vt(r[0],.57,.83);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},d=function(){c?(e>o&&(e=-l),e+=2,g=setTimeout(d,60)):e=0,wt.repaint()},k=function(){var n;nt=!0,rt=p(b,y,w);if(null!==f&&0<f.length)for(n=0;n<f.length;n++)et[n]=dt(b,y,f[n].color,w),ut[n]=yt(f[n].color)};return this.setValue=function(n){a!==n&&(a=n,this.repaint())},this.setLcdColor=function(n){w=n,k(),this.repaint()},this.setSection=function(n){f=n,resetBuffers({foreground:!0}),k({background:!0,foreground:!0}),this.repaint()},this.setScrolling=function(n){if(n){if(c)return;c=n,d()}else c=n},this.repaint=function(){nt||k(),u.clearRect(0,0,u.canvas.width,u.canvas.height);var t=rt,i=w.textColor,n;if(null!==f&&0<f.length)for(n=0;n<f.length;n++)if(a>=f[n].start&&a<=f[n].stop){t=et[n],i=ut[n];break}u.drawImage(t,0,0),kt(a,i)},this.repaint(),this},wr=function(n,t){t=t||{};var h=undefined===t.width?128:t.width,c=undefined===t.height?64:t.height,f=undefined===t.lcdColor?steelseries.LcdColor.STANDARD:t.lcdColor,k=undefined===t.lcdDecimals?2:t.lcdDecimals,d=undefined===t.unitString?"":t.unitString,b=undefined===t.unitStringVisible?!1:t.unitStringVisible,w=undefined===t.digitalFont?!1:t.digitalFont,ut=undefined===t.valuesNumeric?!0:t.valuesNumeric,o=undefined===t.value?0:t.value,s=0,i=r.getElementById(n).getContext("2d");i.save(),i.clearRect(0,0,i.canvas.width,i.canvas.height),i.canvas.width=h,i.canvas.height=c;var e=h,u=c,g=Math.floor(u/1.875)+"px sans-serif",it=Math.floor(u/1.875)+"px "+tt,y=Math.floor(u/3.5)+"px sans-serif",rt=Math.floor(u/3.5)+"px "+tt,v=!1,l,nt=function(n){var t,o,r;i.save(),i.textAlign="right",i.textBaseline="middle",i.strokeStyle=f.textColor,i.fillStyle=f.textColor,(f===steelseries.LcdColor.STANDARD||f===steelseries.LcdColor.STANDARD_GREEN)&&(i.shadowColor="gray",i.shadowOffsetX=u*.05,i.shadowOffsetY=u*.05,i.shadowBlur=u*.06),ut?(i.font=Math.floor(u/2.5)+"px sans-serif",t=0,b&&(i.font=Math.floor(u/2.5)+"px sans-serif",t=i.measureText(d).width),i.font=w?it:g,o=n.toFixed(k),i.fillText(o,e-t-4,u*.38),b&&(i.font=Math.floor(u/3)+"px sans-serif",i.fillText(d,e-2,u*.46)),r=s.toFixed(k),i.font=w?rt:y,i.textAlign="center",i.fillText(r,e/2,u*.8)):(i.font=Math.floor(u/2.5)+"px sans-serif",i.fillText(n,e-2,u*.38),i.font=y,i.textAlign="center",i.fillText(s,e/2,u*.8)),i.restore()},a=function(){v=!0,l=p(h,c,f)};return this.setValue=function(n){(o!==n||s!==n)&&(s=o,o=n,this.repaint())},this.setLcdColor=function(n){f=n,a(),this.repaint()},this.repaint=function(){v||a(),i.clearRect(0,0,i.canvas.width,i.canvas.height),i.drawImage(l,0,0),nt(o)},this.repaint(),this},br=function(n,u){u=u||{};var c=undefined===u.size?200:u.size,st=undefined===u.decimalsVisible?!1:u.decimalsVisible,gt=undefined===u.textOrientationFixed?!1:u.textOrientationFixed,bt=undefined===u.frameDesign?steelseries.FrameDesign.METAL:u.frameDesign,at=undefined===u.frameVisible?!0:u.frameVisible,p=undefined===u.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:u.backgroundColor,vt=undefined===u.backgroundVisible?!0:u.backgroundVisible,b=undefined===u.pointerColor?steelseries.ColorDef.RED:u.pointerColor,lt=undefined===u.foregroundType?steelseries.ForegroundType.TYPE1:u.foregroundType,yt=undefined===u.foregroundVisible?!0:u.foregroundVisible,d,e=0,k=0,h=0,kt=Math.PI/180,ot=this.value,wt=st?1:0,s=r.getElementById(n).getContext("2d");s.save(),s.clearRect(0,0,s.canvas.width,s.canvas.height),s.canvas.width=c,s.canvas.height=c;var f=c,o=c,l=f/2,a=o/2,pt=!1,g=t(c,c),ut=g.getContext("2d"),nt=t(c,c),et=nt.getContext("2d"),rt=t(c,c),ht=rt.getContext("2d"),tt=t(c,c),ct=tt.getContext("2d"),ni=function(n){n.textAlign="center",n.textBaseline="middle",n.save(),n.strokeStyle=p.labelColor.getRgbaColor(),n.fillStyle=p.labelColor.getRgbaColor(),n.translate(l,a);var r,t;for(i=0;360>i;i++){n.strokeStyle=p.labelColor.getRgbaColor(),n.lineWidth=.5,n.beginPath(),n.moveTo(f*.38,0),n.lineTo(f*.37,0),n.closePath(),n.stroke(),0==i%5&&(n.strokeStyle=p.labelColor.getRgbaColor(),n.lineWidth=1,n.beginPath(),n.moveTo(f*.38,0),n.lineTo(f*.36,0),n.closePath(),n.stroke()),0==i%45&&(n.strokeStyle=p.labelColor.getRgbaColor(),n.lineWidth=1,n.beginPath(),n.moveTo(f*.38,0),n.lineTo(f*.34,0),n.closePath(),n.stroke()),300<f&&(r="14px sans-serif",t="12px sans-serif"),300<f||(r="12px sans-serif",t="10px sans-serif"),200<f||(r="10px sans-serif",t="8px sans-serif"),100<f||(r="8px sans-serif",t="6px sans-serif"),n.save();switch(i){case 0:n.translate(f*.31,0),n.rotate(i*Math.PI/180+Math.PI/2),n.font=r,n.fillText("0\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)+Math.PI/2),n.translate(-f*.31,0),n.translate(f*.41,0),n.rotate(i*Math.PI/180-Math.PI/2),n.font=t,n.fillText("0%",0,0,f);break;case 45:n.translate(f*.31,0),n.rotate(i*Math.PI/180+.25*Math.PI),n.font=r,n.fillText("45\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)+.25*Math.PI),n.translate(-f*.31,0),n.translate(f*.31,f*.085),n.rotate(i*Math.PI/180-.25*Math.PI),n.font=t,n.fillText("100%",0,0,f);break;case 90:n.translate(f*.31,0),n.rotate(i*Math.PI/180),n.font=r,n.fillText("90\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)),n.translate(-f*.31,0),n.translate(f*.21,0),n.rotate(i*Math.PI/180),n.font=t,n.fillText("\u221e",0,0,f);break;case 135:n.translate(f*.31,0),n.rotate(i*Math.PI/180-.25*Math.PI),n.font=r,n.fillText("45\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)-.25*Math.PI),n.translate(-f*.31,0),n.translate(f*.31,-f*.085),n.rotate(i*Math.PI/180+.25*Math.PI),n.font=t,n.fillText("100%",0,0,f);break;case 180:n.translate(f*.31,0),n.rotate(i*Math.PI/180-Math.PI/2),n.font=r,n.fillText("0\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)-Math.PI/2),n.translate(-f*.31,0),n.translate(f*.41,0),n.rotate(i*Math.PI/180+Math.PI/2),n.font=t,n.fillText("0%",0,0,f),n.translate(-f*.41,0);break;case 225:n.translate(f*.31,0),n.rotate(i*Math.PI/180-.75*Math.PI),n.font=r,n.fillText("45\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)-.75*Math.PI),n.translate(-f*.31,0),n.translate(f*.31,f*.085),n.rotate(i*Math.PI/180+.75*Math.PI),n.font=t,n.fillText("100%",0,0,f);break;case 270:n.translate(f*.31,0),n.rotate(i*Math.PI/180-Math.PI),n.font=r,n.fillText("90\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)-Math.PI),n.translate(-f*.31,0),n.translate(f*.21,0),n.rotate(i*Math.PI/180-Math.PI),n.font=t,n.fillText("\u221e",0,0,f);break;case 315:n.translate(f*.31,0),n.rotate(i*Math.PI/180-1.25*Math.PI),n.font=r,n.fillText("45\u00b0",0,0,f),n.rotate(-(i*Math.PI/180)-1.25*Math.PI),n.translate(-f*.31,0),n.translate(f*.31,-f*.085),n.rotate(i*Math.PI/180+1.25*Math.PI),n.font=t,n.fillText("100%",0,0,f)}n.restore(),n.rotate(kt)}n.translate(-l,-a),n.restore()},ti=function(n){n.save(),n.strokeStyle=p.labelColor.getRgbaColor(),n.fillStyle=p.labelColor.getRgbaColor(),n.save(),n.beginPath(),n.moveTo(f*.200934,o*.434579),n.lineTo(f*.163551,o*.434579),n.lineTo(f*.163551,o*.560747),n.lineTo(f*.200934,o*.560747),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.stroke(),n.save(),n.beginPath(),n.moveTo(f*.163551,o*.471962),n.lineTo(f*.205607,o*.5),n.lineTo(f*.163551,o*.523364),n.lineTo(f*.163551,o*.471962),n.closePath(),n.fill(),n.save(),n.beginPath(),n.moveTo(f*.799065,o*.434579),n.lineTo(f*.836448,o*.434579),n.lineTo(f*.836448,o*.560747),n.lineTo(f*.799065,o*.560747),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.stroke(),n.save(),n.beginPath(),n.moveTo(f*.836448,o*.471962),n.lineTo(f*.794392,o*.5),n.lineTo(f*.836448,o*.523364),n.lineTo(f*.836448,o*.471962),n.closePath(),n.fill(),n.restore()},ii=function(n){var u;n.save(),n.save(),n.beginPath(),n.moveTo(f*.523364,o*.350467),n.lineTo(f*.5,o*.130841),n.lineTo(f*.476635,o*.350467),n.bezierCurveTo(f*.476635,o*.350467,f*.490654,o*.345794,f*.5,o*.345794),n.bezierCurveTo(f*.509345,o*.345794,f*.523364,o*.350467,f*.523364,o*.350467),n.closePath();var t=n.createLinearGradient(0,.154205*o,0,.350466*o),r=b.dark,i=b.light;r.setAlpha(.70588),i.setAlpha(.70588),t.addColorStop(0,r.getRgbaColor()),t.addColorStop(.3,i.getRgbaColor()),t.addColorStop(.59,i.getRgbaColor()),t.addColorStop(1,r.getRgbaColor()),n.fillStyle=t,u=b.light.getRgbaColor(),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.strokeStyle=u,n.fill(),n.stroke(),r.setAlpha(1),i.setAlpha(1),n.restore()},dt=function(n){var i,t,u,s,r,e;n.save(),i=b.dark,t=b.light,i.setAlpha(.70588),t.setAlpha(.70588),n.save(),n.beginPath(),n.moveTo(f*.285046,o*.514018),n.lineTo(f*.21028,o*.5),n.lineTo(f*.285046,o*.481308),n.bezierCurveTo(f*.285046,o*.481308,f*.280373,o*.490654,f*.280373,o*.495327),n.bezierCurveTo(f*.280373,o*.504672,f*.285046,o*.514018,f*.285046,o*.514018),n.closePath(),u=n.createLinearGradient(.224299*f,0,.289719*f,0),u.addColorStop(0,i.getRgbaColor()),u.addColorStop(.3,t.getRgbaColor()),u.addColorStop(.59,t.getRgbaColor()),u.addColorStop(1,i.getRgbaColor()),n.fillStyle=u,s=b.light.getRgbaColor(),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.strokeStyle=s,n.fill(),n.stroke(),n.save(),n.beginPath(),n.moveTo(f*.714953,o*.514018),n.lineTo(f*.789719,o*.5),n.lineTo(f*.714953,o*.481308),n.bezierCurveTo(f*.714953,o*.481308,f*.719626,o*.490654,f*.719626,o*.495327),n.bezierCurveTo(f*.719626,o*.504672,f*.714953,o*.514018,f*.714953,o*.514018),n.closePath(),r=n.createLinearGradient(.7757*f,0,.71028*f,0),r.addColorStop(0,i.getRgbaColor()),r.addColorStop(.3,t.getRgbaColor()),r.addColorStop(.59,t.getRgbaColor()),r.addColorStop(1,i.getRgbaColor()),n.fillStyle=r,e=b.light.getRgbaColor(),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.strokeStyle=e,n.fill(),n.stroke(),i.setAlpha(1),t.setAlpha(1),n.restore()},it=function(){pt=!0,at&&v(ut,bt,l,a,f,o),vt&&(w(ut,p,l,a,f,o),ni(ut)),ti(et),ii(et),dt(ht),yt&&y(ct,lt,f,o,!1)},ft=function(){g.width=c,g.height=c,ut=g.getContext("2d"),nt.width=c,nt.height=c,et=nt.getContext("2d"),rt.width=c,rt.height=c,ht=rt.getContext("2d"),tt.width=c,tt.height=c,ct=tt.getContext("2d")};return this.setValue=function(n){var t;t=0>n?360+n:n,t=359.9<n?n-360:n,e!==t&&(e=t,k=2*(Math.abs(e)*10%10),10<k&&(k-=20),0===e&&(h=90),0<e&&90>=e&&(h=90-e),90<e&&180>=e&&(h=e-90),180<e&&270>=e&&(h=270-e),270<e&&360>=e&&(h=e-270),0>e&&e>=-90&&(h=90-Math.abs(e)),e<-90&&e>=-180&&(h=Math.abs(e)-90),e<-180&&e>=-270&&(h=270-Math.abs(e)),e<-270&&e>=-360&&(h=Math.abs(e)-270),this.repaint())},this.getValue=function(){return e},this.setValueAnimated=function(n){360-n+e<n-e&&(n=360-n);if(e!==n){undefined!==d&&d.playing&&d.stop(),d=new Tween({},"",Tween.regularEaseInOut,e,n,1);var t=this;d.onMotionChanged=function(n){e=n.target._pos,k=2*(Math.abs(e)*10%10),10<k&&(k-=20),0===e&&(h=90),0<e&&90>=e&&(h=90-e),90<e&&180>=e&&(h=e-90),180<e&&270>=e&&(h=270-e),270<e&&360>=e&&(h=e-270),0>e&&e>=-90&&(h=90-Math.abs(e)),e<-90&&e>=-180&&(h=Math.abs(e)-90),e<-180&&e>=-270&&(h=270-Math.abs(e)),e<-270&&e>=-360&&(h=Math.abs(e)-270),t.repaint()},d.start()}},this.setFrameDesign=function(n){ft(),bt=n,it(),this.repaint()},this.setBackgroundColor=function(n){ft(),p=n,it(),this.repaint()},this.setForegroundType=function(n){ft(),lt=n,it(),this.repaint()},this.setPointerColor=function(n){ft(),b=n,it(),this.repaint()},this.repaint=function(){pt||it(),s.save(),s.clearRect(0,0,s.canvas.width,s.canvas.height),(at||vt)&&s.drawImage(g,0,0),ot=Math.PI/2+e*kt-Math.PI/2,s.save(),s.translate(l,a),s.rotate(ot),s.translate(-l,-a),s.drawImage(nt,0,0),s.fillStyle=p.labelColor.getRgbaColor(),s.textAlign="center",s.textBaseline="middle",gt?(s.restore(),s.font=st?f*.1+"px sans-serif":f*.15+"px sans-serif",s.fillText(h.toFixed(wt)+"\u00b0",l,a,f*.35)):(s.font=st?f*.15+"px sans-serif":f*.2+"px sans-serif",s.fillText(h.toFixed(wt)+"\u00b0",l,a,f*.35),s.restore()),s.translate(l,a),s.rotate(ot+k*Math.PI/180),s.translate(-l,-a),s.drawImage(rt,0,0),s.restore(),yt&&s.drawImage(tt,0,0),s.restore()},this.repaint(),this},gr=function(n,u){u=u||{};var o=undefined===u.size?200:u.size,pt=undefined===u.frameDesign?steelseries.FrameDesign.METAL:u.frameDesign,ct=undefined===u.frameVisible?!0:u.frameVisible,tt=undefined===u.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:u.backgroundColor,dt=undefined===u.backgroundVisible?!0:u.backgroundVisible,bt=undefined===u.pointerType?steelseries.PointerType.TYPE2:u.pointerType,h=undefined===u.pointerColor?steelseries.ColorDef.RED:u.pointerColor,ei=undefined===u.knobType?steelseries.KnobType.STANDARD_KNOB:u.knobType,ui=undefined===u.knobStyle?steelseries.KnobStyle.SILVER:u.knobStyle,gt=undefined===u.foregroundType?steelseries.ForegroundType.TYPE1:u.foregroundType,at=undefined===u.foregroundVisible?!0:u.foregroundVisible,c=undefined===u.pointSymbols?["N","NE","E","SE","S","SW","W","NW"]:u.pointSymbols,fi=undefined===u.customLayer?null:u.customLayer,ii=undefined===u.degreeScale?!1:u.degreeScale,yt=undefined===u.roseVisible?!0:u.roseVisible,d,p=0,st=Math.PI/180,ht=this.value,s=r.getElementById(n).getContext("2d");s.save(),s.clearRect(0,0,s.canvas.width,s.canvas.height),s.canvas.width=o,s.canvas.height=o;var f=o,e=o,a=f/2,l=e/2,kt=!1,ft=t(o,o),g=ft.getContext("2d"),ut=t(o,o),ti=ut.getContext("2d"),it=t(o,o),lt=it.getContext("2d"),ot=t(o,o),b=ot.getContext("2d"),et=t(o,o),vt=et.getContext("2d"),ri=function(n){var t,r,u;n.textAlign="center",n.textBaseline="middle",n.save(),n.strokeStyle=tt.labelColor.getRgbaColor(),n.fillStyle=tt.labelColor.getRgbaColor(),n.translate(a,l);if(ii)for(t=.08*f+"px serif",r=f*.033+"px serif",n.rotate(st*10),i=10;360>=i;i+=10){n.save();switch(i){case 360:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[2],0,0,f),n.translate(-f*.35,0);break;case 90:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[4],0,0,f),n.translate(-f*.35,0);break;case 180:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[6],0,0,f),n.translate(-f*.35,0);break;case 270:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[0],0,0,f),n.translate(-f*.35,0);break;default:u=(i+90)%360,n.translate(f*.37,0),n.rotate(Math.PI/2),n.font=r,n.fillText("0".substring(u>=100)+u,0,0,f),n.translate(-f*.37,0)}n.restore(),n.rotate(st*10)}else for(t=.12*f+"px serif",r=.06*f+"px serif",i=0;360>i;i+=2.5){0==i%5&&(n.lineWidth=1,n.beginPath(),n.moveTo(f*.38,0),n.lineTo(f*.36,0),n.closePath(),n.stroke()),n.save();switch(i){case 0:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[2],0,0,f),n.translate(-f*.35,0);break;case 45:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=r,n.fillText(c[3],0,0,f),n.translate(-f*.29,0);break;case 90:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[4],0,0,f),n.translate(-f*.35,0);break;case 135:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=r,n.fillText(c[5],0,0,f),n.translate(-f*.29,0);break;case 180:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[6],0,0,f),n.translate(-f*.35,0);break;case 225:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=r,n.fillText(c[7],0,0,f),n.translate(-f*.29,0);break;case 270:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(c[0],0,0,f),n.translate(-f*.35,0);break;case 315:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=r,n.fillText(c[1],0,0,f),n.translate(-f*.29,0)}n.restore(),yt&&(0===i||22.5===i||45===i||67.5===i||90===i||112.5===i||135===i||157.5===i||180===i||202.5===i||225===i||247.5===i||270===i||292.5===i||315===i||337.5===i||360===i)&&(n.save(),n.beginPath(),i%45?n.moveTo(f*.29,0):n.moveTo(f*.38,0),n.lineTo(f*.1,0),n.closePath(),n.restore(),n.lineWidth=1,n.strokeStyle=tt.symbolColor.getRgbaColor(),n.stroke()),n.rotate(st*2.5)}n.translate(-a,-l),n.restore()},wt=function(n,t){var o,s,l,i,r,u,c;n.save(),t&&(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)");switch(bt.type){case"type2":n.beginPath(),n.moveTo(f*.53271,e*.453271),n.bezierCurveTo(f*.53271,e*.453271,f*.5,e*.149532,f*.5,e*.149532),n.bezierCurveTo(f*.5,e*.149532,f*.467289,e*.453271,f*.467289,e*.453271),n.bezierCurveTo(f*.453271,e*.462616,f*.443925,e*.481308,f*.443925,e*.5),n.bezierCurveTo(f*.443925,e*.5,f*.556074,e*.5,f*.556074,e*.5),n.bezierCurveTo(f*.556074,e*.481308,f*.546728,e*.462616,f*.53271,e*.453271),n.closePath(),t||(o=n.createLinearGradient(.471962*f,0,.528036*f,0),o.addColorStop(0,h.light.getRgbaColor()),o.addColorStop(.46,h.light.getRgbaColor()),o.addColorStop(.47,h.medium.getRgbaColor()),o.addColorStop(1,h.medium.getRgbaColor()),n.fillStyle=o,n.strokeStyle=h.dark.getRgbaColor()),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.fill(),n.stroke(),n.beginPath(),n.moveTo(f*.467289,e*.546728),n.bezierCurveTo(f*.467289,e*.546728,f*.5,e*.850467,f*.5,e*.850467),n.bezierCurveTo(f*.5,e*.850467,f*.53271,e*.546728,f*.53271,e*.546728),n.bezierCurveTo(f*.546728,e*.537383,f*.556074,e*.518691,f*.556074,e*.5),n.bezierCurveTo(f*.556074,e*.5,f*.443925,e*.5,f*.443925,e*.5),n.bezierCurveTo(f*.443925,e*.518691,f*.453271,e*.537383,f*.467289,e*.546728),n.closePath(),t||(s=n.createLinearGradient(.471962*f,0,.528036*f,0),s.addColorStop(0,"#e3e5e8"),s.addColorStop(.48,"#e3e5e8"),s.addColorStop(.48,"#abb1b8"),s.addColorStop(1,"#abb1b8"),n.fillStyle=s,l="#abb1b8",n.strokeStyle=l),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.fill(),n.stroke();break;case"type3":n.beginPath(),n.moveTo(f*.5,e*.149532),n.bezierCurveTo(f*.5,e*.149532,f*.443925,e*.490654,f*.443925,e*.5),n.bezierCurveTo(f*.443925,e*.53271,f*.467289,e*.556074,f*.5,e*.556074),n.bezierCurveTo(f*.53271,e*.556074,f*.556074,e*.53271,f*.556074,e*.5),n.bezierCurveTo(f*.556074,e*.490654,f*.5,e*.149532,f*.5,e*.149532),n.closePath(),t||(i=n.createLinearGradient(.471962*f,0,.528036*f,0),i.addColorStop(0,h.light.getRgbaColor()),i.addColorStop(.46,h.light.getRgbaColor()),i.addColorStop(.47,h.medium.getRgbaColor()),i.addColorStop(1,h.medium.getRgbaColor()),n.fillStyle=i,n.strokeStyle=h.dark.getRgbaColor()),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.fill(),n.stroke();break;case"type1:":default:n.beginPath(),n.moveTo(f*.5,e*.495327),n.lineTo(f*.528037,e*.495327),n.lineTo(f*.5,e*.149532),n.lineTo(f*.471962,e*.495327),n.lineTo(f*.5,e*.495327),n.closePath(),t||(r=n.createLinearGradient(.471962*f,0,.528036*f,0),r.addColorStop(0,h.light.getRgbaColor()),r.addColorStop(.46,h.light.getRgbaColor()),r.addColorStop(.47,h.medium.getRgbaColor()),r.addColorStop(1,h.medium.getRgbaColor()),n.fillStyle=r,n.strokeStyle=h.dark.getRgbaColor()),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.fill(),n.stroke(),n.beginPath(),n.moveTo(f*.5,e*.504672),n.lineTo(f*.471962,e*.504672),n.lineTo(f*.5,e*.850467),n.lineTo(f*.528037,e*.504672),n.lineTo(f*.5,e*.504672),n.closePath(),t||(u=n.createLinearGradient(.471962*f,0,.528036*f,0),u.addColorStop(0,"#e3e5e8"),u.addColorStop(.48,"#e3e5e8"),u.addColorStop(.480099,"#abb1b8"),u.addColorStop(1,"#abb1b8"),n.fillStyle=u,c="#abb1b8",n.strokeStyle=c),n.lineWidth=1,n.lineCap="square",n.lineJoin="miter",n.fill(),n.stroke()}t&&blur(n,f,e,Math.floor(f*.006)),n.restore()},k=function(){kt=!0,ct&&v(g,pt,a,l,f,e),dt&&(w(g,tt,a,l,f,e),rt(g,fi,a,l,f,e),yt&&hi(g,a,l,f,e,tt),ri(g)),wt(ti,!1),wt(lt,!0),at&&y(vt,gt,f,e,!0,ei,ui)},nt=function(){ft.width=o,ft.height=o,g=ft.getContext("2d"),ut.width=o,ut.height=o,ti=ut.getContext("2d"),it.width=o,it.height=o,lt=it.getContext("2d"),ot.width=o,ot.height=o,b=ot.getContext("2d"),et.width=o,et.height=o,vt=et.getContext("2d")};return this.setValue=function(n){n=n%360,p!==n&&(p=n,this.repaint())},this.getValue=function(){return p},this.setValueAnimated=function(n){var i=n%360,r=this,t;p!==i&&(undefined!==d&&d.playing&&d.stop(),t=ni(p,i),d=new Tween({},"",Tween.elasticEaseOut,p,p+t,2),d.onMotionChanged=function(n){p=n.target._pos%360,r.repaint()},d.start())},this.setFrameDesign=function(n){nt(),pt=n,k(),this.repaint()},this.setBackgroundColor=function(n){nt(),tt=n,k(),this.repaint()},this.setForegroundType=function(n){nt(),gt=n,k(),this.repaint()},this.setPointerColor=function(n){nt(),h=n,k(),this.repaint()},this.setPointerType=function(n){nt(),bt=n,k(),this.repaint()},this.setPointSymbols=function(n){nt(),c=n,k(),this.repaint()},this.repaint=function(){kt||k(),s.clearRect(0,0,s.canvas.width,s.canvas.height),(ct||dt)&&s.drawImage(ft,0,0),ht=Math.PI/2+p*st-Math.PI/2;var n=f*.006;b.clearRect(0,0,f,e),b.save(),b.translate(a,l),b.rotate(ht),b.translate(-a,-l),b.drawImage(it,0,0),b.restore(),s.drawImage(ot,0,0,f,e,n,n,f+n,e+n),s.save(),s.translate(a,l),s.rotate(ht),s.translate(-a,-l),s.drawImage(ut,0,0),s.restore(),at&&s.drawImage(et,0,0),s.restore()},this.repaint(),this},iu=function(n,u){u=u||{};var o=undefined===u.size?200:u.size,pi=undefined===u.frameDesign?steelseries.FrameDesign.METAL:u.frameDesign,di=undefined===u.frameVisible?!0:u.frameVisible,k=undefined===u.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:u.backgroundColor,gi=undefined===u.backgroundVisible?!0:u.backgroundVisible,dt=undefined===u.pointerTypeLatest?steelseries.PointerType.TYPE1:u.pointerTypeLatest,ri=undefined===u.pointerTypeAverage?steelseries.PointerType.TYPE8:u.pointerTypeAverage,kt=undefined===u.pointerColor?steelseries.ColorDef.RED:u.pointerColor,ii=undefined===u.pointerColorAverage?steelseries.ColorDef.BLUE:u.pointerColorAverage,kr=undefined===u.knobType?steelseries.KnobType.STANDARD_KNOB:u.knobType,dr=undefined===u.knobStyle?steelseries.KnobStyle.SILVER:u.knobStyle,si=undefined===u.foregroundType?steelseries.ForegroundType.TYPE1:u.foregroundType,ir=undefined===u.foregroundVisible?!0:u.foregroundVisible,a=undefined===u.pointSymbols?["N","NE","E","SE","S","SW","W","NW"]:u.pointSymbols,vr=undefined===u.customLayer?null:u.customLayer,wr=undefined===u.degreeScale?!0:u.degreeScale,nr=undefined===u.roseVisible?!1:u.roseVisible,ht=undefined===u.lcdColor?steelseries.LcdColor.STANDARD:u.lcdColor,fr=undefined===u.lcdVisible?!0:u.lcdVisible,pr=undefined===u.digitalFont?!1:u.digitalFont,et=undefined===u.section?null:u.section,ft=undefined===u.area?null:u.area,ti=undefined===u.lcdTitleStrings?["Latest","Average"]:u.lcdTitleStrings,rr=undefined===u.titleString?"":u.titleString,tr=undefined===u.useColorLabels?!1:u.useColorLabels,ut,st,it=0,g=0,ct=Math.PI/180,ei=this.valueLatest,fi=this.valueAverage,ar=-Math.PI/2,bi=Math.PI*2,ur=360,e=r.getElementById(n).getContext("2d");e.save(),e.clearRect(0,0,e.canvas.width,e.canvas.height),e.canvas.width=o,e.canvas.height=o;var f=o,s=o,c=f/2,h=s/2,ui=Math.floor(f/10),br=ui+"px sans-serif",gr=ui+"px "+tt,gt=f*.3,er=s*.12,hr=(f-gt)/2,or=s*.32,cr=s*.565,sr=!1,bt=t(o,o),d=bt.getContext("2d"),oi,at=t(o,o),ci=at.getContext("2d"),vt=t(o,o),li=vt.getContext("2d"),yt=t(o,o),ai=yt.getContext("2d"),lt=t(o,o),ki=lt.getContext("2d"),ot=t(o,o),l=ot.getContext("2d"),wt=t(o,o),yi=wt.getContext("2d"),vi=function(n,t){e.save(),e.textAlign="center",e.strokeStyle=ht.textColor,e.fillStyle=ht.textColor,n<0&&(n+=360),n="00"+Math.round(n),n=n.substring(n.length,n.length-3),(ht===steelseries.LcdColor.STANDARD||ht===steelseries.LcdColor.STANDARD_GREEN)&&(e.shadowColor="gray",e.shadowOffsetX=f*.007,e.shadowOffsetY=f*.007,e.shadowBlur=f*.007),e.font=pr?gr:br,e.fillText(n+"\u00b0",f/2+gt*.05,(t?or:cr)+er*.5+ui*.38,gt*.9),e.restore()},wi=function(n,t,i,r,u){n.save(),n.strokeStyle=r,n.fillStyle=r,n.lineWidth=f*.035;var e=bi/ur*t,o=e+(i-t)/(ur/bi);n.translate(c,h),n.rotate(ar),n.beginPath(),u?(n.moveTo(0,0),n.arc(0,0,f*.365-n.lineWidth/2,e,o,!1)):n.arc(0,0,f*.365,e,o,!1),u?(n.moveTo(0,0),n.fill()):n.stroke(),n.translate(-c,-h),n.restore()},lr=function(n){var l,o;n.textAlign="center",n.textBaseline="middle";var s=f*.38,v=f*.35,w=f*.355,p=f*.36,e=f*.1,y=f*.31,r=f*.36,t,u;n.save(),n.strokeStyle=k.labelColor.getRgbaColor(),n.fillStyle=k.labelColor.getRgbaColor(),n.translate(c,h);if(wr)for(t=Math.floor(.1*f)+"px serif bold",u=Math.floor(f*.04)+"px sans-serif",n.rotate(ct*5),i=5;360>=i;i+=5){n.save();switch(i){case 360:n.translate(r,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[2],0,0,e),n.translate(-r,0);break;case 90:n.translate(r,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[4],0,0,e),n.translate(-r,0);break;case 180:n.translate(r,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[6],0,0,e),n.translate(-r,0);break;case 270:n.translate(r,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[0],0,0,e),n.translate(-r,0);break;case 5:case 85:case 95:case 175:case 185:case 265:case 275:case 355:break;default:(i+90)%20?(n.lineWidth=(i+90)%5?1.5:1,n.beginPath(),n.moveTo(s,0),l=(i+90)%10?p:v,n.lineTo(l,0),n.closePath(),n.stroke()):(n.lineWidth=1.5,n.beginPath(),n.moveTo(s,0),n.lineTo(v,0),n.closePath(),n.stroke(),o=(i+90)%360,n.translate(y,0),n.rotate(Math.PI/2),n.font=u,n.fillText("0".substring(o>=100)+o,0,0,e),n.translate(-y,0))}n.restore(),n.rotate(ct*5)}else for(t=.12*f+"px serif",u=.06*f+"px serif",n.lineWidth=1,n.strokeStyle=k.symbolColor.getRgbaColor(),i=0;360>i;i+=2.5){0==i%5&&(n.beginPath(),n.moveTo(f*.38,0),n.lineTo(f*.36,0),n.closePath(),n.stroke()),n.save();switch(i){case 0:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[2],0,0),n.translate(-f*.35,0);break;case 45:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=u,n.fillText(a[3],0,0),n.translate(-f*.29,0);break;case 90:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[4],0,0),n.translate(-f*.35,0);break;case 135:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=u,n.fillText(a[5],0,0),n.translate(-f*.29,0);break;case 180:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[6],0,0),n.translate(-f*.35,0);break;case 225:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=u,n.fillText(a[7],0,0),n.translate(-f*.29,0);break;case 270:n.translate(f*.35,0),n.rotate(Math.PI/2),n.font=t,n.fillText(a[0],0,0),n.translate(-f*.35,0);break;case 315:n.translate(f*.29,0),n.rotate(Math.PI/2),n.font=u,n.fillText(a[1],0,0),n.translate(-f*.29,0)}n.restore(),nr&&(0===i||22.5===i||45===i||67.5===i||90===i||112.5===i||135===i||157.5===i||180===i||202.5===i||225===i||247.5===i||270===i||292.5===i||315===i||337.5===i||360===i)&&(n.save(),n.beginPath(),i%45?n.moveTo(f*.29,0):n.moveTo(f*.38,0),n.lineTo(f*.1,0),n.closePath(),n.restore(),n.stroke()),n.rotate(ct*2.5)}n.translate(-c,-h),n.restore()},yr=function(n){ti.length>0&&(n.save(),n.textAlign="center",n.textBaseline="middle",n.fillStyle=tr?kt.medium.getRgbaColor():k.labelColor.getRgbaColor(),n.font=.04*f+"px sans-serif",n.fillText(ti[0],f/2,s*.29,f*.3),n.fillStyle=tr?ii.medium.getRgbaColor():k.labelColor.getRgbaColor(),n.fillText(ti[1],f/2,s*.71,f*.3),rr.length>0&&(n.fillStyle=k.labelColor.getRgbaColor(),n.font=.0467*f+"px sans-serif",n.fillText(rr,f/2,s*.5,f*.3)))},b=function(n){var t,i,u;n=n||{};var o=undefined===n.frame?!1:n.frame,r=undefined===n.background?!1:n.background,e=undefined===n.pointer?!1:n.pointer,l=undefined===n.foreground?!1:n.foreground;sr=!0,o&&di&&v(d,pi,c,h,f,s);if(r&&gi){w(d,k,c,h,f,s),rt(d,vr,c,h,f,s);if(null!==et&&0<et.length){t=et.length;do t--,wi(d,et[t].start,et[t].stop,et[t].color,!1);while(0<t)}if(null!==ft&&0<ft.length){i=ft.length;do i--,wi(d,ft[i].start,ft[i].stop,ft[i].color,!0);while(0<i)}lr(d)}r&&nr&&hi(d,c,h,f,s,k),r&&fr&&(oi=p(gt,er,ht),d.drawImage(oi,hr,or),d.drawImage(oi,hr,cr),yr(d)),e&&(nt(ai,f,ri,ii,k.labelColor),nt(ki,f,ri,kt,k.labelColor,!0),nt(ci,f,dt,kt,k.labelColor),nt(li,f,dt,kt,k.labelColor,!0)),l&&ir&&(u=dt.type==="type15"||dt.type==="type16"?!1:!0,y(yi,si,f,s,u,kr,dr))},pt=function(n){n=n||{};var r=undefined===n.background?!1:n.background,i=undefined===n.pointer?!1:n.pointer,t=undefined===n.foreground?!1:n.foreground;r&&(bt.width=o,bt.height=o,d=bt.getContext("2d")),i&&(at.width=o,at.height=o,ci=at.getContext("2d"),vt.width=o,vt.height=o,li=vt.getContext("2d"),yt.width=o,yt.height=o,ai=yt.getContext("2d"),lt.width=o,lt.height=o,ki=lt.getContext("2d"),ot.width=o,ot.height=o,l=ot.getContext("2d")),t&&(wt.width=o,wt.height=o,yi=wt.getContext("2d"))};return this.setValueLatest=function(n){n=n===360?360:n%360,it!==n&&(it=n,this.repaint())},this.getValueLatest=function(){return it},this.setValueAverage=function(n){n=n%360,g!==n&&(g=n,this.repaint())},this.getValueAverage=function(){return g},this.setValueAnimatedLatest=function(n){var t=n===360?360:n%360,i,r;it!==t&&(i=this,undefined!==ut&&ut.playing&&ut.stop(),r=ni(it,t),ut=new Tween({},"",Tween.regularEaseInOut,it,it+r,2.5),ut.onMotionChanged=function(n){it=n.target._pos%360,i.repaint()},t===360&&(ut.onMotionFinished=function(){it=t,i.repaint()}),ut.start())},this.setValueAnimatedAverage=function(n){var i=n===360?360:n%360,t,r;g!==n&&(t=this,undefined!==st&&st.playing&&st.stop(),r=ni(g,i),st=new Tween({},"",Tween.regularEaseInOut,g,g+r,2.5),st.onMotionChanged=function(n){g=n.target._pos%360,t.repaint()},i===360&&(ut.onMotionFinished=function(){g=i,t.repaint()}),st.start())},this.setArea=function(n){ft=n,b({background:!0}),this.repaint()},this.setSection=function(n){et=n,b({background:!0}),this.repaint()},this.setFrameDesign=function(n){pi=n,b({frame:!0,background:!0}),this.repaint()},this.setBackgroundColor=function(n){k=n,b({frame:!0,background:!0}),this.repaint()},this.setForegroundType=function(n){pt({foreground:!0}),si=n,b({foreground:!0}),this.repaint()},this.setPointerColor=function(n){pt({pointer:!0}),kt=n,b({pointer:!0}),this.repaint()},this.setPointerColorAverage=function(n){pt({pointer:!0}),ii=n,b({pointer:!0}),this.repaint()},this.setPointerType=function(n){pt({pointer:!0}),dt=n,b({pointer:!0}),this.repaint()},this.setPointerTypeAverage=function(n){pt({pointer:!0}),ri=n,b({pointer:!0}),this.repaint()},this.setPointSymbols=function(n){a=n,b({frame:!0,background:!0}),this.repaint()},this.setLcdColor=function(n){ht=n,b({background:!0}),this.repaint()},this.setLcdTitleStrings=function(n){ti=n,b({background:!0}),this.repaint()},this.repaint=function(){sr||b({frame:!0,background:!0,led:!0,pointer:!0,foreground:!0}),e.clearRect(0,0,e.canvas.width,e.canvas.height),(di||gi)&&e.drawImage(bt,0,0),fr&&(vi(it,!0),vi(g,!1)),fi=Math.PI/2+g*ct-Math.PI/2;var n=f*.006;l.clearRect(0,0,f,s),l.save(),l.translate(c,h),l.rotate(fi),l.translate(-c,-h),l.drawImage(lt,0,0),l.restore(),e.drawImage(ot,0,0,f,s,n,n,f+n,s+n),e.save(),e.translate(c,h),e.rotate(fi),e.translate(-c,-h),e.drawImage(yt,0,0),e.restore(),ei=Math.PI/2+it*ct-Math.PI/2,l.clearRect(0,0,f,s),l.save(),l.translate(c,h),l.rotate(ei),l.translate(-c,-h),l.drawImage(vt,0,0),l.restore(),e.drawImage(ot,0,0,f,s,n,n,f+n,s+n),e.save(),e.translate(c,h),e.rotate(ei),e.translate(-c,-h),e.drawImage(at,0,0),e.restore(),ir&&e.drawImage(wt,0,0)},this.repaint(),this},ru=function(n,i){i=i||{};var o=undefined===i.size?200:i.size,lt=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,vt=undefined===i.frameVisible?!0:i.frameVisible,st=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,yt=undefined===i.foregroundVisible?!0:i.foregroundVisible,nt=undefined===i.pointerColor?steelseries.ColorDef.WHITE:i.pointerColor,w,b,h=0,s=0,ot=Math.PI*o/360,et=0,g=!1,e=r.getElementById(n).getContext("2d");e.save(),e.clearRect(0,0,e.canvas.width,e.canvas.height),e.canvas.width=o,e.canvas.height=o;var u=o,f=o,l=u/2,c=f/2,ct=!1,k=t(o,o),ht=k.getContext("2d"),p=t(o,o*Math.PI),ft=p.getContext("2d"),a=t(o*.037383,o*.056074),ut=a.getContext("2d"),d=t(o,o),tt=d.getContext("2d"),pt=function(n){var t,u,i,f,h;n.save(),t=o,u=o*Math.PI,n.beginPath(),n.rect(0,0,t,u),n.closePath(),f=n.createLinearGradient(0,0,0,u),f.addColorStop(0,"#7fd5f0"),f.addColorStop(.5,"#7fd5f0"),f.addColorStop(.5,"#3c4439"),f.addColorStop(1,"#3c4439"),n.fillStyle=f,n.fill(),n.lineWidth=1;var s=u/72,e=!1,r=10;for(n.textAlign="center",n.textBaseline="middle",h=t*.04,n.font=h+"px sans-serif",n.fillStyle="#37596e",i=u/2-s;i>0;i-=s)r>90||(e?(n.fillText(r,(t-t*.2)/2-8,i,t*.375),n.fillText(r,t-(t-t*.2)/2+8,i,t*.375),n.beginPath(),n.moveTo((t-t*.2)/2,i),n.lineTo(t-(t-t*.2)/2,i),n.closePath(),r+=10):(n.beginPath(),n.moveTo((t-t*.1)/2,i),n.lineTo(t-(t-t*.1)/2,i),n.closePath()),n.stroke()),e^=!0;for(e=!1,r=10,n.strokeStyle="#FFFFFF",n.lineWidth=1.5,n.beginPath(),n.moveTo(0,u/2),n.lineTo(t,u/2),n.closePath(),n.stroke(),n.fillStyle="#FFFFFF",n.lineWidth=1,i=u/2+s;i<=u;i+=s)r>90||(e?(n.fillText(-r,(t-t*.2)/2-8,i,t*.375),n.fillText(-r,t-(t-t*.2)/2+8,i,t*.375),n.beginPath(),n.moveTo((t-t*.2)/2,i),n.lineTo(t-(t-t*.2)/2,i),n.closePath(),r+=10):(n.beginPath(),n.moveTo((t-t*.1)/2,i),n.lineTo(t-(t-t*.1)/2,i),n.closePath()),n.stroke()),e^=!0;n.restore()},wt=function(n){var i,r,t;for(n.save(),n.fillStyle=nt.light.getRgbaColor(),n.beginPath(),n.moveTo(u*.476635,f*.5),n.bezierCurveTo(u*.476635,f*.514018,u*.485981,f*.523364,u*.5,f*.523364),n.bezierCurveTo(u*.514018,f*.523364,u*.523364,f*.514018,u*.523364,f*.5),n.bezierCurveTo(u*.523364,f*.485981,u*.514018,f*.476635,u*.5,f*.476635),n.bezierCurveTo(u*.485981,f*.476635,u*.476635,f*.485981,u*.476635,f*.5),n.closePath(),n.moveTo(u*.415887,f*.504672),n.lineTo(u*.415887,f*.495327),n.bezierCurveTo(u*.415887,f*.495327,u*.467289,f*.495327,u*.467289,f*.495327),n.bezierCurveTo(u*.471962,f*.481308,u*.481308,f*.471962,u*.495327,f*.467289),n.bezierCurveTo(u*.495327,f*.467289,u*.495327,f*.415887,u*.495327,f*.415887),n.lineTo(u*.504672,f*.415887),n.bezierCurveTo(u*.504672,f*.415887,u*.504672,f*.467289,u*.504672,f*.467289),n.bezierCurveTo(u*.518691,f*.471962,u*.528037,f*.481308,u*.53271,f*.495327),n.bezierCurveTo(u*.53271,f*.495327,u*.584112,f*.495327,u*.584112,f*.495327),n.lineTo(u*.584112,f*.504672),n.bezierCurveTo(u*.584112,f*.504672,u*.53271,f*.504672,u*.53271,f*.504672),n.bezierCurveTo(u*.528037,f*.518691,u*.518691,f*.53271,u*.5,f*.53271),n.bezierCurveTo(u*.481308,f*.53271,u*.471962,f*.518691,u*.467289,f*.504672),n.bezierCurveTo(u*.467289,f*.504672,u*.415887,f*.504672,u*.415887,f*.504672),n.closePath(),n.fill(),i=5,r=Math.PI/36,n.translate(l,c),n.rotate(-Math.PI/2),n.translate(-l,-c),t=-90;t<=90;t+=i)t%45==0||t===0?(n.strokeStyle=nt.medium.getRgbaColor(),n.lineWidth=2,n.beginPath(),n.moveTo(u*.5,f*.088785),n.lineTo(u*.5,f*.113),n.closePath(),n.stroke()):t%15==0?(n.strokeStyle="#FFFFFF",n.lineWidth=1,n.beginPath(),n.moveTo(u*.5,f*.088785),n.lineTo(u*.5,f*.103785),n.closePath(),n.stroke()):(n.strokeStyle="#FFFFFF",n.lineWidth=.5,n.beginPath(),n.moveTo(u*.5,f*.088785),n.lineTo(u*.5,f*.093785),n.closePath(),n.stroke()),n.translate(l,c),n.rotate(r,l,c),n.translate(-l,-c);n.restore()},at=function(n){n.save();var i=u*.037383,t=f*.056074;n.beginPath(),n.moveTo(i*.5,0),n.lineTo(0,t),n.lineTo(i,t),n.closePath(),n.fillStyle=nt.light.getRgbaColor(),n.fill(),n.strokeStyle=nt.medium.getRgbaColor(),n.stroke(),n.restore()},it=function(){ct=!0,vt&&v(ht,lt,l,c,u,f),pt(ft),at(ut),wt(tt),yt&&y(tt,st,u,f,!0,fi,oi,ai)},rt=function(){k.width=o,k.height=o,ht=k.getContext("2d"),p.width=o,p.height=o*Math.PI,ft=p.getContext("2d"),a.width=o*.037383,a.height=o*.056074,ut=a.getContext("2d"),d.width=o,d.height=o,tt=d.getContext("2d")};return this.setRoll=function(n){n=n%360,h!==n&&(h=n,this.repaint())},this.getRoll=function(){return h},this.setRollAnimated=function(n){n=n%360;if(h!==n){var t=this;undefined!==w&&w.playing&&w.stop(),w=new Tween({},"",Tween.regularEaseInOut,h,n,1),w.onMotionChanged=function(n){h=n.target._pos,t.repaint()},w.start()}},this.setPitch=function(n){n=(n+180-et)%360-180,s!==n&&(s=n,s>90?(s=180-s,g||this.setRoll(h-180),g=!0):s<-90?(s=-180-s,g||this.setRoll(h+180),g=!0):g=!1,this.repaint())},this.getPitch=function(){return s},this.setPitchAnimated=function(n){if(s!==n){undefined!==b&&b.playing&&b.stop();var t=this;b=new Tween({},"",Tween.regularEaseInOut,s,n,1),b.onMotionChanged=function(n){t.setPitch(n.target._pos)},b.start()}},this.setPitchOffset=function(n){et=n,this.repaint()},this.setFrameDesign=function(n){rt(),lt=n,it(),this.repaint()},this.setForegroundType=function(n){rt(),st=n,it(),this.repaint()},this.repaint=function(){ct||it(),e.save(),e.clearRect(0,0,e.canvas.width,e.canvas.height),e.drawImage(k,0,0),e.save(),e.beginPath(),e.arc(l,c,u*.831775/2,0,Math.PI*2,!0),e.closePath(),e.clip(),e.translate(l,c),e.rotate(-(h*Math.PI/180)),e.translate(-l,0),e.translate(0,s*ot),e.drawImage(p,0,-p.height/2),e.translate(0,-(s*ot)-c),e.drawImage(a,u*.5-a.width/2,u*.107476),e.restore(),e.drawImage(d,0,0),e.restore()},this.repaint(),this},nu=function(n,t){var a,e,h,f,c;t=t||{};var u=undefined===t.size?32:t.size,y=undefined===t.ledColor?steelseries.LedColor.RED_LED:t.ledColor,v=!1,p=0,i=r.getElementById(n).getContext("2d");i.save(),i.clearRect(0,0,i.canvas.width,i.canvas.height),i.canvas.width=u,i.canvas.height=u,a=!1,e=r.createElement("canvas"),e.width=u,e.height=u,h=e.getContext("2d"),f=r.createElement("canvas"),f.width=u,f.height=u;var l=f.getContext("2d"),s=f,w=function(){a=!0,h.clearRect(0,0,h.canvas.width,h.canvas.height),h.drawImage(o(u,1,y),0,0),l.clearRect(0,0,l.canvas.width,l.canvas.height),l.drawImage(o(u,0,y),0,0)};return this.toggleLed=function(){s=s===e?f:e,c()},this.setLedColor=function(n){y=n,a=!1,c()},this.setLedOnOff=function(n){s=!0===n?e:f,c()},this.blink=function(n){n?v||(p=setInterval(this.toggleLed,1e3),v=!0):v&&(clearInterval(p),v=!1)},c=function(){a||w(),i.save(),i.clearRect(0,0,i.canvas.width,i.canvas.height),i.drawImage(s,0,0),i.restore()},c(),this},tu=function(n,i){i=i||{};var f=undefined===i.size?200:i.size,di=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,ki=undefined===i.frameVisible?!0:i.frameVisible,p=undefined===i.pointerType?steelseries.PointerType.TYPE1:i.pointerType,b=undefined===i.pointerColor?p===steelseries.PointerType.TYPE1?steelseries.ColorDef.GRAY:steelseries.ColorDef.BLACK:i.pointerColor,ut=undefined===i.backgroundColor?p===steelseries.PointerType.TYPE1?steelseries.BackgroundColor.ANTHRACITE:steelseries.BackgroundColor.LIGHT_GRAY:i.backgroundColor,yi=undefined===i.backgroundVisible?!0:i.backgroundVisible,bi=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,si=undefined===i.foregroundVisible?!0:i.foregroundVisible,or=undefined===i.customLayer?null:i.customLayer,nt=undefined===i.isAutomatic?!0:i.isAutomatic,l=undefined===i.hour?11:i.hour,a=undefined===i.minute?5:i.minute,d=undefined===i.second?0:i.second,kt=undefined===i.secondMovesContinuous?!1:i.secondMovesContinuous,dt=undefined===i.timeZoneOffsetHour?0:i.timeZoneOffsetHour,wt=undefined===i.timeZoneOffsetMinute?0:i.timeZoneOffsetMinute,bt=undefined===i.secondPointerVisible?!0:i.secondPointerVisible,k=new Date,ui,ri,ii,nr,it=kt?100:1e3;it=bt?it:100;var sr=this,hr=Math.PI/2,gt=Math.PI*2,tt=Math.PI/180,fi=6,o=r.getElementById(n).getContext("2d");o.save(),o.clearRect(0,0,o.canvas.width,o.canvas.height),o.canvas.width=f,o.canvas.height=f;var u=f,e=f,s=u/2,h=e/2,pi=!1,st=t(f,f),wi=st.getContext("2d"),et=t(f,f),ti=et.getContext("2d"),ot=t(f,f),gi=ot.getContext("2d"),pt=t(f,f),tr=pt.getContext("2d"),vt=t(f,f),rr=vt.getContext("2d"),lt=t(f,f),ir=lt.getContext("2d"),ht=t(f,f),hi=ht.getContext("2d"),yt=t(f,f),ai=yt.getContext("2d"),g=t(f,f),c=g.getContext("2d"),at=t(f,f),oi=at.getContext("2d"),fr=function(n,t){var i,e,o,r,f;r=u*.405,n.save(),n.translate(s,h);switch(t.type){case"type1":for(e=u*.074766,f=r-e,n.strokeStyle=ut.labelColor.getRgbaColor(),n.lineWidth=u*.014018,i=0;i<360;i+=30)n.beginPath(),n.moveTo(r,0),n.lineTo(f,0),n.closePath(),n.stroke(),n.rotate(30*tt);for(o=u*.126168,f=r-o,n.lineWidth=u*.03271,i=0;i<360;i+=90)n.beginPath(),n.moveTo(r,0),n.lineTo(f,0),n.closePath(),n.stroke(),n.rotate(90*tt);break;case"type2":default:for(e=u*.037383,f=r-e,n.strokeStyle=ut.labelColor.getRgbaColor(),n.lineWidth=u*.009345,i=0;i<360;i+=6)n.beginPath(),n.moveTo(r,0),n.lineTo(f,0),n.closePath(),n.stroke(),n.rotate(6*tt);for(o=u*.084112,f=r-o,n.lineWidth=u*.028037,i=0;i<360;i+=30)n.beginPath(),n.moveTo(r,0),n.lineTo(f,0),n.closePath(),n.stroke(),n.rotate(30*tt)}n.translate(-s,-h),n.restore()},vi=function(n,t,i){n.save();var r;i&&(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)");switch(t.type){case"type2":n.beginPath(),n.lineWidth=u*.046728,n.moveTo(s,u*.289719),n.lineTo(s,u*.289719+u*.224299),i||(n.strokeStyle=b.medium.getRgbaColor()),n.closePath(),n.stroke();break;case"type1":default:n.beginPath(),n.moveTo(u*.471962,e*.560747),n.lineTo(u*.471962,e*.214953),n.lineTo(u*.5,e*.182242),n.lineTo(u*.528037,e*.214953),n.lineTo(u*.528037,e*.560747),n.lineTo(u*.471962,e*.560747),n.closePath(),i||(r=n.createLinearGradient(u*.471962,e*.560747,u*.528037,e*.214953),r.addColorStop(1,b.veryLight.getRgbaColor()),r.addColorStop(0,b.light.getRgbaColor()),n.fillStyle=r,n.strokeStyle=b.light.getRgbaColor()),n.fill(),n.stroke()}i&&blur(n,u,e,Math.floor(u*.006)),n.restore()},li=function(n,t,i){n.save();var r;i&&(n.fillStyle="rgba(0, 0, 0, 0.4)",n.strokeStyle="rgba(0, 0, 0, 0.4)");switch(t.type){case"type2":n.beginPath(),n.lineWidth=u*.03271,n.moveTo(s,u*.116822),n.lineTo(s,u*.116822+u*.38785),i||(n.strokeStyle=b.medium.getRgbaColor()),n.closePath(),n.stroke();break;case"type1":default:n.beginPath(),n.moveTo(u*.518691,e*.574766),n.lineTo(u*.523364,e*.135514),n.lineTo(u*.5,e*.107476),n.lineTo(u*.476635,e*.140186),n.lineTo(u*.476635,e*.574766),n.lineTo(u*.518691,e*.574766),n.closePath(),i||(r=n.createLinearGradient(u*.518691,e*.574766,u*.476635,e*.140186),r.addColorStop(1,b.veryLight.getRgbaColor()),r.addColorStop(0,b.light.getRgbaColor()),n.fillStyle=r,n.strokeStyle=b.light.getRgbaColor()),n.fill(),n.stroke()}i&&blur(n,u,e,Math.floor(u*.006)),n.restore()},ci=function(n,t,i){n.save();var r;i?(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)"):(n.fillStyle=steelseries.ColorDef.RED.medium.getRgbaColor(),n.strokeStyle=steelseries.ColorDef.RED.medium.getRgbaColor());switch(t.type){case"type2":n.lineWidth=u*.009345,n.beginPath(),n.moveTo(s,u*.09813),n.lineTo(s,u*.09813+u*.126168),n.closePath(),n.stroke(),n.lineWidth=u*.018691,n.beginPath(),n.moveTo(s,u*.308411),n.lineTo(s,u*.308411+u*.191588),n.closePath(),n.stroke(),n.lineWidth=u*.016,n.beginPath(),n.arc(s,u*.26,u*.085/2,0,gt),n.closePath(),n.stroke();break;case"type1":default:n.beginPath(),n.moveTo(u*.509345,e*.116822),n.lineTo(u*.509345,e*.574766),n.lineTo(u*.490654,e*.574766),n.lineTo(u*.490654,e*.116822),n.lineTo(u*.509345,e*.116822),n.closePath(),i||(r=n.createLinearGradient(u*.509345,e*.116822,u*.490654,e*.574766),r.addColorStop(0,steelseries.ColorDef.RED.light.getRgbaColor()),r.addColorStop(.47,steelseries.ColorDef.RED.medium.getRgbaColor()),r.addColorStop(1,steelseries.ColorDef.RED.dark.getRgbaColor()),n.fillStyle=r,n.strokeStyle=steelseries.ColorDef.RED.dark.getRgbaColor()),n.fill(),n.stroke()}i&&blur(n,u,e,Math.floor(u*.006)),n.restore()},er=function(n){var i=u*.006,t;n.beginPath(),n.arc(s,h,u*.045,0,gt),n.closePath(),t=n.createLinearGradient(s-u*.045/2,h-u*.045/2,s+u*.045/2,h+u*.045/2),t.addColorStop(0,"#eef0f2"),t.addColorStop(1,"#65696d"),n.fillStyle=t,n.fill()},ur=function(n,t){var r=u*.006,i;n.save();switch(t.type){case"type2":n.fillStyle="#000000",n.beginPath(),n.arc(s,h,u*.088785/2,0,gt),n.closePath(),n.fill();break;case"type1":default:i=n.createLinearGradient(s-u*.027/2,h-u*.027/2,s+u*.027/2,h+u*.027/2),i.addColorStop(0,"#f3f4f7"),i.addColorStop(.11,"#f3f5f7"),i.addColorStop(.12,"#f1f3f5"),i.addColorStop(.2,"#c0c5cb"),i.addColorStop(.2,"#bec3c9"),i.addColorStop(1,"#bec3c9"),n.fillStyle=i,n.beginPath(),n.arc(s,h,u*.027,0,gt),n.closePath(),n.fill()}n.restore()},ni=function(n,t,i){ii=i*fi*tt,ui=t*fi*tt,ri=(n+t/60)*fi*5*tt},ei=function(){nt?k=new Date:(k.setHours(l),k.setMinutes(a),k.setSeconds(d)),d=k.getSeconds()+(kt?k.getMilliseconds()/1e3:0),l=dt!==0&&wt!==0?k.getUTCHours()+dt:k.getHours(),l=l%12,a=dt!==0&&wt!==0?k.getUTCMinutes()+wt:k.getMinutes(),a>60&&(a-=60,l++),a<0&&(a+=60,l--),l=l%12,ni(l,a,d),nt&&(nr=setTimeout(ei,it)),sr.repaint()},ft=function(n){n=n||{};var r=undefined===n.frame?!1:n.frame,f=undefined===n.background?!1:n.background,t=undefined===n.pointers?!1:n.pointers,i=undefined===n.foreground?!1:n.foreground;pi=!0,r&&ki&&v(wi,di,s,h,u,e),f&&yi&&(w(ti,ut,s,h,u,e),rt(ti,or,s,h,u,e),fr(ti,p)),t&&(vi(gi,p,!1),vi(tr,p,!0),li(rr,p,!1),li(ir,p,!0),ci(hi,p,!1),ci(ai,p,!0)),i&&si&&(ur(oi,p),y(oi,bi,u,e,!1))},ct=function(n){n=n||{};var r=undefined===n.frame?!1:n.frame,u=undefined===n.background?!1:n.background,t=undefined===n.pointers?!1:n.pointers,i=undefined===n.foreground?!1:n.foreground;r&&(st.width=f,st.height=f,wi=st.getContext("2d")),u&&(et.width=f,et.height=f,ti=et.getContext("2d")),t&&(ot.width=f,ot.height=f,gi=ot.getContext("2d"),pt.width=f,pt.height=f,tr=pt.getContext("2d"),vt.width=f,vt.height=f,rr=vt.getContext("2d"),lt.width=f,lt.height=f,ir=lt.getContext("2d"),ht.width=f,ht.height=f,hi=ht.getContext("2d"),yt.width=f,yt.height=f,ai=yt.getContext("2d"),g.width=f,g.height=f,c=g.getContext("2d")),i&&(at.width=f,at.height=f,oi=at.getContext("2d"))};return this.getAutomatic=function(){return nt},this.setAutomatic=function(n){nt&&!n?(clearTimer(nr),nt=n):!nt&&n&&(nt=n,ei())},this.getHour=function(){return l},this.setHour=function(n){n=n%12,l!==n&&(l=n,ni(l,a,d),this.repaint())},this.getMinute=function(){return a},this.setMinute=function(n){n=n%60,a!==n&&(a=n,ni(l,a,d),this.repaint())},this.getSecond=function(){return d},this.setSecond=function(n){d=n%60,d!==n&&(d=n,ni(l,a,d),this.repaint())},this.getTimeZoneOffsetHour=function(){return dt},this.setTimeZoneOffsetHour=function(n){dt=n,this.repaint()},this.getTimeZoneOffsetMinute=function(){return wt},this.setTimeZoneOffsetMinute=function(n){wt=n,this.repaint()},this.getSecondPointerVisible=function(){return bt},this.setSecondPointerVisible=function(n){bt=n,this.repaint()},this.getSecondMovesContinuous=function(){return kt},this.setSecondMovesContinuous=function(n){kt=n,it=kt?100:1e3,it=bt?it:100},this.setFrameDesign=function(n){ct({frame:!0}),di=n,ft({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){ct({frame:!0,background:!0}),ut=n,ft({frame:!0,background:!0}),this.repaint()},this.setForegroundType=function(n){ct({foreground:!0}),bi=n,ft({foreground:!0}),this.repaint()},this.setPointerType=function(n){ct({foreground:!0,pointers:!0}),p=n,p.type==="type1"?(b=steelseries.ColorDef.GRAY,ut=steelseries.BackgroundColor.ANTHRACITE):(b=steelseries.ColorDef.BLACK,ut=steelseries.BackgroundColor.LIGHT_GRAY),ft({background:!0,foreground:!0,pointers:!0}),this.repaint()},this.setPointerColor=function(n){ct({pointers:!0}),b=n,ft({pointers:!0}),this.repaint()},this.repaint=function(){pi||ft({frame:!0,background:!0,pointers:!0,foreground:!0}),o.clearRect(0,0,o.canvas.width,o.canvas.height),ki&&o.drawImage(st,0,0),yi&&o.drawImage(et,0,0);var n=u*.006;c.clearRect(0,0,u,e),c.save(),c.translate(s,h),c.rotate(ri),c.translate(-s,-h),c.drawImage(pt,0,0),c.restore(),o.drawImage(g,0,0,u,e,n,n,u+n,e+n),o.save(),o.translate(s,h),o.rotate(ri),o.translate(-s,-h),o.drawImage(ot,0,0),o.restore(),c.clearRect(0,0,u,e),c.save(),c.translate(s,h),c.rotate(ui),c.translate(-s,-h),c.drawImage(lt,0,0),c.restore(),o.drawImage(g,0,0,u,e,n,n,u+n,e+n),o.save(),o.translate(s,h),o.rotate(ui),o.translate(-s,-h),o.drawImage(vt,0,0),o.restore(),p.type==="type1"&&er(o),bt&&(c.clearRect(0,0,u,e),c.save(),c.translate(s,h),c.rotate(ii),c.translate(-s,-h),c.drawImage(yt,0,0),c.restore(),o.drawImage(g,0,0,u,e,n,n,u+n,e+n),o.save(),o.translate(s,h),o.rotate(ii),o.translate(-s,-h),o.drawImage(ht,0,0),o.restore()),si&&o.drawImage(at,0,0)},ei(),this},hr=function(t,i){var s;i=i||{};var o=undefined===i.size?50:i.size,f=undefined===i.value?50:i.value,e=o,h=Math.ceil(o*.45),u=r.getElementById(t).getContext("2d");return u.save(),u.clearRect(0,0,u.canvas.width,u.canvas.height),u.canvas.width=e,u.canvas.height=e,s=function(t,i,r,u){var f,e;t.beginPath(),t.moveTo(i*.025,r*.055555),t.lineTo(i*.9,r*.055555),t.lineTo(i*.9,r*.944444),t.lineTo(i*.025,r*.944444),t.lineTo(i*.025,r*.055555),t.closePath(),t.beginPath(),t.moveTo(i*.925,0),t.lineTo(0,0),t.lineTo(0,r),t.lineTo(i*.925,r),t.lineTo(i*.925,r*.722222),t.bezierCurveTo(i*.925,r*.722222,i*.975,r*.722222,i*.975,r*.722222),t.bezierCurveTo(i,r*.722222,i,r*.666666,i,r*.666666),t.bezierCurveTo(i,r*.666666,i,r*.333333,i,r*.333333),t.bezierCurveTo(i,r*.333333,i,r*.277777,i*.975,r*.277777),t.bezierCurveTo(i*.975,r*.277777,i*.925,r*.277777,i*.925,r*.277777),t.lineTo(i*.925,0),t.closePath(),f=t.createLinearGradient(0,0,0,r),f.addColorStop(0,"#ffffff"),f.addColorStop(1,"#7e7e7e"),t.fillStyle=f,t.fill(),t.beginPath(),e=Math.max(i*.875*(u/100),Math.ceil(i*.01)),t.rect(i*.025,i*.025,e,r*.888888),t.closePath();var a=[0,.4,1],l=[new n(177,25,2,1),new n(219,167,21,1),new n(121,162,75,1)],y=new kt(0,100,a,l);t.fillStyle=y.getColorAt(u/100).getRgbColor(),t.fill(),t.beginPath(),e=Math.max(e-i*.05,0),t.rect(i*.05,i*.05,e,r*.777777),t.closePath();var v=[new n(198,39,5,1),new n(228,189,32,1),new n(163,216,102,1)],h=[new n(246,121,48,1),new n(246,244,157,1),new n(223,233,86,1)],o=[0,.4,1],s=new kt(0,100,o,v),c=new kt(0,100,o,h);f=t.createLinearGradient(i*.05,0,i*.875,0),f.addColorStop(0,s.getColorAt(u/100).getRgbColor()),f.addColorStop(.5,c.getColorAt(u/100).getRgbColor()),f.addColorStop(1,s.getColorAt(u/100).getRgbColor()),t.fillStyle=f,t.fill(),t.beginPath(),t.rect(i*.025,i*.025,i*.875,r*.444444),t.closePath(),f=t.createLinearGradient(i*.025,i*.025,i*.875,r*.444444),f.addColorStop(0,"rgba(255, 255, 255, 0)"),f.addColorStop(1,"rgba(255, 255, 255, 0.8)"),t.fillStyle=f,t.fill()},this.setValue=function(n){n=n<0?0:n>100?100:n,f!==n&&(f=n,this.repaint())},this.getValue=function(){return f},this.repaint=function(){u.clearRect(0,0,u.canvas.width,u.canvas.height),s(u,e,h,f)},this.repaint(),this},cr=function(n,i){i=i||{};var f=undefined===i.size?200:i.size,li=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,oi=undefined===i.frameVisible?!0:i.frameVisible,h=undefined===i.pointerColor?steelseries.ColorDef.BLACK:i.pointerColor,lt=undefined===i.backgroundColor?steelseries.BackgroundColor.LIGHT_GRAY:i.backgroundColor,gt=undefined===i.backgroundVisible?!0:i.backgroundVisible,fi=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,vi=undefined===i.foregroundVisible?!0:i.foregroundVisible,gi=undefined===i.customLayer?null:i.customLayer,bt=0,at=0,kt,di=6,ct=Math.PI*2,pt=Math.PI/180,ki=this,yt=0,d=0,yi=0,pi=0,si=0,a=!1,k=!1,e=r.getElementById(n).getContext("2d"),u=f,c=f,o=u/2,l=c/2,p=.285*u,bi=o-p/2,b=.17*u,ci=!1,ht=t(f,f),hi=ht.getContext("2d"),nt=t(f,f),it=nt.getContext("2d"),tt=t(f,f),wi=tt.getContext("2d"),ut=t(f,f),ai=ut.getContext("2d"),st=t(f,f),ni=st.getContext("2d"),ft=t(f,f),ti=ft.getContext("2d"),g=t(f,f),s=g.getContext("2d"),et=t(f,f),ei=et.getContext("2d"),ui=function(n,t,i,r,u,f,e){var vt,ut=r*t,rt=ut+"px sans-serif",ot=t*.15,at=.5,g=1,ht=1.5,tt=u*t,d=Math.round(.025*t),nt=Math.round(.035*t),it=Math.round(.045*t),st=lt.labelColor.getRgbaColor(),k=lt.labelColor.getRgbaColor(),s=t/2,o=t*.4,h,a,w,y=0,b=0,ct=0,v,c=0,l=0,p,et=-Math.PI,ft=2*Math.PI/i;for(n.width=n.height=t,n.save(),n.textAlign="center",n.textBaseline="middle",n.font=rt,p=et,v=0;v<=i+1;p-=ft*.1,v+=.1)n.lineWidth=at,c=Math.sin(p),l=Math.cos(p),y%2==0&&(h=[s+(o-d)*c+f,s+(o-d)*l+e],a=[s+o*c+f,s+o*l+e],n.strokeStyle=k,n.beginPath(),n.moveTo(h[0],h[1]),n.lineTo(a[0],a[1]),n.closePath(),n.stroke()),(y===10||y===0)&&(n.fillStyle=st,n.lineWidth=g,a=[s+o*c+f,s+o*l+e],w=[s+(o-tt)*c+f,s+(o-tt)*l+e],b===5?(v!==i&&Math.round(v)!==60&&n.fillText(Math.round(v),w[0],w[1],ot),n.lineWidth=ht,h=[s+(o-it)*c+f,s+(o-it)*l+e],b=0):(n.lineWidth=g,h=[s+(o-nt)*c+f,s+(o-nt)*l+e]),n.strokeStyle=k,n.beginPath(),n.moveTo(h[0],h[1]),n.lineTo(a[0],a[1]),n.closePath(),n.stroke(),y=0,ct++,b++),y++;n.restore()},ii=function(n,t){var i,r;n.save(),n.beginPath(),n.moveTo(u*.509345,u*.457943),n.lineTo(u*.5,u*.102803),n.lineTo(u*.490654,u*.457943),n.bezierCurveTo(u*.490654,u*.457943,u*.490654,u*.457943,u*.490654,u*.457943),n.bezierCurveTo(u*.471962,u*.462616,u*.457943,u*.481308,u*.457943,u*.5),n.bezierCurveTo(u*.457943,u*.518691,u*.471962,u*.537383,u*.490654,u*.542056),n.bezierCurveTo(u*.490654,u*.542056,u*.490654,u*.542056,u*.490654,u*.542056),n.lineTo(u*.490654,u*.621495),n.lineTo(u*.509345,u*.621495),n.lineTo(u*.509345,u*.542056),n.bezierCurveTo(u*.509345,u*.542056,u*.509345,u*.542056,u*.509345,u*.542056),n.bezierCurveTo(u*.528037,u*.537383,u*.542056,u*.518691,u*.542056,u*.5),n.bezierCurveTo(u*.542056,u*.481308,u*.528037,u*.462616,u*.509345,u*.457943),n.bezierCurveTo(u*.509345,u*.457943,u*.509345,u*.457943,u*.509345,u*.457943),n.closePath(),t?(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)",n.fill()):(i=n.createLinearGradient(0,0,0,u*.621495),i.addColorStop(0,h.medium.getRgbaColor()),i.addColorStop(.388888,h.medium.getRgbaColor()),i.addColorStop(.5,h.light.getRgbaColor()),i.addColorStop(.611111,h.medium.getRgbaColor()),i.addColorStop(1,h.medium.getRgbaColor()),n.fillStyle=i,n.strokeStyle=h.dark.getRgbaColor(),n.fill(),n.stroke()),t?blur(n,u,c,Math.floor(u*.006)):(n.beginPath(),r=u*.06542/2,n.arc(o,l,r,0,ct),i=n.createLinearGradient(o-r,o+r,0,o+r),i.addColorStop(0,"#e6b35c"),i.addColorStop(.01,"#e6b35c"),i.addColorStop(.99,"#c48200"),i.addColorStop(1,"#c48200"),n.fillStyle=i,n.closePath(),n.fill(),n.beginPath(),r=u*.046728/2,n.arc(o,l,r,0,ct),i=n.createRadialGradient(o,o,0,o,o,r),i.addColorStop(0,"#c5c5c5"),i.addColorStop(.19,"#c5c5c5"),i.addColorStop(.22,"#000000"),i.addColorStop(.8,"#000000"),i.addColorStop(.99,"#707070"),i.addColorStop(1,"#707070"),n.fillStyle=i,n.closePath(),n.fill()),n.restore()},ri=function(n,t){var i,r;n.save(),n.beginPath(),n.moveTo(u*.476635,u*.313084),n.bezierCurveTo(u*.476635,u*.322429,u*.485981,u*.331775,u*.495327,u*.336448),n.bezierCurveTo(u*.495327,u*.336448,u*.495327,u*.350467,u*.495327,u*.350467),n.lineTo(u*.504672,u*.350467),n.bezierCurveTo(u*.504672,u*.350467,u*.504672,u*.336448,u*.504672,u*.336448),n.bezierCurveTo(u*.514018,u*.331775,u*.523364,u*.322429,u*.523364,u*.313084),n.bezierCurveTo(u*.523364,u*.303738,u*.514018,u*.294392,u*.504672,u*.289719),n.bezierCurveTo(u*.504672,u*.289719,u*.5,u*.200934,u*.5,u*.200934),n.bezierCurveTo(u*.5,u*.200934,u*.495327,u*.289719,u*.495327,u*.289719),n.bezierCurveTo(u*.485981,u*.294392,u*.476635,u*.303738,u*.476635,u*.313084),n.closePath(),t?(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)",n.fill()):(i=n.createLinearGradient(0,0,u,0),i.addColorStop(0,h.medium.getRgbaColor()),i.addColorStop(.388888,h.medium.getRgbaColor()),i.addColorStop(.5,h.light.getRgbaColor()),i.addColorStop(.611111,h.medium.getRgbaColor()),i.addColorStop(1,h.medium.getRgbaColor()),n.fillStyle=i,n.strokeStyle=h.dark.getRgbaColor(),n.fill(),n.stroke()),t?blur(n,u,c,Math.floor(u*.006)):(n.beginPath(),r=u*.037383/2,n.arc(o,b+p/2,r,0,ct),n.fillStyle="#C48200",n.closePath(),n.fill(),n.beginPath(),r=u*.028037/2,n.arc(o,b+p/2,r,0,ct),n.fillStyle="#999999",n.closePath(),n.fill(),n.beginPath(),r=u*.018691/2,n.arc(o,b+p/2,r,0,ct),n.fillStyle="#000000",n.closePath(),n.fill()),n.restore()},wt=function(){d=+new Date-yt,at=d*di/1e3,bt=at%10800/30,yi=d/6e4%30,pi=d/1e3%60,si=d%1e3},ot=function(n){n=n||{};var r=undefined===n.frame?!1:n.frame,f=undefined===n.background?!1:n.background,t=undefined===n.pointers?!1:n.pointers,i=undefined===n.foreground?!1:n.foreground;ci=!0,r&&oi&&v(hi,li,o,l,u,c),f&&gt&&(w(it,lt,o,l,u,c),rt(it,gi,o,l,u,c),ui(it,u,60,.075,.1,0,0),ui(it,p,30,.095,.13,bi,b)),t&&(ii(ni,!1),ii(ti,!0),ri(wi,!1),ri(ai,!0)),i&&vi&&y(ei,fi,u,c,!1)},vt=function(n){n=n||{};var r=undefined===n.frame?!1:n.frame,u=undefined===n.background?!1:n.background,t=undefined===n.pointers?!1:n.pointers,i=undefined===n.foreground?!1:n.foreground;r&&(ht.width=f,ht.height=f,hi=ht.getContext("2d")),u&&(nt.width=f,nt.height=f,it=nt.getContext("2d")),t&&(tt.width=f,tt.height=f,wi=tt.getContext("2d"),ut.width=f,ut.height=f,ai=ut.getContext("2d"),st.width=f,st.height=f,ni=st.getContext("2d"),ft.width=f,ft.height=f,ti=ft.getContext("2d"),g.width=f,g.height=f,s=g.getContext("2d")),i&&(et.width=f,et.height=f,ei=et.getContext("2d"))},dt=function(){k||(wt(),ki.repaint()),a&&(kt=setTimeout(dt,200))};return this.isRunning=function(){return a},this.start=function(){a||(a=!0,yt=+new Date-d,dt())},this.stop=function(){a&&(a=!1,clearTimeout(kt)),k&&(k=!1,wt(),this.repaint())},this.reset=function(){a&&(a=!1,k=!1,clearTimeout(kt)),yt=+new Date,wt(),this.repaint()},this.lap=function(){a&&!k?k=!0:k&&(k=!1)},this.getMeasuredTime=function(){return yi+":"+pi+":"+si},this.setFrameDesign=function(n){vt({frame:!0}),li=n,ot({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){vt({background:!0}),lt=n,ot({background:!0}),this.repaint()},this.setForegroundType=function(n){vt({foreground:!0}),fi=n,ot({foreground:!0}),this.repaint()},this.setPointerColor=function(n){vt({pointers:!0}),h=n,ot({pointers:!0}),this.repaint()},this.repaint=function(){ci||ot({frame:!0,background:!0,pointers:!0,foreground:!0}),e.clearRect(0,0,e.canvas.width,e.canvas.height),oi&&e.drawImage(ht,0,0),gt&&e.drawImage(nt,0,0);var n=u*.006,t=(bt+2*Math.sin(bt*pt))*pt;s.clearRect(0,0,u,c),s.save(),s.translate(o,b+p/2),s.rotate(t),s.translate(-o,-(b+p/2)),s.drawImage(ut,0,0),s.restore(),e.drawImage(g,0,0,u,c,n/2,n/2,u+n/2,c+n/2),e.save(),e.translate(o,b+p/2),e.rotate(t),e.translate(-o,-(b+p/2)),e.drawImage(tt,0,0),e.restore(),t=(at+2*Math.sin(at*pt))*pt,s.clearRect(0,0,u,c),s.save(),s.translate(o,l),s.rotate(t),s.translate(-o,-l),s.drawImage(ft,0,0),s.restore(),e.drawImage(g,0,0,u,c,n,n,u+n,c+n),e.save(),e.translate(o,l),e.rotate(t),e.translate(-o,-l),e.drawImage(st,0,0),e.restore(),vi&&e.drawImage(et,0,0)},e.save(),e.clearRect(0,0,e.canvas.width,e.canvas.height),e.canvas.width=f,e.canvas.height=f,yt=+new Date,dt(),this},sr=function(n,i){function gi(){nt=2*Math.PI/(li-a),ht=nt/10,pt=ht/10}function nr(){yt=l%1e3/100,kt=l%1e4/100,wt=l%1e5/100}var o,bt,at,ut,ui,ri,fi,k,vt,vi,ct,lt,ai,a=0,li=10,l=a,yt=0,kt=0,wt=0,nt,ht,pt,rr=1,g,u,f,c,h,hi,e=r.getElementById(n).getContext("2d"),hr=Math.PI/2,cr=Math.PI/180,ur=Math.PI,ci=!1,ot=t(o,o),bi=ot.getContext("2d"),et=t(o,o),it=et.getContext("2d"),yi,pi=t(o,o),tr=pi.getContext("2d"),ti=t(o,o),ir=ti.getContext("2d"),ni=t(o,o),or=ni.getContext("2d"),ii=t(o,o),sr=ii.getContext("2d"),ei=t(o,o),fr=ei.getContext("2d"),oi=t(o,o),er=oi.getContext("2d"),b=t(o,o),s=b.getContext("2d"),ft=t(o,o),si=ft.getContext("2d"),d,st;i=i||{},o=undefined===i.size?200:i.size,bt=undefined===i.frameDesign?steelseries.FrameDesign.METAL:i.frameDesign,at=undefined===i.frameVisible?!0:i.frameVisible,ut=undefined===i.backgroundColor?steelseries.BackgroundColor.DARK_GRAY:i.backgroundColor,ui=undefined===i.backgroundVisible?!0:i.backgroundVisible,ri=undefined===i.knobType?steelseries.KnobType.METAL_KNOB:i.knobType,fi=undefined===i.knobStyle?steelseries.KnobStyle.BLACK:i.knobStyle,k=undefined===i.lcdColor?steelseries.LcdColor.BLACK:i.lcdColor,vt=undefined===i.lcdVisible?!0:i.lcdVisible,vi=undefined===i.digitalFont?!1:i.digitalFont,ct=undefined===i.foregroundType?steelseries.ForegroundType.TYPE1:i.foregroundType,lt=undefined===i.foregroundVisible?!0:i.foregroundVisible,ai=undefined===i.customLayer?null:i.customLayer,e.save(),e.clearRect(0,0,e.canvas.width,e.canvas.height),e.canvas.width=o,e.canvas.height=o,u=o,f=o,c=u/2,h=f/2,hi=Math.floor(u*.09)+"px sans-serif";var di=function(n){e.save(),e.textAlign="right",e.textBaseline="middle",e.strokeStyle=k.textColor,e.fillStyle=k.textColor,(k===steelseries.LcdColor.STANDARD||k===steelseries.LcdColor.STANDARD_GREEN)&&(e.shadowColor="gray",e.shadowOffsetX=u*.007,e.shadowOffsetY=u*.007,e.shadowBlur=u*.009),e.font=vi?Math.floor(u*.075)+"px "+tt:Math.floor(u*.075)+"px sans-serif",e.fillText(Math.round(n),(u+u*.4)/2-4,u*.607,u*.4),e.restore()},ki=function(n,t,i,r,f,e){var it=Math.max(u*.012,2),rt=Math.max(u*.007,1.5),d=u*.13,g=u*.05,nt=u*.07,v=u*.4,b=0,ft=0,p=0,y=0,k,w,tt=-i-t/2;for(n.save(),n.textAlign="center",n.textBaseline="middle",n.font=hi,n.strokeStyle=ut.labelColor.getRgbaColor(),n.fillStyle=ut.labelColor.getRgbaColor(),k=tt,w=0;w<=10;k-=e*.1,w+=.1)p=Math.sin(k),y=Math.cos(k),b%2==0&&(n.lineWidth=rt,n.beginPath(),n.moveTo(c+(v-g)*p,h+(v-g)*y),n.lineTo(c+v*p,h+v*y),n.closePath(),n.stroke()),(b===10||b===0)&&(n.lineWidth=it,t===0&&Math.round(w)!==li&&n.fillText(Math.round(w).toString(),c+(v-d)*p,h+(v-d)*y),b=0,ft++,n.beginPath(),n.moveTo(c+(v-nt)*p,h+(v-nt)*y),n.lineTo(c+v*p,h+v*y),n.closePath(),n.stroke()),b++;n.restore()},dt=function(n,t){var i;t?(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)"):(i=n.createLinearGradient(0,f*.168224,0,f*.626168),i.addColorStop(0,"#ffffff"),i.addColorStop(.31,"#ffffff"),i.addColorStop(.3101,"#ffffff"),i.addColorStop(.32,"#202020"),i.addColorStop(1,"#202020"),n.fillStyle=i),n.save(),n.beginPath(),n.moveTo(u*.518691,f*.471962),n.bezierCurveTo(u*.514018,f*.471962,u*.509345,f*.467289,u*.509345,f*.467289),n.lineTo(u*.509345,f*.200934),n.lineTo(u*.5,f*.168224),n.lineTo(u*.490654,f*.200934),n.lineTo(u*.490654,f*.467289),n.bezierCurveTo(u*.490654,f*.467289,u*.481308,f*.471962,u*.481308,f*.471962),n.bezierCurveTo(u*.471962,f*.481308,u*.467289,f*.490654,u*.467289,f*.5),n.bezierCurveTo(u*.467289,f*.514018,u*.476635,f*.528037,u*.490654,f*.53271),n.bezierCurveTo(u*.490654,f*.53271,u*.490654,f*.579439,u*.490654,f*.588785),n.bezierCurveTo(u*.485981,f*.593457,u*.481308,f*.59813,u*.481308,f*.607476),n.bezierCurveTo(u*.481308,f*.616822,u*.490654,f*.626168,u*.5,f*.626168),n.bezierCurveTo(u*.509345,f*.626168,u*.518691,f*.616822,u*.518691,f*.607476),n.bezierCurveTo(u*.518691,f*.59813,u*.514018,f*.593457,u*.504672,f*.588785),n.bezierCurveTo(u*.504672,f*.579439,u*.504672,f*.53271,u*.509345,f*.53271),n.bezierCurveTo(u*.523364,f*.528037,u*.53271,f*.514018,u*.53271,f*.5),n.bezierCurveTo(u*.53271,f*.490654,u*.528037,f*.481308,u*.518691,f*.471962),n.closePath(),n.fill(),t&&blur(n,u,f,Math.floor(u*.006)),n.restore()},gt=function(n,t){var i;t?(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)"):(i=n.createLinearGradient(0,f*.401869,0,f*.616822),i.addColorStop(0,"#ffffff"),i.addColorStop(.51,"#ffffff"),i.addColorStop(.52,"#ffffff"),i.addColorStop(.5201,"#202020"),i.addColorStop(.53,"#202020"),i.addColorStop(1,"#202020"),n.fillStyle=i),n.beginPath(),n.moveTo(u*.518691,f*.471962),n.bezierCurveTo(u*.514018,f*.462616,u*.528037,f*.401869,u*.528037,f*.401869),n.lineTo(u*.5,f*.331775),n.lineTo(u*.471962,f*.401869),n.bezierCurveTo(u*.471962,f*.401869,u*.485981,f*.462616,u*.481308,f*.471962),n.bezierCurveTo(u*.471962,f*.481308,u*.467289,f*.490654,u*.467289,f*.5),n.bezierCurveTo(u*.467289,f*.514018,u*.476635,f*.528037,u*.490654,f*.53271),n.bezierCurveTo(u*.490654,f*.53271,u*.462616,f*.574766,u*.462616,f*.593457),n.bezierCurveTo(u*.467289,f*.616822,u*.5,f*.612149,u*.5,f*.612149),n.bezierCurveTo(u*.5,f*.612149,u*.53271,f*.616822,u*.537383,f*.593457),n.bezierCurveTo(u*.537383,f*.574766,u*.509345,f*.53271,u*.509345,f*.53271),n.bezierCurveTo(u*.523364,f*.528037,u*.53271,f*.514018,u*.53271,f*.5),n.bezierCurveTo(u*.53271,f*.490654,u*.528037,f*.481308,u*.518691,f*.471962),n.closePath(),n.fill(),t&&blur(n,u,f,Math.floor(u*.006)),n.restore()},wi=function(n,t){t?(n.fillStyle="rgba(0, 0, 0, 0.5)",n.strokeStyle="rgba(0, 0, 0, 0.5)"):n.fillStyle="#ffffff",n.beginPath(),n.moveTo(u*.518691,f*.471962),n.bezierCurveTo(u*.514018,f*.471962,u*.514018,f*.467289,u*.514018,f*.467289),n.lineTo(u*.514018,f*.317757),n.lineTo(u*.504672,f*.303738),n.lineTo(u*.504672,f*.182242),n.lineTo(u*.53271,f*.116822),n.lineTo(u*.462616,f*.116822),n.lineTo(u*.495327,f*.182242),n.lineTo(u*.495327,f*.299065),n.lineTo(u*.485981,f*.317757),n.lineTo(u*.485981,f*.467289),n.bezierCurveTo(u*.485981,f*.467289,u*.485981,f*.471962,u*.481308,f*.471962),n.bezierCurveTo(u*.471962,f*.481308,u*.467289,f*.490654,u*.467289,f*.5),n.bezierCurveTo(u*.467289,f*.518691,u*.481308,f*.53271,u*.5,f*.53271),n.bezierCurveTo(u*.518691,f*.53271,u*.53271,f*.518691,u*.53271,f*.5),n.bezierCurveTo(u*.53271,f*.490654,u*.528037,f*.481308,u*.518691,f*.471962),n.closePath(),n.fill(),t&&blur(n,u,f,Math.floor(u*.006))};return d=function(n){var e,t,r,i;n=n||{},e=undefined===n.frame?!1:n.frame,t=undefined===n.background?!1:n.background,r=undefined===n.pointers?!1:n.pointers,i=undefined===n.foreground?!1:n.foreground,ci=!0,gi(),e&&at&&v(bi,bt,c,h,u,f),t&&ui&&(w(it,ut,c,h,u,f),rt(it,ai,c,h,u,f),ki(it,0,ur,0,10,nt,rr,0,!0,!0,null)),t&&vt&&(yi=p(u*.4,f*.09,k),it.drawImage(yi,(u-u*.4)/2,f*.56)),r&&(dt(fr,!1),dt(er,!0),gt(or,!1),gt(sr,!0),wi(tr,!1),wi(ir,!0)),i&&lt&&y(si,ct,u,f,!0,ri,fi)},st=function(n){var r,u,t,i;n=n||{},r=undefined===n.frame?!1:n.frame,u=undefined===n.background?!1:n.background,t=undefined===n.pointers?!1:n.pointers,i=undefined===n.foreground?!1:n.foreground,r&&(ot.width=o,ot.height=o,bi=ot.getContext("2d")),u&&(et.width=o,et.height=o,it=et.getContext("2d")),t&&(pointer100ftBuffer.width=o,pointer100ftBuffer.height=o,pointer100ftContext=pointer100ftBuffer.getContext("2d"),pointer100ftShadowBuffer.width=o,pointer100ftShadowBuffer.height=o,pointer100ftShadowContext=pointer100ftShadowBuffer.getContext("2d"),pointer1000ftBuffer.width=o,pointer1000ftBuffer.height=o,pointer1000ftContext=pointer1000ftBuffer.getContext("2d"),pointer1000ftShadowBuffer.width=o,pointer1000ftShadowBuffer.height=o,pointer1000ftShadowContext=pointer1000ftShadowBuffer.getContext("2d"),pointer10000ftBuffer.width=o,pointer10000ftBuffer.height=o,pointer10000ftContext=pointer10000ftBuffer.getContext("2d"),pointer10000ftShadowBuffer.width=o,pointer10000ftShadowBuffer.height=o,pointer10000ftShadowContext=pointer10000ftShadowBuffer.getContext("2d"),b.width=o,b.height=o,s=b.getContext("2d")),i&&(ft.width=o,ft.height=o,si=ft.getContext("2d"))},this.setValue=function(n){l=n,this.repaint()},this.getValue=function(){return l},this.setValueAnimated=function(n){var t=n<a?a:n,r=this,i;l!==t&&(undefined!==g&&g.playing&&g.stop(),i=Math.max(Math.abs(l-t)/2e3,1),g=new Tween({},"",Tween.regularEaseInOut,l,t,i),g.onMotionChanged=function(n){l=n.target._pos,r.repaint()},g.start())},this.setFrameDesign=function(n){st({frame:!0}),bt=n,d({frame:!0}),this.repaint()},this.setBackgroundColor=function(n){st({background:!0,pointer:!0}),ut=n,d({background:!0,pointer:!0}),this.repaint()},this.setForegroundType=function(n){st({foreground:!0}),ct=n,d({foreground:!0}),this.repaint()},this.setLcdColor=function(n){k=n,d({background:!0}),this.repaint()},this.repaint=function(){ci||d({frame:!0,background:!0,led:!0,pointers:!0,foreground:!0}),e.clearRect(0,0,e.canvas.width,e.canvas.height),at&&e.drawImage(ot,0,0),e.drawImage(et,0,0),vt&&di(l),nr();var n=u*.006*.5;s.clearRect(0,0,u,f),s.save(),s.translate(c,h),s.rotate((wt-a)*pt),s.translate(-c,-h),s.drawImage(ti,0,0),s.restore(),e.drawImage(b,0,0,u,f,n,n,u+n,f+n),e.save(),e.translate(c,h),e.rotate((wt-a)*pt),e.translate(-c,-h),e.drawImage(pi,0,0),e.restore(),s.clearRect(0,0,u,f),s.save(),s.translate(c,h),s.rotate((kt-a)*ht),s.translate(-c,-h),s.drawImage(ii,0,0),s.restore(),e.drawImage(b,0,0,u,f,n,n,u+n,f+n),n=u*.006*.75,e.save(),e.translate(c,h),e.rotate((kt-a)*ht),e.translate(-c,-h),e.drawImage(ni,0,0),e.restore(),n=u*.006,s.clearRect(0,0,u,f),s.save(),s.translate(c,h),s.rotate((yt-a)*nt),s.translate(-c,-h),s.drawImage(oi,0,0),s.restore(),e.drawImage(b,0,0,u,f,n,n,u+n,f+n),e.save(),e.translate(c,h),e.rotate((yt-a)*nt),e.translate(-c,-h),e.drawImage(ei,0,0),e.restore(),lt&&e.drawImage(ft,0,0)},this.repaint(),this},lr=function(n,t){function at(n,t,i){var u=r.createElement("canvas");return u.width=n,u.height=t,i(u.getContext("2d")),u}var w,b,f,tt,i,u,d=!1,nt=!1,g=!1,rt=!1,k=r.createElement("canvas"),bt=k.getContext("2d"),p=r.createElement("canvas"),ft=p.getContext("2d"),s=r.createElement("canvas"),kt=s.getContext("2d"),h=r.createElement("canvas"),wt=h.getContext("2d"),o=r.createElement("canvas"),yt=o.getContext("2d"),e=r.createElement("canvas"),pt=e.getContext("2d"),c=r.createElement("canvas"),dt=c.getContext("2d"),v=r.createElement("canvas"),ii=v.getContext("2d"),l=r.createElement("canvas"),ri=l.getContext("2d"),a=r.createElement("canvas"),ti=a.getContext("2d"),y,it;t=t||{},w=undefined===t.width?98:t.width,b=undefined===t.height?278:t.height,f=r.getElementById(n).getContext("2d"),f.save(),f.clearRect(0,0,f.canvas.width,f.canvas.height),f.canvas.width=w,f.canvas.height=b,tt=w<b*.352517?w*2.836734:b,i=tt*.352517,u=tt,k.width=i,k.height=u,p.width=i,p.height=u,s.width=i,s.height=u,h.width=i,h.height=u,o.width=i,o.height=u,e.width=i,e.height=u,c.width=i,c.height=u,v.width=i,v.height=u,l.width=i,l.height=u,a.width=i,a.height=u;var gt=function(n){var r,t;n.save(),n.save(),n.beginPath(),n.moveTo(.107142*i,0),n.lineTo(i-.107142*i,0),n.quadraticCurveTo(i,0,i,.107142*i),n.lineTo(i,u-.107142*i),n.quadraticCurveTo(i,u,i-.107142*i,u),n.lineTo(.107142*i,u),n.quadraticCurveTo(0,u,0,u-.107142*i),n.lineTo(0,.107142*i),n.quadraticCurveTo(0,0,.107142*i,u),n.closePath(),r=n.createLinearGradient(.040816*i,.007194*u,.952101*i,.995882*u),r.addColorStop(0,"rgb(152, 152, 154)"),r.addColorStop(.01,"rgb(152, 152, 154)"),r.addColorStop(.09,"#333333"),r.addColorStop(.24,"rgb(152, 152, 154)"),r.addColorStop(.55,"rgb(31, 31, 31)"),r.addColorStop(.78,"#363636"),r.addColorStop(.98,"#000000"),r.addColorStop(1,"#000000"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(.030612*i+.084183*i,.010791*u),n.lineTo(.030612*i+.938775*i-.084183*i,.010791*u),n.quadraticCurveTo(.030612*i+.938775*i,.010791*u,.030612*i+.938775*i,.010791*u+.084183*i),n.lineTo(.030612*i+.938775*i,.010791*u+.978417*u-.084183*i),n.quadraticCurveTo(.030612*i+.938775*i,.010791*u+.978417*u,.030612*i+.938775*i-.084183*i,.010791*u+.978417*u),n.lineTo(.030612*i+.084183*i,.010791*u+.978417*u),n.quadraticCurveTo(.030612*i,.010791*u+.978417*u,.030612*i,.010791*u+.978417*u-.084183*i),n.lineTo(.030612*i,.010791*u+.084183*i),n.quadraticCurveTo(.030612*i,.010791*u,.030612*i+.084183*i,.010791*u),n.closePath(),t=n.createLinearGradient(-.132653*i,-.053956*u,2.061408*i,.667293*u),t.addColorStop(0,"#000000"),t.addColorStop(.01,"#000000"),t.addColorStop(.16,"#373735"),t.addColorStop(.31,"#000000"),t.addColorStop(.44,"#303030"),t.addColorStop(.65,"#000000"),t.addColorStop(.87,"#363636"),t.addColorStop(.98,"#000000"),t.addColorStop(1,"#000000"),n.fillStyle=t,n.fill(),n.restore(),n.restore()},ni=function(n){var t,f,r,e;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.805755*u,.397959*i,0,2*Math.PI,!1),t=n.createLinearGradient(0,.665467*u,0,.946043*u),t.addColorStop(0,"#ffffff"),t.addColorStop(.05,"rgb(204, 204, 204)"),t.addColorStop(.1,"rgb(153, 153, 153)"),t.addColorStop(.17,"#666666"),t.addColorStop(.27,"#333333"),t.addColorStop(1,"#010101"),n.fillStyle=t,n.fill(),n.restore(),n.save(),n.scale(1.083333,1),n.beginPath(),n.arc(.461538*i,.816546*u,.367346*i,0,2*Math.PI,!1),f=n.createLinearGradient(0,.68705*u,0,.946043*u),f.addColorStop(0,"#000000"),f.addColorStop(.35,"#040404"),f.addColorStop(.66,"#000000"),f.addColorStop(1,"#010101"),n.fillStyle=f,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.809352*u,.357142*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.809352*u,0,.5*i,.809352*u,.362244*i),r.addColorStop(0,"#000000"),r.addColorStop(.88,"#000000"),r.addColorStop(.95,"rgb(94, 94, 94)"),r.addColorStop(1,"#010101"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.809352*u,.357142*i,0,2*Math.PI,!1),e=n.createLinearGradient(0,.68705*u,0,.917266*u),e.addColorStop(0,"#000000"),e.addColorStop(1,"rgba(1, 1, 1, 0)"),n.fillStyle=e,n.fill(),n.restore(),n.restore()},et=function(n){var r,t;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.809352*u,.32653*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.809352*u,0,.5*i,.809352*u,.32653*i),r.addColorStop(0,"rgb(85, 185, 123)"),r.addColorStop(1,"rgb(0, 31, 0)"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(0,.812949*u),n.bezierCurveTo(0,.910071*u,.224489*i,.989208*u,.5*i,.989208*u),n.bezierCurveTo(.77551*i,.989208*u,i,.910071*u,i,.809352*u),n.bezierCurveTo(.908163*i,.751798*u,.704081*i,.68705*u,.5*i,.68705*u),n.bezierCurveTo(.285714*i,.68705*u,.081632*i,.751798*u,0,.812949*u),n.closePath(),t=n.createRadialGradient(.5*i,.809352*u,0,.5*i,.809352*u,.515306*i),t.addColorStop(0,"rgb(65, 187, 126)"),t.addColorStop(1,"rgba(4, 37, 8, 0)"),n.fillStyle=t,n.fill(),n.restore(),n.restore()},ot=function(n){var r,t;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.809352*u,.32653*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.809352*u,0,.5*i,.809352*u,.32653*i),r.addColorStop(0,"rgba(0, 255, 0, 0.25)"),r.addColorStop(1,"rgba(0, 255, 0, 0.05)"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.809352*u,.32653*i,0,2*Math.PI,!1),t=n.createRadialGradient(.5*i,.809352*u,0,.5*i,.809352*u,.32653*i),t.addColorStop(0,"rgba(1, 1, 1, 0)"),t.addColorStop(.55,"rgba(0, 0, 0, 0)"),t.addColorStop(.5501,"rgba(0, 0, 0, 0)"),t.addColorStop(.78,"rgba(0, 0, 0, 0.12)"),t.addColorStop(.79,"rgba(0, 0, 0, 0.12)"),t.addColorStop(1,"rgba(0, 0, 0, 0.5)"),n.fillStyle=t,n.fill(),n.restore(),n.fillStyle=n.createPattern(y,"repeat"),n.fill(),n.restore()},st=function(n){var t,f,r,e;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.496402*u,.397959*i,0,2*Math.PI,!1),t=n.createLinearGradient(0,.356115*u,0,.63669*u),t.addColorStop(0,"#ffffff"),t.addColorStop(.05,"rgb(204, 204, 204)"),t.addColorStop(.1,"rgb(153, 153, 153)"),t.addColorStop(.17,"#666666"),t.addColorStop(.27,"#333333"),t.addColorStop(1,"#010101"),n.fillStyle=t,n.fill(),n.restore(),n.save(),n.scale(1.083333,1),n.beginPath(),n.arc(.461538*i,.507194*u,.367346*i,0,2*Math.PI,!1),f=n.createLinearGradient(0,.377697*u,0,.63669*u),f.addColorStop(0,"#000000"),f.addColorStop(.35,"#040404"),f.addColorStop(.66,"#000000"),f.addColorStop(1,"#010101"),n.fillStyle=f,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.5*u,.357142*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.5*u,0,.5*i,.5*u,.362244*i),r.addColorStop(0,"#000000"),r.addColorStop(.88,"#000000"),r.addColorStop(.95,"#5e5e5e"),r.addColorStop(1,"#010101"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.5*u,.357142*i,0,2*Math.PI,!1),e=n.createLinearGradient(0,.377697*u,0,.607913*u),e.addColorStop(0,"#000000"),e.addColorStop(1,"rgba(1, 1, 1, 0)"),n.fillStyle=e,n.fill(),n.restore(),n.restore()},ut=function(n){var r,t;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.5*u,.32653*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.5*u,0,.5*i,.5*u,.32653*i),r.addColorStop(0,"#fed434"),r.addColorStop(1,"#82330c"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(0,.503597*u),n.bezierCurveTo(0,.600719*u,.224489*i,.679856*u,.5*i,.679856*u),n.bezierCurveTo(.77551*i,.679856*u,i,.600719*u,i,.5*u),n.bezierCurveTo(.908163*i,.442446*u,.704081*i,.377697*u,.5*i,.377697*u),n.bezierCurveTo(.285714*i,.377697*u,.081632*i,.442446*u,0,.503597*u),n.closePath(),t=n.createRadialGradient(.5*i,.5*u,0,.5*i,.5*u,.515306*i),t.addColorStop(0,"#fed434"),t.addColorStop(1,"rgba(130, 51, 12, 0)"),n.fillStyle=t,n.fill(),n.restore(),n.restore()},vt=function(n){var r,t;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.5*u,.32653*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.5*u,0,.5*i,.5*u,.32653*i),r.addColorStop(0,"rgba(255, 255, 0, 0.25)"),r.addColorStop(1,"rgba(255, 255, 0, 0.05)"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.5*u,.32653*i,0,2*Math.PI,!1),t=n.createRadialGradient(.5*i,.5*u,0,.5*i,.5*u,.32653*i),t.addColorStop(0,"rgba(1, 1, 1, 0)"),t.addColorStop(.55,"rgba(0, 0, 0, 0)"),t.addColorStop(.5501,"rgba(0, 0, 0, 0)"),t.addColorStop(.78,"rgba(0, 0, 0, 0.12)"),t.addColorStop(.79,"rgba(0, 0, 0, 0.13)"),t.addColorStop(1,"rgba(0, 0, 0, 0.5)"),n.fillStyle=t,n.fill(),n.restore(),n.fillStyle=n.createPattern(y,"repeat"),n.fill(),n.restore()},lt=function(n){var t,f,r,e;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.18705*u,.397959*i,0,2*Math.PI,!1),t=n.createLinearGradient(.5*i,.046762*u,.5*i,.327338*u),t.addColorStop(0,"#ffffff"),t.addColorStop(.05,"#cccccc"),t.addColorStop(.1,"#999999"),t.addColorStop(.17,"#666666"),t.addColorStop(.27,"#333333"),t.addColorStop(1,"#010101"),n.fillStyle=t,n.fill(),n.restore(),n.save(),n.scale(1.083333,1),n.beginPath(),n.arc(.461538*i,.197841*u,.367346*i,0,2*Math.PI,!1),f=n.createLinearGradient(.5*i,.068345*u,.5*i,.327338*u),f.addColorStop(0,"#000000"),f.addColorStop(.35,"#040404"),f.addColorStop(.66,"#000000"),f.addColorStop(1,"#010101"),n.fillStyle=f,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.190647*u,.357142*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.190647*u,0,.5*i,.190647*u,.362244*i),r.addColorStop(0,"#000000"),r.addColorStop(.88,"#000000"),r.addColorStop(.95,"#5e5e5e"),r.addColorStop(1,"#010101"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.190647*u,.357142*i,0,2*Math.PI,!1),e=n.createLinearGradient(.5*i,.068345*u,.5*i,.298561*u),e.addColorStop(0,"#000000"),e.addColorStop(1,"rgba(1, 1, 1, 0)"),n.fillStyle=e,n.fill(),n.restore(),n.restore()},ct=function(n){var r,t;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.190647*u,.32653*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.190647*u,0,.5*i,.190647*u,.32653*i),r.addColorStop(0,"#ff0000"),r.addColorStop(1,"#410004"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(0,.194244*u),n.bezierCurveTo(0,.291366*u,.224489*i,.370503*u,.5*i,.370503*u),n.bezierCurveTo(.77551*i,.370503*u,i,.291366*u,i,.190647*u),n.bezierCurveTo(.908163*i,.133093*u,.704081*i,.068345*u,.5*i,.068345*u),n.bezierCurveTo(.285714*i,.068345*u,.081632*i,.133093*u,0,.194244*u),n.closePath(),t=n.createRadialGradient(.5*i,.190647*u,0,.5*i,.190647*u,.515306*i),t.addColorStop(0,"#ff0000"),t.addColorStop(1,"rgba(118, 5, 1, 0)"),n.fillStyle=t,n.fill(),n.restore(),n.restore()},ht=function(n){var r,t;n.save(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.190647*u,.32653*i,0,2*Math.PI,!1),r=n.createRadialGradient(.5*i,.190647*u,0,.5*i,.190647*u,.32653*i),r.addColorStop(0,"rgba(255, 0, 0, 0.25)"),r.addColorStop(1,"rgba(255, 0, 0, 0.05)"),n.fillStyle=r,n.fill(),n.restore(),n.save(),n.scale(1,1),n.beginPath(),n.arc(.5*i,.190647*u,.32653*i,0,2*Math.PI,!1),t=n.createRadialGradient(.5*i,.190647*u,0,.5*i,.190647*u,.32653*i),t.addColorStop(0,"rgba(1, 1, 1, 0)"),t.addColorStop(.55,"rgba(0, 0, 0, 0)"),t.addColorStop(.5501,"rgba(0, 0, 0, 0)"),t.addColorStop(.78,"rgba(0, 0, 0, 0.12)"),t.addColorStop(.79,"rgba(0, 0, 0, 0.13)"),t.addColorStop(1,"rgba(0, 0, 0, 0.5)"),n.fillStyle=t,n.fill(),n.restore(),n.fillStyle=n.createPattern(y,"repeat"),n.fill(),n.restore()};return y=at(2,2,function(n){n.save(),n.strokeStyle="rgba(0, 0, 0, 0.1)",n.beginPath(),n.lineTo(0,0,1,0),n.lineTo(0,1,0,1),n.stroke(),n.restore()}),it=function(){rt=!0,gt(bt),ni(ft),et(kt),ot(wt),st(yt),ut(pt),vt(dt),lt(ii),ct(ri),ht(ti)},this.setRedOn=function(n){d=n,this.repaint()},this.isRedOn=function(){return d},this.setYellowOn=function(n){nt=n,this.repaint()},this.isYellowOn=function(){return nt},this.setGreenOn=function(n){g=n,this.repaint()},this.isGreenOn=function(){return g},this.repaint=function(){rt||it(),f.save(),f.clearRect(0,0,f.canvas.width,f.canvas.height),f.drawImage(k,0,0),f.drawImage(p,0,0),g&&f.drawImage(s,0,0),f.drawImage(h,0,0),f.drawImage(o,0,0),nt&&f.drawImage(e,0,0),f.drawImage(c,0,0),f.drawImage(v,0,0),d&&f.drawImage(l,0,0),f.drawImage(a,0,0),f.restore()},this.repaint(),this},yr=function(n,t){function tt(n,t,i){var u=r.createElement("canvas");return u.width=n,u.height=t,i(u.getContext("2d")),u}var f,l,c,e,a,u,i,b=!1,v=!1,y=1,h=r.createElement("canvas"),g=h.getContext("2d"),o=r.createElement("canvas"),ut=o.getContext("2d"),s=r.createElement("canvas"),it=s.getContext("2d"),p;t=t||{},l=undefined===t.width?100:t.width,c=undefined===t.height?100:t.height,e=undefined===t.glowColor?"#ffff00":t.glowColor,f=document.getElementById(n).getContext("2d"),f.save(),f.clearRect(0,0,f.canvas.width,f.canvas.height),f.canvas.width=l,f.canvas.height=c,a=l<c?l:c,u=a,i=a,p=function(n){var t,i=tt(1,1,function(t){t.fillStyle=n,t.beginPath(),t.rect(0,0,1,1),t.fill()});return t=i.getContext("2d").getImageData(0,0,2,2).data,[t[0],t[1],t[2]]},h.width=u,h.height=i,o.width=u,o.height=i,s.width=u,s.height=i;var rt=function(n){var t;n.save(),n.clearRect(0,0,n.canvas.width,n.canvas.height),n.save(),n.beginPath(),n.moveTo(.289473*u,.438596*i),n.bezierCurveTo(.289473*u,.561403*i,.385964*u,.605263*i,.385964*u,.745614*i),n.bezierCurveTo(.385964*u,.745614*i,.587719*u,.745614*i,.587719*u,.745614*i),n.bezierCurveTo(.587719*u,.605263*i,.692982*u,.561403*i,.692982*u,.438596*i),n.bezierCurveTo(.692982*u,.324561*i,.605263*u,.22807*i,.5*u,.22807*i),n.bezierCurveTo(.385964*u,.22807*i,.289473*u,.324561*i,.289473*u,.438596*i),n.closePath(),t=n.createLinearGradient(0,.289473*i,0,.701754*i),t.addColorStop(0,"#eeeeee"),t.addColorStop(.99,"#999999"),t.addColorStop(1,"#999999"),n.fillStyle=t,n.fill(),n.lineCap="butt",n.lineJoin="round",n.lineWidth=.008771*u,n.strokeStyle="#cccccc",n.stroke(),n.restore(),n.restore()},d=function(n){var t,h=p(e),o=h[0],r=h[1],s=h[2],f=ar(o,r,s);n.save(),n.clearRect(0,0,n.canvas.width,n.canvas.height),n.save(),n.beginPath(),n.moveTo(.289473*u,.438596*i),n.bezierCurveTo(.289473*u,.561403*i,.385964*u,.605263*i,.385964*u,.745614*i),n.bezierCurveTo(.385964*u,.745614*i,.587719*u,.745614*i,.587719*u,.745614*i),n.bezierCurveTo(.587719*u,.605263*i,.692982*u,.561403*i,.692982*u,.438596*i),n.bezierCurveTo(.692982*u,.324561*i,.605263*u,.22807*i,.5*u,.22807*i),n.bezierCurveTo(.385964*u,.22807*i,.289473*u,.324561*i,.289473*u,.438596*i),n.closePath(),t=n.createLinearGradient(0,.289473*i,0,.701754*i),o===r&&r===s?(t.addColorStop(0,"hsl(0, 60%, 0%)"),t.addColorStop(1,"hsl(0, 40%, 0%)")):(t.addColorStop(0,"hsl("+f[0]*255+", "+f[1]*100+"%, 70%)"),t.addColorStop(1,"hsl("+f[0]*255+", "+f[1]*100+"%, 80%)")),n.fillStyle=t,n.shadowOffsetX=0,n.shadowOffsetY=0,n.shadowBlur=30,n.shadowColor=e,n.fill(),n.lineCap="butt",n.lineJoin="round",n.lineWidth=.008771*u,n.strokeStyle="rgba("+o+", "+r+", "+s+", 0.4)",n.stroke(),n.restore(),n.restore()},k=function(n){var e,t,f,r;n.save(),n.clearRect(0,0,n.canvas.width,n.canvas.height),n.save(),n.beginPath(),n.moveTo(.350877*u,.333333*i),n.bezierCurveTo(.350877*u,.280701*i,.41228*u,.236842*i,.5*u,.236842*i),n.bezierCurveTo(.578947*u,.236842*i,.64035*u,.280701*i,.64035*u,.333333*i),n.bezierCurveTo(.64035*u,.385964*i,.578947*u,.429824*i,.5*u,.429824*i),n.bezierCurveTo(.41228*u,.429824*i,.350877*u,.385964*i,.350877*u,.333333*i),n.closePath(),e=n.createLinearGradient(0,.245614*i,0,.429824*i),e.addColorStop(0,"#ffffff"),e.addColorStop(.99,"rgba(255, 255, 255, 0)"),e.addColorStop(1,"rgba(255, 255, 255, 0)"),n.fillStyle=e,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(.377192*u,.745614*i),n.bezierCurveTo(.377192*u,.745614*i,.429824*u,.72807*i,.491228*u,.72807*i),n.bezierCurveTo(.561403*u,.72807*i,.605263*u,.736842*i,.605263*u,.736842*i),n.lineTo(.605263*u,.763157*i),n.lineTo(.596491*u,.780701*i),n.lineTo(.605263*u,.798245*i),n.lineTo(.596491*u,.815789*i),n.lineTo(.605263*u,.833333*i),n.lineTo(.596491*u,.850877*i),n.lineTo(.605263*u,.868421*i),n.lineTo(.596491*u,.885964*i),n.lineTo(.605263*u,.894736*i),n.bezierCurveTo(.605263*u,.894736*i,.570175*u,.95614*i,.535087*u,.991228*i),n.bezierCurveTo(.526315*u,.991228*i,.517543*u,i,.5*u,i),n.bezierCurveTo(.482456*u,i,.473684*u,i,.464912*u,.991228*i),n.bezierCurveTo(.421052*u,.947368*i,.394736*u,.903508*i,.394736*u,.903508*i),n.lineTo(.394736*u,.894736*i),n.lineTo(.385964*u,.885964*i),n.lineTo(.394736*u,.868421*i),n.lineTo(.385964*u,.850877*i),n.lineTo(.394736*u,.833333*i),n.lineTo(.385964*u,.815789*i),n.lineTo(.394736*u,.798245*i),n.lineTo(.377192*u,.789473*i),n.lineTo(.394736*u,.771929*i),n.lineTo(.377192*u,.763157*i),n.lineTo(.377192*u,.745614*i),n.closePath(),t=n.createLinearGradient(.473684*u,.72807*i,.484702*u,.938307*i),t.addColorStop(0,"#333333"),t.addColorStop(.04,"#d9dad6"),t.addColorStop(.19,"#e4e5e0"),t.addColorStop(.24,"#979996"),t.addColorStop(.31,"#fbffff"),t.addColorStop(.4,"#818584"),t.addColorStop(.48,"#f5f7f4"),t.addColorStop(.56,"#959794"),t.addColorStop(.64,"#f2f2f0"),t.addColorStop(.7,"#828783"),t.addColorStop(.78,"#fcfcfc"),t.addColorStop(1,"#666666"),n.fillStyle=t,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(.377192*u,.745614*i),n.bezierCurveTo(.377192*u,.745614*i,.429824*u,.72807*i,.491228*u,.72807*i),n.bezierCurveTo(.561403*u,.72807*i,.605263*u,.736842*i,.605263*u,.736842*i),n.lineTo(.605263*u,.763157*i),n.lineTo(.596491*u,.780701*i),n.lineTo(.605263*u,.798245*i),n.lineTo(.596491*u,.815789*i),n.lineTo(.605263*u,.833333*i),n.lineTo(.596491*u,.850877*i),n.lineTo(.605263*u,.868421*i),n.lineTo(.596491*u,.885964*i),n.lineTo(.605263*u,.894736*i),n.bezierCurveTo(.605263*u,.894736*i,.570175*u,.95614*i,.535087*u,.991228*i),n.bezierCurveTo(.526315*u,.991228*i,.517543*u,i,.5*u,i),n.bezierCurveTo(.482456*u,i,.473684*u,i,.464912*u,.991228*i),n.bezierCurveTo(.421052*u,.947368*i,.394736*u,.903508*i,.394736*u,.903508*i),n.lineTo(.394736*u,.894736*i),n.lineTo(.385964*u,.885964*i),n.lineTo(.394736*u,.868421*i),n.lineTo(.385964*u,.850877*i),n.lineTo(.394736*u,.833333*i),n.lineTo(.385964*u,.815789*i),n.lineTo(.394736*u,.798245*i),n.lineTo(.377192*u,.789473*i),n.lineTo(.394736*u,.771929*i),n.lineTo(.377192*u,.763157*i),n.lineTo(.377192*u,.745614*i),n.closePath(),f=n.createLinearGradient(.377192*u,.789473*i,.605263*u,.789473*i),f.addColorStop(0,"rgba(0, 0, 0, 0.4)"),f.addColorStop(.15,"rgba(0, 0, 0, 0.32)"),f.addColorStop(.85,"rgba(0, 0, 0, 0.33)"),f.addColorStop(1,"rgba(0, 0, 0, 0.4)"),n.fillStyle=f,n.fill(),n.restore(),n.save(),n.beginPath(),n.moveTo(.421052*u,.947368*i),n.bezierCurveTo(.438596*u,.95614*i,.447368*u,.973684*i,.464912*u,.991228*i),n.bezierCurveTo(.473684*u,i,.482456*u,i,.5*u,i),n.bezierCurveTo(.517543*u,i,.526315*u,.991228*i,.535087*u,.991228*i),n.bezierCurveTo(.543859*u,.982456*i,.561403*u,.95614*i,.578947*u,.947368*i),n.bezierCurveTo(.552631*u,.938596*i,.526315*u,.938596*i,.5*u,.938596*i),n.bezierCurveTo(.473684*u,.938596*i,.447368*u,.938596*i,.421052*u,.947368*i),n.closePath(),r=n.createLinearGradient(0,.938596*i,0,i),r.addColorStop(0,"#050a06"),r.addColorStop(.61,"#070602"),r.addColorStop(.71,"#999288"),r.addColorStop(.83,"#010101"),r.addColorStop(1,"#000000"),n.fillStyle=r,n.fill(),n.restore(),n.restore()},nt=function(n){n.save(),n.setTransform(1,0,0,1,0,0),n.clearRect(0,0,n.canvas.width,n.canvas.height),n.restore()},w=function(){b=!0,rt(g),d(ut),k(it)};return this.setOn=function(n){v=n,this.repaint()},this.isOn=function(){return v},this.setAlpha=function(n){y=n,this.repaint()},this.getAlpha=function(){return y},this.setGlowColor=function(n){e=n,w(),this.repaint()},this.getGlowColor=function(){return e},this.repaint=function(){b||w(),nt(f),f.save(),f.drawImage(h,0,0),f.globalAlpha=y,v&&f.drawImage(o,0,0),f.globalAlpha=1,f.drawImage(s,0,0),f.restore()},this.repaint(),this},pr=function(n,i){function vt(){var t,n;for(ht=!0,v.rect(0,0,c,o),t=v.createLinearGradient(0,0,0,o),t.addColorStop(0,"rgba(0, 0, 0, 1)"),t.addColorStop(.1,"rgba(0, 0, 0, 0.4)"),t.addColorStop(.33,"rgba(255, 255, 255, 0.45)"),t.addColorStop(.46,"rgba(255, 255, 255, 0)"),t.addColorStop(.9,"rgba(0, 0, 0, 0.4)"),t.addColorStop(1,"rgba(0, 0, 0, 1)"),v.fillStyle=t,v.fill(),r.rect(0,0,f,s*1.1),r.fillStyle=lt,r.fill(),r.strokeStyle="#f0f0f0",r.lineWidth="1px",r.moveTo(0,0),r.lineTo(0,s*1.1),r.stroke(),r.strokeStyle="#202020",r.moveTo(f,0),r.lineTo(f,s*1.1),r.stroke(),r.textAlign="center",r.textBaseline="middle",r.font=ut,r.fillStyle=ct,n=9;n<21;n++)r.fillText(n%10,f*.5,h*(n-9)+h/2);if(l>0)for(u.rect(0,0,f,s*1.1),u.fillStyle=ot,u.fill(),u.strokeStyle="#f0f0f0",u.lineWidth="1px",u.moveTo(0,0),u.lineTo(0,s*1.1),u.stroke(),u.strokeStyle="#202020",u.moveTo(f,0),u.lineTo(f,s*1.1),u.stroke(),u.textAlign="center",u.textBaseline="middle",u.font=ut,u.fillStyle=et,n=9;n<21;n++)u.fillText(n%10,f*.5,h*(n-9)+h/2);for(n=0;n<p+l;n++)g[n]=Math.random()*rt*o-rt*o/2}function at(){for(var s=1,u,t,i,o,r=e,n=0;n<l;n++)r*=10;for(t=Math.floor(r),i=r-t,t=String(t),o=9,n=0;n<l+p;n++)u=+t.substring(t.length-n-1,t.length-n)||0,o!==9&&(i=0),n<l?w.drawImage(d,c-f*s,-(h*(u+i)+tt+g[n])):w.drawImage(b,c-f*s,-(h*(u+i)+tt+g[n])),s++,o=u}var yt=document,it,o,p,l,ot,et,st,e,lt,ct,rt,ht=!1,a,y,ft,f,ut,c,s,h,tt,g=[],k,w,nt,v,b,r,d,u;i=i||{},it=undefined===i.height?null:i._context,o=undefined===i.height?40:i.height,p=undefined===i.digits?6:i.digits,l=undefined===i.decimals?1:i.decimals,ot=undefined===i.decimalBackColor?"#F0F0F0":i.decimalBackColor,et=undefined===i.decimalForeColor?"#F01010":i.decimalForeColor,st=undefined===i.font?"sans-serif":i.font,e=undefined===i.value?0:i.value,lt=undefined===i.valueBackColor?"#050505":i.valueBackColor,ct=undefined===i.valueForeColor?"#F8F8F8":i.valueForeColor,rt=undefined===i.wobbleFactor?.07:i.wobbleFactor,y=it?it:yt.getElementById(n).getContext("2d"),e<0&&(e=0),ft=Math.floor(o*.85),ut="600 "+ft+"px "+st,f=Math.floor(o*.68),c=f*(p+l),s=ft*11,h=s/12,tt=h*.81,y.canvas.width=c,y.canvas.height=o,k=t(c,o),w=k.getContext("2d"),nt=t(c,o),v=nt.getContext("2d"),b=t(f,s*1.1),r=b.getContext("2d"),d=t(f,s*1.1),u=d.getContext("2d"),this.setValueAnimated=function(n){var t=this;n<0&&(n=0),e!==n&&(undefined!==a&&a.playing&&a.stop(),a=new Tween({},"",Tween.strongEaseOut,e,n,2),a.onMotionChanged=function(n){e=n.target._pos,t.repaint()},a.start()),this.repaint()},this.setValue=function(n){e=n,e<0&&(e=0),this.repaint()},this.getValue=function(){return e},this.repaint=function(){ht||vt(),at(),w.drawImage(nt,0,0),y.drawImage(k,0,0)},this.repaint()},hi=function(n,t,i,r,u,f){var c=!0,e,o,h=Math.PI/180,s=f.symbolColor.getRgbaColor();for(n.save(),n.lineWidth=1,n.fillStyle=s,n.strokeStyle=s,n.translate(t,i),e=0;e<360;e+=15)c=!c,n.beginPath(),n.arc(0,0,r*.26,e*h,(e+15)*h,!1),n.arc(0,0,r*.23,(e+15)*h,e*h,!0),n.closePath(),c&&n.fill(),n.stroke();for(n.translate(-t,-i),e=0;360>=e;e+=90)n.beginPath(),n.moveTo(r*.560747,u*.584112),n.lineTo(r*.640186,u*.644859),n.lineTo(r*.584112,u*.560747),n.lineTo(r*.560747,u*.584112),n.closePath(),n.fillStyle=s,n.fill(),n.stroke(),n.beginPath(),n.moveTo(r*.523364,u*.397196),n.lineTo(r*.5,u*.196261),n.lineTo(r*.471962,u*.397196),n.lineTo(r*.523364,u*.397196),n.closePath(),o=n.createLinearGradient(.476635*r,0,.518691*r,0),o.addColorStop(0,"rgb(222, 223, 218)"),o.addColorStop(.48,"rgb(222, 223, 218)"),o.addColorStop(.49,s),o.addColorStop(1,s),n.fillStyle=o,n.fill(),n.stroke(),n.translate(t,i),n.rotate(e*Math.PI/180),n.translate(-t,-i);n.beginPath(),n.translate(t,i),n.arc(0,0,r*.1,0,Math.PI*2,!1),n.lineWidth=r*.022,n.stroke(),n.translate(-t,-i),n.restore()},nt=function(n,i,r,u,f,e){var l,o,s,h,c=i.toString()+r.type+u.light.getHexColor()+u.medium.getHexColor()+e;if(!nt.cache[c]){l=t(i,i),o=l.getContext("2d"),e&&(o.fillStyle="rgba(0, 0, 0, 1)",o.strokeStyle="rgba(0, 0, 0, 1)",o.shadowBlur=3,o.globalAlpha=.5);switch(r.type){case"type2":e||(s=o.createLinearGradient(0,i*.471962,0,i*.130841),s.addColorStop(0,f.getRgbaColor()),s.addColorStop(.36,f.getRgbaColor()),s.addColorStop(.361,u.light.getRgbaColor()),s.addColorStop(1,u.light.getRgbaColor()),o.fillStyle=s),o.beginPath(),o.moveTo(i*.518691,i*.471962),o.lineTo(i*.509345,i*.462616),o.lineTo(i*.509345,i*.341121),o.lineTo(i*.504672,i*.130841),o.lineTo(i*.495327,i*.130841),o.lineTo(i*.490654,i*.341121),o.lineTo(i*.490654,i*.462616),o.lineTo(i*.481308,i*.471962),o.closePath(),o.fill();break;case"type3":o.beginPath(),o.rect(i*.495327,i*.130841,i*.009345,i*.373831),o.closePath(),e||(o.fillStyle=u.light.getRgbaColor()),o.fill();break;case"type4":e||(s=o.createLinearGradient(.467289*i,0,.528036*i,0),s.addColorStop(0,u.dark.getRgbaColor()),s.addColorStop(.51,u.dark.getRgbaColor()),s.addColorStop(.52,u.light.getRgbaColor()),s.addColorStop(1,u.light.getRgbaColor()),o.fillStyle=s),o.beginPath(),o.moveTo(i*.5,i*.126168),o.lineTo(i*.514018,i*.135514),o.lineTo(i*.53271,i*.5),o.lineTo(i*.523364,i*.602803),o.lineTo(i*.476635,i*.602803),o.lineTo(i*.467289,i*.5),o.lineTo(i*.485981,i*.135514),o.lineTo(i*.5,i*.126168),o.closePath(),o.fill();break;case"type5":e||(s=o.createLinearGradient(.471962*i,0,.528036*i,0),s.addColorStop(0,u.light.getRgbaColor()),s.addColorStop(.5,u.light.getRgbaColor()),s.addColorStop(.5,u.medium.getRgbaColor()),s.addColorStop(1,u.medium.getRgbaColor()),o.fillStyle=s),o.beginPath(),o.moveTo(i*.5,i*.495327),o.lineTo(i*.528037,i*.495327),o.lineTo(i*.5,i*.149532),o.lineTo(i*.471962,i*.495327),o.lineTo(i*.5,i*.495327),o.closePath(),o.fill(),o.lineWidth=1,o.lineCap="square",o.lineJoin="miter",e||(o.strokeStyle=u.dark.getRgbaColor()),o.stroke();break;case"type6":e||(o.fillStyle=u.medium.getRgbaColor()),o.beginPath(),o.moveTo(i*.481308,i*.485981),o.lineTo(i*.481308,i*.392523),o.lineTo(i*.485981,i*.317757),o.lineTo(i*.495327,i*.130841),o.lineTo(i*.504672,i*.130841),o.lineTo(i*.514018,i*.317757),o.lineTo(i*.518691,i*.38785),o.lineTo(i*.518691,i*.485981),o.lineTo(i*.504672,i*.485981),o.lineTo(i*.504672,i*.38785),o.lineTo(i*.5,i*.317757),o.lineTo(i*.495327,i*.392523),o.lineTo(i*.495327,i*.485981),o.lineTo(i*.481308,i*.485981),o.closePath(),o.fill();break;case"type7":e||(s=o.createLinearGradient(.481308*i,0,.518691*i,0),s.addColorStop(0,u.dark.getRgbaColor()),s.addColorStop(1,u.medium.getRgbaColor()),o.fillStyle=s),o.beginPath(),o.moveTo(i*.490654,i*.130841),o.lineTo(i*.481308,i*.5),o.lineTo(i*.518691,i*.5),o.lineTo(i*.504672,i*.130841),o.lineTo(i*.490654,i*.130841),o.closePath(),o.fill();break;case"type8":e||(s=o.createLinearGradient(.471962*i,0,.528036*i,0),s.addColorStop(0,u.light.getRgbaColor()),s.addColorStop(.5,u.light.getRgbaColor()),s.addColorStop(.5,u.medium.getRgbaColor()),s.addColorStop(1,u.medium.getRgbaColor()),o.fillStyle=s,o.strokeStyle=u.dark.getRgbaColor()),o.beginPath(),o.moveTo(i*.5,i*.53271),o.lineTo(i*.53271,i*.5),o.bezierCurveTo(i*.53271,i*.5,i*.509345,i*.457943,i*.5,i*.149532),o.bezierCurveTo(i*.490654,i*.457943,i*.467289,i*.5,i*.467289,i*.5),o.lineTo(i*.5,i*.53271),o.closePath(),o.fill(),o.stroke();break;case"type9":e||(s=o.createLinearGradient(.471962*i,0,.528036*i,0),s.addColorStop(0,"rgb(50, 50, 50)"),s.addColorStop(.5,"#666666"),s.addColorStop(1,"rgb(50, 50, 50)"),o.fillStyle=s,o.strokeStyle="#2E2E2E"),o.beginPath(),o.moveTo(i*.495327,i*.233644),o.lineTo(i*.504672,i*.233644),o.lineTo(i*.514018,i*.439252),o.lineTo(i*.485981,i*.439252),o.lineTo(i*.495327,i*.233644),o.closePath(),o.moveTo(i*.490654,i*.130841),o.lineTo(i*.471962,i*.471962),o.lineTo(i*.471962,i*.528037),o.bezierCurveTo(i*.471962,i*.528037,i*.476635,i*.602803,i*.476635,i*.602803),o.bezierCurveTo(i*.476635,i*.607476,i*.481308,i*.607476,i*.5,i*.607476),o.bezierCurveTo(i*.518691,i*.607476,i*.523364,i*.607476,i*.523364,i*.602803),o.bezierCurveTo(i*.523364,i*.602803,i*.528037,i*.528037,i*.528037,i*.528037),o.lineTo(i*.528037,i*.471962),o.lineTo(i*.509345,i*.130841),o.lineTo(i*.490654,i*.130841),o.closePath(),o.fill(),o.beginPath(),o.moveTo(i*.495327,i*.219626),o.lineTo(i*.504672,i*.219626),o.lineTo(i*.504672,i*.135514),o.lineTo(i*.495327,i*.135514),o.lineTo(i*.495327,i*.219626),o.closePath(),e||(o.fillStyle=u.medium.getRgbaColor()),o.fill();break;case"type10":o.beginPath(),o.moveTo(i*.5,i*.149532),o.bezierCurveTo(i*.5,i*.149532,i*.443925,i*.490654,i*.443925,i*.5),o.bezierCurveTo(i*.443925,i*.53271,i*.467289,i*.556074,i*.5,i*.556074),o.bezierCurveTo(i*.53271,i*.556074,i*.556074,i*.53271,i*.556074,i*.5),o.bezierCurveTo(i*.556074,i*.490654,i*.5,i*.149532,i*.5,i*.149532),o.closePath(),e||(s=o.createLinearGradient(.471962*i,0,.528036*i,0),s.addColorStop(0,u.light.getRgbaColor()),s.addColorStop(.5,u.light.getRgbaColor()),s.addColorStop(.5,u.medium.getRgbaColor()),s.addColorStop(1,u.medium.getRgbaColor()),o.fillStyle=s,o.strokeStyle=u.medium.getRgbaColor()),o.lineWidth=1,o.lineCap="square",o.lineJoin="miter",o.fill(),o.stroke();break;case"type11":o.beginPath(),o.moveTo(.5*i,.168224*i),o.lineTo(.485981*i,.5*i),o.bezierCurveTo(.485981*i,.5*i,.481308*i,.584112*i,.5*i,.584112*i),o.bezierCurveTo(.514018*i,.584112*i,.509345*i,.5*i,.509345*i,.5*i),o.lineTo(.5*i,.168224*i),o.closePath(),e||(s=o.createLinearGradient(0,.168224*i,0,.584112*i),s.addColorStop(0,u.medium.getRgbaColor()),s.addColorStop(1,u.dark.getRgbaColor()),o.fillStyle=s,o.strokeStyle=u.dark.getRgbaColor()),o.fill(),o.stroke();break;case"type12":o.beginPath(),o.moveTo(.5*i,.168224*i),o.lineTo(.485981*i,.5*i),o.lineTo(.5*i,.504672*i),o.lineTo(.509345*i,.5*i),o.lineTo(.5*i,.168224*i),o.closePath(),e||(s=o.createLinearGradient(0,.168224*i,0,.504672*i),s.addColorStop(0,u.medium.getRgbaColor()),s.addColorStop(1,u.dark.getRgbaColor()),o.fillStyle=s,o.strokeStyle=u.dark.getRgbaColor()),o.fill(),o.stroke();break;case"type13":case"type14":o.beginPath(),o.moveTo(.485981*i,.168224*i),o.lineTo(.5*i,.130841*i),o.lineTo(.509345*i,.168224*i),o.lineTo(.509345*i,.509345*i),o.lineTo(.485981*i,.509345*i),o.lineTo(.485981*i,.168224*i),o.closePath(),e||(r.type==="type13"?(s=o.createLinearGradient(0,.5*i,0,.130841*i),s.addColorStop(0,f.getRgbaColor()),s.addColorStop(.85,f.getRgbaColor()),s.addColorStop(.85,u.medium.getRgbaColor()),s.addColorStop(1,u.medium.getRgbaColor()),o.fillStyle=s):(s=o.createLinearGradient(.485981*i,0,.509345*i,0),s.addColorStop(0,u.veryDark.getRgbaColor()),s.addColorStop(.5,u.light.getRgbaColor()),s.addColorStop(1,u.veryDark.getRgbaColor()),o.fillStyle=s)),o.fill();break;case"type15":case"type16":o.beginPath(),o.moveTo(i*.509345,i*.457943),o.lineTo(i*.5015,i*.13),o.lineTo(i*.4985,i*.13),o.lineTo(i*.490654,i*.457943),o.bezierCurveTo(i*.490654,i*.457943,i*.490654,i*.457943,i*.490654,i*.457943),o.bezierCurveTo(i*.471962,i*.462616,i*.457943,i*.481308,i*.457943,i*.5),o.bezierCurveTo(i*.457943,i*.518691,i*.471962,i*.537383,i*.490654,i*.542056),o.bezierCurveTo(i*.490654,i*.542056,i*.490654,i*.542056,i*.490654,i*.542056),r.type==="type15"?(o.lineTo(i*.490654,i*.57),o.bezierCurveTo(i*.46,i*.58,i*.46,i*.62,i*.490654,i*.63),o.bezierCurveTo(i*.47,i*.62,i*.48,i*.59,i*.5,i*.59),o.bezierCurveTo(i*.53,i*.59,i*.52,i*.62,i*.509345,i*.63),o.bezierCurveTo(i*.54,i*.62,i*.54,i*.58,i*.509345,i*.57),o.lineTo(i*.509345,i*.57)):(o.lineTo(i*.490654,i*.621495),o.lineTo(i*.509345,i*.621495)),o.lineTo(i*.509345,i*.542056),o.bezierCurveTo(i*.509345,i*.542056,i*.509345,i*.542056,i*.509345,i*.542056),o.bezierCurveTo(i*.528037,i*.537383,i*.542056,i*.518691,i*.542056,i*.5),o.bezierCurveTo(i*.542056,i*.481308,i*.528037,i*.462616,i*.509345,i*.457943),o.bezierCurveTo(i*.509345,i*.457943,i*.509345,i*.457943,i*.509345,i*.457943),o.closePath(),e?o.fill():(s=r.type==="type15"?o.createLinearGradient(0,0,0,i*.63):o.createLinearGradient(0,0,0,i*.621495),s.addColorStop(0,u.medium.getRgbaColor()),s.addColorStop(.388888,u.medium.getRgbaColor()),s.addColorStop(.5,u.light.getRgbaColor()),s.addColorStop(.611111,u.medium.getRgbaColor()),s.addColorStop(1,u.medium.getRgbaColor()),o.fillStyle=s,o.strokeStyle=u.dark.getRgbaColor(),o.fill(),o.stroke()),e||(o.beginPath(),h=i*.06542/2,o.arc(i*.5,i*.5,h,0,Math.PI*2),s=o.createLinearGradient(i*.5-h,i*.5+h,0,i*.5+h),s.addColorStop(0,"#e6b35c"),s.addColorStop(.01,"#e6b35c"),s.addColorStop(.99,"#c48200"),s.addColorStop(1,"#c48200"),o.fillStyle=s,o.closePath(),o.fill(),o.beginPath(),h=i*.046728/2,o.arc(i*.5,i*.5,h,0,Math.PI*2),s=o.createRadialGradient(i*.5,i*.5,0,i*.5,i*.5,h),s.addColorStop(0,"#c5c5c5"),s.addColorStop(.19,"#c5c5c5"),s.addColorStop(.22,"#000000"),s.addColorStop(.8,"#000000"),s.addColorStop(.99,"#707070"),s.addColorStop(1,"#707070"),o.fillStyle=s,o.closePath(),o.fill());break;case"type1":default:e||(s=o.createLinearGradient(0,i*.471962,0,i*.130841),s.addColorStop(0,u.veryDark.getRgbaColor()),s.addColorStop(.3,u.medium.getRgbaColor()),s.addColorStop(.59,u.medium.getRgbaColor()),s.addColorStop(1,u.veryDark.getRgbaColor()),o.fillStyle=s),o.beginPath(),o.moveTo(i*.518691,i*.471962),o.bezierCurveTo(i*.514018,i*.457943,i*.509345,i*.415887,i*.509345,i*.401869),o.bezierCurveTo(i*.504672,i*.383177,i*.5,i*.130841,i*.5,i*.130841),o.bezierCurveTo(i*.5,i*.130841,i*.490654,i*.383177,i*.490654,i*.397196),o.bezierCurveTo(i*.490654,i*.415887,i*.485981,i*.457943,i*.481308,i*.471962),o.bezierCurveTo(i*.471962,i*.481308,i*.467289,i*.490654,i*.467289,i*.5),o.bezierCurveTo(i*.467289,i*.518691,i*.481308,i*.53271,i*.5,i*.53271),o.bezierCurveTo(i*.518691,i*.53271,i*.53271,i*.518691,i*.53271,i*.5),o.bezierCurveTo(i*.53271,i*.490654,i*.528037,i*.481308,i*.518691,i*.471962),o.closePath(),o.fill()}nt.cache[c]=l}return n.drawImage(nt.cache[c],0,0),this},v,ht,w,rt,ot,y,et,e,o,p,a,d,h,u,b,ut,at,pt,ri,dt,g,l,lt,gt,bt,yt;nt.cache={},v=function(i,r,u,f,e,o){var w,s,h,y,c,l,a,p=e.toString()+o+r.design;if(!v.cache[p]){w=t(e,o),s=w.getContext("2d"),s.fillStyle="#848484",s.strokeStyle="rgba(132, 132, 132, 0.5)",s.beginPath(),s.arc(u,f,e/2,0,Math.PI*2,!0),s.closePath(),s.fill(),s.stroke(),s.beginPath(),s.arc(u,f,e*.990654/2,0,Math.PI*2,!0),s.closePath();switch(r.design){case"metal":h=s.createLinearGradient(0,e*.004672,0,o*.990654),h.addColorStop(0,"#fefefe"),h.addColorStop(.07,"rgb(210, 210, 210)"),h.addColorStop(.12,"rgb(179, 179, 179)"),h.addColorStop(1,"rgb(213, 213, 213)"),s.fillStyle=h,s.fill();break;case"brass":h=s.createLinearGradient(0,e*.004672,0,o*.990654),h.addColorStop(0,"rgb(249, 243, 155)"),h.addColorStop(.05,"rgb(246, 226, 101)"),h.addColorStop(.1,"rgb(240, 225, 132)"),h.addColorStop(.5,"rgb(90, 57, 22)"),h.addColorStop(.9,"rgb(249, 237, 139)"),h.addColorStop(.95,"rgb(243, 226, 108)"),h.addColorStop(1,"rgb(202, 182, 113)"),s.fillStyle=h,s.fill();break;case"steel":h=s.createLinearGradient(0,e*.004672,0,o*.990654),h.addColorStop(0,"rgb(231, 237, 237)"),h.addColorStop(.05,"rgb(189, 199, 198)"),h.addColorStop(.1,"rgb(192, 201, 200)"),h.addColorStop(.5,"rgb(23, 31, 33)"),h.addColorStop(.9,"rgb(196, 205, 204)"),h.addColorStop(.95,"rgb(194, 204, 203)"),h.addColorStop(1,"rgb(189, 201, 199)"),s.fillStyle=h,s.fill();break;case"gold":h=s.createLinearGradient(0,e*.004672,0,o*.990654),h.addColorStop(0,"rgb(255, 255, 207)"),h.addColorStop(.15,"rgb(255, 237, 96)"),h.addColorStop(.22,"rgb(254, 199, 57)"),h.addColorStop(.3,"rgb(255, 249, 203)"),h.addColorStop(.38,"rgb(255, 199, 64)"),h.addColorStop(.44,"rgb(252, 194, 60)"),h.addColorStop(.51,"rgb(255, 204, 59)"),h.addColorStop(.6,"rgb(213, 134, 29)"),h.addColorStop(.68,"rgb(255, 201, 56)"),h.addColorStop(.75,"rgb(212, 135, 29)"),h.addColorStop(1,"rgb(247, 238, 101)"),s.fillStyle=h,s.fill();break;case"anthracite":h=s.createLinearGradient(0,.004672*o,0,.995326*o),h.addColorStop(0,"rgb(118, 117, 135)"),h.addColorStop(.06,"rgb(74, 74, 82)"),h.addColorStop(.12,"rgb(50, 50, 54)"),h.addColorStop(1,"rgb(79, 79, 87)"),s.fillStyle=h,s.fill();break;case"tiltedGray":h=s.createLinearGradient(.233644*e,.084112*o,.81258*e,.910919*o),h.addColorStop(0,"#ffffff"),h.addColorStop(.07,"rgb(210, 210, 210)"),h.addColorStop(.16,"rgb(179, 179, 179)"),h.addColorStop(.33,"#ffffff"),h.addColorStop(.55,"#c5c5c5"),h.addColorStop(.79,"#ffffff"),h.addColorStop(1,"#666666"),s.fillStyle=h,s.fill();break;case"tiltedBlack":h=s.createLinearGradient(.228971*e,.079439*o,.802547*e,.898591*o),h.addColorStop(0,"#666666"),h.addColorStop(.21,"#000000"),h.addColorStop(.47,"#666666"),h.addColorStop(.99,"#000000"),h.addColorStop(1,"#000000"),s.fillStyle=h,s.fill();break;case"glossyMetal":h=s.createRadialGradient(.5*e,.5*o,0,.5*e,.5*e,.5*e),h.addColorStop(0,"rgb(207, 207, 207)"),h.addColorStop(.96,"rgb(205, 204, 205)"),h.addColorStop(1,"rgb(244, 244, 244)"),s.fillStyle=h,s.fill(),s.beginPath(),s.arc(.5*e,.5*o,.973962*e/2,0,Math.PI*2),s.closePath(),h=s.createLinearGradient(0,o-.971962*o,0,.971962*o),h.addColorStop(0,"rgb(249, 249, 249)"),h.addColorStop(.23,"rgb(200, 195, 191)"),h.addColorStop(.36,"#ffffff"),h.addColorStop(.59,"rgb(29, 29, 29)"),h.addColorStop(.76,"rgb(200, 194, 192)"),h.addColorStop(1,"rgb(209, 209, 209)"),s.fillStyle=h,s.fill(),s.beginPath(),s.arc(.5*e,.5*o,.869158*e/2,0,Math.PI*2),s.closePath(),s.fillStyle="#f6f6f6",s.fill(),s.beginPath(),s.arc(.5*e,.5*o,.85*e/2,0,Math.PI*2),s.closePath(),s.fillStyle="#333333",s.fill();break;case"blackMetal":l=[0,.125,.347222,.5,.680555,.875,1],a=[new n(254,254,254,1),new n(0,0,0,1),new n(153,153,153,1),new n(0,0,0,1),new n(153,153,153,1),new n(0,0,0,1),new n(254,254,254,1)],s.save(),s.clip(s.arc(u,f,e*.990654/2,0,Math.PI*2,!0)),y=e*.495327,c=e*.42056,h=new it(l,a,-Math.PI/2),h.fill(s,u,f,c,y),s.strokeStyle="#848484",s.strokeStyle="rgba(132, 132, 132, 0.8)",s.beginPath(),s.lineWidth=e/90,s.arc(u,f,e/2,0,Math.PI*2,!0),s.closePath(),s.stroke(),s.restore();break;case"shinyMetal":l=[0,.125,.25,.347222,.5,.652777,.75,.875,1],a=[new n(254,254,254,1),new n(210,210,210,1),new n(179,179,179,1),new n(238,238,238,1),new n(160,160,160,1),new n(238,238,238,1),new n(179,179,179,1),new n(210,210,210,1),new n(254,254,254,1)],s.save(),s.clip(s.arc(u,f,e*.990654/2,0,Math.PI*2,!0)),y=e*.495327,c=e*.42056,h=new it(l,a,-Math.PI/2),h.fill(s,u,f,c,y),s.strokeStyle="#848484",s.strokeStyle="rgba(132, 132, 132, 0.8)",s.beginPath(),s.lineWidth=e/90,s.arc(u,f,e/2,0,Math.PI*2,!0),s.closePath(),s.stroke(),s.restore();break;case"chrome":l=[0,.09,.12,.16,.25,.29,.33,.38,.48,.52,.63,.68,.8,.83,.87,.97,1],a=[new n(255,255,255,1),new n(255,255,255,1),new n(136,136,138,1),new n(164,185,190,1),new n(158,179,182,1),new n(112,112,112,1),new n(221,227,227,1),new n(155,176,179,1),new n(156,176,177,1),new n(254,255,255,1),new n(255,255,255,1),new n(156,180,180,1),new n(198,209,211,1),new n(246,248,247,1),new n(204,216,216,1),new n(164,188,190,1),new n(255,255,255,1)],s.save(),s.clip(s.arc(u,f,e*.990654/2,0,Math.PI*2,!0)),y=e*.495327,c=e*.42056,h=new it(l,a,-Math.PI/2),h.fill(s,u,f,c,y),s.strokeStyle="#848484",s.strokeStyle="rgba(132, 132, 132, 0.8)",s.beginPath(),s.lineWidth=e/90,s.arc(u,f,e/2,0,Math.PI*2,!0),s.closePath(),s.stroke(),s.restore()}s.fillStyle="rgb(191, 191, 191)",s.beginPath(),s.arc(u,f,e*.841121/2,0,Math.PI*2,!0),s.closePath(),s.fill(),s.globalCompositeOperation="destination-out",s.beginPath(),s.arc(u,f,e*.831775/2,0,Math.PI*2,!0),s.closePath(),s.fill(),v.cache[p]=w}return i.drawImage(v.cache[p],0,0),this},v.cache={},ht=function(i,r,u,f,e){var c,k,h,l,d,w,o,p=[],v=[],y,a,b=u.toString()+f+r.design+e;if(!ht.cache[b]){c=Math.sqrt(u*u+f*f)*.04,c=Math.min(c,(e?u:f)*.1),k=t(u,f),h=k.getContext("2d"),e?(l=u*.05,d=l-1,w=u*.028571):(l=f*.05,d=l-1,w=f*.028571),s(h,0,0,u,f,l),h.fillStyle="#838383",h.fill(),s(h,1,1,u-2,f-2,d);switch(r.design){case"metal":o=h.createLinearGradient(0,u*.004672,0,f*.990654),o.addColorStop(0,"#fefefe"),o.addColorStop(.07,"rgb(210, 210, 210)"),o.addColorStop(.12,"rgb(179, 179, 179)"),o.addColorStop(1,"rgb(213, 213, 213)"),h.fillStyle=o,h.fill();break;case"brass":o=h.createLinearGradient(0,u*.004672,0,f*.990654),o.addColorStop(0,"rgb(249, 243, 155)"),o.addColorStop(.05,"rgb(246, 226, 101)"),o.addColorStop(.1,"rgb(240, 225, 132)"),o.addColorStop(.5,"rgb(90, 57, 22)"),o.addColorStop(.9,"rgb(249, 237, 139)"),o.addColorStop(.95,"rgb(243, 226, 108)"),o.addColorStop(1,"rgb(202, 182, 113)"),h.fillStyle=o,h.fill();break;case"steel":o=h.createLinearGradient(0,u*.004672,0,f*.990654),o.addColorStop(0,"rgb(231, 237, 237)"),o.addColorStop(.05,"rgb(189, 199, 198)"),o.addColorStop(.1,"rgb(192, 201, 200)"),o.addColorStop(.5,"rgb(23, 31, 33)"),o.addColorStop(.9,"rgb(196, 205, 204)"),o.addColorStop(.95,"rgb(194, 204, 203)"),o.addColorStop(1,"rgb(189, 201, 199)"),h.fillStyle=o,h.fill();break;case"gold":o=h.createLinearGradient(0,u*.004672,0,f*.990654),o.addColorStop(0,"rgb(255, 255, 207)"),o.addColorStop(.15,"rgb(255, 237, 96)"),o.addColorStop(.22,"rgb(254, 199, 57)"),o.addColorStop(.3,"rgb(255, 249, 203)"),o.addColorStop(.38,"rgb(255, 199, 64)"),o.addColorStop(.44,"rgb(252, 194, 60)"),o.addColorStop(.51,"rgb(255, 204, 59)"),o.addColorStop(.6,"rgb(213, 134, 29)"),o.addColorStop(.68,"rgb(255, 201, 56)"),o.addColorStop(.75,"rgb(212, 135, 29)"),o.addColorStop(1,"rgb(247, 238, 101)"),h.fillStyle=o,h.fill();break;case"anthracite":o=h.createLinearGradient(0,.004672*f,0,.995326*f),o.addColorStop(0,"rgb(118, 117, 135)"),o.addColorStop(.06,"rgb(74, 74, 82)"),o.addColorStop(.12,"rgb(50, 50, 54)"),o.addColorStop(1,"rgb(79, 79, 87)"),h.fillStyle=o,h.fill();break;case"tiltedGray":o=h.createLinearGradient(.233644*u,.084112*f,.81258*u,.910919*f),o.addColorStop(0,"#ffffff"),o.addColorStop(.07,"rgb(210, 210, 210)"),o.addColorStop(.16,"rgb(179, 179, 179)"),o.addColorStop(.33,"#ffffff"),o.addColorStop(.55,"#c5c5c5"),o.addColorStop(.79,"#ffffff"),o.addColorStop(1,"#666666"),h.fillStyle=o,h.fill();break;case"tiltedBlack":o=h.createLinearGradient(.228971*u,.079439*f,.802547*u,.898591*f),o.addColorStop(0,"#666666"),o.addColorStop(.21,"#000000"),o.addColorStop(.47,"#666666"),o.addColorStop(.99,"#000000"),o.addColorStop(1,"#000000"),h.fillStyle=o,h.fill();break;case"glossyMetal":h.clip(s(h,1,1,u-2,f-2,l)),o=h.createLinearGradient(0,1,0,f-2),o.addColorStop(0,"rgb(249, 249, 249)"),o.addColorStop(.2,"rgb(200, 195, 191)"),o.addColorStop(.3,"#ffffff"),o.addColorStop(.6,"rgb(29, 29, 29)"),o.addColorStop(.8,"rgb(200, 194, 192)"),o.addColorStop(1,"rgb(209, 209, 209)"),h.fillStyle=o,h.fill(),h.clip(s(h,c-2,c-2,u-(c-2)*2,f-(c-2)*2,w)),h.fillStyle="#f6f6f6",h.fill(),h.clip(s(h,c-1,c-1,u-(c-1)*2,f-(c-1)*2,w)),h.fillStyle="#333333",h.fill();break;case"blackMetal":p=[0,.125,.347222,.5,.680555,.875,1],v=[new n(254,254,254,1),new n(0,0,0,1),new n(153,153,153,1),new n(0,0,0,1),new n(153,153,153,1),new n(0,0,0,1),new n(254,254,254,1)],y=0,a=Math.sqrt(f*f+u*u)/2,h.clip(s(h,1,1,u-2,f-2,l)),o=new it(p,v,-Math.PI/2),o.fill(h,u/2,f/2,y,a);break;case"shinyMetal":p=[0,.125,.25,.347222,.5,.652777,.75,.875,1],v=[new n(254,254,254,1),new n(210,210,210,1),new n(179,179,179,1),new n(238,238,238,1),new n(160,160,160,1),new n(238,238,238,1),new n(179,179,179,1),new n(210,210,210,1),new n(254,254,254,1)],y=0,a=Math.sqrt(f*f+u*u)/2,h.clip(s(h,1,1,u-2,f-2,l)),o=new it(p,v,-Math.PI/2),o.fill(h,u/2,f/2,y,a);break;case"chrome":p=[0,.09,.12,.16,.25,.29,.33,.38,.48,.52,.63,.68,.8,.83,.87,.97,1],v=[new n(255,255,255,1),new n(255,255,255,1),new n(136,136,138,1),new n(164,185,190,1),new n(158,179,182,1),new n(112,112,112,1),new n(221,227,227,1),new n(155,176,179,1),new n(156,176,177,1),new n(254,255,255,1),new n(255,255,255,1),new n(156,180,180,1),new n(198,209,211,1),new n(246,248,247,1),new n(204,216,216,1),new n(164,188,190,1),new n(255,255,255,1)],y=0,a=Math.sqrt(f*f+u*u)/2,h.clip(s(h,1,1,u-2,f-2,l)),o=new it(p,v,-Math.PI/2),o.fill(h,u/2,f/2,y,a)}s(h,c-1,c-1,u-(c-1)*2,f-(c-1)*2,w-1),h.fillStyle="rgb(192, 192, 192)",h.globalCompositeOperation="destination-out",s(h,c,c,u-c*2,f-c*2,4),h.fill(),ht.cache[b]=k}return i.drawImage(ht.cache[b],0,0),this},ht.cache={},w=function(i,r,u,f,e,o){var p,s,h,k,d,g=e*.831775/2,et,ft,rt,tt,ut,c,v,a,l,nt,y,b=e.toString()+o+r.name;if(!w.cache[b]){p=t(e,o),s=p.getContext("2d"),s.beginPath(),s.arc(u,f,e*.831775/2,0,Math.PI*2,!0),s.closePath();if(r.name==="CARBON"||r.name==="PUNCHED_SHEET"||r.name==="BRUSHED_METAL"||r.name==="BRUSHED_STAINLESS")r.name==="CARBON"&&(s.fillStyle=s.createPattern(vi,"repeat"),s.fill()),r.name==="PUNCHED_SHEET"&&(s.fillStyle=s.createPattern(ci,"repeat"),s.fill()),h=s.createLinearGradient(g,0,e-g,0),h.addColorStop(0,"rgba(0, 0, 0, 0.25)"),h.addColorStop(.5,"rgba(0, 0, 0, 0)"),h.addColorStop(1,"rgba(0, 0, 0, 0.25)"),s.fillStyle=h,s.beginPath(),s.arc(u,f,e*.831775/2,0,Math.PI*2,!0),s.closePath(),s.fill(),(r.name==="BRUSHED_METAL"||r.name==="BRUSHED_STAINLESS")&&(et=r.name==="BRUSHED_METAL"?!0:!1,ft=parseInt(r.gradientStop.getHexColor().substr(-6),16),rt=li(ft,5,.1,et,.5),s.fillStyle=s.createPattern(rt.fill(0,0,e,o),"no-repeat"),s.fill());else if(r.name==="STAINLESS"||r.name==="TURNED"){k=[0,.03,.1,.14,.24,.33,.38,.5,.62,.67,.76,.81,.85,.97,1],d=[new n("#FDFDFD"),new n("#FDFDFD"),new n("#B2B2B4"),new n("#ACACAE"),new n("#FDFDFD"),new n("#6E6E70"),new n("#6E6E70"),new n("#FDFDFD"),new n("#6E6E70"),new n("#6E6E70"),new n("#FDFDFD"),new n("#ACACAE"),new n("#B2B2B4"),new n("#FDFDFD"),new n("#FDFDFD")],tt=e*.831775/2,ut=0,h=new it(k,d,Math.PI/1.75),h.fill(s,u,f,ut,tt),s.closePath();if(r.name==="TURNED"){for(c=Math.PI*2,v=e*.831775/2,a=v*.55,l=c/360*(500/v),s.save(),s.clip(s.arc(u,f,v,0,c)),s.lineWidth=.5,nt=c-l*.3,y=0;y<nt;y+=l)s.strokeStyle="rgba(240, 240, 255, 0.25)",s.beginPath(),s.arc(u+a,f,a,0,c),s.stroke(),s.translate(u,f),s.rotate(l*.3),s.translate(-u,-f),s.strokeStyle="rgba(25, 10, 10, 0.1)",s.beginPath(),s.arc(u+a,f,a,0,c),s.stroke(),s.translate(u,f),s.rotate(-l*.3),s.translate(-u,-f),s.translate(u,f),s.rotate(l),s.translate(-u,-f);s.restore()}}else h=s.createLinearGradient(0,e*.084112,0,o*.831775),h.addColorStop(0,r.gradientStart.getRgbaColor()),h.addColorStop(.4,r.gradientFraction.getRgbaColor()),h.addColorStop(1,r.gradientStop.getRgbaColor()),s.fillStyle=h,s.fill();h=s.createRadialGradient(u,f,0,u,f,e*.831775/2),h.addColorStop(0,"rgba(0, 0, 0, 0)"),h.addColorStop(.7,"rgba(0, 0, 0, 0)"),h.addColorStop(.71,"rgba(0, 0, 0, 0)"),h.addColorStop(.86,"rgba(0, 0, 0, 0.03)"),h.addColorStop(.92,"rgba(0, 0, 0, 0.07)"),h.addColorStop(.97,"rgba(0, 0, 0, 0.15)"),h.addColorStop(1,"rgba(0, 0, 0, 0.3)"),s.fillStyle=h,s.beginPath(),s.arc(u,f,e*.831775/2,0,c,!0),s.closePath(),s.fill(),w.cache[b]=p}return i.drawImage(w.cache[b],0,0),this},w.cache={},rt=function(n,t,i,r,u,f){var o=u*.831775,e=f*.831775,h=(u-o)/2,s=(f-e)/2;return t!==null&&t.height>0&&t.width>0&&(n.save(),n.beginPath(),n.arc(i,r,u*.831775/2,0,Math.PI*2,!0),n.clip(),n.drawImage(t,h,s,o,e),n.restore()),this},ot=function(i,r,u,f,e){var l,tt,c,ut,b,h,g,o,k,w,p,v,a,y,ft,rt,nt,d=u.toString()+f+e+r.name;if(!ot.cache[d]){h=Math.sqrt(u*u+f*f)*.04,h=Math.min(h,(e?u:f)*.1),g=t(u,f),o=g.getContext("2d"),linBColor=r,s(o,h,h,u-h*2,f-h*2,4);if(r.name==="CARBON"||r.name==="PUNCHED_SHEET"||r.name==="STAINLESS"||r.name==="BRUSHED_METAL"||r.name==="BRUSHED_STAINLESS"||r.name==="TURNED"){r.name==="CARBON"&&(o.fillStyle=o.createPattern(vi,"repeat"),o.fill()),r.name==="PUNCHED_SHEET"&&(o.fillStyle=o.createPattern(ci,"repeat"),o.fill());if(r.name==="STAINLESS"||r.name==="TURNED"){ut=[0,.03,.1,.14,.24,.33,.38,.5,.62,.67,.76,.81,.85,.97,1],b=[new n("#FDFDFD"),new n("#FDFDFD"),new n("#B2B2B4"),new n("#ACACAE"),new n("#FDFDFD"),new n("#6E6E70"),new n("#6E6E70"),new n("#FDFDFD"),new n("#6E6E70"),new n("#6E6E70"),new n("#FDFDFD"),new n("#ACACAE"),new n("#B2B2B4"),new n("#FDFDFD"),new n("#FDFDFD")],c=new it(ut,b,Math.PI/1.75),o.clip(s(o,h,h,u-h*2,f-h*2,4)),k=Math.sqrt((u-h*2)*(u-h*2)+(f-h*2)*(f-h*2))/2,c.fill(o,u/2,f/2,0,k),c=o.createLinearGradient(0,h,0,f-h*2),c.addColorStop(0,"rgba(0, 0, 0, 0.25)"),c.addColorStop(.1,"rgba(0, 0, 0, 0.05)"),c.addColorStop(1,"rgba(0, 0, 0, 0)"),o.fillStyle=c,s(o,h,h,u-h*2,f-h*2,4),o.fill(),o.restore();if(r.name==="TURNED"){for(w=Math.PI*2,p=k*.55,v=u/2,a=f/2,y=w/360*(400/k),o.save(),o.lineWidth=.5,tt=w-y*.3,l=0;l<tt;l+=y)o.strokeStyle="rgba(240, 240, 255, 0.25)",o.beginPath(),o.arc(v+p,a,p,0,w),o.stroke(),o.translate(v,a),o.rotate(y*.3),o.translate(-v,-a),o.strokeStyle="rgba(25, 10, 10, 0.1)",o.beginPath(),o.arc(v+p,a,p,0,w),o.stroke(),o.translate(v,a),o.rotate(-y*.3),o.translate(-v,-a),o.translate(v,a),o.rotate(y),o.translate(-v,-a);o.restore()}}c=o.createLinearGradient(h,h,u-h*2,f-h*2),c.addColorStop(0,"rgba(0, 0, 0, 0.25)"),c.addColorStop(.5,"rgba(0, 0, 0, 0)"),c.addColorStop(1,"rgba(0, 0, 0, 0.25)"),o.fillStyle=c,s(o,h,h,u-h*2,f-h*2,4),o.fill(),(r.name==="BRUSHED_METAL"||r.name==="BRUSHED_STAINLESS")&&(ft=r.name==="BRUSHED_METAL"?!0:!1,rt=parseInt(r.gradientStop.getHexColor().substr(-6),16),nt=li(rt,5,.1,ft,.5),o.fillStyle=o.createPattern(nt.fill(0,0,u,f),"no-repeat"),o.fill())}else c=o.createLinearGradient(0,h,0,f-h*2),c.addColorStop(0,r.gradientStart.getRgbaColor()),c.addColorStop(.4,r.gradientFraction.getRgbaColor()),c.addColorStop(1,r.gradientStop.getRgbaColor()),o.fillStyle=c,o.fill();for(b=["rgba(0, 0, 0, 0.3)","rgba(0, 0, 0, 0.15)","rgba(0, 0, 0, 0.07)","rgba(0, 0, 0, 0.03)","rgba(0, 0, 0, 0)","rgba(0, 0, 0, 0)","rgba(0, 0, 0, 0)"],l=0;l<7;l++)s(o,h+l,h+l,u-h*2-2*l,f-h*2-2*l,4),o.strokeStyle=b[l],o.stroke();ot.cache[d]=g}return i.drawImage(ot.cache[d],0,0),this},ot.cache={},y=function(n,i,r,u,f,o,s,h,c){var g,l,v=Math.ceil(u*.084112),p=r*.5-v/2,b=u*.5-v/2,w=r*.008,a,k,d=i.type+r+u+f+(o!==undefined?o.type:"-")+(s!==undefined?s.style:"-")+(h!==undefined?h.type:"-")+(c!==undefined?c.type:"-");if(!y.cache[d]){g=t(r,u),l=g.getContext("2d"),f&&(h===steelseries.GaugeType.TYPE5?steelseries.Orientation.WEST===c?(p=r*.733644-v/2,l.drawImage(e(v,o,s,!0),p+w,b+w),l.drawImage(e(v,o,s,!1),p,b)):(b=u*.733644-v/2,l.drawImage(e(v,o,s,!0),p+w,u*.6857+w),l.drawImage(e(v,o,s,!1),p,u*.6857)):(l.drawImage(e(v,o,s,!0),p+w,b+w),l.drawImage(e(v,o,s,!1),p,b)));switch(i.type){case"type2":l.beginPath(),l.moveTo(r*.135514,u*.696261),l.bezierCurveTo(r*.214953,u*.588785,r*.317757,u*.5,r*.462616,u*.425233),l.bezierCurveTo(r*.612149,u*.345794,r*.733644,u*.317757,r*.873831,u*.322429),l.bezierCurveTo(r*.766355,u*.112149,r*.528037,u*.023364,r*.313084,u*.130841),l.bezierCurveTo(r*.09813,u*.238317,r*.028037,u*.485981,r*.135514,u*.696261),l.closePath(),a=l.createLinearGradient(.313084*r,.135514*u,.495528*r,.493582*u),a.addColorStop(0,"rgba(255, 255, 255, 0.275)"),a.addColorStop(1,"rgba(255, 255, 255, 0.015)");break;case"type3":l.beginPath(),l.moveTo(r*.084112,u*.509345),l.bezierCurveTo(r*.21028,u*.556074,r*.462616,u*.560747,r*.5,u*.560747),l.bezierCurveTo(r*.537383,u*.560747,r*.794392,u*.560747,r*.915887,u*.509345),l.bezierCurveTo(r*.915887,u*.2757,r*.738317,u*.084112,r*.5,u*.084112),l.bezierCurveTo(r*.261682,u*.084112,r*.084112,u*.2757,r*.084112,u*.509345),l.closePath(),a=l.createLinearGradient(0,.093457*u,0,.556073*u),a.addColorStop(0,"rgba(255, 255, 255, 0.275)"),a.addColorStop(1,"rgba(255, 255, 255, 0.015)");break;case"type4":l.beginPath(),l.moveTo(r*.67757,u*.24299),l.bezierCurveTo(r*.771028,u*.308411,r*.822429,u*.411214,r*.813084,u*.528037),l.bezierCurveTo(r*.799065,u*.654205,r*.719626,u*.757009,r*.593457,u*.799065),l.bezierCurveTo(r*.485981,u*.831775,r*.369158,u*.808411,r*.285046,u*.728971),l.bezierCurveTo(r*.2757,u*.719626,r*.252336,u*.714953,r*.233644,u*.728971),l.bezierCurveTo(r*.214953,u*.747663,r*.219626,u*.771028,r*.228971,u*.7757),l.bezierCurveTo(r*.331775,u*.878504,r*.476635,u*.915887,r*.616822,u*.869158),l.bezierCurveTo(r*.771028,u*.822429,r*.873831,u*.691588,r*.88785,u*.53271),l.bezierCurveTo(r*.897196,u*.38785,r*.836448,u*.257009,r*.719626,u*.182242),l.bezierCurveTo(r*.705607,u*.172897,r*.682242,u*.163551,r*.663551,u*.186915),l.bezierCurveTo(r*.654205,u*.205607,r*.668224,u*.238317,r*.67757,u*.24299),l.closePath(),a=l.createRadialGradient(.5*r,.5*u,0,.5*r,.5*u,.38785*r),a.addColorStop(0,"rgba(255, 255, 255, 0)"),a.addColorStop(.82,"rgba(255, 255, 255, 0)"),a.addColorStop(.83,"rgba(255, 255, 255, 0)"),a.addColorStop(1,"rgba(255, 255, 255, 0.15)"),l.beginPath(),l.moveTo(r*.261682,u*.224299),l.bezierCurveTo(r*.285046,u*.238317,r*.252336,u*.285046,r*.24299,u*.317757),l.bezierCurveTo(r*.24299,u*.350467,r*.271028,u*.383177,r*.271028,u*.397196),l.bezierCurveTo(r*.2757,u*.415887,r*.261682,u*.457943,r*.238317,u*.509345),l.bezierCurveTo(r*.224299,u*.542056,r*.17757,u*.612149,r*.158878,u*.612149),l.bezierCurveTo(r*.144859,u*.612149,r*.088785,u*.546728,r*.130841,u*.369158),l.bezierCurveTo(r*.140186,u*.336448,r*.214953,u*.200934,r*.261682,u*.224299),l.closePath(),k=l.createLinearGradient(.130841*r,.369158*u,.273839*r,.412877*u),k.addColorStop(0,"rgba(255, 255, 255, 0.275)"),k.addColorStop(1,"rgba(255, 255, 255, 0.015)"),l.fillStyle=k,l.fill();break;case"type5":l.beginPath(),l.moveTo(r*.084112,u*.5),l.bezierCurveTo(r*.084112,u*.271028,r*.271028,u*.084112,r*.5,u*.084112),l.bezierCurveTo(r*.700934,u*.084112,r*.864485,u*.224299,r*.906542,u*.411214),l.bezierCurveTo(r*.911214,u*.439252,r*.911214,u*.518691,r*.845794,u*.537383),l.bezierCurveTo(r*.794392,u*.546728,r*.551401,u*.411214,r*.392523,u*.457943),l.bezierCurveTo(r*.168224,u*.509345,r*.135514,u*.7757,r*.093457,u*.593457),l.bezierCurveTo(r*.088785,u*.560747,r*.084112,u*.53271,r*.084112,u*.5),l.closePath(),a=l.createLinearGradient(0,.084112*u,0,.644859*u),a.addColorStop(0,"rgba(255, 255, 255, 0.275)"),a.addColorStop(1,"rgba(255, 255, 255, 0.015)");break;case"type1":default:l.beginPath(),l.moveTo(r*.084112,u*.509345),l.bezierCurveTo(r*.205607,u*.448598,r*.336448,u*.415887,r*.5,u*.415887),l.bezierCurveTo(r*.672897,u*.415887,r*.789719,u*.443925,r*.915887,u*.509345),l.bezierCurveTo(r*.915887,u*.2757,r*.738317,u*.084112,r*.5,u*.084112),l.bezierCurveTo(r*.261682,u*.084112,r*.084112,u*.2757,r*.084112,u*.509345),l.closePath(),a=l.createLinearGradient(0,.088785*u,0,.490654*u),a.addColorStop(0,"rgba(255, 255, 255, 0.275)"),a.addColorStop(1,"rgba(255, 255, 255, 0.015)")}l.fillStyle=a,l.fill(),y.cache[d]=g}return n.drawImage(y.cache[d],0,0),this},y.cache={},et=function(n,i,r,u){var l,o,e,h,f,s,c=i.toString()+r+u;return et.cache[c]||(l=t(i,r),o=l.getContext("2d"),h=Math.sqrt(i*i+r*r)*.04,h=Math.min(h,(u?i:r)*.1),f=h*1.3,s=f*1.33,o.beginPath(),o.moveTo(f,r-f),o.lineTo(i-f,r-f),o.bezierCurveTo(i-f,r-f,i-s,r*.7,i-s,r*.5),o.bezierCurveTo(i-s,s,i-f,f,i-h,f),o.lineTo(f,f),o.bezierCurveTo(f,f,s,r*.285714,s,r*.5),o.bezierCurveTo(s,r*.7,f,r-f,h,r-f),o.closePath(),e=o.createLinearGradient(0,r-h,0,h),e.addColorStop(0,"rgba(255, 255, 255, 0)"),e.addColorStop(.06,"rgba(255, 255, 255, 0)"),e.addColorStop(.07,"rgba(255, 255, 255, 0)"),e.addColorStop(.12,"rgba(255, 255, 255, 0)"),e.addColorStop(.17,"rgba(255, 255, 255, 0.013546)"),e.addColorStop(.1701,"rgba(255, 255, 255, 0)"),e.addColorStop(.79,"rgba(255, 255, 255, 0)"),e.addColorStop(.8,"rgba(255, 255, 255, 0)"),e.addColorStop(.84,"rgba(255, 255, 255, 0.082217)"),e.addColorStop(.93,"rgba(255, 255, 255, 0.288702)"),e.addColorStop(.94,"rgba(255, 255, 255, 0.298039)"),e.addColorStop(.96,"rgba(255, 255, 255, 0.119213)"),e.addColorStop(.97,"rgba(255, 255, 255, 0)"),e.addColorStop(1,"rgba(255, 255, 255, 0)"),o.fillStyle=e,o.fill(),et.cache[c]=l),n.drawImage(et.cache[c],0,0),this},et.cache={},e=function(n,i,r,u){var l,f,h=n/2,s=n/2,o,c=n.toString()+i.type+r.style+u;if(!e.cache[c]){l=t(n*1.18889,n*1.18889),f=l.getContext("2d"),u&&(f.fillStyle="rgba(0, 0, 0, 1)",f.strokeStyle="rgba(0, 0, 0, 1)",f.shadowBlur=3,f.globalAlpha=.5);switch(i.type){case"metalKnob":f.beginPath(),f.moveTo(0,n*.5),f.bezierCurveTo(0,n*.222222,n*.222222,0,n*.5,0),f.bezierCurveTo(n*.777777,0,n,n*.222222,n,n*.5),f.bezierCurveTo(n,n*.777777,n*.777777,n,n*.5,n),f.bezierCurveTo(n*.222222,n,0,n*.777777,0,n*.5),f.closePath(),u||(o=f.createLinearGradient(0,0,0,n),o.addColorStop(0,"rgb(92, 95, 101)"),o.addColorStop(.47,"rgb(46, 49, 53)"),o.addColorStop(1,"rgb(22, 23, 26)"),f.fillStyle=o),f.fill(),f.beginPath(),f.moveTo(n*.055555,n*.5),f.bezierCurveTo(n*.055555,n*.277777,n*.277777,n*.055555,n*.5,n*.055555),f.bezierCurveTo(n*.722222,n*.055555,n*.944444,n*.277777,n*.944444,n*.5),f.bezierCurveTo(n*.944444,n*.722222,n*.722222,n*.944444,n*.5,n*.944444),f.bezierCurveTo(n*.277777,n*.944444,n*.055555,n*.722222,n*.055555,n*.5),f.closePath();if(u)f.fill();else{o=f.createLinearGradient(0,.055555*n,0,.944443*n);switch(r.style){case"black":o.addColorStop(0,"rgb(43, 42, 47)"),o.addColorStop(1,"rgb(26, 27, 32)");break;case"brass":o.addColorStop(0,"rgb(150, 110, 54)"),o.addColorStop(1,"rgb(124, 95, 61)");break;case"silver":default:o.addColorStop(0,"rgb(204, 204, 204)"),o.addColorStop(1,"rgb(87, 92, 98)")}f.fillStyle=o,f.fill()}f.beginPath(),f.moveTo(n*.777777,n*.833333),f.bezierCurveTo(n*.722222,n*.722222,n*.611111,n*.666666,n*.5,n*.666666),f.bezierCurveTo(n*.388888,n*.666666,n*.277777,n*.722222,n*.222222,n*.833333),f.bezierCurveTo(n*.277777,n*.888888,n*.388888,n*.944444,n*.5,n*.944444),f.bezierCurveTo(n*.611111,n*.944444,n*.722222,n*.888888,n*.777777,n*.833333),f.closePath(),u||(o=f.createRadialGradient(.555555*n,.944444*n,0,.555555*n,.944444*n,.388888*n),o.addColorStop(0,"rgba(255, 255, 255, 0.6)"),o.addColorStop(1,"rgba(255, 255, 255, 0)"),f.fillStyle=o),f.fill(),f.beginPath(),f.moveTo(n*.944444,n*.277777),f.bezierCurveTo(n*.833333,n*.111111,n*.666666,0,n*.5,0),f.bezierCurveTo(n*.333333,0,n*.166666,n*.111111,n*.055555,n*.277777),f.bezierCurveTo(n*.166666,n*.333333,n*.333333,n*.388888,n*.5,n*.388888),f.bezierCurveTo(n*.666666,n*.388888,n*.833333,n*.333333,n*.944444,n*.277777),f.closePath(),u||(o=f.createRadialGradient(.5*n,0,0,.5*n,0,.583333*n),o.addColorStop(0,"rgba(255, 255, 255, 0.749019)"),o.addColorStop(1,"rgba(255, 255, 255, 0)"),f.fillStyle=o),f.fill(),f.beginPath(),f.moveTo(n*.277777,n*.555555),f.bezierCurveTo(n*.277777,n*.388888,n*.388888,n*.277777,n*.5,n*.277777),f.bezierCurveTo(n*.611111,n*.277777,n*.777777,n*.388888,n*.777777,n*.555555),f.bezierCurveTo(n*.777777,n*.666666,n*.611111,n*.777777,n*.5,n*.777777),f.bezierCurveTo(n*.388888,n*.777777,n*.277777,n*.666666,n*.277777,n*.555555),f.closePath(),u||(o=f.createLinearGradient(0,.277777*n,0,.722221*n),o.addColorStop(0,"#000000"),o.addColorStop(1,"rgb(204, 204, 204)"),f.fillStyle=o),f.fill(),f.beginPath(),f.moveTo(n*.333333,n*.555555),f.bezierCurveTo(n*.333333,n*.444444,n*.388888,n*.333333,n*.5,n*.333333),f.bezierCurveTo(n*.611111,n*.333333,n*.722222,n*.444444,n*.722222,n*.555555),f.bezierCurveTo(n*.722222,n*.611111,n*.611111,n*.722222,n*.5,n*.722222),f.bezierCurveTo(n*.388888,n*.722222,n*.333333,n*.611111,n*.333333,n*.555555),f.closePath(),u||(o=f.createLinearGradient(0,.333333*n,0,.666666*n),o.addColorStop(0,"rgb(10, 9, 1)"),o.addColorStop(1,"rgb(42, 41, 37)"),f.fillStyle=o),f.fill();break;case"standardKnob":u||(o=f.createLinearGradient(0,0,0,n),o.addColorStop(0,"rgb(180, 180, 180)"),o.addColorStop(.46,"rgb(63, 63, 63)"),o.addColorStop(1,"rgb(40, 40, 40)"),f.fillStyle=o),f.beginPath(),f.arc(h,s,n/2,0,Math.PI*2,!0),f.closePath(),f.fill();if(!u){o=f.createLinearGradient(0,n-n*.77,0,n-n*.77+n*.77);switch(r.style){case"black":o.addColorStop(0,"rgb(191, 191, 191)"),o.addColorStop(.5,"rgb(45, 44, 49)"),o.addColorStop(1,"rgb(125, 126, 128)");break;case"brass":o.addColorStop(0,"rgb(223, 208, 174)"),o.addColorStop(.5,"rgb(123, 95, 63)"),o.addColorStop(1,"rgb(207, 190, 157)");break;case"silver":default:o.addColorStop(0,"rgb(215, 215, 215)"),o.addColorStop(.5,"rgb(116, 116, 116)"),o.addColorStop(1,"rgb(215, 215, 215)")}f.fillStyle=o}f.beginPath(),f.arc(h,s,n*.77/2,0,Math.PI*2,!0),f.closePath(),f.fill(),u||(o=f.createRadialGradient(h,s,0,h,s,n*.77/2),o.addColorStop(0,"rgba(0, 0, 0, 0)"),o.addColorStop(.75,"rgba(0, 0, 0, 0)"),o.addColorStop(.76,"rgba(0, 0, 0, 0.01)"),o.addColorStop(1,"rgba(0, 0, 0, 0.2)"),f.fillStyle=o),f.beginPath(),f.arc(h,s,n*.77/2,0,Math.PI*2,!0),f.closePath(),f.fill()}e.cache[c]=l}return e.cache[c]},e.cache={},o=function(n,i,r){var l,u,s=n/2,h=n/2,e,c=n.toString()+i+r.outerColor_ON;if(!o.cache[c]){l=t(n,n),u=l.getContext("2d");switch(i){case 0:e=u.createRadialGradient(s,h,0,s,h,n*.5/2),e.addColorStop(0,r.innerColor1_OFF),e.addColorStop(.2,r.innerColor2_OFF),e.addColorStop(1,r.outerColor_OFF),u.fillStyle=e,u.beginPath(),u.arc(s,h,n*.5/2,0,Math.PI*2,!0),u.closePath(),u.fill(),e=u.createRadialGradient(s,h,0,s,h,n*.5/2),e.addColorStop(0,"rgba(0, 0, 0, 0)"),e.addColorStop(.8,"rgba(0, 0, 0, 0)"),e.addColorStop(1,"rgba(0, 0, 0, 0.4)"),u.fillStyle=e,u.beginPath(),u.arc(s,h,n*.5/2,0,Math.PI*2,!0),u.closePath(),u.fill(),e=u.createLinearGradient(0,.35*n,0,.35*n+.15*n),e.addColorStop(0,"rgba(255, 255, 255, 0.4)"),e.addColorStop(1,"rgba(255, 255, 255, 0)"),u.fillStyle=e,u.beginPath(),u.arc(s,.35*n+.2*n/2,n*.2,0,Math.PI*2,!0),u.closePath(),u.fill();break;case 1:e=u.createRadialGradient(s,h,0,s,h,n*.5/2),e.addColorStop(0,r.innerColor1_ON),e.addColorStop(.2,r.innerColor2_ON),e.addColorStop(1,r.outerColor_ON),u.fillStyle=e,u.beginPath(),u.arc(s,h,n*.5/2,0,Math.PI*2,!0),u.closePath(),u.fill(),e=u.createRadialGradient(s,h,0,s,h,n*.5/2),e.addColorStop(0,"rgba(0, 0, 0, 0)"),e.addColorStop(.8,"rgba(0, 0, 0, 0)"),e.addColorStop(1,"rgba(0, 0, 0, 0.4)"),u.fillStyle=e,u.beginPath(),u.arc(s,h,n*.5/2,0,Math.PI*2,!0),u.closePath(),u.fill(),e=u.createLinearGradient(0,.35*n,0,.35*n+.15*n),e.addColorStop(0,"rgba(255, 255, 255, 0.4)"),e.addColorStop(1,"rgba(255, 255, 255, 0)"),u.fillStyle=e,u.beginPath(),u.arc(s,.35*n+.2*n/2,n*.2,0,Math.PI*2,!0),u.closePath(),u.fill(),e=u.createRadialGradient(s,h,0,s,h,n/2),e.addColorStop(0,f(r.coronaColor,0).color),e.addColorStop(.6,f(r.coronaColor,.4).color),e.addColorStop(.7,f(r.coronaColor,.25).color),e.addColorStop(.8,f(r.coronaColor,.15).color),e.addColorStop(.85,f(r.coronaColor,.05).color),e.addColorStop(1,f(r.coronaColor,0).color),u.fillStyle=e,u.beginPath(),u.arc(s,h,n/2,0,Math.PI*2,!0),u.closePath(),u.fill()}o.cache[c]=l}return o.cache[c]},o.cache={},p=function(n,i,r){var c,f,b=0,h=0,y=n,a=i,v=Math.min(n,i)*.095,u,k=1,o=1,d=n-2,l=i-2,w=v-1,e=n.toString()+i+JSON.stringify(r);return p.cache[e]||(c=t(n,i),f=c.getContext("2d"),u=f.createLinearGradient(0,h,0,h+a),u.addColorStop(0,"#4c4c4c"),u.addColorStop(.08,"#666666"),u.addColorStop(.92,"#666666"),u.addColorStop(1,"#e6e6e6"),f.fillStyle=u,s(f,b,h,y,a,v),f.fill(),u=f.createLinearGradient(0,o,0,o+l),u.addColorStop(0,r.gradientStartColor),u.addColorStop(.03,r.gradientFraction1Color),u.addColorStop(.49,r.gradientFraction2Color),u.addColorStop(.5,r.gradientFraction3Color),u.addColorStop(1,r.gradientStopColor),f.fillStyle=u,s(f,k,o,d,l,w),f.fill(),p.cache[e]=c),p.cache[e]},p.cache={},a=function(n,t,i,u){var e,f,o=n.toString()+t+i+u;return a.cache[o]||(e=r.createElement("canvas"),f=e.getContext("2d"),e.width=n,e.height=n,f.fillStyle=t,i?(f.beginPath(),f.moveTo(n*.5,n),f.lineTo(0,0),f.lineTo(n,0),f.closePath(),f.fill()):u?(f.beginPath(),f.moveTo(n,n*.5),f.lineTo(0,0),f.lineTo(0,n),f.closePath(),f.fill()):(f.beginPath(),f.moveTo(n*.5,0),f.lineTo(n,n),f.lineTo(0,n),f.closePath(),f.fill()),a.cache[o]=e),a.cache[o]},a.cache={},d=function(n,i,r){var e=n*2,l,u,o,v,c=i.state+n+JSON.stringify(r),s=function(){var t=r[0];i.state==="up"?(o=u.createRadialGradient(.5*n,.2*e,0,.5*n,.2*e,.5*n),o.addColorStop(0,t.innerColor1_ON),o.addColorStop(.2,t.innerColor2_ON),o.addColorStop(1,t.outerColor_ON)):(o=u.createLinearGradient(0,0,0,.5*e),o.addColorStop(0,"#323232"),o.addColorStop(1,"#5c5c5c")),u.fillStyle=o,u.beginPath(),u.moveTo(.5*n,0),u.lineTo(n,.2*e),u.lineTo(.752*n,.2*e),u.lineTo(.752*n,.37*e),u.lineTo(.252*n,.37*e),u.lineTo(.252*n,.2*e),u.lineTo(0,.2*e),u.closePath(),u.fill(),i.state!=="up"?(u.strokeStyle="rgba(0, 0, 0, 0.4)",u.beginPath(),u.moveTo(0,.2*e),u.lineTo(.5*n,0),u.lineTo(n,.2*e),u.moveTo(.252*n,.2*e),u.lineTo(.252*n,.37*e),u.stroke(),u.strokeStyle="rgba(255, 255, 255, 0.3)",u.beginPath(),u.moveTo(.252*n,.37*e),u.lineTo(.752*n,.37*e),u.lineTo(.752*n,.2*e),u.lineTo(n,.2*e),u.stroke()):(o=u.createRadialGradient(.5*n,.2*e,0,.5*n,.2*e,.7*n),o.addColorStop(0,f(t.coronaColor,0).color),o.addColorStop(.5,f(t.coronaColor,.3).color),o.addColorStop(.7,f(t.coronaColor,.2).color),o.addColorStop(.8,f(t.coronaColor,.1).color),o.addColorStop(.85,f(t.coronaColor,.05).color),o.addColorStop(1,f(t.coronaColor,0).color),u.fillStyle=o,u.beginPath(),u.arc(.5*n,.2*e,.7*n,0,Math.PI*2,!0),u.closePath(),u.fill())},h=function(){var t=r[1];u.beginPath(),i.state==="steady"?(o=t.outerColor_ON,u.fillStyle=o,u.rect(.128*n,.41*e,.744*n,.074*e),u.rect(.128*n,.516*e,.744*n,.074*e),u.closePath(),u.fill()):(o=u.createLinearGradient(0,.41*e,0,.41*e+.074*e),o.addColorStop(0,"#323232"),o.addColorStop(1,"#5c5c5c"),u.fillStyle=o,u.rect(.128*n,.41*e,.744*n,.074*e),u.closePath(),u.fill(),o=u.createLinearGradient(0,.516*e,0,.516*e+.074*e),o.addColorStop(0,"#323232"),o.addColorStop(1,"#5c5c5c"),u.fillStyle=o,u.rect(.128*n,.516*e,.744*n,.074*e),u.closePath(),u.fill()),i.state!=="steady"?(u.strokeStyle="rgba(0, 0, 0, 0.4)",u.beginPath(),u.moveTo(.128*n,.41*e+.074*e),u.lineTo(.128*n,.41*e),u.lineTo(.128*n+.744*n,.41*e),u.stroke(),u.beginPath(),u.moveTo(.128*n,.516*e+.074*e),u.lineTo(.128*n,.516*e),u.lineTo(.128*n+.744*n,.516*e),u.stroke(),u.strokeStyle="rgba(255, 255, 255, 0.3)",u.beginPath(),u.moveTo(.128*n+.744*n,.41*e),u.lineTo(.128*n+.744*n,.41*e+.074*e),u.lineTo(.128*n,.41*e+.074*e),u.stroke(),u.beginPath(),u.moveTo(.128*n+.744*n,.516*e),u.lineTo(.128*n+.744*n,.516*e+.074*e),u.lineTo(.128*n,.516*e+.074*e),u.stroke()):(o=u.createRadialGradient(.5*n,.5*e,0,.5*n,.5*e,.7*n),o.addColorStop(0,f(t.coronaColor,0).color),o.addColorStop(.5,f(t.coronaColor,.3).color),o.addColorStop(.7,f(t.coronaColor,.2).color),o.addColorStop(.8,f(t.coronaColor,.1).color),o.addColorStop(.85,f(t.coronaColor,.05).color),o.addColorStop(1,f(t.coronaColor,0).color),u.fillStyle=o,u.beginPath(),u.arc(.5*n,.5*e,.7*n,0,Math.PI*2,!0),u.closePath(),u.fill())},a=function(){var t=r[2];i.state==="down"?(o=u.createRadialGradient(.5*n,.8*e,0,.5*n,.8*e,.5*n),o.addColorStop(0,t.innerColor1_ON),o.addColorStop(.2,t.innerColor2_ON),o.addColorStop(1,t.outerColor_ON)):(o=u.createLinearGradient(0,.63*e,0,e),o.addColorStop(0,"#323232"),o.addColorStop(1,"#5c5c5c")),u.beginPath(),u.fillStyle=o,u.moveTo(.5*n,e),u.lineTo(n,.8*e),u.lineTo(.725*n,.8*e),u.lineTo(.725*n,.63*e),u.lineTo(.252*n,.63*e),u.lineTo(.252*n,.8*e),u.lineTo(0,.8*e),u.closePath(),u.fill(),i.state!=="down"?(u.strokeStyle="rgba(0, 0, 0, 0.4)",u.beginPath(),u.moveTo(0,.8*e),u.lineTo(.252*n,.8*e),u.moveTo(.252*n,.63*e),u.lineTo(.752*n,.63*e),u.stroke(),u.beginPath(),u.moveTo(.752*n,.8*e),u.lineTo(n,.8*e),u.stroke(),u.strokeStyle="rgba(255, 255, 255, 0.3)",u.beginPath(),u.moveTo(0,.8*e),u.lineTo(.5*n,e),u.lineTo(n,.8*e),u.stroke(),u.beginPath(),u.moveTo(.752*n,.8*e),u.lineTo(.752*n,.63*e),u.stroke()):(o=u.createRadialGradient(.5*n,.8*e,0,.5*n,.8*e,.7*n),o.addColorStop(0,f(t.coronaColor,0).color),o.addColorStop(.5,f(t.coronaColor,.3).color),o.addColorStop(.7,f(t.coronaColor,.2).color),o.addColorStop(.8,f(t.coronaColor,.1).color),o.addColorStop(.85,f(t.coronaColor,.05).color),o.addColorStop(1,f(t.coronaColor,0).color),u.fillStyle=o,u.beginPath(),u.arc(.5*n,.8*e,.7*n,0,Math.PI*2,!0),u.closePath(),u.fill())};if(!d.cache[c]){l=t(n*2,n*4),u=l.getContext("2d"),u.translate(n*.5,n*.5);switch(i.state){case"up":a(),h(),s();break;case"steady":a(),s(),h();break;case"down":default:s(),h(),a()}d.cache[c]=l}return d.cache[c]},d.cache={};var ct=function(n,t,i,r,u,f,e,o,s,h){h=undefined===h?h=steelseries.GaugeType.TYPE1:h,n.save(),n.textAlign=o?"center":"left",n.textBaseline="middle",n.strokeStyle=f.labelColor.getRgbaColor(),n.fillStyle=f.labelColor.getRgbaColor(),o?(n.font=.046728*t+"px sans-serif",n.fillText(r,t/2,i*.3,t*.3),n.fillText(u,t/2,i*.38,t*.3)):e?(n.font=.1*t+"px sans-serif",n.save(),n.translate(.671428*t,.1375*i),n.rotate(1.570796),n.fillText(r,0,0),n.translate(-.671428*t,-.1375*i),n.restore(),n.font=.071428*t+"px sans-serif",s?h.type==="type2"?(n.textAlign="right",n.fillText(u,.36*t,i*.79,t*.25)):n.fillText(u,.63*t,i*.85,t*.2):(n.textAlign="center",h.type==="type2"?n.fillText(u,t/2,i*.92,t*.2):n.fillText(u,t/2,i*.89,t*.2))):(n.font=.035*t+"px sans-serif",n.fillText(r,t*.15,i*.25,t*.3),n.font=.025*t+"px sans-serif",n.fillText(u,t*.0625,i*.7,t*.07)),n.restore()},vi=ii(12,12,function(n){var u=n.canvas.width,t=n.canvas.height,f=0,r=0,i;n.save(),n.save(),n.beginPath(),n.rect(0,0,u*.5,t*.5),n.closePath(),n.restore(),i=n.createLinearGradient(0,r*t,0,.5*t+r*t),i.addColorStop(0,"rgb(35, 35, 35)"),i.addColorStop(1,"rgb(23, 23, 23)"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(u*.083333,0,u*.333333,t*.416666),n.closePath(),n.restore(),f=.083333,r=0,i=n.createLinearGradient(0,r*t,0,.416666*t+r*t),i.addColorStop(0,"rgb(38, 38, 38)"),i.addColorStop(1,"rgb(30, 30, 30)"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(u*.5,t*.5,u*.5,t*.5),n.closePath(),n.restore(),f=.5,r=.5,i=n.createLinearGradient(0,r*t,0,.5*t+r*t),i.addColorStop(0,"rgb(35, 35, 35)"),i.addColorStop(1,"rgb(23, 23, 23)"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(u*.583333,t*.5,u*.333333,t*.416666),n.closePath(),n.restore(),f=.583333,r=.5,i=n.createLinearGradient(0,r*t,0,.416666*t+r*t),i.addColorStop(0,"rgb(38, 38, 38)"),i.addColorStop(1,"rgb(30, 30, 30)"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(u*.5,0,u*.5,t*.5),n.closePath(),n.restore(),f=.5,r=0,i=n.createLinearGradient(0,r*t,0,.5*t+r*t),i.addColorStop(0,"#303030"),i.addColorStop(1,"rgb(40, 40, 40)"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(u*.583333,t*.083333,u*.333333,t*.416666),n.closePath(),n.restore(),f=.583333,r=.083333,i=n.createLinearGradient(0,r*t,0,.416666*t+r*t),i.addColorStop(0,"rgb(53, 53, 53)"),i.addColorStop(1,"rgb(45, 45, 45)"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(0,t*.5,u*.5,t*.5),n.closePath(),n.restore(),f=0,r=.5,i=n.createLinearGradient(0,r*t,0,.5*t+r*t),i.addColorStop(0,"#303030"),i.addColorStop(1,"#282828"),n.fillStyle=i,n.fill(),n.save(),n.beginPath(),n.rect(u*.083333,t*.583333,u*.333333,t*.416666),n.closePath(),n.restore(),f=.083333,r=.583333,i=n.createLinearGradient(0,r*t,0,.416666*t+r*t),i.addColorStop(0,"#353535"),i.addColorStop(1,"#2d2d2d"),n.fillStyle=i,n.fill(),n.restore()}),ci=ii(15,15,function(n){var i=n.canvas.width,t=n.canvas.height,r;n.save(),n.save(),n.beginPath(),n.rect(0,0,i,t),n.closePath(),n.restore(),n.fillStyle="#1D2123",n.fill(),n.save(),n.beginPath(),n.moveTo(0,t*.266666),n.bezierCurveTo(0,t*.4,i*.066666,t*.466666,i*.2,t*.466666),n.bezierCurveTo(i*.333333,t*.466666,i*.4,t*.4,i*.4,t*.266666),n.bezierCurveTo(i*.4,t*.133333,i*.333333,t*.066666,i*.2,t*.066666),n.bezierCurveTo(i*.066666,t*.066666,0,t*.133333,0,t*.266666),n.closePath(),r=n.createLinearGradient(0,.066666*t,0,.466666*t),r.addColorStop(0,"#000000"),r.addColorStop(1,"#444444"),n.fillStyle=r,n.fill(),n.save(),n.beginPath(),n.moveTo(0,t*.2),n.bezierCurveTo(0,t*.333333,i*.066666,t*.4,i*.2,t*.4),n.bezierCurveTo(i*.333333,t*.4,i*.4,t*.333333,i*.4,t*.2),n.bezierCurveTo(i*.4,t*.066666,i*.333333,0,i*.2,0),n.bezierCurveTo(i*.066666,0,0,t*.066666,0,t*.2),n.closePath(),n.fillStyle="#050506",n.fill(),n.save(),n.beginPath(),n.moveTo(i*.466666,t*.733333),n.bezierCurveTo(i*.466666,t*.866666,i*.533333,t*.933333,i*.666666,t*.933333),n.bezierCurveTo(i*.8,t*.933333,i*.866666,t*.866666,i*.866666,t*.733333),n.bezierCurveTo(i*.866666,t*.6,i*.8,t*.533333,i*.666666,t*.533333),n.bezierCurveTo(i*.533333,t*.533333,i*.466666,t*.6,i*.466666,t*.733333),n.closePath(),r=n.createLinearGradient(0,.533333*t,0,.933333*t),r.addColorStop(0,"#000000"),r.addColorStop(1,"#444444"),n.fillStyle=r,n.fill(),n.save(),n.beginPath(),n.moveTo(i*.466666,t*.666666),n.bezierCurveTo(i*.466666,t*.8,i*.533333,t*.866666,i*.666666,t*.866666),n.bezierCurveTo(i*.8,t*.866666,i*.866666,t*.8,i*.866666,t*.666666),n.bezierCurveTo(i*.866666,t*.533333,i*.8,t*.466666,i*.666666,t*.466666),n.bezierCurveTo(i*.533333,t*.466666,i*.466666,t*.533333,i*.466666,t*.666666),n.closePath(),n.fillStyle="#050506",n.fill(),n.restore()}),li=function(n,i,r,u,f){function o(n,t){return n+=(2*Math.random()-1)*t|0,n<0?0:n>255?255:n}function e(n){return n<0?0:n>255?255:n}function s(n,t,i,r,u,f){var o,v,e,a,s,h,c,l;for(u<i||(u=i-1),a=1/(u*2+1),s=0,v=0;v<r;v++){for(h=c=l=0,o=0;o<u;o++)e=(s+o)*4,h+=n.data[e],c+=n.data[e+1],l+=n.data[e+2];for(o=0;o<i;o++)o>u&&(e=(s-u-1)*4,h-=n.data[e],c-=n.data[e+1],l-=n.data[e+2]),o+u<i&&(e=(s+u)*4,h+=n.data[e],c+=n.data[e+1],l+=n.data[e+2]),e=s*4,t.data[e]=h*a|0,t.data[e+1]=c*a|0,t.data[e+2]=l*a|0,t.data[e+3]=f,s++}}return this.fill=function(h,c,l,a){var k,d,ut,st,p,w,et,b,v,ot,ht=255,lt=n>>16&255,ct=n>>8&255,at=n&255,g=0,nt=255*r,vt,pt,yt,y,rt,it,tt,ft;h=Math.floor(h),c=Math.floor(c),l=Math.ceil(l),a=Math.ceil(a),p=l-h,w=a-c,et=t(p,w),b=et.getContext("2d"),v=b.createImageData(p,w),ot=b.createImageData(p,w);if(f!==0)for(st=[],k=0;k<p;k++)st[k]=255*f*Math.sin(k/p*Math.PI)|0;for(ut=0;ut<w;ut++)for(i!==0&&(vt=pt=yt=0),d=0;d<p;d++)y=ut*p*4+d*4,rt=lt,it=ct,tt=at,f!==0&&(ft=st[d],rt+=ft,it+=ft,tt+=ft),u?(g=(2*Math.random()-1)*nt|0,v.data[y]=e(rt+g),v.data[y+1]=e(it+g),v.data[y+2]=e(tt+g),v.data[y+3]=ht):(v.data[y]=o(rt,nt),v.data[y+1]=o(it,nt),v.data[y+2]=o(tt,nt),v.data[y+3]=ht);return i>0?(s(v,ot,p,w,i,ht),b.putImageData(ot,h,c)):b.putImageData(v,h,c),et},this},n=function(n,t,i,r){function s(){e=k(n,255),f=k(t,255),u=k(i,255),o=k(r,1)}var e,f,u,o;arguments.length===1?(i=parseInt(n.substr(5,2),16),t=parseInt(n.substr(3,2),16),n=parseInt(n.substr(1,2),16),r=1):arguments.length===3&&(r=1),s(),this.getRed=function(){return e},this.setRed=function(n){e=k(n,255)},this.getGreen=function(){return f},this.setGreen=function(n){f=k(n,255)},this.getBlue=function(){return u},this.setBlue=function(n){u=k(n,255)},this.getAlpha=function(){return o},this.setAlpha=function(n){o=k(n,1)},this.getRgbaColor=function(){return"rgba("+e+", "+f+", "+u+", "+o+")"},this.getRgbColor=function(){return"rgb("+e+", "+f+", "+u+")"},this.getHexColor=function(){return"#"+e.toString(16)+f.toString(16)+u.toString(16)}},it=function(n,t,i){i=i===undefined?-Math.PI/2:i,this.fill=function(r,u,f,e,o){var h,s,p,v=2*Math.PI,c,a,w,b,y,k=180/Math.PI,d=e+(o-e)*.5,l=v/Math.max(360,o*2.2);for(r.save(),r.lineWidth=1.5,r.translate(u,f),r.rotate(i),r.translate(-u,-f),h=0,p=n.length-1;h<p;h++)for(c=v*n[h],a=v*n[h+1],w=a-c,b=t[h],y=t[h+1],s=c;s<a;s+=l)r.beginPath(),r.fillStyle=ui(b,y,w,s-c).getRgbaColor(),r.strokeStyle=r.fillStyle,e>0?r.arc(u,f,e,s+l,s,!0):r.moveTo(u,f),r.arc(u,f,o,s,s+l),r.fill(),r.stroke();r.restore()}},kt=function(n,t,i,r){this.getColorAt=function(n){var u=0,e=0,f=1,s=1,h=0,t,o;for(n=n<0?0:n>1?1:n,t=0;t<i.length;t++){i[t]<n&&u<i[t]&&(u=i[t],e=t);if(i[t]===n)return r[t];i[t]>n&&f>=i[t]&&(f=i[t],s=t)}return o=(n-u)/(f-u),ui(r[e],r[s],1,o)},this.getStart=function(){return n},this.getEnd=function(){return t}};Math.log10=function(n){return Math.log(n)/Math.LN10},function(){h=function(n,t,i,r,u,f){this.gradientStart=n,this.gradientFraction=t,this.gradientStop=i,this.labelColor=r,this.symbolColor=u,this.name=f}}(),function(){u=function(n,t,i,r,u,f){this.gradientStartColor=n,this.gradientFraction1Color=t,this.gradientFraction2Color=i,this.gradientFraction3Color=r,this.gradientStopColor=u,this.textColor=f}}(),function(){b=function(n,t,i,r,u,f){this.veryDark=n,this.dark=t,this.medium=i,this.light=r,this.lighter=u,this.veryLight=f}}(),function(){ut=function(n,t,i,r,u,f,e){this.innerColor1_ON=n,this.innerColor2_ON=t,this.outerColor_ON=i,this.coronaColor=r,this.innerColor1_OFF=u,this.innerColor2_OFF=f,this.outerColor_OFF=e}}(),function(){at=function(n){this.type=n}}(),function(){pt=function(n){this.type=n}}(),function(){ri=function(n){this.type=n}}(),function(){dt=function(n){this.style=n}}(),function(){g=function(n){this.design=n}}(),function(){l=function(n){this.type=n}}(),function(){lt=function(n){this.type=n}}(),function(){gt=function(n){this.format=n}}(),function(){bt=function(n){this.type=n}}(),function(){yt=function(n){this.state=n}}();var di={DARK_GRAY:new h(new n(0,0,0,1),new n(51,51,51,1),new n(153,153,153,1),new n(255,255,255,1),new n(180,180,180,1),"DARK_GRAY"),SATIN_GRAY:new h(new n(45,57,57,1),new n(45,57,57,1),new n(45,57,57,1),new n(167,184,180,1),new n(137,154,150,1),"SATIN_GRAY"),LIGHT_GRAY:new h(new n(130,130,130,1),new n(181,181,181,1),new n(253,253,253,1),new n(0,0,0,1),new n(80,80,80,1),"LIGHT_GRAY"),WHITE:new h(new n(255,255,255,1),new n(255,255,255,1),new n(255,255,255,1),new n(0,0,0,1),new n(80,80,80,1),"WHITE"),BLACK:new h(new n(0,0,0,1),new n(0,0,0,1),new n(0,0,0,1),new n(255,255,255,1),new n(150,150,150,1),"BLACK"),BEIGE:new h(new n(178,172,150,1),new n(204,205,184,1),new n(231,231,214,1),new n(0,0,0,1),new n(80,80,80,1),"BEIGE"),BROWN:new h(new n(245,225,193,1),new n(245,225,193,1),new n(255,250,240,1),new n(109,73,47,1),new n(89,53,27,1),"BROWN"),RED:new h(new n(198,93,95,1),new n(212,132,134,1),new n(242,218,218,1),new n(0,0,0,1),new n(90,0,0,1),"RED"),GREEN:new h(new n(65,120,40,1),new n(129,171,95,1),new n(218,237,202,1),new n(0,0,0,1),new n(0,90,0,1),"GREEN"),BLUE:new h(new n(45,83,122,1),new n(115,144,170,1),new n(227,234,238,1),new n(0,0,0,1),new n(0,0,90,1),"BLUE"),ANTHRACITE:new h(new n(50,50,54,1),new n(47,47,51,1),new n(69,69,74,1),new n(250,250,250,1),new n(180,180,180,1),"ANTHRACITE"),MUD:new h(new n(80,86,82,1),new n(70,76,72,1),new n(57,62,58,1),new n(255,255,240,1),new n(225,225,210,1),"MUD"),PUNCHED_SHEET:new h(new n(50,50,54,1),new n(47,47,51,1),new n(69,69,74,1),new n(255,255,255,1),new n(180,180,180,1),"PUNCHED_SHEET"),CARBON:new h(new n(50,50,54,1),new n(47,47,51,1),new n(69,69,74,1),new n(255,255,255,1),new n(180,180,180,1),"CARBON"),STAINLESS:new h(new n(130,130,130,1),new n(181,181,181,1),new n(253,253,253,1),new n(0,0,0,1),new n(80,80,80,1),"STAINLESS"),BRUSHED_METAL:new h(new n(50,50,54,1),new n(47,47,51,1),new n(69,69,74,1),new n(0,0,0,1),new n(80,80,80,1),"BRUSHED_METAL"),BRUSHED_STAINLESS:new h(new n(50,50,54,1),new n(47,47,51,1),new n(110,110,112,1),new n(0,0,0,1),new n(80,80,80,1),"BRUSHED_STAINLESS"),TURNED:new h(new n(130,130,130,1),new n(181,181,181,1),new n(253,253,253,1),new n(0,0,0,1),new n(80,80,80,1),"TURNED")},ki={BEIGE:new u("#c8c8b1","rgb(241, 237, 207)","rgb(234, 230, 194)","rgb(225, 220, 183)","rgb(237, 232, 191)","#000000"),BLUE:new u("#ffffff","rgb(231, 246, 255)","rgb(170, 224, 255)","rgb(136, 212, 255)","rgb(192, 232, 255)","#124564"),ORANGE:new u("#ffffff","rgb(255, 245, 225)","rgb(255, 217, 147)","rgb(255, 201, 104)","rgb(255, 227, 173)","#503700"),RED:new u("#ffffff","rgb(255, 225, 225)","rgb(253, 152, 152)","rgb(252, 114, 115)","rgb(254, 178, 178)","#4f0c0e"),YELLOW:new u("#ffffff","rgb(245, 255, 186)","rgb(210, 255, 0)","rgb(158, 205, 0)","rgb(210, 255, 0)","#405300"),WHITE:new u("#ffffff","#ffffff","rgb(241, 246, 242)","rgb(229, 239, 244)","#ffffff","#000000"),GRAY:new u("#414141","rgb(117, 117, 117)","rgb(87, 87, 87)","#414141","rgb(81, 81, 81)","#ffffff"),BLACK:new u("#414141","#666666","#333333","#000000","#333333","#cccccc"),GREEN:new u("rgb(33, 67, 67)","rgb(33, 67, 67)","rgb(29, 58, 58)","rgb(28, 57, 57)","rgb(23, 46, 46)","rgba(0, 185, 165, 255)"),BLUE2:new u("rgb(0, 68, 103)","rgb(8, 109, 165)","rgb(0, 72, 117)","rgb(0, 72, 117)","rgb(0, 68, 103)","rgb(111, 182, 228)"),BLUE_BLACK:new u("rgb(22, 125, 212)","rgb(3, 162, 254)","rgb(3, 162, 254)","rgb(3, 162, 254)","rgb(11, 172, 244)","#000000"),BLUE_DARKBLUE:new u("rgb(18, 33, 88)","rgb(18, 33, 88)","rgb(19, 30, 90)","rgb(17, 31, 94)","rgb(21, 25, 90)","rgb(23, 99, 221)"),BLUE_GRAY:new u("rgb(135, 174, 255)","rgb(101, 159, 255)","rgb(44, 93, 255)","rgb(27, 65, 254)","rgb(12, 50, 255)","#b2b4ed"),STANDARD:new u("rgb(131, 133, 119)","rgb(176, 183, 167)","rgb(165, 174, 153)","rgb(166, 175, 156)","rgb(175, 184, 165)","rgb(35, 42, 52)"),STANDARD_GREEN:new u("#ffffff","rgb(219, 230, 220)","rgb(179, 194, 178)","rgb(153, 176, 151)","rgb(114, 138, 109)","#080C06"),BLUE_BLUE:new u("rgb(100, 168, 253)","rgb(100, 168, 253)","rgb(95, 160, 250)","rgb(80, 144, 252)","rgb(74, 134, 255)","#002cbb"),RED_DARKRED:new u("rgb(72, 36, 50)","rgb(185, 111, 110)","rgb(148, 66, 72)","rgb(83, 19, 20)","rgb(7, 6, 14)","#FE8B92"),DARKBLUE:new u("rgb(14, 24, 31)","rgb(46, 105, 144)","rgb(19, 64, 96)","rgb(6, 20, 29)","rgb(8, 9, 10)","#3DB3FF"),LILA:new u("rgb(175, 164, 255)","rgb(188, 168, 253)","rgb(176, 159, 255)","rgb(174, 147, 252)","rgb(168, 136, 233)","#076148"),BLACKRED:new u("rgb(8, 12, 11)","rgb(10, 11, 13)","rgb(11, 10, 15)","rgb(7, 13, 9)","rgb(9, 13, 14)","#B50026"),DARKGREEN:new u("rgb(25, 85, 0)","rgb(47, 154, 0)","rgb(30, 101, 0)","rgb(30, 101, 0)","rgb(25, 85, 0)","#233123"),AMBER:new u("rgb(182, 71, 0)","rgb(236, 155, 25)","rgb(212, 93, 5)","rgb(212, 93, 5)","rgb(182, 71, 0)","#593A0A"),LIGHTBLUE:new u("rgb(125, 146, 184)","rgb(197, 212, 231)","rgb(138, 155, 194)","rgb(138, 155, 194)","rgb(125, 146, 184)","#090051"),SECTIONS:new u("#b2b2b2","#ffffff","#c4c4c4","#c4c4c4","#b2b2b2","#000000")},pi={RED:new b(new n(82,0,0,1),new n(158,0,19,1),new n(213,0,25,1),new n(240,82,88,1),new n(255,171,173,1),new n(255,217,218,1)),GREEN:new b(new n(8,54,4,1),new n(0,107,14,1),new n(15,148,0,1),new n(121,186,37,1),new n(190,231,141,1),new n(234,247,218,1)),BLUE:new b(new n(0,11,68,1),new n(0,73,135,1),new n(0,108,201,1),new n(0,141,242,1),new n(122,200,255,1),new n(204,236,255,1)),ORANGE:new b(new n(118,83,30,1),new n(215,67,0,1),new n(240,117,0,1),new n(255,166,0,1),new n(255,255,128,1),new n(255,247,194,1)),YELLOW:new b(new n(41,41,0,1),new n(102,102,0,1),new n(177,165,0,1),new n(255,242,0,1),new n(255,250,153,1),new n(255,252,204,1)),CYAN:new b(new n(15,109,109,1),new n(0,109,144,1),new n(0,144,191,1),new n(0,174,239,1),new n(153,223,249,1),new n(204,239,252,1)),MAGENTA:new b(new n(98,0,114,1),new n(128,24,72,1),new n(191,36,107,1),new n(255,48,143,1),new n(255,172,210,1),new n(255,214,23,1)),WHITE:new b(new n(210,210,210,1),new n(220,220,220,1),new n(235,235,235,1),new n(255,255,255,1),new n(255,255,255,1),new n(255,255,255,1)),GRAY:new b(new n(25,25,25,1),new n(51,51,51,1),new n(76,76,76,1),new n(128,128,128,1),new n(204,204,204,1),new n(243,243,243,1)),BLACK:new b(new n(0,0,0,1),new n(5,5,5,1),new n(10,10,10,1),new n(15,15,15,1),new n(20,20,20,1),new n(25,25,25,1)),RAITH:new b(new n(0,32,65,1),new n(0,65,125,1),new n(0,106,172,1),new n(130,180,214,1),new n(148,203,242,1),new n(191,229,255,1)),GREEN_LCD:new b(new n(0,55,45,1),new n(15,109,93,1),new n(0,185,165,1),new n(48,255,204,1),new n(153,255,227,1),new n(204,255,241,1)),JUG_GREEN:new b(new n(0,56,0,1),new n(32,69,36,1),new n(50,161,0,1),new n(129,206,0,1),new n(190,231,141,1),new n(234,247,218,1))},gi={RED_LED:new ut("#FF9A89","#FF9A89","#FF3300","#FF8D70","#7E1C00","#7E1C00","#641B00"),GREEN_LED:new ut("#9AFF89","#9AFF89","#59FF2A","#A5FF00","#1C7E00","#1C7E00","#1B6400"),BLUE_LED:new ut("#899AFF","#899AFF","#0033FF","#708DFF","#001C7E","#001C7E","#001B64"),ORANGE_LED:new ut("#FEA23F","#FEA23F","#FD6C00","#FD6C00","#592800","#592800","#421F00"),YELLOW_LED:new ut("#FFFF62","#FFFF62","#FFFF00","#FFFF00","#6B6D00","#6B6D00","#515300"),CYAN_LED:new ut("#00FFFF","#00FFFF","#1BC3C3","#00FFFF","#083B3B","#083B3B","#052727"),MAGENTA_LED:new ut("#D300FF","#D300FF","#8600CB","#C300FF","#38004B","#38004B","#280035")},ai={TYPE1:new at("type1"),TYPE2:new at("type2"),TYPE3:new at("type3"),TYPE4:new at("type4"),TYPE5:new at("type5")},ur={NORTH:new pt("north"),SOUTH:new pt("south"),EAST:new pt("east"),WEST:new pt("west")},fi={STANDARD_KNOB:new ri("standardKnob"),METAL_KNOB:new ri("metalKnob")},oi={BLACK:new dt("black"),BRASS:new dt("brass"),SILVER:new dt("silver")},fr={BLACK_METAL:new g("blackMetal"),METAL:new g("metal"),SHINY_METAL:new g("shinyMetal"),BRASS:new g("brass"),STEEL:new g("steel"),CHROME:new g("chrome"),GOLD:new g("gold"),ANTHRACITE:new g("anthracite"),TILTED_GRAY:new g("tiltedGray"),TILTED_BLACK:new g("tiltedBlack"),GLOSSY_METAL:new g("glossyMetal")},er={TYPE1:new l("type1"),TYPE2:new l("type2"),TYPE3:new l("type3"),TYPE4:new l("type4"),TYPE5:new l("type5"),TYPE6:new l("type6"),TYPE7:new l("type7"),TYPE8:new l("type8"),TYPE9:new l("type9"),TYPE10:new l("type10"),TYPE11:new l("type11"),TYPE12:new l("type12"),TYPE13:new l("type13"),TYPE14:new l("type14"),TYPE15:new l("type15"),TYPE16:new l("type16")},rr={TYPE1:new lt("type1"),TYPE2:new lt("type2"),TYPE3:new lt("type3"),TYPE4:new lt("type4"),TYPE5:new lt("type5")},nr={STANDARD:new gt("standard"),FRACTIONAL:new gt("fractional"),SCIENTIFIC:new gt("scientific")},tr={NORMAL:new bt("normal"),HORIZONTAL:new bt("horizontal"),TANGENT:new bt("tangent")},ir={UP:new yt("up"),STEADY:new yt("steady"),DOWN:new yt("down"),OFF:new yt("off")};return{Radial:ei,RadialBargraph:wi,RadialVertical:bi,Linear:yi,LinearBargraph:kr,DisplaySingle:dr,DisplayMulti:wr,Level:br,Compass:gr,WindDirection:iu,Horizon:ru,Led:nu,Clock:tu,Battery:hr,StopWatch:cr,Altimeter:sr,TrafficLight:lr,LightBulb:yr,Odometer:pr,drawFrame:v,drawBackground:w,drawForeground:y,rgbaColor:n,ConicalGradient:it,setAlpha:f,getColorFromFraction:ui,gradientWrapper:kt,BackgroundColor:di,LcdColor:ki,ColorDef:pi,LedColor:gi,GaugeType:ai,Orientation:ur,FrameDesign:fr,PointerType:er,ForegroundType:rr,KnobType:fi,KnobStyle:oi,LabelNumberFormat:nr,TickLabelOrientation:tr,TrendState:ir,Section:vr}}() \ No newline at end of file
diff --git a/samples/javascript/uglify.js b/samples/javascript/uglify.js
new file mode 100644
index 0000000..ce01a3d
--- /dev/null
+++ b/samples/javascript/uglify.js
@@ -0,0 +1,1320 @@
+/***********************************************************************
+
+ A JavaScript tokenizer / parser / beautifier / compressor.
+
+ This version is suitable for Node.js. With minimal changes (the
+ exports stuff) it should work on any JS platform.
+
+ This file contains the tokenizer/parser. It is a port to JavaScript
+ of parse-js [1], a JavaScript parser library written in Common Lisp
+ by Marijn Haverbeke. Thank you Marijn!
+
+ [1] http://marijn.haverbeke.nl/parse-js/
+
+ Exported functions:
+
+ - tokenizer(code) -- returns a function. Call the returned
+ function to fetch the next token.
+
+ - parse(code) -- returns an AST of the given JavaScript code.
+
+ -------------------------------- (C) ---------------------------------
+
+ Author: Mihai Bazon
+ <mihai.bazon@gmail.com>
+ http://mihai.bazon.net/blog
+
+ Distributed under the BSD license:
+
+ Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
+ Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS†AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ ***********************************************************************/
+
+/* -----[ Tokenizer (constants) ]----- */
+
+var KEYWORDS = array_to_hash([
+ "break",
+ "case",
+ "catch",
+ "const",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "else",
+ "finally",
+ "for",
+ "function",
+ "if",
+ "in",
+ "instanceof",
+ "new",
+ "return",
+ "switch",
+ "throw",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "while",
+ "with"
+]);
+
+var RESERVED_WORDS = array_to_hash([
+ "abstract",
+ "boolean",
+ "byte",
+ "char",
+ "class",
+ "debugger",
+ "double",
+ "enum",
+ "export",
+ "extends",
+ "final",
+ "float",
+ "goto",
+ "implements",
+ "import",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "short",
+ "static",
+ "super",
+ "synchronized",
+ "throws",
+ "transient",
+ "volatile"
+]);
+
+var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
+ "return",
+ "new",
+ "delete",
+ "throw",
+ "else",
+ "case"
+]);
+
+var KEYWORDS_ATOM = array_to_hash([
+ "false",
+ "null",
+ "true",
+ "undefined"
+]);
+
+var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
+
+var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
+var RE_OCT_NUMBER = /^0[0-7]+$/;
+var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
+
+var OPERATORS = array_to_hash([
+ "in",
+ "instanceof",
+ "typeof",
+ "new",
+ "void",
+ "delete",
+ "++",
+ "--",
+ "+",
+ "-",
+ "!",
+ "~",
+ "&",
+ "|",
+ "^",
+ "*",
+ "/",
+ "%",
+ ">>",
+ "<<",
+ ">>>",
+ "<",
+ ">",
+ "<=",
+ ">=",
+ "==",
+ "===",
+ "!=",
+ "!==",
+ "?",
+ "=",
+ "+=",
+ "-=",
+ "/=",
+ "*=",
+ "%=",
+ ">>=",
+ "<<=",
+ ">>>=",
+ "|=",
+ "^=",
+ "&=",
+ "&&",
+ "||"
+]);
+
+var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\v\u200b"));
+
+var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
+
+var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
+
+var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
+
+/* -----[ Tokenizer ]----- */
+
+// regexps adapted from http://xregexp.com/plugins/#unicode
+var UNICODE = {
+ letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
+ non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
+ space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),
+ connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
+};
+
+function is_letter(ch) {
+ return UNICODE.letter.test(ch);
+};
+
+function is_digit(ch) {
+ ch = ch.charCodeAt(0);
+ return ch >= 48 && ch <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
+};
+
+function is_alphanumeric_char(ch) {
+ return is_digit(ch) || is_letter(ch);
+};
+
+function is_unicode_combining_mark(ch) {
+ return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
+};
+
+function is_unicode_connector_punctuation(ch) {
+ return UNICODE.connector_punctuation.test(ch);
+};
+
+function is_identifier_start(ch) {
+ return ch == "$" || ch == "_" || is_letter(ch);
+};
+
+function is_identifier_char(ch) {
+ return is_identifier_start(ch)
+ || is_unicode_combining_mark(ch)
+ || is_digit(ch)
+ || is_unicode_connector_punctuation(ch)
+ || ch == "\u200c" // zero-width non-joiner <ZWNJ>
+ || ch == "\u200d" // zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
+ ;
+};
+
+function parse_js_number(num) {
+ if (RE_HEX_NUMBER.test(num)) {
+ return parseInt(num.substr(2), 16);
+ } else if (RE_OCT_NUMBER.test(num)) {
+ return parseInt(num.substr(1), 8);
+ } else if (RE_DEC_NUMBER.test(num)) {
+ return parseFloat(num);
+ }
+};
+
+function JS_Parse_Error(message, line, col, pos) {
+ this.message = message;
+ this.line = line;
+ this.col = col;
+ this.pos = pos;
+ try {
+ var ex = new Error(message);
+ ex.name = 'JS_Parse_Error'
+ throw ex
+ } catch(ex) {
+ this.stack = ex.stack;
+ };
+};
+
+JS_Parse_Error.prototype.toString = function() {
+ return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
+};
+
+function js_error(message, line, col, pos) {
+ throw new JS_Parse_Error(message, line, col, pos);
+};
+
+function is_token(token, type, val) {
+ return token.type == type && (val == null || token.value == val);
+};
+
+var EX_EOF = {};
+
+function tokenizer($TEXT) {
+
+ var S = {
+ text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
+ pos : 0,
+ tokpos : 0,
+ line : 0,
+ tokline : 0,
+ col : 0,
+ tokcol : 0,
+ newline_before : false,
+ regex_allowed : false,
+ comments_before : []
+ };
+
+ function peek() { return S.text.charAt(S.pos); };
+
+ function next(signal_eof) {
+ var ch = S.text.charAt(S.pos++);
+ if (signal_eof && !ch)
+ throw EX_EOF;
+ if (ch == "\n") {
+ S.newline_before = true;
+ ++S.line;
+ S.col = 0;
+ } else {
+ ++S.col;
+ }
+ return ch;
+ };
+
+ function eof() {
+ return !S.peek();
+ };
+
+ function find(what, signal_eof) {
+ var pos = S.text.indexOf(what, S.pos);
+ if (signal_eof && pos == -1) throw EX_EOF;
+ return pos;
+ };
+
+ function start_token() {
+ S.tokline = S.line;
+ S.tokcol = S.col;
+ S.tokpos = S.pos;
+ };
+
+ function token(type, value, is_comment) {
+ S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
+ (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
+ (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
+ var ret = {
+ type : type,
+ value : value,
+ line : S.tokline,
+ col : S.tokcol,
+ pos : S.tokpos,
+ nlb : S.newline_before
+ };
+ if (!is_comment) {
+ ret.comments_before = S.comments_before;
+ S.comments_before = [];
+ }
+ S.newline_before = false;
+ return ret;
+ };
+
+ function skip_whitespace() {
+ while (HOP(WHITESPACE_CHARS, peek()))
+ next();
+ };
+
+ function read_while(pred) {
+ var ret = "", ch = peek(), i = 0;
+ while (ch && pred(ch, i++)) {
+ ret += next();
+ ch = peek();
+ }
+ return ret;
+ };
+
+ function parse_error(err) {
+ js_error(err, S.tokline, S.tokcol, S.tokpos);
+ };
+
+ function read_num(prefix) {
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
+ var num = read_while(function(ch, i){
+ if (ch == "x" || ch == "X") {
+ if (has_x) return false;
+ return has_x = true;
+ }
+ if (!has_x && (ch == "E" || ch == "e")) {
+ if (has_e) return false;
+ return has_e = after_e = true;
+ }
+ if (ch == "-") {
+ if (after_e || (i == 0 && !prefix)) return true;
+ return false;
+ }
+ if (ch == "+") return after_e;
+ after_e = false;
+ if (ch == ".") {
+ if (!has_dot && !has_x)
+ return has_dot = true;
+ return false;
+ }
+ return is_alphanumeric_char(ch);
+ });
+ if (prefix)
+ num = prefix + num;
+ var valid = parse_js_number(num);
+ if (!isNaN(valid)) {
+ return token("num", valid);
+ } else {
+ parse_error("Invalid syntax: " + num);
+ }
+ };
+
+ function read_escaped_char() {
+ var ch = next(true);
+ switch (ch) {
+ case "n" : return "\n";
+ case "r" : return "\r";
+ case "t" : return "\t";
+ case "b" : return "\b";
+ case "v" : return "\v";
+ case "f" : return "\f";
+ case "0" : return "\0";
+ case "x" : return String.fromCharCode(hex_bytes(2));
+ case "u" : return String.fromCharCode(hex_bytes(4));
+ default : return ch;
+ }
+ };
+
+ function hex_bytes(n) {
+ var num = 0;
+ for (; n > 0; --n) {
+ var digit = parseInt(next(true), 16);
+ if (isNaN(digit))
+ parse_error("Invalid hex-character pattern in string");
+ num = (num << 4) | digit;
+ }
+ return num;
+ };
+
+ function read_string() {
+ return with_eof_error("Unterminated string constant", function(){
+ var quote = next(), ret = "";
+ for (;;) {
+ var ch = next(true);
+ if (ch == "\\") ch = read_escaped_char();
+ else if (ch == quote) break;
+ ret += ch;
+ }
+ return token("string", ret);
+ });
+ };
+
+ function read_line_comment() {
+ next();
+ var i = find("\n"), ret;
+ if (i == -1) {
+ ret = S.text.substr(S.pos);
+ S.pos = S.text.length;
+ } else {
+ ret = S.text.substring(S.pos, i);
+ S.pos = i;
+ }
+ return token("comment1", ret, true);
+ };
+
+ function read_multiline_comment() {
+ next();
+ return with_eof_error("Unterminated multiline comment", function(){
+ var i = find("*/", true),
+ text = S.text.substring(S.pos, i),
+ tok = token("comment2", text, true);
+ S.pos = i + 2;
+ S.line += text.split("\n").length - 1;
+ S.newline_before = text.indexOf("\n") >= 0;
+
+ // https://github.com/mishoo/UglifyJS/issues/#issue/100
+ if (/^@cc_on/i.test(text)) {
+ warn("WARNING: at line " + S.line);
+ warn("*** Found \"conditional comment\": " + text);
+ warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer.");
+ }
+
+ return tok;
+ });
+ };
+
+ function read_name() {
+ var backslash = false, name = "", ch;
+ while ((ch = peek()) != null) {
+ if (!backslash) {
+ if (ch == "\\") backslash = true, next();
+ else if (is_identifier_char(ch)) name += next();
+ else break;
+ }
+ else {
+ if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
+ ch = read_escaped_char();
+ if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
+ name += ch;
+ backslash = false;
+ }
+ }
+ return name;
+ };
+
+ function read_regexp() {
+ return with_eof_error("Unterminated regular expression", function(){
+ var prev_backslash = false, regexp = "", ch, in_class = false;
+ while ((ch = next(true))) if (prev_backslash) {
+ regexp += "\\" + ch;
+ prev_backslash = false;
+ } else if (ch == "[") {
+ in_class = true;
+ regexp += ch;
+ } else if (ch == "]" && in_class) {
+ in_class = false;
+ regexp += ch;
+ } else if (ch == "/" && !in_class) {
+ break;
+ } else if (ch == "\\") {
+ prev_backslash = true;
+ } else {
+ regexp += ch;
+ }
+ var mods = read_name();
+ return token("regexp", [ regexp, mods ]);
+ });
+ };
+
+ function read_operator(prefix) {
+ function grow(op) {
+ if (!peek()) return op;
+ var bigger = op + peek();
+ if (HOP(OPERATORS, bigger)) {
+ next();
+ return grow(bigger);
+ } else {
+ return op;
+ }
+ };
+ return token("operator", grow(prefix || next()));
+ };
+
+ function handle_slash() {
+ next();
+ var regex_allowed = S.regex_allowed;
+ switch (peek()) {
+ case "/":
+ S.comments_before.push(read_line_comment());
+ S.regex_allowed = regex_allowed;
+ return next_token();
+ case "*":
+ S.comments_before.push(read_multiline_comment());
+ S.regex_allowed = regex_allowed;
+ return next_token();
+ }
+ return S.regex_allowed ? read_regexp() : read_operator("/");
+ };
+
+ function handle_dot() {
+ next();
+ return is_digit(peek())
+ ? read_num(".")
+ : token("punc", ".");
+ };
+
+ function read_word() {
+ var word = read_name();
+ return !HOP(KEYWORDS, word)
+ ? token("name", word)
+ : HOP(OPERATORS, word)
+ ? token("operator", word)
+ : HOP(KEYWORDS_ATOM, word)
+ ? token("atom", word)
+ : token("keyword", word);
+ };
+
+ function with_eof_error(eof_error, cont) {
+ try {
+ return cont();
+ } catch(ex) {
+ if (ex === EX_EOF) parse_error(eof_error);
+ else throw ex;
+ }
+ };
+
+ function next_token(force_regexp) {
+ if (force_regexp)
+ return read_regexp();
+ skip_whitespace();
+ start_token();
+ var ch = peek();
+ if (!ch) return token("eof");
+ if (is_digit(ch)) return read_num();
+ if (ch == '"' || ch == "'") return read_string();
+ if (HOP(PUNC_CHARS, ch)) return token("punc", next());
+ if (ch == ".") return handle_dot();
+ if (ch == "/") return handle_slash();
+ if (HOP(OPERATOR_CHARS, ch)) return read_operator();
+ if (ch == "\\" || is_identifier_start(ch)) return read_word();
+ parse_error("Unexpected character '" + ch + "'");
+ };
+
+ next_token.context = function(nc) {
+ if (nc) S = nc;
+ return S;
+ };
+
+ return next_token;
+
+};
+
+/* -----[ Parser (constants) ]----- */
+
+var UNARY_PREFIX = array_to_hash([
+ "typeof",
+ "void",
+ "delete",
+ "--",
+ "++",
+ "!",
+ "~",
+ "-",
+ "+"
+]);
+
+var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
+
+var ASSIGNMENT = (function(a, ret, i){
+ while (i < a.length) {
+ ret[a[i]] = a[i].substr(0, a[i].length - 1);
+ i++;
+ }
+ return ret;
+})(
+ ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
+ { "=": true },
+ 0
+);
+
+var PRECEDENCE = (function(a, ret){
+ for (var i = 0, n = 1; i < a.length; ++i, ++n) {
+ var b = a[i];
+ for (var j = 0; j < b.length; ++j) {
+ ret[b[j]] = n;
+ }
+ }
+ return ret;
+})(
+ [
+ ["||"],
+ ["&&"],
+ ["|"],
+ ["^"],
+ ["&"],
+ ["==", "===", "!=", "!=="],
+ ["<", ">", "<=", ">=", "in", "instanceof"],
+ [">>", "<<", ">>>"],
+ ["+", "-"],
+ ["*", "/", "%"]
+ ],
+ {}
+);
+
+var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
+
+var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
+
+/* -----[ Parser ]----- */
+
+function NodeWithToken(str, start, end) {
+ this.name = str;
+ this.start = start;
+ this.end = end;
+};
+
+NodeWithToken.prototype.toString = function() { return this.name; };
+
+function parse($TEXT, exigent_mode, embed_tokens) {
+
+ var S = {
+ input : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
+ token : null,
+ prev : null,
+ peeked : null,
+ in_function : 0,
+ in_loop : 0,
+ labels : []
+ };
+
+ S.token = next();
+
+ function is(type, value) {
+ return is_token(S.token, type, value);
+ };
+
+ function peek() { return S.peeked || (S.peeked = S.input()); };
+
+ function next() {
+ S.prev = S.token;
+ if (S.peeked) {
+ S.token = S.peeked;
+ S.peeked = null;
+ } else {
+ S.token = S.input();
+ }
+ return S.token;
+ };
+
+ function prev() {
+ return S.prev;
+ };
+
+ function croak(msg, line, col, pos) {
+ var ctx = S.input.context();
+ js_error(msg,
+ line != null ? line : ctx.tokline,
+ col != null ? col : ctx.tokcol,
+ pos != null ? pos : ctx.tokpos);
+ };
+
+ function token_error(token, msg) {
+ croak(msg, token.line, token.col);
+ };
+
+ function unexpected(token) {
+ if (token == null)
+ token = S.token;
+ token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
+ };
+
+ function expect_token(type, val) {
+ if (is(type, val)) {
+ return next();
+ }
+ token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
+ };
+
+ function expect(punc) { return expect_token("punc", punc); };
+
+ function can_insert_semicolon() {
+ return !exigent_mode && (
+ S.token.nlb || is("eof") || is("punc", "}")
+ );
+ };
+
+ function semicolon() {
+ if (is("punc", ";")) next();
+ else if (!can_insert_semicolon()) unexpected();
+ };
+
+ function as() {
+ return slice(arguments);
+ };
+
+ function parenthesised() {
+ expect("(");
+ var ex = expression();
+ expect(")");
+ return ex;
+ };
+
+ function add_tokens(str, start, end) {
+ return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
+ };
+
+ function maybe_embed_tokens(parser) {
+ if (embed_tokens) return function() {
+ var start = S.token;
+ var ast = parser.apply(this, arguments);
+ ast[0] = add_tokens(ast[0], start, prev());
+ return ast;
+ };
+ else return parser;
+ };
+
+ var statement = maybe_embed_tokens(function() {
+ if (is("operator", "/")) {
+ S.peeked = null;
+ S.token = S.input(true); // force regexp
+ }
+ switch (S.token.type) {
+ case "num":
+ case "string":
+ case "regexp":
+ case "operator":
+ case "atom":
+ return simple_statement();
+
+ case "name":
+ return is_token(peek(), "punc", ":")
+ ? labeled_statement(prog1(S.token.value, next, next))
+ : simple_statement();
+
+ case "punc":
+ switch (S.token.value) {
+ case "{":
+ return as("block", block_());
+ case "[":
+ case "(":
+ return simple_statement();
+ case ";":
+ next();
+ return as("block");
+ default:
+ unexpected();
+ }
+
+ case "keyword":
+ switch (prog1(S.token.value, next)) {
+ case "break":
+ return break_cont("break");
+
+ case "continue":
+ return break_cont("continue");
+
+ case "debugger":
+ semicolon();
+ return as("debugger");
+
+ case "do":
+ return (function(body){
+ expect_token("keyword", "while");
+ return as("do", prog1(parenthesised, semicolon), body);
+ })(in_loop(statement));
+
+ case "for":
+ return for_();
+
+ case "function":
+ return function_(true);
+
+ case "if":
+ return if_();
+
+ case "return":
+ if (S.in_function == 0)
+ croak("'return' outside of function");
+ return as("return",
+ is("punc", ";")
+ ? (next(), null)
+ : can_insert_semicolon()
+ ? null
+ : prog1(expression, semicolon));
+
+ case "switch":
+ return as("switch", parenthesised(), switch_block_());
+
+ case "throw":
+ return as("throw", prog1(expression, semicolon));
+
+ case "try":
+ return try_();
+
+ case "var":
+ return prog1(var_, semicolon);
+
+ case "const":
+ return prog1(const_, semicolon);
+
+ case "while":
+ return as("while", parenthesised(), in_loop(statement));
+
+ case "with":
+ return as("with", parenthesised(), statement());
+
+ default:
+ unexpected();
+ }
+ }
+ });
+
+ function labeled_statement(label) {
+ S.labels.push(label);
+ var start = S.token, stat = statement();
+ if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
+ unexpected(start);
+ S.labels.pop();
+ return as("label", label, stat);
+ };
+
+ function simple_statement() {
+ return as("stat", prog1(expression, semicolon));
+ };
+
+ function break_cont(type) {
+ var name;
+ if (!can_insert_semicolon()) {
+ name = is("name") ? S.token.value : null;
+ }
+ if (name != null) {
+ next();
+ if (!member(name, S.labels))
+ croak("Label " + name + " without matching loop or statement");
+ }
+ else if (S.in_loop == 0)
+ croak(type + " not inside a loop or switch");
+ semicolon();
+ return as(type, name);
+ };
+
+ function for_() {
+ expect("(");
+ var init = null;
+ if (!is("punc", ";")) {
+ init = is("keyword", "var")
+ ? (next(), var_(true))
+ : expression(true, true);
+ if (is("operator", "in"))
+ return for_in(init);
+ }
+ return regular_for(init);
+ };
+
+ function regular_for(init) {
+ expect(";");
+ var test = is("punc", ";") ? null : expression();
+ expect(";");
+ var step = is("punc", ")") ? null : expression();
+ expect(")");
+ return as("for", init, test, step, in_loop(statement));
+ };
+
+ function for_in(init) {
+ var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
+ next();
+ var obj = expression();
+ expect(")");
+ return as("for-in", init, lhs, obj, in_loop(statement));
+ };
+
+ var function_ = maybe_embed_tokens(function(in_statement) {
+ var name = is("name") ? prog1(S.token.value, next) : null;
+ if (in_statement && !name)
+ unexpected();
+ expect("(");
+ return as(in_statement ? "defun" : "function",
+ name,
+ // arguments
+ (function(first, a){
+ while (!is("punc", ")")) {
+ if (first) first = false; else expect(",");
+ if (!is("name")) unexpected();
+ a.push(S.token.value);
+ next();
+ }
+ next();
+ return a;
+ })(true, []),
+ // body
+ (function(){
+ ++S.in_function;
+ var loop = S.in_loop;
+ S.in_loop = 0;
+ var a = block_();
+ --S.in_function;
+ S.in_loop = loop;
+ return a;
+ })());
+ });
+
+ function if_() {
+ var cond = parenthesised(), body = statement(), belse;
+ if (is("keyword", "else")) {
+ next();
+ belse = statement();
+ }
+ return as("if", cond, body, belse);
+ };
+
+ function block_() {
+ expect("{");
+ var a = [];
+ while (!is("punc", "}")) {
+ if (is("eof")) unexpected();
+ a.push(statement());
+ }
+ next();
+ return a;
+ };
+
+ var switch_block_ = curry(in_loop, function(){
+ expect("{");
+ var a = [], cur = null;
+ while (!is("punc", "}")) {
+ if (is("eof")) unexpected();
+ if (is("keyword", "case")) {
+ next();
+ cur = [];
+ a.push([ expression(), cur ]);
+ expect(":");
+ }
+ else if (is("keyword", "default")) {
+ next();
+ expect(":");
+ cur = [];
+ a.push([ null, cur ]);
+ }
+ else {
+ if (!cur) unexpected();
+ cur.push(statement());
+ }
+ }
+ next();
+ return a;
+ });
+
+ function try_() {
+ var body = block_(), bcatch, bfinally;
+ if (is("keyword", "catch")) {
+ next();
+ expect("(");
+ if (!is("name"))
+ croak("Name expected");
+ var name = S.token.value;
+ next();
+ expect(")");
+ bcatch = [ name, block_() ];
+ }
+ if (is("keyword", "finally")) {
+ next();
+ bfinally = block_();
+ }
+ if (!bcatch && !bfinally)
+ croak("Missing catch/finally blocks");
+ return as("try", body, bcatch, bfinally);
+ };
+
+ function vardefs(no_in) {
+ var a = [];
+ for (;;) {
+ if (!is("name"))
+ unexpected();
+ var name = S.token.value;
+ next();
+ if (is("operator", "=")) {
+ next();
+ a.push([ name, expression(false, no_in) ]);
+ } else {
+ a.push([ name ]);
+ }
+ if (!is("punc", ","))
+ break;
+ next();
+ }
+ return a;
+ };
+
+ function var_(no_in) {
+ return as("var", vardefs(no_in));
+ };
+
+ function const_() {
+ return as("const", vardefs());
+ };
+
+ function new_() {
+ var newexp = expr_atom(false), args;
+ if (is("punc", "(")) {
+ next();
+ args = expr_list(")");
+ } else {
+ args = [];
+ }
+ return subscripts(as("new", newexp, args), true);
+ };
+
+ var expr_atom = maybe_embed_tokens(function(allow_calls) {
+ if (is("operator", "new")) {
+ next();
+ return new_();
+ }
+ if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
+ return make_unary("unary-prefix",
+ prog1(S.token.value, next),
+ expr_atom(allow_calls));
+ }
+ if (is("punc")) {
+ switch (S.token.value) {
+ case "(":
+ next();
+ return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
+ case "[":
+ next();
+ return subscripts(array_(), allow_calls);
+ case "{":
+ next();
+ return subscripts(object_(), allow_calls);
+ }
+ unexpected();
+ }
+ if (is("keyword", "function")) {
+ next();
+ return subscripts(function_(false), allow_calls);
+ }
+ if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
+ var atom = S.token.type == "regexp"
+ ? as("regexp", S.token.value[0], S.token.value[1])
+ : as(S.token.type, S.token.value);
+ return subscripts(prog1(atom, next), allow_calls);
+ }
+ unexpected();
+ });
+
+ function expr_list(closing, allow_trailing_comma, allow_empty) {
+ var first = true, a = [];
+ while (!is("punc", closing)) {
+ if (first) first = false; else expect(",");
+ if (allow_trailing_comma && is("punc", closing)) break;
+ if (is("punc", ",") && allow_empty) {
+ a.push([ "atom", "undefined" ]);
+ } else {
+ a.push(expression(false));
+ }
+ }
+ next();
+ return a;
+ };
+
+ function array_() {
+ return as("array", expr_list("]", !exigent_mode, true));
+ };
+
+ function object_() {
+ var first = true, a = [];
+ while (!is("punc", "}")) {
+ if (first) first = false; else expect(",");
+ if (!exigent_mode && is("punc", "}"))
+ // allow trailing comma
+ break;
+ var type = S.token.type;
+ var name = as_property_name();
+ if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
+ a.push([ as_name(), function_(false), name ]);
+ } else {
+ expect(":");
+ a.push([ name, expression(false) ]);
+ }
+ }
+ next();
+ return as("object", a);
+ };
+
+ function as_property_name() {
+ switch (S.token.type) {
+ case "num":
+ case "string":
+ return prog1(S.token.value, next);
+ }
+ return as_name();
+ };
+
+ function as_name() {
+ switch (S.token.type) {
+ case "name":
+ case "operator":
+ case "keyword":
+ case "atom":
+ return prog1(S.token.value, next);
+ default:
+ unexpected();
+ }
+ };
+
+ function subscripts(expr, allow_calls) {
+ if (is("punc", ".")) {
+ next();
+ return subscripts(as("dot", expr, as_name()), allow_calls);
+ }
+ if (is("punc", "[")) {
+ next();
+ return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
+ }
+ if (allow_calls && is("punc", "(")) {
+ next();
+ return subscripts(as("call", expr, expr_list(")")), true);
+ }
+ if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) {
+ return prog1(curry(make_unary, "unary-postfix", S.token.value, expr),
+ next);
+ }
+ return expr;
+ };
+
+ function make_unary(tag, op, expr) {
+ if ((op == "++" || op == "--") && !is_assignable(expr))
+ croak("Invalid use of " + op + " operator");
+ return as(tag, op, expr);
+ };
+
+ function expr_op(left, min_prec, no_in) {
+ var op = is("operator") ? S.token.value : null;
+ if (op && op == "in" && no_in) op = null;
+ var prec = op != null ? PRECEDENCE[op] : null;
+ if (prec != null && prec > min_prec) {
+ next();
+ var right = expr_op(expr_atom(true), prec, no_in);
+ return expr_op(as("binary", op, left, right), min_prec, no_in);
+ }
+ return left;
+ };
+
+ function expr_ops(no_in) {
+ return expr_op(expr_atom(true), 0, no_in);
+ };
+
+ function maybe_conditional(no_in) {
+ var expr = expr_ops(no_in);
+ if (is("operator", "?")) {
+ next();
+ var yes = expression(false);
+ expect(":");
+ return as("conditional", expr, yes, expression(false, no_in));
+ }
+ return expr;
+ };
+
+ function is_assignable(expr) {
+ if (!exigent_mode) return true;
+ switch (expr[0]) {
+ case "dot":
+ case "sub":
+ case "new":
+ case "call":
+ return true;
+ case "name":
+ return expr[1] != "this";
+ }
+ };
+
+ function maybe_assign(no_in) {
+ var left = maybe_conditional(no_in), val = S.token.value;
+ if (is("operator") && HOP(ASSIGNMENT, val)) {
+ if (is_assignable(left)) {
+ next();
+ return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
+ }
+ croak("Invalid assignment");
+ }
+ return left;
+ };
+
+ var expression = maybe_embed_tokens(function(commas, no_in) {
+ if (arguments.length == 0)
+ commas = true;
+ var expr = maybe_assign(no_in);
+ if (commas && is("punc", ",")) {
+ next();
+ return as("seq", expr, expression(true, no_in));
+ }
+ return expr;
+ });
+
+ function in_loop(cont) {
+ try {
+ ++S.in_loop;
+ return cont();
+ } finally {
+ --S.in_loop;
+ }
+ };
+
+ return as("toplevel", (function(a){
+ while (!is("eof"))
+ a.push(statement());
+ return a;
+ })([]));
+
+};
+
+/* -----[ Utilities ]----- */
+
+function curry(f) {
+ var args = slice(arguments, 1);
+ return function() { return f.apply(this, args.concat(slice(arguments))); };
+};
+
+function prog1(ret) {
+ if (ret instanceof Function)
+ ret = ret();
+ for (var i = 1, n = arguments.length; --n > 0; ++i)
+ arguments[i]();
+ return ret;
+};
+
+function array_to_hash(a) {
+ var ret = {};
+ for (var i = 0; i < a.length; ++i)
+ ret[a[i]] = true;
+ return ret;
+};
+
+function slice(a, start) {
+ return Array.prototype.slice.call(a, start == null ? 0 : start);
+};
+
+function characters(str) {
+ return str.split("");
+};
+
+function member(name, array) {
+ for (var i = array.length; --i >= 0;)
+ if (array[i] === name)
+ return true;
+ return false;
+};
+
+function HOP(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+};
+
+var warn = function() {};
+
+/* -----[ Exports ]----- */
+
+exports.tokenizer = tokenizer;
+exports.parse = parse;
+exports.slice = slice;
+exports.curry = curry;
+exports.member = member;
+exports.array_to_hash = array_to_hash;
+exports.PRECEDENCE = PRECEDENCE;
+exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
+exports.RESERVED_WORDS = RESERVED_WORDS;
+exports.KEYWORDS = KEYWORDS;
+exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
+exports.OPERATORS = OPERATORS;
+exports.is_alphanumeric_char = is_alphanumeric_char;
+exports.set_logger = function(logger) {
+ warn = logger;
+};
diff --git a/samples/julia/stockcorr.jl b/samples/julia/stockcorr.jl
new file mode 100644
index 0000000..58ba80a
--- /dev/null
+++ b/samples/julia/stockcorr.jl
@@ -0,0 +1,37 @@
+## Test case from Issue #445
+
+#STOCKCORR - The original, unoptimised code that simulates two correlated assets
+function stockcorr()
+
+ ## Correlated asset information
+ CurrentPrice = [78. 102.] # Initial Prices of the two stocks
+ Corr = [1. 0.4; 0.4 1.] # Correlation Matrix
+ T = 500 # Number of days to simulate = 2years = 500days
+ n = 100000 # Number of simulations
+ dt = 1/250 # Time step (1year = 250days)
+ Div=[0.01 0.01] # Dividend
+ Vol=[0.2 0.3] # Volatility
+
+ ## Market Information
+ r = 0.03 # Risk-free rate
+
+ ## Define storages
+ SimulPriceA = zeros(T,n) # Simulated Price of Asset A
+ SimulPriceA[1,:] = CurrentPrice[1]
+ SimulPriceB = zeros(T,n) # Simulated Price of Asset B
+ SimulPriceB[1,:] = CurrentPrice[2]
+
+ ## Generating the paths of stock prices by Geometric Brownian Motion
+ UpperTriangle=chol(Corr) # UpperTriangle Matrix by Cholesky decomposition
+
+ for i = 1:n
+ Wiener = randn(T-1,2)
+ CorrWiener = Wiener*UpperTriangle
+ for j = 2:T
+ SimulPriceA[j,i] = SimulPriceA[j-1,i]*exp((r-Div[1]-Vol[1]^2/2)*dt+Vol[1]*sqrt(dt)*CorrWiener[j-1,1])
+ SimulPriceB[j,i] = SimulPriceB[j-1,i]*exp((r-Div[2]-Vol[2]^2/2)*dt+Vol[2]*sqrt(dt)*CorrWiener[j-1,2])
+ end
+ end
+
+ return (SimulPriceA, SimulPriceB)
+end
diff --git a/samples/kotlin/Foo.kt b/samples/kotlin/Foo.kt
new file mode 100644
index 0000000..5929133
--- /dev/null
+++ b/samples/kotlin/Foo.kt
@@ -0,0 +1,47 @@
+package addressbook
+
+class Contact(
+ val name : String,
+ val emails : List<EmailAddress>,
+ val addresses : List<PostalAddress>,
+ val phonenums : List<PhoneNumber>
+)
+
+class EmailAddress(
+ val user : String,
+ val host : String
+)
+
+class PostalAddress(
+ val streetAddress : String,
+ val city : String,
+ val zip : String,
+ val state : USState?,
+ val country : Country
+) {
+ assert {(state == null) xor (country == Countries["US"]) }
+}
+
+class PhoneNumber(
+ val country : Country,
+ val areaCode : Int,
+ val number : Long
+)
+
+object Countries {
+ fun get(id : CountryID) : Country = countryTable[id]
+
+ private var table : Map<String, Country>? = null
+ private val countryTable : Map<String, Country>
+ get() {
+ if (table == null) {
+ table = HashMap()
+ for (line in TextFile("countries.txt").lines(stripWhiteSpace = true)) {
+ table[line] = Country(line)
+ }
+ }
+ return table
+ }
+}
+
+class Country(val name : String) \ No newline at end of file
diff --git a/samples/logtalk/foo.lgt b/samples/logtalk/foo.lgt
new file mode 100644
index 0000000..673fab2
--- /dev/null
+++ b/samples/logtalk/foo.lgt
@@ -0,0 +1,9 @@
+% this is a Logtalk source file
+
+:- object(hello_world).
+
+ % the initialization/1 directive argument is automatically executed
+ % when the object is loaded into memory:
+ :- initialization((nl, write('********** Hello World! **********'), nl)).
+
+:- end_object.
diff --git a/samples/markdown/tender.md b/samples/markdown/tender.md
new file mode 100644
index 0000000..82d3a02
--- /dev/null
+++ b/samples/markdown/tender.md
@@ -0,0 +1,2 @@
+Tender
+======
diff --git a/samples/matlab/average.m b/samples/matlab/average.m
new file mode 100644
index 0000000..65eef8b
--- /dev/null
+++ b/samples/matlab/average.m
@@ -0,0 +1,9 @@
+function y = average(x)
+% AVERAGE Mean of vector elements.
+% AVERAGE(X), where X is a vector, is the mean of vector
+% elements. Nonvector input results in an error.
+[m,n] = size(x);
+if (~((m == 1) | (n == 1)) | (m == 1 & n == 1))
+ error('Input must be a vector')
+end
+y = sum(x)/length(x);
diff --git a/samples/matlab/make_filter.m b/samples/matlab/make_filter.m
new file mode 100644
index 0000000..3ad6c5c
--- /dev/null
+++ b/samples/matlab/make_filter.m
@@ -0,0 +1,38 @@
+function [filtfcn, statefcn] = makeFilter(b, a)
+% FILTFCN = MAKEFILTER(B, A) creates an IIR filtering
+% function and returns it in the form of a function handle,
+% FILTFCN. Each time you call FILTFCN with a new filter
+% input value, it computes the corresponding new filter
+% output value, updating its internal state vector at the
+% same time.
+%
+% [FILTFCN, STATEFCN] = MAKEFILTER(B, A) also returns a
+% function (in the form of a function handle, STATEFCN)
+% that can return the filter's internal state. The internal
+% state vector is in the form of a transposed direct form
+% II delay line.
+
+% Initialize state vector. To keep this example a bit
+% simpler, assume that a and b have the same length.
+% Also assume that a(1) is 1.
+
+v = zeros(size(a));
+
+filtfcn = @iirFilter;
+statefcn = @getState;
+
+ function yn = iirFilter(xn)
+ % Update the state vector
+ v(1) = v(2) + b(1) * xn;
+ v(2:end-1) = v(3:end) + b(2:end-1) * xn - ...
+ a(2:end-1) * v(1);
+ v(end) = b(end) * xn - a(end) * v(1);
+
+ % Output is the first element of the state vector.
+ yn = v(1);
+ end
+
+ function vOut = getState
+ vOut = v;
+ end
+end
diff --git a/samples/matlab/matlab_class.m b/samples/matlab/matlab_class.m
new file mode 100644
index 0000000..7377a4d
--- /dev/null
+++ b/samples/matlab/matlab_class.m
@@ -0,0 +1,29 @@
+classdef matlab_class
+ properties
+ R;
+ G;
+ B;
+ end
+ methods
+ function obj = matlab_class(r,g,b)
+ obj.R = r;
+ obj.G = g;
+ obj.B = b;
+ end
+ function disp(obj)
+ disp(['Red: ' num2str(obj.R) ...
+ ', Green: ' num2str(obj.G) ...
+ ', Blue: ' num2str(obj.B)]);
+ end
+ end
+ enumeration
+ red (1,0,0)
+ green (0,1,0)
+ blue (0,0,1)
+ cyan (0,1,1)
+ magenta (1,0,1)
+ yellow (1,1,0)
+ black (0,0,0)
+ white (1,1,1)
+ end
+end \ No newline at end of file
diff --git a/samples/matlab/matlab_function.m b/samples/matlab/matlab_function.m
new file mode 100644
index 0000000..b73502e
--- /dev/null
+++ b/samples/matlab/matlab_function.m
@@ -0,0 +1,9 @@
+function ret = matlab_function(A,B)
+% Simple function adding two values and displaying the return value
+
+ret = A+B;
+% Display the return value
+disp('Return value in function');
+disp(ret);
+
+
diff --git a/samples/matlab/matlab_script.m b/samples/matlab/matlab_script.m
new file mode 100644
index 0000000..89f1d95
--- /dev/null
+++ b/samples/matlab/matlab_script.m
@@ -0,0 +1,12 @@
+% Matlab example script
+
+%Call matlab_function function which resides in the same directory
+
+value1 = 5 % semicolon at end of line is not mandatory, only suppresses output to command line.
+value2 = 3
+
+% Calculate sum of value1 and value2
+result = matlab_function(value1,value2);
+
+disp('called from script')
+disp(result);
diff --git a/samples/matlab/matlab_script2.m b/samples/matlab/matlab_script2.m
new file mode 100644
index 0000000..8e16eca
--- /dev/null
+++ b/samples/matlab/matlab_script2.m
@@ -0,0 +1,13 @@
+ % Matlab example script 2
+ % Comments precended with arbitrary whitespace (spaces or tabs)
+
+ %Call matlab_function function which resides in the same directory
+
+value1 = 5 % semicolon at end of line is not mandatory, only suppresses output to command line.
+value2 = 3
+
+ % Calculate sum of value1 and value2
+result = matlab_function(value1,value2);
+
+disp('called from script')
+disp(result); \ No newline at end of file
diff --git a/samples/nemerle/hello.n b/samples/nemerle/hello.n
new file mode 100644
index 0000000..7d36714
--- /dev/null
+++ b/samples/nemerle/hello.n
@@ -0,0 +1,9 @@
+using System.Console;
+
+module Program
+{
+ Main() : void
+ {
+ WriteLine("Hello world");
+ }
+} \ No newline at end of file
diff --git a/samples/nimrod/foo.nim b/samples/nimrod/foo.nim
new file mode 100644
index 0000000..58b7f62
--- /dev/null
+++ b/samples/nimrod/foo.nim
@@ -0,0 +1,3 @@
+# Hello world program
+
+echo "Hello world!"
diff --git a/samples/nu/script.nu b/samples/nu/script.nu
new file mode 100755
index 0000000..4e02da8
--- /dev/null
+++ b/samples/nu/script.nu
@@ -0,0 +1,2 @@
+#!/usr/bin/env nush
+(puts "Hello")
diff --git a/samples/objective-c/ASIHTTPRequest.h b/samples/objective-c/ASIHTTPRequest.h
new file mode 100644
index 0000000..ed6edc8
--- /dev/null
+++ b/samples/objective-c/ASIHTTPRequest.h
@@ -0,0 +1,1004 @@
+//
+// ASIHTTPRequest.h
+//
+// Created by Ben Copsey on 04/10/2007.
+// Copyright 2007-2011 All-Seeing Interactive. All rights reserved.
+//
+// A guide to the main features is available at:
+// http://allseeing-i.com/ASIHTTPRequest
+//
+// Portions are based on the ImageClient example from Apple:
+// See: http://developer.apple.com/samplecode/ImageClient/listing37.html
+
+#import <Foundation/Foundation.h>
+#if TARGET_OS_IPHONE
+ #import <CFNetwork/CFNetwork.h>
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
+ #import <UIKit/UIKit.h> // Necessary for background task support
+ #endif
+#endif
+
+#import <stdio.h>
+#import "ASIHTTPRequestConfig.h"
+#import "ASIHTTPRequestDelegate.h"
+#import "ASIProgressDelegate.h"
+#import "ASICacheDelegate.h"
+
+@class ASIDataDecompressor;
+
+extern NSString *ASIHTTPRequestVersion;
+
+// Make targeting different platforms more reliable
+// See: http://www.blumtnwerx.com/blog/2009/06/cross-sdk-code-hygiene-in-xcode/
+#ifndef __IPHONE_3_2
+ #define __IPHONE_3_2 30200
+#endif
+#ifndef __IPHONE_4_0
+ #define __IPHONE_4_0 40000
+#endif
+#ifndef __MAC_10_5
+ #define __MAC_10_5 1050
+#endif
+#ifndef __MAC_10_6
+ #define __MAC_10_6 1060
+#endif
+
+typedef enum _ASIAuthenticationState {
+ ASINoAuthenticationNeededYet = 0,
+ ASIHTTPAuthenticationNeeded = 1,
+ ASIProxyAuthenticationNeeded = 2
+} ASIAuthenticationState;
+
+typedef enum _ASINetworkErrorType {
+ ASIConnectionFailureErrorType = 1,
+ ASIRequestTimedOutErrorType = 2,
+ ASIAuthenticationErrorType = 3,
+ ASIRequestCancelledErrorType = 4,
+ ASIUnableToCreateRequestErrorType = 5,
+ ASIInternalErrorWhileBuildingRequestType = 6,
+ ASIInternalErrorWhileApplyingCredentialsType = 7,
+ ASIFileManagementError = 8,
+ ASITooMuchRedirectionErrorType = 9,
+ ASIUnhandledExceptionError = 10,
+ ASICompressionError = 11
+
+} ASINetworkErrorType;
+
+
+// The error domain that all errors generated by ASIHTTPRequest use
+extern NSString* const NetworkRequestErrorDomain;
+
+// You can use this number to throttle upload and download bandwidth in iPhone OS apps send or receive a large amount of data
+// This may help apps that might otherwise be rejected for inclusion into the app store for using excessive bandwidth
+// This number is not official, as far as I know there is no officially documented bandwidth limit
+extern unsigned long const ASIWWANBandwidthThrottleAmount;
+
+#if NS_BLOCKS_AVAILABLE
+typedef void (^ASIBasicBlock)(void);
+typedef void (^ASIHeadersBlock)(NSDictionary *responseHeaders);
+typedef void (^ASISizeBlock)(long long size);
+typedef void (^ASIProgressBlock)(unsigned long long size, unsigned long long total);
+typedef void (^ASIDataBlock)(NSData *data);
+#endif
+
+@interface ASIHTTPRequest : NSOperation <NSCopying> {
+
+ // The url for this operation, should include GET params in the query string where appropriate
+ NSURL *url;
+
+ // Will always contain the original url used for making the request (the value of url can change when a request is redirected)
+ NSURL *originalURL;
+
+ // Temporarily stores the url we are about to redirect to. Will be nil again when we do redirect
+ NSURL *redirectURL;
+
+ // The delegate - will be notified of various changes in state via the ASIHTTPRequestDelegate protocol
+ id <ASIHTTPRequestDelegate> delegate;
+
+ // Another delegate that is also notified of request status changes and progress updates
+ // Generally, you won't use this directly, but ASINetworkQueue sets itself as the queue so it can proxy updates to its own delegates
+ // NOTE: WILL BE RETAINED BY THE REQUEST
+ id <ASIHTTPRequestDelegate, ASIProgressDelegate> queue;
+
+ // HTTP method to use (eg: GET / POST / PUT / DELETE / HEAD etc). Defaults to GET
+ NSString *requestMethod;
+
+ // Request body - only used when the whole body is stored in memory (shouldStreamPostDataFromDisk is false)
+ NSMutableData *postBody;
+
+ // gzipped request body used when shouldCompressRequestBody is YES
+ NSData *compressedPostBody;
+
+ // When true, post body will be streamed from a file on disk, rather than loaded into memory at once (useful for large uploads)
+ // Automatically set to true in ASIFormDataRequests when using setFile:forKey:
+ BOOL shouldStreamPostDataFromDisk;
+
+ // Path to file used to store post body (when shouldStreamPostDataFromDisk is true)
+ // You can set this yourself - useful if you want to PUT a file from local disk
+ NSString *postBodyFilePath;
+
+ // Path to a temporary file used to store a deflated post body (when shouldCompressPostBody is YES)
+ NSString *compressedPostBodyFilePath;
+
+ // Set to true when ASIHTTPRequest automatically created a temporary file containing the request body (when true, the file at postBodyFilePath will be deleted at the end of the request)
+ BOOL didCreateTemporaryPostDataFile;
+
+ // Used when writing to the post body when shouldStreamPostDataFromDisk is true (via appendPostData: or appendPostDataFromFile:)
+ NSOutputStream *postBodyWriteStream;
+
+ // Used for reading from the post body when sending the request
+ NSInputStream *postBodyReadStream;
+
+ // Dictionary for custom HTTP request headers
+ NSMutableDictionary *requestHeaders;
+
+ // Set to YES when the request header dictionary has been populated, used to prevent this happening more than once
+ BOOL haveBuiltRequestHeaders;
+
+ // Will be populated with HTTP response headers from the server
+ NSDictionary *responseHeaders;
+
+ // Can be used to manually insert cookie headers to a request, but it's more likely that sessionCookies will do this for you
+ NSMutableArray *requestCookies;
+
+ // Will be populated with cookies
+ NSArray *responseCookies;
+
+ // If use useCookiePersistence is true, network requests will present valid cookies from previous requests
+ BOOL useCookiePersistence;
+
+ // If useKeychainPersistence is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented
+ BOOL useKeychainPersistence;
+
+ // If useSessionPersistence is true, network requests will save credentials and reuse for the duration of the session (until clearSession is called)
+ BOOL useSessionPersistence;
+
+ // If allowCompressedResponse is true, requests will inform the server they can accept compressed data, and will automatically decompress gzipped responses. Default is true.
+ BOOL allowCompressedResponse;
+
+ // If shouldCompressRequestBody is true, the request body will be gzipped. Default is false.
+ // You will probably need to enable this feature on your webserver to make this work. Tested with apache only.
+ BOOL shouldCompressRequestBody;
+
+ // When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location
+ // If downloadDestinationPath is not set, download data will be stored in memory
+ NSString *downloadDestinationPath;
+
+ // The location that files will be downloaded to. Once a download is complete, files will be decompressed (if necessary) and moved to downloadDestinationPath
+ NSString *temporaryFileDownloadPath;
+
+ // If the response is gzipped and shouldWaitToInflateCompressedResponses is NO, a file will be created at this path containing the inflated response as it comes in
+ NSString *temporaryUncompressedDataDownloadPath;
+
+ // Used for writing data to a file when downloadDestinationPath is set
+ NSOutputStream *fileDownloadOutputStream;
+
+ NSOutputStream *inflatedFileDownloadOutputStream;
+
+ // When the request fails or completes successfully, complete will be true
+ BOOL complete;
+
+ // external "finished" indicator, subject of KVO notifications; updates after 'complete'
+ BOOL finished;
+
+ // True if our 'cancel' selector has been called
+ BOOL cancelled;
+
+ // If an error occurs, error will contain an NSError
+ // If error code is = ASIConnectionFailureErrorType (1, Connection failure occurred) - inspect [[error userInfo] objectForKey:NSUnderlyingErrorKey] for more information
+ NSError *error;
+
+ // Username and password used for authentication
+ NSString *username;
+ NSString *password;
+
+ // User-Agent for this request
+ NSString *userAgentString;
+
+ // Domain used for NTLM authentication
+ NSString *domain;
+
+ // Username and password used for proxy authentication
+ NSString *proxyUsername;
+ NSString *proxyPassword;
+
+ // Domain used for NTLM proxy authentication
+ NSString *proxyDomain;
+
+ // Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
+ id <ASIProgressDelegate> uploadProgressDelegate;
+
+ // Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
+ id <ASIProgressDelegate> downloadProgressDelegate;
+
+ // Whether we've seen the headers of the response yet
+ BOOL haveExaminedHeaders;
+
+ // Data we receive will be stored here. Data may be compressed unless allowCompressedResponse is false - you should use [request responseData] instead in most cases
+ NSMutableData *rawResponseData;
+
+ // Used for sending and receiving data
+ CFHTTPMessageRef request;
+ NSInputStream *readStream;
+
+ // Used for authentication
+ CFHTTPAuthenticationRef requestAuthentication;
+ NSDictionary *requestCredentials;
+
+ // Used during NTLM authentication
+ int authenticationRetryCount;
+
+ // Authentication scheme (Basic, Digest, NTLM)
+ // If you are using Basic authentication and want to force ASIHTTPRequest to send an authorization header without waiting for a 401, you must set this to (NSString *)kCFHTTPAuthenticationSchemeBasic
+ NSString *authenticationScheme;
+
+ // Realm for authentication when credentials are required
+ NSString *authenticationRealm;
+
+ // When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a server that requires authentication
+ // The dialog will not be shown if your delegate responds to authenticationNeededForRequest:
+ // Default is NO.
+ BOOL shouldPresentAuthenticationDialog;
+
+ // When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a proxy server that requires authentication
+ // The dialog will not be shown if your delegate responds to proxyAuthenticationNeededForRequest:
+ // Default is YES (basically, because most people won't want the hassle of adding support for authenticating proxies to their apps)
+ BOOL shouldPresentProxyAuthenticationDialog;
+
+ // Used for proxy authentication
+ CFHTTPAuthenticationRef proxyAuthentication;
+ NSDictionary *proxyCredentials;
+
+ // Used during authentication with an NTLM proxy
+ int proxyAuthenticationRetryCount;
+
+ // Authentication scheme for the proxy (Basic, Digest, NTLM)
+ NSString *proxyAuthenticationScheme;
+
+ // Realm for proxy authentication when credentials are required
+ NSString *proxyAuthenticationRealm;
+
+ // HTTP status code, eg: 200 = OK, 404 = Not found etc
+ int responseStatusCode;
+
+ // Description of the HTTP status code
+ NSString *responseStatusMessage;
+
+ // Size of the response
+ unsigned long long contentLength;
+
+ // Size of the partially downloaded content
+ unsigned long long partialDownloadSize;
+
+ // Size of the POST payload
+ unsigned long long postLength;
+
+ // The total amount of downloaded data
+ unsigned long long totalBytesRead;
+
+ // The total amount of uploaded data
+ unsigned long long totalBytesSent;
+
+ // Last amount of data read (used for incrementing progress)
+ unsigned long long lastBytesRead;
+
+ // Last amount of data sent (used for incrementing progress)
+ unsigned long long lastBytesSent;
+
+ // This lock prevents the operation from being cancelled at an inopportune moment
+ NSRecursiveLock *cancelledLock;
+
+ // Called on the delegate (if implemented) when the request starts. Default is requestStarted:
+ SEL didStartSelector;
+
+ // Called on the delegate (if implemented) when the request receives response headers. Default is request:didReceiveResponseHeaders:
+ SEL didReceiveResponseHeadersSelector;
+
+ // Called on the delegate (if implemented) when the request receives a Location header and shouldRedirect is YES
+ // The delegate can then change the url if needed, and can restart the request by calling [request redirectToURL:], or simply cancel it
+ SEL willRedirectSelector;
+
+ // Called on the delegate (if implemented) when the request completes successfully. Default is requestFinished:
+ SEL didFinishSelector;
+
+ // Called on the delegate (if implemented) when the request fails. Default is requestFailed:
+ SEL didFailSelector;
+
+ // Called on the delegate (if implemented) when the request receives data. Default is request:didReceiveData:
+ // If you set this and implement the method in your delegate, you must handle the data yourself - ASIHTTPRequest will not populate responseData or write the data to downloadDestinationPath
+ SEL didReceiveDataSelector;
+
+ // Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate
+ NSDate *lastActivityTime;
+
+ // Number of seconds to wait before timing out - default is 10
+ NSTimeInterval timeOutSeconds;
+
+ // Will be YES when a HEAD request will handle the content-length before this request starts
+ BOOL shouldResetUploadProgress;
+ BOOL shouldResetDownloadProgress;
+
+ // Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request
+ ASIHTTPRequest *mainRequest;
+
+ // When NO, this request will only update the progress indicator when it completes
+ // When YES, this request will update the progress indicator according to how much data it has received so far
+ // The default for requests is YES
+ // Also see the comments in ASINetworkQueue.h
+ BOOL showAccurateProgress;
+
+ // Used to ensure the progress indicator is only incremented once when showAccurateProgress = NO
+ BOOL updatedProgress;
+
+ // Prevents the body of the post being built more than once (largely for subclasses)
+ BOOL haveBuiltPostBody;
+
+ // Used internally, may reflect the size of the internal buffer used by CFNetwork
+ // POST / PUT operations with body sizes greater than uploadBufferSize will not timeout unless more than uploadBufferSize bytes have been sent
+ // Likely to be 32KB on iPhone 3.0, 128KB on Mac OS X Leopard and iPhone 2.2.x
+ unsigned long long uploadBufferSize;
+
+ // Text encoding for responses that do not send a Content-Type with a charset value. Defaults to NSISOLatin1StringEncoding
+ NSStringEncoding defaultResponseEncoding;
+
+ // The text encoding of the response, will be defaultResponseEncoding if the server didn't specify. Can't be set.
+ NSStringEncoding responseEncoding;
+
+ // Tells ASIHTTPRequest not to delete partial downloads, and allows it to use an existing file to resume a download. Defaults to NO.
+ BOOL allowResumeForFileDownloads;
+
+ // Custom user information associated with the request (not sent to the server)
+ NSDictionary *userInfo;
+ NSInteger tag;
+
+ // Use HTTP 1.0 rather than 1.1 (defaults to false)
+ BOOL useHTTPVersionOne;
+
+ // When YES, requests will automatically redirect when they get a HTTP 30x header (defaults to YES)
+ BOOL shouldRedirect;
+
+ // Used internally to tell the main loop we need to stop and retry with a new url
+ BOOL needsRedirect;
+
+ // Incremented every time this request redirects. When it reaches 5, we give up
+ int redirectCount;
+
+ // When NO, requests will not check the secure certificate is valid (use for self-signed certificates during development, DO NOT USE IN PRODUCTION) Default is YES
+ BOOL validatesSecureCertificate;
+
+ // If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate
+ SecIdentityRef clientCertificateIdentity;
+ NSArray *clientCertificates;
+
+ // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings
+ NSString *proxyHost;
+ int proxyPort;
+
+ // ASIHTTPRequest will assume kCFProxyTypeHTTP if the proxy type could not be automatically determined
+ // Set to kCFProxyTypeSOCKS if you are manually configuring a SOCKS proxy
+ NSString *proxyType;
+
+ // URL for a PAC (Proxy Auto Configuration) file. If you want to set this yourself, it's probably best if you use a local file
+ NSURL *PACurl;
+
+ // See ASIAuthenticationState values above. 0 == default == No authentication needed yet
+ ASIAuthenticationState authenticationNeeded;
+
+ // When YES, ASIHTTPRequests will present credentials from the session store for requests to the same server before being asked for them
+ // This avoids an extra round trip for requests after authentication has succeeded, which is much for efficient for authenticated requests with large bodies, or on slower connections
+ // Set to NO to only present credentials when explicitly asked for them
+ // This only affects credentials stored in the session cache when useSessionPersistence is YES. Credentials from the keychain are never presented unless the server asks for them
+ // Default is YES
+ // For requests using Basic authentication, set authenticationScheme to (NSString *)kCFHTTPAuthenticationSchemeBasic, and credentials can be sent on the very first request when shouldPresentCredentialsBeforeChallenge is YES
+ BOOL shouldPresentCredentialsBeforeChallenge;
+
+ // YES when the request hasn't finished yet. Will still be YES even if the request isn't doing anything (eg it's waiting for delegate authentication). READ-ONLY
+ BOOL inProgress;
+
+ // Used internally to track whether the stream is scheduled on the run loop or not
+ // Bandwidth throttling can unschedule the stream to slow things down while a request is in progress
+ BOOL readStreamIsScheduled;
+
+ // Set to allow a request to automatically retry itself on timeout
+ // Default is zero - timeout will stop the request
+ int numberOfTimesToRetryOnTimeout;
+
+ // The number of times this request has retried (when numberOfTimesToRetryOnTimeout > 0)
+ int retryCount;
+
+ // Temporarily set to YES when a closed connection forces a retry (internally, this stops ASIHTTPRequest cleaning up a temporary post body)
+ BOOL willRetryRequest;
+
+ // When YES, requests will keep the connection to the server alive for a while to allow subsequent requests to re-use it for a substantial speed-boost
+ // Persistent connections will not be used if the server explicitly closes the connection
+ // Default is YES
+ BOOL shouldAttemptPersistentConnection;
+
+ // Number of seconds to keep an inactive persistent connection open on the client side
+ // Default is 60
+ // If we get a keep-alive header, this is this value is replaced with how long the server told us to keep the connection around
+ // A future date is created from this and used for expiring the connection, this is stored in connectionInfo's expires value
+ NSTimeInterval persistentConnectionTimeoutSeconds;
+
+ // Set to yes when an appropriate keep-alive header is found
+ BOOL connectionCanBeReused;
+
+ // Stores information about the persistent connection that is currently in use.
+ // It may contain:
+ // * The id we set for a particular connection, incremented every time we want to specify that we need a new connection
+ // * The date that connection should expire
+ // * A host, port and scheme for the connection. These are used to determine whether that connection can be reused by a subsequent request (all must match the new request)
+ // * An id for the request that is currently using the connection. This is used for determining if a connection is available or not (we store a number rather than a reference to the request so we don't need to hang onto a request until the connection expires)
+ // * A reference to the stream that is currently using the connection. This is necessary because we need to keep the old stream open until we've opened a new one.
+ // The stream will be closed + released either when another request comes to use the connection, or when the timer fires to tell the connection to expire
+ NSMutableDictionary *connectionInfo;
+
+ // When set to YES, 301 and 302 automatic redirects will use the original method and and body, according to the HTTP 1.1 standard
+ // Default is NO (to follow the behaviour of most browsers)
+ BOOL shouldUseRFC2616RedirectBehaviour;
+
+ // Used internally to record when a request has finished downloading data
+ BOOL downloadComplete;
+
+ // An ID that uniquely identifies this request - primarily used for debugging persistent connections
+ NSNumber *requestID;
+
+ // Will be ASIHTTPRequestRunLoopMode for synchronous requests, NSDefaultRunLoopMode for all other requests
+ NSString *runLoopMode;
+
+ // This timer checks up on the request every 0.25 seconds, and updates progress
+ NSTimer *statusTimer;
+
+ // The download cache that will be used for this request (use [ASIHTTPRequest setDefaultCache:cache] to configure a default cache
+ id <ASICacheDelegate> downloadCache;
+
+ // The cache policy that will be used for this request - See ASICacheDelegate.h for possible values
+ ASICachePolicy cachePolicy;
+
+ // The cache storage policy that will be used for this request - See ASICacheDelegate.h for possible values
+ ASICacheStoragePolicy cacheStoragePolicy;
+
+ // Will be true when the response was pulled from the cache rather than downloaded
+ BOOL didUseCachedResponse;
+
+ // Set secondsToCache to use a custom time interval for expiring the response when it is stored in a cache
+ NSTimeInterval secondsToCache;
+
+ #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
+ BOOL shouldContinueWhenAppEntersBackground;
+ UIBackgroundTaskIdentifier backgroundTask;
+ #endif
+
+ // When downloading a gzipped response, the request will use this helper object to inflate the response
+ ASIDataDecompressor *dataDecompressor;
+
+ // Controls how responses with a gzipped encoding are inflated (decompressed)
+ // When set to YES (This is the default):
+ // * gzipped responses for requests without a downloadDestinationPath will be inflated only when [request responseData] / [request responseString] is called
+ // * gzipped responses for requests with a downloadDestinationPath set will be inflated only when the request completes
+ //
+ // When set to NO
+ // All requests will inflate the response as it comes in
+ // * If the request has no downloadDestinationPath set, the raw (compressed) response is discarded and rawResponseData will contain the decompressed response
+ // * If the request has a downloadDestinationPath, the raw response will be stored in temporaryFileDownloadPath as normal, the inflated response will be stored in temporaryUncompressedDataDownloadPath
+ // Once the request completes successfully, the contents of temporaryUncompressedDataDownloadPath are moved into downloadDestinationPath
+ //
+ // Setting this to NO may be especially useful for users using ASIHTTPRequest in conjunction with a streaming parser, as it will allow partial gzipped responses to be inflated and passed on to the parser while the request is still running
+ BOOL shouldWaitToInflateCompressedResponses;
+
+ // Will be YES if this is a request created behind the scenes to download a PAC file - these requests do not attempt to configure their own proxies
+ BOOL isPACFileRequest;
+
+ // Used for downloading PAC files from http / https webservers
+ ASIHTTPRequest *PACFileRequest;
+
+ // Used for asynchronously reading PAC files from file:// URLs
+ NSInputStream *PACFileReadStream;
+
+ // Used for storing PAC data from file URLs as it is downloaded
+ NSMutableData *PACFileData;
+
+ // Set to YES in startSynchronous. Currently used by proxy detection to download PAC files synchronously when appropriate
+ BOOL isSynchronous;
+
+ #if NS_BLOCKS_AVAILABLE
+ //block to execute when request starts
+ ASIBasicBlock startedBlock;
+
+ //block to execute when headers are received
+ ASIHeadersBlock headersReceivedBlock;
+
+ //block to execute when request completes successfully
+ ASIBasicBlock completionBlock;
+
+ //block to execute when request fails
+ ASIBasicBlock failureBlock;
+
+ //block for when bytes are received
+ ASIProgressBlock bytesReceivedBlock;
+
+ //block for when bytes are sent
+ ASIProgressBlock bytesSentBlock;
+
+ //block for when download size is incremented
+ ASISizeBlock downloadSizeIncrementedBlock;
+
+ //block for when upload size is incremented
+ ASISizeBlock uploadSizeIncrementedBlock;
+
+ //block for handling raw bytes received
+ ASIDataBlock dataReceivedBlock;
+
+ //block for handling authentication
+ ASIBasicBlock authenticationNeededBlock;
+
+ //block for handling proxy authentication
+ ASIBasicBlock proxyAuthenticationNeededBlock;
+
+ //block for handling redirections, if you want to
+ ASIBasicBlock requestRedirectedBlock;
+ #endif
+}
+
+#pragma mark init / dealloc
+
+// Should be an HTTP or HTTPS url, may include username and password if appropriate
+- (id)initWithURL:(NSURL *)newURL;
+
+// Convenience constructor
++ (id)requestWithURL:(NSURL *)newURL;
+
++ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache;
++ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache andCachePolicy:(ASICachePolicy)policy;
+
+#if NS_BLOCKS_AVAILABLE
+- (void)setStartedBlock:(ASIBasicBlock)aStartedBlock;
+- (void)setHeadersReceivedBlock:(ASIHeadersBlock)aReceivedBlock;
+- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock;
+- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock;
+- (void)setBytesReceivedBlock:(ASIProgressBlock)aBytesReceivedBlock;
+- (void)setBytesSentBlock:(ASIProgressBlock)aBytesSentBlock;
+- (void)setDownloadSizeIncrementedBlock:(ASISizeBlock) aDownloadSizeIncrementedBlock;
+- (void)setUploadSizeIncrementedBlock:(ASISizeBlock) anUploadSizeIncrementedBlock;
+- (void)setDataReceivedBlock:(ASIDataBlock)aReceivedBlock;
+- (void)setAuthenticationNeededBlock:(ASIBasicBlock)anAuthenticationBlock;
+- (void)setProxyAuthenticationNeededBlock:(ASIBasicBlock)aProxyAuthenticationBlock;
+- (void)setRequestRedirectedBlock:(ASIBasicBlock)aRedirectBlock;
+#endif
+
+#pragma mark setup request
+
+// Add a custom header to the request
+- (void)addRequestHeader:(NSString *)header value:(NSString *)value;
+
+// Called during buildRequestHeaders and after a redirect to create a cookie header from request cookies and the global store
+- (void)applyCookieHeader;
+
+// Populate the request headers dictionary. Called before a request is started, or by a HEAD request that needs to borrow them
+- (void)buildRequestHeaders;
+
+// Used to apply authorization header to a request before it is sent (when shouldPresentCredentialsBeforeChallenge is YES)
+- (void)applyAuthorizationHeader;
+
+
+// Create the post body
+- (void)buildPostBody;
+
+// Called to add data to the post body. Will append to postBody when shouldStreamPostDataFromDisk is false, or write to postBodyWriteStream when true
+- (void)appendPostData:(NSData *)data;
+- (void)appendPostDataFromFile:(NSString *)file;
+
+#pragma mark get information about this request
+
+// Returns the contents of the result as an NSString (not appropriate for binary data - used responseData instead)
+- (NSString *)responseString;
+
+// Response data, automatically uncompressed where appropriate
+- (NSData *)responseData;
+
+// Returns true if the response was gzip compressed
+- (BOOL)isResponseCompressed;
+
+#pragma mark running a request
+
+
+// Run a request synchronously, and return control when the request completes or fails
+- (void)startSynchronous;
+
+// Run request in the background
+- (void)startAsynchronous;
+
+// Clears all delegates and blocks, then cancels the request
+- (void)clearDelegatesAndCancel;
+
+#pragma mark HEAD request
+
+// Used by ASINetworkQueue to create a HEAD request appropriate for this request with the same headers (though you can use it yourself)
+- (ASIHTTPRequest *)HEADRequest;
+
+#pragma mark upload/download progress
+
+// Called approximately every 0.25 seconds to update the progress delegates
+- (void)updateProgressIndicators;
+
+// Updates upload progress (notifies the queue and/or uploadProgressDelegate of this request)
+- (void)updateUploadProgress;
+
+// Updates download progress (notifies the queue and/or uploadProgressDelegate of this request)
+- (void)updateDownloadProgress;
+
+// Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete
+- (void)removeUploadProgressSoFar;
+
+// Called when we get a content-length header and shouldResetDownloadProgress is true
+- (void)incrementDownloadSizeBy:(long long)length;
+
+// Called when a request starts and shouldResetUploadProgress is true
+// Also called (with a negative length) to remove the size of the underlying buffer used for uploading
+- (void)incrementUploadSizeBy:(long long)length;
+
+// Helper method for interacting with progress indicators to abstract the details of different APIS (NSProgressIndicator and UIProgressView)
++ (void)updateProgressIndicator:(id *)indicator withProgress:(unsigned long long)progress ofTotal:(unsigned long long)total;
+
+// Helper method used for performing invocations on the main thread (used for progress)
++ (void)performSelector:(SEL)selector onTarget:(id *)target withObject:(id)object amount:(void *)amount callerToRetain:(id)caller;
+
+#pragma mark talking to delegates
+
+// Called when a request starts, lets the delegate know via didStartSelector
+- (void)requestStarted;
+
+// Called when a request receives response headers, lets the delegate know via didReceiveResponseHeadersSelector
+- (void)requestReceivedResponseHeaders:(NSDictionary *)newHeaders;
+
+// Called when a request completes successfully, lets the delegate know via didFinishSelector
+- (void)requestFinished;
+
+// Called when a request fails, and lets the delegate know via didFailSelector
+- (void)failWithError:(NSError *)theError;
+
+// Called to retry our request when our persistent connection is closed
+// Returns YES if we haven't already retried, and connection will be restarted
+// Otherwise, returns NO, and nothing will happen
+- (BOOL)retryUsingNewConnection;
+
+// Can be called by delegates from inside their willRedirectSelector implementations to restart the request with a new url
+- (void)redirectToURL:(NSURL *)newURL;
+
+#pragma mark parsing HTTP response headers
+
+// Reads the response headers to find the content length, encoding, cookies for the session
+// Also initiates request redirection when shouldRedirect is true
+// And works out if HTTP auth is required
+- (void)readResponseHeaders;
+
+// Attempts to set the correct encoding by looking at the Content-Type header, if this is one
+- (void)parseStringEncodingFromHeaders;
+
++ (void)parseMimeType:(NSString **)mimeType andResponseEncoding:(NSStringEncoding *)stringEncoding fromContentType:(NSString *)contentType;
+
+#pragma mark http authentication stuff
+
+// Apply credentials to this request
+- (BOOL)applyCredentials:(NSDictionary *)newCredentials;
+- (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials;
+
+// Attempt to obtain credentials for this request from the URL, username and password or keychain
+- (NSMutableDictionary *)findCredentials;
+- (NSMutableDictionary *)findProxyCredentials;
+
+// Unlock (unpause) the request thread so it can resume the request
+// Should be called by delegates when they have populated the authentication information after an authentication challenge
+- (void)retryUsingSuppliedCredentials;
+
+// Should be called by delegates when they wish to cancel authentication and stop
+- (void)cancelAuthentication;
+
+// Apply authentication information and resume the request after an authentication challenge
+- (void)attemptToApplyCredentialsAndResume;
+- (void)attemptToApplyProxyCredentialsAndResume;
+
+// Attempt to show the built-in authentication dialog, returns YES if credentials were supplied, NO if user cancelled dialog / dialog is disabled / running on main thread
+// Currently only used on iPhone OS
+- (BOOL)showProxyAuthenticationDialog;
+- (BOOL)showAuthenticationDialog;
+
+// Construct a basic authentication header from the username and password supplied, and add it to the request headers
+// Used when shouldPresentCredentialsBeforeChallenge is YES
+- (void)addBasicAuthenticationHeaderWithUsername:(NSString *)theUsername andPassword:(NSString *)thePassword;
+
+#pragma mark stream status handlers
+
+// CFnetwork event handlers
+- (void)handleNetworkEvent:(CFStreamEventType)type;
+- (void)handleBytesAvailable;
+- (void)handleStreamComplete;
+- (void)handleStreamError;
+
+#pragma mark cleanup
+
+// Cleans up and lets the queue know this operation is finished.
+// Appears in this header for subclassing only, do not call this method from outside your request!
+- (void)markAsFinished;
+
+// Cleans up temporary files. There's normally no reason to call these yourself, they are called automatically when a request completes or fails
+
+// Clean up the temporary file used to store the downloaded data when it comes in (if downloadDestinationPath is set)
+- (BOOL)removeTemporaryDownloadFile;
+
+// Clean up the temporary file used to store data that is inflated (decompressed) as it comes in
+- (BOOL)removeTemporaryUncompressedDownloadFile;
+
+// Clean up the temporary file used to store the request body (when shouldStreamPostDataFromDisk is YES)
+- (BOOL)removeTemporaryUploadFile;
+
+// Clean up the temporary file used to store a deflated (compressed) request body when shouldStreamPostDataFromDisk is YES
+- (BOOL)removeTemporaryCompressedUploadFile;
+
+// Remove a file on disk, returning NO and populating the passed error pointer if it fails
++ (BOOL)removeFileAtPath:(NSString *)path error:(NSError **)err;
+
+#pragma mark persistent connections
+
+// Get the ID of the connection this request used (only really useful in tests and debugging)
+- (NSNumber *)connectionID;
+
+// Called automatically when a request is started to clean up any persistent connections that have expired
++ (void)expirePersistentConnections;
+
+#pragma mark default time out
+
++ (NSTimeInterval)defaultTimeOutSeconds;
++ (void)setDefaultTimeOutSeconds:(NSTimeInterval)newTimeOutSeconds;
+
+#pragma mark client certificate
+
+- (void)setClientCertificateIdentity:(SecIdentityRef)anIdentity;
+
+#pragma mark session credentials
+
++ (NSMutableArray *)sessionProxyCredentialsStore;
++ (NSMutableArray *)sessionCredentialsStore;
+
++ (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials;
++ (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials;
+
++ (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials;
++ (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials;
+
+- (NSDictionary *)findSessionProxyAuthenticationCredentials;
+- (NSDictionary *)findSessionAuthenticationCredentials;
+
+#pragma mark keychain storage
+
+// Save credentials for this request to the keychain
+- (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials;
+
+// Save credentials to the keychain
++ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
++ (void)saveCredentials:(NSURLCredential *)credentials forProxy:(NSString *)host port:(int)port realm:(NSString *)realm;
+
+// Return credentials from the keychain
++ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
++ (NSURLCredential *)savedCredentialsForProxy:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
+
+// Remove credentials from the keychain
++ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
++ (void)removeCredentialsForProxy:(NSString *)host port:(int)port realm:(NSString *)realm;
+
+// We keep track of any cookies we accept, so that we can remove them from the persistent store later
++ (void)setSessionCookies:(NSMutableArray *)newSessionCookies;
++ (NSMutableArray *)sessionCookies;
+
+// Adds a cookie to our list of cookies we've accepted, checking first for an old version of the same cookie and removing that
++ (void)addSessionCookie:(NSHTTPCookie *)newCookie;
+
+// Dump all session data (authentication and cookies)
++ (void)clearSession;
+
+#pragma mark get user agent
+
+// Will be used as a user agent if requests do not specify a custom user agent
+// Is only used when you have specified a Bundle Display Name (CFDisplayBundleName) or Bundle Name (CFBundleName) in your plist
++ (NSString *)defaultUserAgentString;
++ (void)setDefaultUserAgentString:(NSString *)agent;
+
+#pragma mark mime-type detection
+
+// Return the mime type for a file
++ (NSString *)mimeTypeForFileAtPath:(NSString *)path;
+
+#pragma mark bandwidth measurement / throttling
+
+// The maximum number of bytes ALL requests can send / receive in a second
+// This is a rough figure. The actual amount used will be slightly more, this does not include HTTP headers
++ (unsigned long)maxBandwidthPerSecond;
++ (void)setMaxBandwidthPerSecond:(unsigned long)bytes;
+
+// Get a rough average (for the last 5 seconds) of how much bandwidth is being used, in bytes
++ (unsigned long)averageBandwidthUsedPerSecond;
+
+- (void)performThrottling;
+
+// Will return YES is bandwidth throttling is currently in use
++ (BOOL)isBandwidthThrottled;
+
+// Used internally to record bandwidth use, and by ASIInputStreams when uploading. It's probably best if you don't mess with this.
++ (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes;
+
+// On iPhone, ASIHTTPRequest can automatically turn throttling on and off as the connection type changes between WWAN and WiFi
+
+#if TARGET_OS_IPHONE
+// Set to YES to automatically turn on throttling when WWAN is connected, and automatically turn it off when it isn't
++ (void)setShouldThrottleBandwidthForWWAN:(BOOL)throttle;
+
+// Turns on throttling automatically when WWAN is connected using a custom limit, and turns it off automatically when it isn't
++ (void)throttleBandwidthForWWANUsingLimit:(unsigned long)limit;
+
+#pragma mark reachability
+
+// Returns YES when an iPhone OS device is connected via WWAN, false when connected via WIFI or not connected
++ (BOOL)isNetworkReachableViaWWAN;
+
+#endif
+
+#pragma mark queue
+
+// Returns the shared queue
++ (NSOperationQueue *)sharedQueue;
+
+#pragma mark cache
+
++ (void)setDefaultCache:(id <ASICacheDelegate>)cache;
++ (id <ASICacheDelegate>)defaultCache;
+
+// Returns the maximum amount of data we can read as part of the current measurement period, and sleeps this thread if our allowance is used up
++ (unsigned long)maxUploadReadLength;
+
+#pragma mark network activity
+
++ (BOOL)isNetworkInUse;
+
++ (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate;
+
+// Shows the network activity spinner thing on iOS. You may wish to override this to do something else in Mac projects
++ (void)showNetworkActivityIndicator;
+
+// Hides the network activity spinner thing on iOS
++ (void)hideNetworkActivityIndicator;
+
+#pragma mark miscellany
+
+// Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true
+// And also by ASIS3Request
++ (NSString *)base64forData:(NSData *)theData;
+
+// Returns the expiration date for the request.
+// Calculated from the Expires response header property, unless maxAge is non-zero or
+// there exists a non-zero max-age property in the Cache-Control response header.
++ (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
+
+// Returns a date from a string in RFC1123 format
++ (NSDate *)dateFromRFC1123String:(NSString *)string;
+
+
+// Used for detecting multitasking support at runtime (for backgrounding requests)
+#if TARGET_OS_IPHONE
++ (BOOL)isMultitaskingSupported;
+#endif
+
+#pragma mark threading behaviour
+
+// In the default implementation, all requests run in a single background thread
+// Advanced users only: Override this method in a subclass for a different threading behaviour
+// Eg: return [NSThread mainThread] to run all requests in the main thread
+// Alternatively, you can create a thread on demand, or manage a pool of threads
+// Threads returned by this method will need to run the runloop in default mode (eg CFRunLoopRun())
+// Requests will stop the runloop when they complete
+// If you have multiple requests sharing the thread you'll need to restart the runloop when this happens
++ (NSThread *)threadForRequest:(ASIHTTPRequest *)request;
+
+
+#pragma mark ===
+
+@property (retain) NSString *username;
+@property (retain) NSString *password;
+@property (retain) NSString *userAgentString;
+@property (retain) NSString *domain;
+
+@property (retain) NSString *proxyUsername;
+@property (retain) NSString *proxyPassword;
+@property (retain) NSString *proxyDomain;
+
+@property (retain) NSString *proxyHost;
+@property (assign) int proxyPort;
+@property (retain) NSString *proxyType;
+
+@property (retain,setter=setURL:, nonatomic) NSURL *url;
+@property (retain) NSURL *originalURL;
+@property (assign, nonatomic) id delegate;
+@property (retain, nonatomic) id queue;
+@property (assign, nonatomic) id uploadProgressDelegate;
+@property (assign, nonatomic) id downloadProgressDelegate;
+@property (assign) BOOL useKeychainPersistence;
+@property (assign) BOOL useSessionPersistence;
+@property (retain) NSString *downloadDestinationPath;
+@property (retain) NSString *temporaryFileDownloadPath;
+@property (retain) NSString *temporaryUncompressedDataDownloadPath;
+@property (assign) SEL didStartSelector;
+@property (assign) SEL didReceiveResponseHeadersSelector;
+@property (assign) SEL willRedirectSelector;
+@property (assign) SEL didFinishSelector;
+@property (assign) SEL didFailSelector;
+@property (assign) SEL didReceiveDataSelector;
+@property (retain,readonly) NSString *authenticationRealm;
+@property (retain,readonly) NSString *proxyAuthenticationRealm;
+@property (retain) NSError *error;
+@property (assign,readonly) BOOL complete;
+@property (retain) NSDictionary *responseHeaders;
+@property (retain) NSMutableDictionary *requestHeaders;
+@property (retain) NSMutableArray *requestCookies;
+@property (retain,readonly) NSArray *responseCookies;
+@property (assign) BOOL useCookiePersistence;
+@property (retain) NSDictionary *requestCredentials;
+@property (retain) NSDictionary *proxyCredentials;
+@property (assign,readonly) int responseStatusCode;
+@property (retain,readonly) NSString *responseStatusMessage;
+@property (retain) NSMutableData *rawResponseData;
+@property (assign) NSTimeInterval timeOutSeconds;
+@property (retain, nonatomic) NSString *requestMethod;
+@property (retain) NSMutableData *postBody;
+@property (assign) unsigned long long contentLength;
+@property (assign) unsigned long long postLength;
+@property (assign) BOOL shouldResetDownloadProgress;
+@property (assign) BOOL shouldResetUploadProgress;
+@property (assign) ASIHTTPRequest *mainRequest;
+@property (assign) BOOL showAccurateProgress;
+@property (assign) unsigned long long totalBytesRead;
+@property (assign) unsigned long long totalBytesSent;
+@property (assign) NSStringEncoding defaultResponseEncoding;
+@property (assign) NSStringEncoding responseEncoding;
+@property (assign) BOOL allowCompressedResponse;
+@property (assign) BOOL allowResumeForFileDownloads;
+@property (retain) NSDictionary *userInfo;
+@property (assign) NSInteger tag;
+@property (retain) NSString *postBodyFilePath;
+@property (assign) BOOL shouldStreamPostDataFromDisk;
+@property (assign) BOOL didCreateTemporaryPostDataFile;
+@property (assign) BOOL useHTTPVersionOne;
+@property (assign, readonly) unsigned long long partialDownloadSize;
+@property (assign) BOOL shouldRedirect;
+@property (assign) BOOL validatesSecureCertificate;
+@property (assign) BOOL shouldCompressRequestBody;
+@property (retain) NSURL *PACurl;
+@property (retain) NSString *authenticationScheme;
+@property (retain) NSString *proxyAuthenticationScheme;
+@property (assign) BOOL shouldPresentAuthenticationDialog;
+@property (assign) BOOL shouldPresentProxyAuthenticationDialog;
+@property (assign, readonly) ASIAuthenticationState authenticationNeeded;
+@property (assign) BOOL shouldPresentCredentialsBeforeChallenge;
+@property (assign, readonly) int authenticationRetryCount;
+@property (assign, readonly) int proxyAuthenticationRetryCount;
+@property (assign) BOOL haveBuiltRequestHeaders;
+@property (assign, nonatomic) BOOL haveBuiltPostBody;
+@property (assign, readonly) BOOL inProgress;
+@property (assign) int numberOfTimesToRetryOnTimeout;
+@property (assign, readonly) int retryCount;
+@property (assign) BOOL shouldAttemptPersistentConnection;
+@property (assign) NSTimeInterval persistentConnectionTimeoutSeconds;
+@property (assign) BOOL shouldUseRFC2616RedirectBehaviour;
+@property (assign, readonly) BOOL connectionCanBeReused;
+@property (retain, readonly) NSNumber *requestID;
+@property (assign) id <ASICacheDelegate> downloadCache;
+@property (assign) ASICachePolicy cachePolicy;
+@property (assign) ASICacheStoragePolicy cacheStoragePolicy;
+@property (assign, readonly) BOOL didUseCachedResponse;
+@property (assign) NSTimeInterval secondsToCache;
+@property (retain) NSArray *clientCertificates;
+#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
+@property (assign) BOOL shouldContinueWhenAppEntersBackground;
+#endif
+@property (retain) ASIDataDecompressor *dataDecompressor;
+@property (assign) BOOL shouldWaitToInflateCompressedResponses;
+
+@end
diff --git a/samples/objective-c/ASIHTTPRequest.m b/samples/objective-c/ASIHTTPRequest.m
new file mode 100644
index 0000000..8dd162c
--- /dev/null
+++ b/samples/objective-c/ASIHTTPRequest.m
@@ -0,0 +1,5125 @@
+//
+// ASIHTTPRequest.m
+//
+// Created by Ben Copsey on 04/10/2007.
+// Copyright 2007-2011 All-Seeing Interactive. All rights reserved.
+//
+// A guide to the main features is available at:
+// http://allseeing-i.com/ASIHTTPRequest
+//
+// Portions are based on the ImageClient example from Apple:
+// See: http://developer.apple.com/samplecode/ImageClient/listing37.html
+
+#import "ASIHTTPRequest.h"
+
+#if TARGET_OS_IPHONE
+#import "Reachability.h"
+#import "ASIAuthenticationDialog.h"
+#import <MobileCoreServices/MobileCoreServices.h>
+#else
+#import <SystemConfiguration/SystemConfiguration.h>
+#endif
+#import "ASIInputStream.h"
+#import "ASIDataDecompressor.h"
+#import "ASIDataCompressor.h"
+
+// Automatically set on build
+NSString *ASIHTTPRequestVersion = @"v1.8.1-61 2011-09-19";
+
+static NSString *defaultUserAgent = nil;
+
+NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
+
+static NSString *ASIHTTPRequestRunLoopMode = @"ASIHTTPRequestRunLoopMode";
+
+static const CFOptionFlags kNetworkEvents = kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred;
+
+// In memory caches of credentials, used on when useSessionPersistence is YES
+static NSMutableArray *sessionCredentialsStore = nil;
+static NSMutableArray *sessionProxyCredentialsStore = nil;
+
+// This lock mediates access to session credentials
+static NSRecursiveLock *sessionCredentialsLock = nil;
+
+// We keep track of cookies we have received here so we can remove them from the sharedHTTPCookieStorage later
+static NSMutableArray *sessionCookies = nil;
+
+// The number of times we will allow requests to redirect before we fail with a redirection error
+const int RedirectionLimit = 5;
+
+// The default number of seconds to use for a timeout
+static NSTimeInterval defaultTimeOutSeconds = 10;
+
+static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo) {
+ [((ASIHTTPRequest*)clientCallBackInfo) handleNetworkEvent: type];
+}
+
+// This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
+static NSRecursiveLock *progressLock;
+
+static NSError *ASIRequestCancelledError;
+static NSError *ASIRequestTimedOutError;
+static NSError *ASIAuthenticationError;
+static NSError *ASIUnableToCreateRequestError;
+static NSError *ASITooMuchRedirectionError;
+
+static NSMutableArray *bandwidthUsageTracker = nil;
+static unsigned long averageBandwidthUsedPerSecond = 0;
+
+// These are used for queuing persistent connections on the same connection
+
+// Incremented every time we specify we want a new connection
+static unsigned int nextConnectionNumberToCreate = 0;
+
+// An array of connectionInfo dictionaries.
+// When attempting a persistent connection, we look here to try to find an existing connection to the same server that is currently not in use
+static NSMutableArray *persistentConnectionsPool = nil;
+
+// Mediates access to the persistent connections pool
+static NSRecursiveLock *connectionsLock = nil;
+
+// Each request gets a new id, we store this rather than a ref to the request itself in the connectionInfo dictionary.
+// We do this so we don't have to keep the request around while we wait for the connection to expire
+static unsigned int nextRequestID = 0;
+
+// Records how much bandwidth all requests combined have used in the last second
+static unsigned long bandwidthUsedInLastSecond = 0;
+
+// A date one second in the future from the time it was created
+static NSDate *bandwidthMeasurementDate = nil;
+
+// Since throttling variables are shared among all requests, we'll use a lock to mediate access
+static NSLock *bandwidthThrottlingLock = nil;
+
+// the maximum number of bytes that can be transmitted in one second
+static unsigned long maxBandwidthPerSecond = 0;
+
+// A default figure for throttling bandwidth on mobile devices
+unsigned long const ASIWWANBandwidthThrottleAmount = 14800;
+
+#if TARGET_OS_IPHONE
+// YES when bandwidth throttling is active
+// This flag does not denote whether throttling is turned on - rather whether it is currently in use
+// It will be set to NO when throttling was turned on with setShouldThrottleBandwidthForWWAN, but a WI-FI connection is active
+static BOOL isBandwidthThrottled = NO;
+
+// When YES, bandwidth will be automatically throttled when using WWAN (3G/Edge/GPRS)
+// Wifi will not be throttled
+static BOOL shouldThrottleBandwidthForWWANOnly = NO;
+#endif
+
+// Mediates access to the session cookies so requests
+static NSRecursiveLock *sessionCookiesLock = nil;
+
+// This lock ensures delegates only receive one notification that authentication is required at once
+// When using ASIAuthenticationDialogs, it also ensures only one dialog is shown at once
+// If a request can't acquire the lock immediately, it means a dialog is being shown or a delegate is handling the authentication challenge
+// Once it gets the lock, it will try to look for existing credentials again rather than showing the dialog / notifying the delegate
+// This is so it can make use of any credentials supplied for the other request, if they are appropriate
+static NSRecursiveLock *delegateAuthenticationLock = nil;
+
+// When throttling bandwidth, Set to a date in future that we will allow all requests to wake up and reschedule their streams
+static NSDate *throttleWakeUpTime = nil;
+
+static id <ASICacheDelegate> defaultCache = nil;
+
+// Used for tracking when requests are using the network
+static unsigned int runningRequestCount = 0;
+
+// You can use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself
+// Alternatively, override showNetworkActivityIndicator / hideNetworkActivityIndicator
+// By default this does nothing on Mac OS X, but again override the above methods for a different behaviour
+static BOOL shouldUpdateNetworkActivityIndicator = YES;
+
+// The thread all requests will run on
+// Hangs around forever, but will be blocked unless there are requests underway
+static NSThread *networkThread = nil;
+
+static NSOperationQueue *sharedQueue = nil;
+
+// Private stuff
+@interface ASIHTTPRequest ()
+
+- (void)cancelLoad;
+
+- (void)destroyReadStream;
+- (void)scheduleReadStream;
+- (void)unscheduleReadStream;
+
+- (BOOL)willAskDelegateForCredentials;
+- (BOOL)willAskDelegateForProxyCredentials;
+- (void)askDelegateForProxyCredentials;
+- (void)askDelegateForCredentials;
+- (void)failAuthentication;
+
++ (void)measureBandwidthUsage;
++ (void)recordBandwidthUsage;
+
+- (void)startRequest;
+- (void)updateStatus:(NSTimer *)timer;
+- (void)checkRequestStatus;
+- (void)reportFailure;
+- (void)reportFinished;
+- (void)markAsFinished;
+- (void)performRedirect;
+- (BOOL)shouldTimeOut;
+- (BOOL)willRedirect;
+- (BOOL)willAskDelegateToConfirmRedirect;
+
++ (void)performInvocation:(NSInvocation *)invocation onTarget:(id *)target releasingObject:(id)objectToRelease;
++ (void)hideNetworkActivityIndicatorAfterDelay;
++ (void)hideNetworkActivityIndicatorIfNeeeded;
++ (void)runRequests;
+
+// Handling Proxy autodetection and PAC file downloads
+- (BOOL)configureProxies;
+- (void)fetchPACFile;
+- (void)finishedDownloadingPACFile:(ASIHTTPRequest *)theRequest;
+- (void)runPACScript:(NSString *)script;
+- (void)timeOutPACRead;
+
+- (void)useDataFromCache;
+
+// Called to update the size of a partial download when starting a request, or retrying after a timeout
+- (void)updatePartialDownloadSize;
+
+#if TARGET_OS_IPHONE
++ (void)registerForNetworkReachabilityNotifications;
++ (void)unsubscribeFromNetworkReachabilityNotifications;
+// Called when the status of the network changes
++ (void)reachabilityChanged:(NSNotification *)note;
+#endif
+
+#if NS_BLOCKS_AVAILABLE
+- (void)performBlockOnMainThread:(ASIBasicBlock)block;
+- (void)releaseBlocksOnMainThread;
++ (void)releaseBlocks:(NSArray *)blocks;
+- (void)callBlock:(ASIBasicBlock)block;
+#endif
+
+
+
+
+
+@property (assign) BOOL complete;
+@property (retain) NSArray *responseCookies;
+@property (assign) int responseStatusCode;
+@property (retain, nonatomic) NSDate *lastActivityTime;
+
+@property (assign) unsigned long long partialDownloadSize;
+@property (assign, nonatomic) unsigned long long uploadBufferSize;
+@property (retain, nonatomic) NSOutputStream *postBodyWriteStream;
+@property (retain, nonatomic) NSInputStream *postBodyReadStream;
+@property (assign, nonatomic) unsigned long long lastBytesRead;
+@property (assign, nonatomic) unsigned long long lastBytesSent;
+@property (retain) NSRecursiveLock *cancelledLock;
+@property (retain, nonatomic) NSOutputStream *fileDownloadOutputStream;
+@property (retain, nonatomic) NSOutputStream *inflatedFileDownloadOutputStream;
+@property (assign) int authenticationRetryCount;
+@property (assign) int proxyAuthenticationRetryCount;
+@property (assign, nonatomic) BOOL updatedProgress;
+@property (assign, nonatomic) BOOL needsRedirect;
+@property (assign, nonatomic) int redirectCount;
+@property (retain, nonatomic) NSData *compressedPostBody;
+@property (retain, nonatomic) NSString *compressedPostBodyFilePath;
+@property (retain) NSString *authenticationRealm;
+@property (retain) NSString *proxyAuthenticationRealm;
+@property (retain) NSString *responseStatusMessage;
+@property (assign) BOOL inProgress;
+@property (assign) int retryCount;
+@property (assign) BOOL willRetryRequest;
+@property (assign) BOOL connectionCanBeReused;
+@property (retain, nonatomic) NSMutableDictionary *connectionInfo;
+@property (retain, nonatomic) NSInputStream *readStream;
+@property (assign) ASIAuthenticationState authenticationNeeded;
+@property (assign, nonatomic) BOOL readStreamIsScheduled;
+@property (assign, nonatomic) BOOL downloadComplete;
+@property (retain) NSNumber *requestID;
+@property (assign, nonatomic) NSString *runLoopMode;
+@property (retain, nonatomic) NSTimer *statusTimer;
+@property (assign) BOOL didUseCachedResponse;
+@property (retain, nonatomic) NSURL *redirectURL;
+
+@property (assign, nonatomic) BOOL isPACFileRequest;
+@property (retain, nonatomic) ASIHTTPRequest *PACFileRequest;
+@property (retain, nonatomic) NSInputStream *PACFileReadStream;
+@property (retain, nonatomic) NSMutableData *PACFileData;
+
+@property (assign, nonatomic, setter=setSynchronous:) BOOL isSynchronous;
+@end
+
+
+@implementation ASIHTTPRequest
+
+#pragma mark init / dealloc
+
++ (void)initialize
+{
+ if (self == [ASIHTTPRequest class]) {
+ persistentConnectionsPool = [[NSMutableArray alloc] init];
+ connectionsLock = [[NSRecursiveLock alloc] init];
+ progressLock = [[NSRecursiveLock alloc] init];
+ bandwidthThrottlingLock = [[NSLock alloc] init];
+ sessionCookiesLock = [[NSRecursiveLock alloc] init];
+ sessionCredentialsLock = [[NSRecursiveLock alloc] init];
+ delegateAuthenticationLock = [[NSRecursiveLock alloc] init];
+ bandwidthUsageTracker = [[NSMutableArray alloc] initWithCapacity:5];
+ ASIRequestTimedOutError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIRequestTimedOutErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request timed out",NSLocalizedDescriptionKey,nil]];
+ ASIAuthenticationError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIAuthenticationErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Authentication needed",NSLocalizedDescriptionKey,nil]];
+ ASIRequestCancelledError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIRequestCancelledErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request was cancelled",NSLocalizedDescriptionKey,nil]];
+ ASIUnableToCreateRequestError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASIUnableToCreateRequestErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create request (bad url?)",NSLocalizedDescriptionKey,nil]];
+ ASITooMuchRedirectionError = [[NSError alloc] initWithDomain:NetworkRequestErrorDomain code:ASITooMuchRedirectionErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request failed because it redirected too many times",NSLocalizedDescriptionKey,nil]];
+ sharedQueue = [[NSOperationQueue alloc] init];
+ [sharedQueue setMaxConcurrentOperationCount:4];
+
+ }
+}
+
+
+- (id)initWithURL:(NSURL *)newURL
+{
+ self = [self init];
+ [self setRequestMethod:@"GET"];
+
+ [self setRunLoopMode:NSDefaultRunLoopMode];
+ [self setShouldAttemptPersistentConnection:YES];
+ [self setPersistentConnectionTimeoutSeconds:60.0];
+ [self setShouldPresentCredentialsBeforeChallenge:YES];
+ [self setShouldRedirect:YES];
+ [self setShowAccurateProgress:YES];
+ [self setShouldResetDownloadProgress:YES];
+ [self setShouldResetUploadProgress:YES];
+ [self setAllowCompressedResponse:YES];
+ [self setShouldWaitToInflateCompressedResponses:YES];
+ [self setDefaultResponseEncoding:NSISOLatin1StringEncoding];
+ [self setShouldPresentProxyAuthenticationDialog:YES];
+
+ [self setTimeOutSeconds:[ASIHTTPRequest defaultTimeOutSeconds]];
+ [self setUseSessionPersistence:YES];
+ [self setUseCookiePersistence:YES];
+ [self setValidatesSecureCertificate:YES];
+ [self setRequestCookies:[[[NSMutableArray alloc] init] autorelease]];
+ [self setDidStartSelector:@selector(requestStarted:)];
+ [self setDidReceiveResponseHeadersSelector:@selector(request:didReceiveResponseHeaders:)];
+ [self setWillRedirectSelector:@selector(request:willRedirectToURL:)];
+ [self setDidFinishSelector:@selector(requestFinished:)];
+ [self setDidFailSelector:@selector(requestFailed:)];
+ [self setDidReceiveDataSelector:@selector(request:didReceiveData:)];
+ [self setURL:newURL];
+ [self setCancelledLock:[[[NSRecursiveLock alloc] init] autorelease]];
+ [self setDownloadCache:[[self class] defaultCache]];
+ return self;
+}
+
++ (id)requestWithURL:(NSURL *)newURL
+{
+ return [[[self alloc] initWithURL:newURL] autorelease];
+}
+
++ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache
+{
+ return [self requestWithURL:newURL usingCache:cache andCachePolicy:ASIUseDefaultCachePolicy];
+}
+
++ (id)requestWithURL:(NSURL *)newURL usingCache:(id <ASICacheDelegate>)cache andCachePolicy:(ASICachePolicy)policy
+{
+ ASIHTTPRequest *request = [[[self alloc] initWithURL:newURL] autorelease];
+ [request setDownloadCache:cache];
+ [request setCachePolicy:policy];
+ return request;
+}
+
+- (void)dealloc
+{
+ [self setAuthenticationNeeded:ASINoAuthenticationNeededYet];
+ if (requestAuthentication) {
+ CFRelease(requestAuthentication);
+ }
+ if (proxyAuthentication) {
+ CFRelease(proxyAuthentication);
+ }
+ if (request) {
+ CFRelease(request);
+ }
+ if (clientCertificateIdentity) {
+ CFRelease(clientCertificateIdentity);
+ }
+ [self cancelLoad];
+ [redirectURL release];
+ [statusTimer invalidate];
+ [statusTimer release];
+ [queue release];
+ [userInfo release];
+ [postBody release];
+ [compressedPostBody release];
+ [error release];
+ [requestHeaders release];
+ [requestCookies release];
+ [downloadDestinationPath release];
+ [temporaryFileDownloadPath release];
+ [temporaryUncompressedDataDownloadPath release];
+ [fileDownloadOutputStream release];
+ [inflatedFileDownloadOutputStream release];
+ [username release];
+ [password release];
+ [domain release];
+ [authenticationRealm release];
+ [authenticationScheme release];
+ [requestCredentials release];
+ [proxyHost release];
+ [proxyType release];
+ [proxyUsername release];
+ [proxyPassword release];
+ [proxyDomain release];
+ [proxyAuthenticationRealm release];
+ [proxyAuthenticationScheme release];
+ [proxyCredentials release];
+ [url release];
+ [originalURL release];
+ [lastActivityTime release];
+ [responseCookies release];
+ [rawResponseData release];
+ [responseHeaders release];
+ [requestMethod release];
+ [cancelledLock release];
+ [postBodyFilePath release];
+ [compressedPostBodyFilePath release];
+ [postBodyWriteStream release];
+ [postBodyReadStream release];
+ [PACurl release];
+ [clientCertificates release];
+ [responseStatusMessage release];
+ [connectionInfo release];
+ [requestID release];
+ [dataDecompressor release];
+ [userAgentString release];
+
+ #if NS_BLOCKS_AVAILABLE
+ [self releaseBlocksOnMainThread];
+ #endif
+
+ [super dealloc];
+}
+
+#if NS_BLOCKS_AVAILABLE
+- (void)releaseBlocksOnMainThread
+{
+ NSMutableArray *blocks = [NSMutableArray array];
+ if (completionBlock) {
+ [blocks addObject:completionBlock];
+ [completionBlock release];
+ completionBlock = nil;
+ }
+ if (failureBlock) {
+ [blocks addObject:failureBlock];
+ [failureBlock release];
+ failureBlock = nil;
+ }
+ if (startedBlock) {
+ [blocks addObject:startedBlock];
+ [startedBlock release];
+ startedBlock = nil;
+ }
+ if (headersReceivedBlock) {
+ [blocks addObject:headersReceivedBlock];
+ [headersReceivedBlock release];
+ headersReceivedBlock = nil;
+ }
+ if (bytesReceivedBlock) {
+ [blocks addObject:bytesReceivedBlock];
+ [bytesReceivedBlock release];
+ bytesReceivedBlock = nil;
+ }
+ if (bytesSentBlock) {
+ [blocks addObject:bytesSentBlock];
+ [bytesSentBlock release];
+ bytesSentBlock = nil;
+ }
+ if (downloadSizeIncrementedBlock) {
+ [blocks addObject:downloadSizeIncrementedBlock];
+ [downloadSizeIncrementedBlock release];
+ downloadSizeIncrementedBlock = nil;
+ }
+ if (uploadSizeIncrementedBlock) {
+ [blocks addObject:uploadSizeIncrementedBlock];
+ [uploadSizeIncrementedBlock release];
+ uploadSizeIncrementedBlock = nil;
+ }
+ if (dataReceivedBlock) {
+ [blocks addObject:dataReceivedBlock];
+ [dataReceivedBlock release];
+ dataReceivedBlock = nil;
+ }
+ if (proxyAuthenticationNeededBlock) {
+ [blocks addObject:proxyAuthenticationNeededBlock];
+ [proxyAuthenticationNeededBlock release];
+ proxyAuthenticationNeededBlock = nil;
+ }
+ if (authenticationNeededBlock) {
+ [blocks addObject:authenticationNeededBlock];
+ [authenticationNeededBlock release];
+ authenticationNeededBlock = nil;
+ }
+ if (requestRedirectedBlock) {
+ [blocks addObject:requestRedirectedBlock];
+ [requestRedirectedBlock release];
+ requestRedirectedBlock = nil;
+ }
+ [[self class] performSelectorOnMainThread:@selector(releaseBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]];
+}
+// Always called on main thread
++ (void)releaseBlocks:(NSArray *)blocks
+{
+ // Blocks will be released when this method exits
+}
+#endif
+
+
+#pragma mark setup request
+
+- (void)addRequestHeader:(NSString *)header value:(NSString *)value
+{
+ if (!requestHeaders) {
+ [self setRequestHeaders:[NSMutableDictionary dictionaryWithCapacity:1]];
+ }
+ [requestHeaders setObject:value forKey:header];
+}
+
+// This function will be called either just before a request starts, or when postLength is needed, whichever comes first
+// postLength must be set by the time this function is complete
+- (void)buildPostBody
+{
+
+ if ([self haveBuiltPostBody]) {
+ return;
+ }
+
+ // Are we submitting the request body from a file on disk
+ if ([self postBodyFilePath]) {
+
+ // If we were writing to the post body via appendPostData or appendPostDataFromFile, close the write stream
+ if ([self postBodyWriteStream]) {
+ [[self postBodyWriteStream] close];
+ [self setPostBodyWriteStream:nil];
+ }
+
+
+ NSString *path;
+ if ([self shouldCompressRequestBody]) {
+ if (![self compressedPostBodyFilePath]) {
+ [self setCompressedPostBodyFilePath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]];
+
+ NSError *err = nil;
+ if (![ASIDataCompressor compressDataFromFile:[self postBodyFilePath] toFile:[self compressedPostBodyFilePath] error:&err]) {
+ [self failWithError:err];
+ return;
+ }
+ }
+ path = [self compressedPostBodyFilePath];
+ } else {
+ path = [self postBodyFilePath];
+ }
+ NSError *err = nil;
+ [self setPostLength:[[[[[NSFileManager alloc] init] autorelease] attributesOfItemAtPath:path error:&err] fileSize]];
+ if (err) {
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to get attributes for file at path '%@'",path],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]];
+ return;
+ }
+
+ // Otherwise, we have an in-memory request body
+ } else {
+ if ([self shouldCompressRequestBody]) {
+ NSError *err = nil;
+ NSData *compressedBody = [ASIDataCompressor compressData:[self postBody] error:&err];
+ if (err) {
+ [self failWithError:err];
+ return;
+ }
+ [self setCompressedPostBody:compressedBody];
+ [self setPostLength:[[self compressedPostBody] length]];
+ } else {
+ [self setPostLength:[[self postBody] length]];
+ }
+ }
+
+ if ([self postLength] > 0) {
+ if ([requestMethod isEqualToString:@"GET"] || [requestMethod isEqualToString:@"DELETE"] || [requestMethod isEqualToString:@"HEAD"]) {
+ [self setRequestMethod:@"POST"];
+ }
+ [self addRequestHeader:@"Content-Length" value:[NSString stringWithFormat:@"%llu",[self postLength]]];
+ }
+ [self setHaveBuiltPostBody:YES];
+
+}
+
+// Sets up storage for the post body
+- (void)setupPostBody
+{
+ if ([self shouldStreamPostDataFromDisk]) {
+ if (![self postBodyFilePath]) {
+ [self setPostBodyFilePath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]];
+ [self setDidCreateTemporaryPostDataFile:YES];
+ }
+ if (![self postBodyWriteStream]) {
+ [self setPostBodyWriteStream:[[[NSOutputStream alloc] initToFileAtPath:[self postBodyFilePath] append:NO] autorelease]];
+ [[self postBodyWriteStream] open];
+ }
+ } else {
+ if (![self postBody]) {
+ [self setPostBody:[[[NSMutableData alloc] init] autorelease]];
+ }
+ }
+}
+
+- (void)appendPostData:(NSData *)data
+{
+ [self setupPostBody];
+ if ([data length] == 0) {
+ return;
+ }
+ if ([self shouldStreamPostDataFromDisk]) {
+ [[self postBodyWriteStream] write:[data bytes] maxLength:[data length]];
+ } else {
+ [[self postBody] appendData:data];
+ }
+}
+
+- (void)appendPostDataFromFile:(NSString *)file
+{
+ [self setupPostBody];
+ NSInputStream *stream = [[[NSInputStream alloc] initWithFileAtPath:file] autorelease];
+ [stream open];
+ NSUInteger bytesRead;
+ while ([stream hasBytesAvailable]) {
+
+ unsigned char buffer[1024*256];
+ bytesRead = [stream read:buffer maxLength:sizeof(buffer)];
+ if (bytesRead == 0) {
+ break;
+ }
+ if ([self shouldStreamPostDataFromDisk]) {
+ [[self postBodyWriteStream] write:buffer maxLength:bytesRead];
+ } else {
+ [[self postBody] appendData:[NSData dataWithBytes:buffer length:bytesRead]];
+ }
+ }
+ [stream close];
+}
+
+- (NSString *)requestMethod
+{
+ [[self cancelledLock] lock];
+ NSString *m = requestMethod;
+ [[self cancelledLock] unlock];
+ return m;
+}
+
+- (void)setRequestMethod:(NSString *)newRequestMethod
+{
+ [[self cancelledLock] lock];
+ if (requestMethod != newRequestMethod) {
+ [requestMethod release];
+ requestMethod = [newRequestMethod retain];
+ if ([requestMethod isEqualToString:@"POST"] || [requestMethod isEqualToString:@"PUT"] || [postBody length] || postBodyFilePath) {
+ [self setShouldAttemptPersistentConnection:NO];
+ }
+ }
+ [[self cancelledLock] unlock];
+}
+
+- (NSURL *)url
+{
+ [[self cancelledLock] lock];
+ NSURL *u = url;
+ [[self cancelledLock] unlock];
+ return u;
+}
+
+
+- (void)setURL:(NSURL *)newURL
+{
+ [[self cancelledLock] lock];
+ if ([newURL isEqual:[self url]]) {
+ [[self cancelledLock] unlock];
+ return;
+ }
+ [url release];
+ url = [newURL retain];
+ if (requestAuthentication) {
+ CFRelease(requestAuthentication);
+ requestAuthentication = NULL;
+ }
+ if (proxyAuthentication) {
+ CFRelease(proxyAuthentication);
+ proxyAuthentication = NULL;
+ }
+ if (request) {
+ CFRelease(request);
+ request = NULL;
+ }
+ [self setRedirectURL:nil];
+ [[self cancelledLock] unlock];
+}
+
+- (id)delegate
+{
+ [[self cancelledLock] lock];
+ id d = delegate;
+ [[self cancelledLock] unlock];
+ return d;
+}
+
+- (void)setDelegate:(id)newDelegate
+{
+ [[self cancelledLock] lock];
+ delegate = newDelegate;
+ [[self cancelledLock] unlock];
+}
+
+- (id)queue
+{
+ [[self cancelledLock] lock];
+ id q = queue;
+ [[self cancelledLock] unlock];
+ return q;
+}
+
+
+- (void)setQueue:(id)newQueue
+{
+ [[self cancelledLock] lock];
+ if (newQueue != queue) {
+ [queue release];
+ queue = [newQueue retain];
+ }
+ [[self cancelledLock] unlock];
+}
+
+#pragma mark get information about this request
+
+// cancel the request - this must be run on the same thread as the request is running on
+- (void)cancelOnRequestThread
+{
+ #if DEBUG_REQUEST_STATUS
+ ASI_DEBUG_LOG(@"[STATUS] Request cancelled: %@",self);
+ #endif
+
+ [[self cancelledLock] lock];
+
+ if ([self isCancelled] || [self complete]) {
+ [[self cancelledLock] unlock];
+ return;
+ }
+ [self failWithError:ASIRequestCancelledError];
+ [self setComplete:YES];
+ [self cancelLoad];
+
+ CFRetain(self);
+ [self willChangeValueForKey:@"isCancelled"];
+ cancelled = YES;
+ [self didChangeValueForKey:@"isCancelled"];
+
+ [[self cancelledLock] unlock];
+ CFRelease(self);
+}
+
+- (void)cancel
+{
+ [self performSelector:@selector(cancelOnRequestThread) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO];
+}
+
+- (void)clearDelegatesAndCancel
+{
+ [[self cancelledLock] lock];
+
+ // Clear delegates
+ [self setDelegate:nil];
+ [self setQueue:nil];
+ [self setDownloadProgressDelegate:nil];
+ [self setUploadProgressDelegate:nil];
+
+ #if NS_BLOCKS_AVAILABLE
+ // Clear blocks
+ [self releaseBlocksOnMainThread];
+ #endif
+
+ [[self cancelledLock] unlock];
+ [self cancel];
+}
+
+
+- (BOOL)isCancelled
+{
+ BOOL result;
+
+ [[self cancelledLock] lock];
+ result = cancelled;
+ [[self cancelledLock] unlock];
+
+ return result;
+}
+
+// Call this method to get the received data as an NSString. Don't use for binary data!
+- (NSString *)responseString
+{
+ NSData *data = [self responseData];
+ if (!data) {
+ return nil;
+ }
+
+ return [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:[self responseEncoding]] autorelease];
+}
+
+- (BOOL)isResponseCompressed
+{
+ NSString *encoding = [[self responseHeaders] objectForKey:@"Content-Encoding"];
+ return encoding && [encoding rangeOfString:@"gzip"].location != NSNotFound;
+}
+
+- (NSData *)responseData
+{
+ if ([self isResponseCompressed] && [self shouldWaitToInflateCompressedResponses]) {
+ return [ASIDataDecompressor uncompressData:[self rawResponseData] error:NULL];
+ } else {
+ return [self rawResponseData];
+ }
+ return nil;
+}
+
+#pragma mark running a request
+
+- (void)startSynchronous
+{
+#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[STATUS] Starting synchronous request %@",self);
+#endif
+ [self setSynchronous:YES];
+ [self setRunLoopMode:ASIHTTPRequestRunLoopMode];
+ [self setInProgress:YES];
+
+ if (![self isCancelled] && ![self complete]) {
+ [self main];
+ while (!complete) {
+ [[NSRunLoop currentRunLoop] runMode:[self runLoopMode] beforeDate:[NSDate distantFuture]];
+ }
+ }
+
+ [self setInProgress:NO];
+}
+
+- (void)start
+{
+ [self setInProgress:YES];
+ [self performSelector:@selector(main) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO];
+}
+
+- (void)startAsynchronous
+{
+#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[STATUS] Starting asynchronous request %@",self);
+#endif
+ [sharedQueue addOperation:self];
+}
+
+#pragma mark concurrency
+
+- (BOOL)isConcurrent
+{
+ return YES;
+}
+
+- (BOOL)isFinished
+{
+ return finished;
+}
+
+- (BOOL)isExecuting {
+ return [self inProgress];
+}
+
+#pragma mark request logic
+
+// Create the request
+- (void)main
+{
+ @try {
+
+ [[self cancelledLock] lock];
+
+ #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
+ if ([ASIHTTPRequest isMultitaskingSupported] && [self shouldContinueWhenAppEntersBackground]) {
+ if (!backgroundTask || backgroundTask == UIBackgroundTaskInvalid) {
+ backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
+ // Synchronize the cleanup call on the main thread in case
+ // the task actually finishes at around the same time.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (backgroundTask != UIBackgroundTaskInvalid)
+ {
+ [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
+ backgroundTask = UIBackgroundTaskInvalid;
+ [self cancel];
+ }
+ });
+ }];
+ }
+ }
+ #endif
+
+
+ // A HEAD request generated by an ASINetworkQueue may have set the error already. If so, we should not proceed.
+ if ([self error]) {
+ [self setComplete:YES];
+ [self markAsFinished];
+ return;
+ }
+
+ [self setComplete:NO];
+ [self setDidUseCachedResponse:NO];
+
+ if (![self url]) {
+ [self failWithError:ASIUnableToCreateRequestError];
+ return;
+ }
+
+ // Must call before we create the request so that the request method can be set if needs be
+ if (![self mainRequest]) {
+ [self buildPostBody];
+ }
+
+ if (![[self requestMethod] isEqualToString:@"GET"]) {
+ [self setDownloadCache:nil];
+ }
+
+
+ // If we're redirecting, we'll already have a CFHTTPMessageRef
+ if (request) {
+ CFRelease(request);
+ }
+
+ // Create a new HTTP request.
+ request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)[self requestMethod], (CFURLRef)[self url], [self useHTTPVersionOne] ? kCFHTTPVersion1_0 : kCFHTTPVersion1_1);
+ if (!request) {
+ [self failWithError:ASIUnableToCreateRequestError];
+ return;
+ }
+
+ //If this is a HEAD request generated by an ASINetworkQueue, we need to let the main request generate its headers first so we can use them
+ if ([self mainRequest]) {
+ [[self mainRequest] buildRequestHeaders];
+ }
+
+ // Even if this is a HEAD request with a mainRequest, we still need to call to give subclasses a chance to add their own to HEAD requests (ASIS3Request does this)
+ [self buildRequestHeaders];
+
+ if ([self downloadCache]) {
+
+ // If this request should use the default policy, set its policy to the download cache's default policy
+ if (![self cachePolicy]) {
+ [self setCachePolicy:[[self downloadCache] defaultCachePolicy]];
+ }
+
+ // If have have cached data that is valid for this request, use that and stop
+ if ([[self downloadCache] canUseCachedDataForRequest:self]) {
+ [self useDataFromCache];
+ return;
+ }
+
+ // If cached data is stale, or we have been told to ask the server if it has been modified anyway, we need to add headers for a conditional GET
+ if ([self cachePolicy] & (ASIAskServerIfModifiedWhenStaleCachePolicy|ASIAskServerIfModifiedCachePolicy)) {
+
+ NSDictionary *cachedHeaders = [[self downloadCache] cachedResponseHeadersForURL:[self url]];
+ if (cachedHeaders) {
+ NSString *etag = [cachedHeaders objectForKey:@"Etag"];
+ if (etag) {
+ [[self requestHeaders] setObject:etag forKey:@"If-None-Match"];
+ }
+ NSString *lastModified = [cachedHeaders objectForKey:@"Last-Modified"];
+ if (lastModified) {
+ [[self requestHeaders] setObject:lastModified forKey:@"If-Modified-Since"];
+ }
+ }
+ }
+ }
+
+ [self applyAuthorizationHeader];
+
+
+ NSString *header;
+ for (header in [self requestHeaders]) {
+ CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[[self requestHeaders] objectForKey:header]);
+ }
+
+ // If we immediately have access to proxy settings, start the request
+ // Otherwise, we'll start downloading the proxy PAC file, and call startRequest once that process is complete
+ if ([self configureProxies]) {
+ [self startRequest];
+ }
+
+ } @catch (NSException *exception) {
+ NSError *underlyingError = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnhandledExceptionError userInfo:[exception userInfo]];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnhandledExceptionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[exception name],NSLocalizedDescriptionKey,[exception reason],NSLocalizedFailureReasonErrorKey,underlyingError,NSUnderlyingErrorKey,nil]]];
+
+ } @finally {
+ [[self cancelledLock] unlock];
+ }
+}
+
+- (void)applyAuthorizationHeader
+{
+ // Do we want to send credentials before we are asked for them?
+ if (![self shouldPresentCredentialsBeforeChallenge]) {
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will not send credentials to the server until it asks for them",self);
+ #endif
+ return;
+ }
+
+ NSDictionary *credentials = nil;
+
+ // Do we already have an auth header?
+ if (![[self requestHeaders] objectForKey:@"Authorization"]) {
+
+ // If we have basic authentication explicitly set and a username and password set on the request, add a basic auth header
+ if ([self username] && [self password] && [[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic]) {
+ [self addBasicAuthenticationHeaderWithUsername:[self username] andPassword:[self password]];
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ has a username and password set, and was manually configured to use BASIC. Will send credentials without waiting for an authentication challenge",self);
+ #endif
+
+ } else {
+
+ // See if we have any cached credentials we can use in the session store
+ if ([self useSessionPersistence]) {
+ credentials = [self findSessionAuthenticationCredentials];
+
+ if (credentials) {
+
+ // When the Authentication key is set, the credentials were stored after an authentication challenge, so we can let CFNetwork apply them
+ // (credentials for Digest and NTLM will always be stored like this)
+ if ([credentials objectForKey:@"Authentication"]) {
+
+ // If we've already talked to this server and have valid credentials, let's apply them to the request
+ if (CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
+ [self setAuthenticationScheme:[credentials objectForKey:@"AuthenticationScheme"]];
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ found cached credentials (%@), will reuse without waiting for an authentication challenge",self,[credentials objectForKey:@"AuthenticationScheme"]);
+ #endif
+ } else {
+ [[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Failed to apply cached credentials to request %@. These will be removed from the session store, and this request will wait for an authentication challenge",self);
+ #endif
+ }
+
+ // If the Authentication key is not set, these credentials were stored after a username and password set on a previous request passed basic authentication
+ // When this happens, we'll need to create the Authorization header ourselves
+ } else {
+ NSDictionary *usernameAndPassword = [credentials objectForKey:@"Credentials"];
+ [self addBasicAuthenticationHeaderWithUsername:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername] andPassword:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]];
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ found cached BASIC credentials from a previous request. Will send credentials without waiting for an authentication challenge",self);
+ #endif
+ }
+ }
+ }
+ }
+ }
+
+ // Apply proxy authentication credentials
+ if ([self useSessionPersistence]) {
+ credentials = [self findSessionProxyAuthenticationCredentials];
+ if (credentials) {
+ if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
+ [[self class] removeProxyAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
+ }
+ }
+ }
+}
+
+- (void)applyCookieHeader
+{
+ // Add cookies from the persistent (mac os global) store
+ if ([self useCookiePersistence]) {
+ NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[[self url] absoluteURL]];
+ if (cookies) {
+ [[self requestCookies] addObjectsFromArray:cookies];
+ }
+ }
+
+ // Apply request cookies
+ NSArray *cookies;
+ if ([self mainRequest]) {
+ cookies = [[self mainRequest] requestCookies];
+ } else {
+ cookies = [self requestCookies];
+ }
+ if ([cookies count] > 0) {
+ NSHTTPCookie *cookie;
+ NSString *cookieHeader = nil;
+ for (cookie in cookies) {
+ if (!cookieHeader) {
+ cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[cookie value]];
+ } else {
+ cookieHeader = [NSString stringWithFormat: @"%@; %@=%@",cookieHeader,[cookie name],[cookie value]];
+ }
+ }
+ if (cookieHeader) {
+ [self addRequestHeader:@"Cookie" value:cookieHeader];
+ }
+ }
+}
+
+- (void)buildRequestHeaders
+{
+ if ([self haveBuiltRequestHeaders]) {
+ return;
+ }
+ [self setHaveBuiltRequestHeaders:YES];
+
+ if ([self mainRequest]) {
+ for (NSString *header in [[self mainRequest] requestHeaders]) {
+ [self addRequestHeader:header value:[[[self mainRequest] requestHeaders] valueForKey:header]];
+ }
+ return;
+ }
+
+ [self applyCookieHeader];
+
+ // Build and set the user agent string if the request does not already have a custom user agent specified
+ if (![[self requestHeaders] objectForKey:@"User-Agent"]) {
+ NSString *tempUserAgentString = [self userAgentString];
+ if (!tempUserAgentString) {
+ tempUserAgentString = [ASIHTTPRequest defaultUserAgentString];
+ }
+ if (tempUserAgentString) {
+ [self addRequestHeader:@"User-Agent" value:tempUserAgentString];
+ }
+ }
+
+
+ // Accept a compressed response
+ if ([self allowCompressedResponse]) {
+ [self addRequestHeader:@"Accept-Encoding" value:@"gzip"];
+ }
+
+ // Configure a compressed request body
+ if ([self shouldCompressRequestBody]) {
+ [self addRequestHeader:@"Content-Encoding" value:@"gzip"];
+ }
+
+ // Should this request resume an existing download?
+ [self updatePartialDownloadSize];
+ if ([self partialDownloadSize]) {
+ [self addRequestHeader:@"Range" value:[NSString stringWithFormat:@"bytes=%llu-",[self partialDownloadSize]]];
+ }
+}
+
+- (void)updatePartialDownloadSize
+{
+ NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
+
+ if ([self allowResumeForFileDownloads] && [self downloadDestinationPath] && [self temporaryFileDownloadPath] && [fileManager fileExistsAtPath:[self temporaryFileDownloadPath]]) {
+ NSError *err = nil;
+ [self setPartialDownloadSize:[[fileManager attributesOfItemAtPath:[self temporaryFileDownloadPath] error:&err] fileSize]];
+ if (err) {
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to get attributes for file at path '%@'",[self temporaryFileDownloadPath]],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]];
+ return;
+ }
+ }
+}
+
+- (void)startRequest
+{
+ if ([self isCancelled]) {
+ return;
+ }
+
+ [self performSelectorOnMainThread:@selector(requestStarted) withObject:nil waitUntilDone:[NSThread isMainThread]];
+
+ [self setDownloadComplete:NO];
+ [self setComplete:NO];
+ [self setTotalBytesRead:0];
+ [self setLastBytesRead:0];
+
+ if ([self redirectCount] == 0) {
+ [self setOriginalURL:[self url]];
+ }
+
+ // If we're retrying a request, let's remove any progress we made
+ if ([self lastBytesSent] > 0) {
+ [self removeUploadProgressSoFar];
+ }
+
+ [self setLastBytesSent:0];
+ [self setContentLength:0];
+ [self setResponseHeaders:nil];
+ if (![self downloadDestinationPath]) {
+ [self setRawResponseData:[[[NSMutableData alloc] init] autorelease]];
+ }
+
+
+ //
+ // Create the stream for the request
+ //
+
+ NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
+
+ [self setReadStreamIsScheduled:NO];
+
+ // Do we need to stream the request body from disk
+ if ([self shouldStreamPostDataFromDisk] && [self postBodyFilePath] && [fileManager fileExistsAtPath:[self postBodyFilePath]]) {
+
+ // Are we gzipping the request body?
+ if ([self compressedPostBodyFilePath] && [fileManager fileExistsAtPath:[self compressedPostBodyFilePath]]) {
+ [self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath] request:self]];
+ } else {
+ [self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self postBodyFilePath] request:self]];
+ }
+ [self setReadStream:[NSMakeCollectable(CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream])) autorelease]];
+ } else {
+
+ // If we have a request body, we'll stream it from memory using our custom stream, so that we can measure bandwidth use and it can be bandwidth-throttled if necessary
+ if ([self postBody] && [[self postBody] length] > 0) {
+ if ([self shouldCompressRequestBody] && [self compressedPostBody]) {
+ [self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self compressedPostBody] request:self]];
+ } else if ([self postBody]) {
+ [self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self postBody] request:self]];
+ }
+ [self setReadStream:[NSMakeCollectable(CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream])) autorelease]];
+
+ } else {
+ [self setReadStream:[NSMakeCollectable(CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request)) autorelease]];
+ }
+ }
+
+ if (![self readStream]) {
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create read stream",NSLocalizedDescriptionKey,nil]]];
+ return;
+ }
+
+
+
+
+ //
+ // Handle SSL certificate settings
+ //
+
+ if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) {
+
+ // Tell CFNetwork not to validate SSL certificates
+ if (![self validatesSecureCertificate]) {
+ // see: http://iphonedevelopment.blogspot.com/2010/05/nsstream-tcp-and-ssl.html
+
+ NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
+ [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
+ kCFNull,kCFStreamSSLPeerName,
+ nil];
+
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream],
+ kCFStreamPropertySSLSettings,
+ (CFTypeRef)sslProperties);
+ [sslProperties release];
+ }
+
+ // Tell CFNetwork to use a client certificate
+ if (clientCertificateIdentity) {
+ NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];
+
+ NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1];
+
+ // The first object in the array is our SecIdentityRef
+ [certificates addObject:(id)clientCertificateIdentity];
+
+ // If we've added any additional certificates, add them too
+ for (id cert in clientCertificates) {
+ [certificates addObject:cert];
+ }
+
+ [sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates];
+
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties);
+ }
+
+ }
+
+ //
+ // Handle proxy settings
+ //
+
+ if ([self proxyHost] && [self proxyPort]) {
+ NSString *hostKey;
+ NSString *portKey;
+
+ if (![self proxyType]) {
+ [self setProxyType:(NSString *)kCFProxyTypeHTTP];
+ }
+
+ if ([[self proxyType] isEqualToString:(NSString *)kCFProxyTypeSOCKS]) {
+ hostKey = (NSString *)kCFStreamPropertySOCKSProxyHost;
+ portKey = (NSString *)kCFStreamPropertySOCKSProxyPort;
+ } else {
+ hostKey = (NSString *)kCFStreamPropertyHTTPProxyHost;
+ portKey = (NSString *)kCFStreamPropertyHTTPProxyPort;
+ if ([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) {
+ hostKey = (NSString *)kCFStreamPropertyHTTPSProxyHost;
+ portKey = (NSString *)kCFStreamPropertyHTTPSProxyPort;
+ }
+ }
+ NSMutableDictionary *proxyToUse = [NSMutableDictionary dictionaryWithObjectsAndKeys:[self proxyHost],hostKey,[NSNumber numberWithInt:[self proxyPort]],portKey,nil];
+
+ if ([[self proxyType] isEqualToString:(NSString *)kCFProxyTypeSOCKS]) {
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySOCKSProxy, proxyToUse);
+ } else {
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPProxy, proxyToUse);
+ }
+ }
+
+
+ //
+ // Handle persistent connections
+ //
+
+ [ASIHTTPRequest expirePersistentConnections];
+
+ [connectionsLock lock];
+
+
+ if (![[self url] host] || ![[self url] scheme]) {
+ [self setConnectionInfo:nil];
+ [self setShouldAttemptPersistentConnection:NO];
+ }
+
+ // Will store the old stream that was using this connection (if there was one) so we can clean it up once we've opened our own stream
+ NSInputStream *oldStream = nil;
+
+ // Use a persistent connection if possible
+ if ([self shouldAttemptPersistentConnection]) {
+
+
+ // If we are redirecting, we will re-use the current connection only if we are connecting to the same server
+ if ([self connectionInfo]) {
+
+ if (![[[self connectionInfo] objectForKey:@"host"] isEqualToString:[[self url] host]] || ![[[self connectionInfo] objectForKey:@"scheme"] isEqualToString:[[self url] scheme]] || [(NSNumber *)[[self connectionInfo] objectForKey:@"port"] intValue] != [[[self url] port] intValue]) {
+ [self setConnectionInfo:nil];
+
+ // Check if we should have expired this connection
+ } else if ([[[self connectionInfo] objectForKey:@"expires"] timeIntervalSinceNow] < 0) {
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Not re-using connection #%i because it has expired",[[[self connectionInfo] objectForKey:@"id"] intValue]);
+ #endif
+ [persistentConnectionsPool removeObject:[self connectionInfo]];
+ [self setConnectionInfo:nil];
+
+ } else if ([[self connectionInfo] objectForKey:@"request"] != nil) {
+ //Some other request reused this connection already - we'll have to create a new one
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"%@ - Not re-using connection #%i for request #%i because it is already used by request #%i",self,[[[self connectionInfo] objectForKey:@"id"] intValue],[[self requestID] intValue],[[[self connectionInfo] objectForKey:@"request"] intValue]);
+ #endif
+ [self setConnectionInfo:nil];
+ }
+ }
+
+
+
+ if (![self connectionInfo] && [[self url] host] && [[self url] scheme]) { // We must have a proper url with a host and scheme, or this will explode
+
+ // Look for a connection to the same server in the pool
+ for (NSMutableDictionary *existingConnection in persistentConnectionsPool) {
+ if (![existingConnection objectForKey:@"request"] && [[existingConnection objectForKey:@"host"] isEqualToString:[[self url] host]] && [[existingConnection objectForKey:@"scheme"] isEqualToString:[[self url] scheme]] && [(NSNumber *)[existingConnection objectForKey:@"port"] intValue] == [[[self url] port] intValue]) {
+ [self setConnectionInfo:existingConnection];
+ }
+ }
+ }
+
+ if ([[self connectionInfo] objectForKey:@"stream"]) {
+ oldStream = [[[self connectionInfo] objectForKey:@"stream"] retain];
+
+ }
+
+ // No free connection was found in the pool matching the server/scheme/port we're connecting to, we'll need to create a new one
+ if (![self connectionInfo]) {
+ [self setConnectionInfo:[NSMutableDictionary dictionary]];
+ nextConnectionNumberToCreate++;
+ [[self connectionInfo] setObject:[NSNumber numberWithInt:nextConnectionNumberToCreate] forKey:@"id"];
+ [[self connectionInfo] setObject:[[self url] host] forKey:@"host"];
+ [[self connectionInfo] setObject:[NSNumber numberWithInt:[[[self url] port] intValue]] forKey:@"port"];
+ [[self connectionInfo] setObject:[[self url] scheme] forKey:@"scheme"];
+ [persistentConnectionsPool addObject:[self connectionInfo]];
+ }
+
+ // If we are retrying this request, it will already have a requestID
+ if (![self requestID]) {
+ nextRequestID++;
+ [self setRequestID:[NSNumber numberWithUnsignedInt:nextRequestID]];
+ }
+ [[self connectionInfo] setObject:[self requestID] forKey:@"request"];
+ [[self connectionInfo] setObject:[self readStream] forKey:@"stream"];
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
+
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Request #%@ will use connection #%i",[self requestID],[[[self connectionInfo] objectForKey:@"id"] intValue]);
+ #endif
+
+
+ // Tag the stream with an id that tells it which connection to use behind the scenes
+ // See http://lists.apple.com/archives/macnetworkprog/2008/Dec/msg00001.html for details on this approach
+
+ CFReadStreamSetProperty((CFReadStreamRef)[self readStream], CFSTR("ASIStreamID"), [[self connectionInfo] objectForKey:@"id"]);
+
+ } else {
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Request %@ will not use a persistent connection",self);
+ #endif
+ }
+
+ [connectionsLock unlock];
+
+ // Schedule the stream
+ if (![self readStreamIsScheduled] && (!throttleWakeUpTime || [throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] < 0)) {
+ [self scheduleReadStream];
+ }
+
+ BOOL streamSuccessfullyOpened = NO;
+
+
+ // Start the HTTP connection
+ CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
+ if (CFReadStreamSetClient((CFReadStreamRef)[self readStream], kNetworkEvents, ReadStreamClientCallBack, &ctxt)) {
+ if (CFReadStreamOpen((CFReadStreamRef)[self readStream])) {
+ streamSuccessfullyOpened = YES;
+ }
+ }
+
+ // Here, we'll close the stream that was previously using this connection, if there was one
+ // We've kept it open until now (when we've just opened a new stream) so that the new stream can make use of the old connection
+ // http://lists.apple.com/archives/Macnetworkprog/2006/Mar/msg00119.html
+ if (oldStream) {
+ [oldStream close];
+ [oldStream release];
+ oldStream = nil;
+ }
+
+ if (!streamSuccessfullyOpened) {
+ [self setConnectionCanBeReused:NO];
+ [self destroyReadStream];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to start HTTP connection",NSLocalizedDescriptionKey,nil]]];
+ return;
+ }
+
+ if (![self mainRequest]) {
+ if ([self shouldResetUploadProgress]) {
+ if ([self showAccurateProgress]) {
+ [self incrementUploadSizeBy:[self postLength]];
+ } else {
+ [self incrementUploadSizeBy:1];
+ }
+ [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:0 ofTotal:1];
+ }
+ if ([self shouldResetDownloadProgress] && ![self partialDownloadSize]) {
+ [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:0 ofTotal:1];
+ }
+ }
+
+
+ // Record when the request started, so we can timeout if nothing happens
+ [self setLastActivityTime:[NSDate date]];
+ [self setStatusTimer:[NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]];
+ [[NSRunLoop currentRunLoop] addTimer:[self statusTimer] forMode:[self runLoopMode]];
+}
+
+- (void)setStatusTimer:(NSTimer *)timer
+{
+ CFRetain(self);
+ // We must invalidate the old timer here, not before we've created and scheduled a new timer
+ // This is because the timer may be the only thing retaining an asynchronous request
+ if (statusTimer && timer != statusTimer) {
+ [statusTimer invalidate];
+ [statusTimer release];
+ }
+ statusTimer = [timer retain];
+ CFRelease(self);
+}
+
+// This gets fired every 1/4 of a second to update the progress and work out if we need to timeout
+- (void)updateStatus:(NSTimer*)timer
+{
+ [self checkRequestStatus];
+ if (![self inProgress]) {
+ [self setStatusTimer:nil];
+ }
+}
+
+- (void)performRedirect
+{
+ [self setURL:[self redirectURL]];
+ [self setComplete:YES];
+ [self setNeedsRedirect:NO];
+ [self setRedirectCount:[self redirectCount]+1];
+
+ if ([self redirectCount] > RedirectionLimit) {
+ // Some naughty / badly coded website is trying to force us into a redirection loop. This is not cool.
+ [self failWithError:ASITooMuchRedirectionError];
+ [self setComplete:YES];
+ } else {
+ // Go all the way back to the beginning and build the request again, so that we can apply any new cookies
+ [self main];
+ }
+}
+
+// Called by delegate to resume loading with a new url after the delegate received request:willRedirectToURL:
+- (void)redirectToURL:(NSURL *)newURL
+{
+ [self setRedirectURL:newURL];
+ [self performSelector:@selector(performRedirect) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO];
+}
+
+- (BOOL)shouldTimeOut
+{
+ NSTimeInterval secondsSinceLastActivity = [[NSDate date] timeIntervalSinceDate:lastActivityTime];
+ // See if we need to timeout
+ if ([self readStream] && [self readStreamIsScheduled] && [self lastActivityTime] && [self timeOutSeconds] > 0 && secondsSinceLastActivity > [self timeOutSeconds]) {
+
+ // We have no body, or we've sent more than the upload buffer size,so we can safely time out here
+ if ([self postLength] == 0 || ([self uploadBufferSize] > 0 && [self totalBytesSent] > [self uploadBufferSize])) {
+ return YES;
+
+ // ***Black magic warning***
+ // We have a body, but we've taken longer than timeOutSeconds to upload the first small chunk of data
+ // Since there's no reliable way to track upload progress for the first 32KB (iPhone) or 128KB (Mac) with CFNetwork, we'll be slightly more forgiving on the timeout, as there's a strong chance our connection is just very slow.
+ } else if (secondsSinceLastActivity > [self timeOutSeconds]*1.5) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)checkRequestStatus
+{
+ // We won't let the request cancel while we're updating progress / checking for a timeout
+ [[self cancelledLock] lock];
+ // See if our NSOperationQueue told us to cancel
+ if ([self isCancelled] || [self complete]) {
+ [[self cancelledLock] unlock];
+ return;
+ }
+
+ [self performThrottling];
+
+ if ([self shouldTimeOut]) {
+ // Do we need to auto-retry this request?
+ if ([self numberOfTimesToRetryOnTimeout] > [self retryCount]) {
+
+ // If we are resuming a download, we may need to update the Range header to take account of data we've just downloaded
+ [self updatePartialDownloadSize];
+ if ([self partialDownloadSize]) {
+ CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)@"Range", (CFStringRef)[NSString stringWithFormat:@"bytes=%llu-",[self partialDownloadSize]]);
+ }
+ [self setRetryCount:[self retryCount]+1];
+ [self unscheduleReadStream];
+ [[self cancelledLock] unlock];
+ [self startRequest];
+ return;
+ }
+ [self failWithError:ASIRequestTimedOutError];
+ [self cancelLoad];
+ [self setComplete:YES];
+ [[self cancelledLock] unlock];
+ return;
+ }
+
+ // readStream will be null if we aren't currently running (perhaps we're waiting for a delegate to supply credentials)
+ if ([self readStream]) {
+
+ // If we have a post body
+ if ([self postLength]) {
+
+ [self setLastBytesSent:totalBytesSent];
+
+ // Find out how much data we've uploaded so far
+ [self setTotalBytesSent:[[NSMakeCollectable(CFReadStreamCopyProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPRequestBytesWrittenCount)) autorelease] unsignedLongLongValue]];
+ if (totalBytesSent > lastBytesSent) {
+
+ // We've uploaded more data, reset the timeout
+ [self setLastActivityTime:[NSDate date]];
+ [ASIHTTPRequest incrementBandwidthUsedInLastSecond:(unsigned long)(totalBytesSent-lastBytesSent)];
+
+ #if DEBUG_REQUEST_STATUS
+ if ([self totalBytesSent] == [self postLength]) {
+ ASI_DEBUG_LOG(@"[STATUS] Request %@ finished uploading data",self);
+ }
+ #endif
+ }
+ }
+
+ [self updateProgressIndicators];
+
+ }
+
+ [[self cancelledLock] unlock];
+}
+
+
+// Cancel loading and clean up. DO NOT USE THIS TO CANCEL REQUESTS - use [request cancel] instead
+- (void)cancelLoad
+{
+ // If we're in the middle of downloading a PAC file, let's stop that first
+ if (PACFileReadStream) {
+ [PACFileReadStream setDelegate:nil];
+ [PACFileReadStream close];
+ [self setPACFileReadStream:nil];
+ [self setPACFileData:nil];
+ } else if (PACFileRequest) {
+ [PACFileRequest setDelegate:nil];
+ [PACFileRequest cancel];
+ [self setPACFileRequest:nil];
+ }
+
+ [self destroyReadStream];
+
+ [[self postBodyReadStream] close];
+ [self setPostBodyReadStream:nil];
+
+ if ([self rawResponseData]) {
+ if (![self complete]) {
+ [self setRawResponseData:nil];
+ }
+ // If we were downloading to a file
+ } else if ([self temporaryFileDownloadPath]) {
+ [[self fileDownloadOutputStream] close];
+ [self setFileDownloadOutputStream:nil];
+
+ [[self inflatedFileDownloadOutputStream] close];
+ [self setInflatedFileDownloadOutputStream:nil];
+
+ // If we haven't said we might want to resume, let's remove the temporary file too
+ if (![self complete]) {
+ if (![self allowResumeForFileDownloads]) {
+ [self removeTemporaryDownloadFile];
+ }
+ [self removeTemporaryUncompressedDownloadFile];
+ }
+ }
+
+ // Clean up any temporary file used to store request body for streaming
+ if (![self authenticationNeeded] && ![self willRetryRequest] && [self didCreateTemporaryPostDataFile]) {
+ [self removeTemporaryUploadFile];
+ [self removeTemporaryCompressedUploadFile];
+ [self setDidCreateTemporaryPostDataFile:NO];
+ }
+}
+
+#pragma mark HEAD request
+
+// Used by ASINetworkQueue to create a HEAD request appropriate for this request with the same headers (though you can use it yourself)
+- (ASIHTTPRequest *)HEADRequest
+{
+ ASIHTTPRequest *headRequest = [[self class] requestWithURL:[self url]];
+
+ // Copy the properties that make sense for a HEAD request
+ [headRequest setRequestHeaders:[[[self requestHeaders] mutableCopy] autorelease]];
+ [headRequest setRequestCookies:[[[self requestCookies] mutableCopy] autorelease]];
+ [headRequest setUseCookiePersistence:[self useCookiePersistence]];
+ [headRequest setUseKeychainPersistence:[self useKeychainPersistence]];
+ [headRequest setUseSessionPersistence:[self useSessionPersistence]];
+ [headRequest setAllowCompressedResponse:[self allowCompressedResponse]];
+ [headRequest setUsername:[self username]];
+ [headRequest setPassword:[self password]];
+ [headRequest setDomain:[self domain]];
+ [headRequest setProxyUsername:[self proxyUsername]];
+ [headRequest setProxyPassword:[self proxyPassword]];
+ [headRequest setProxyDomain:[self proxyDomain]];
+ [headRequest setProxyHost:[self proxyHost]];
+ [headRequest setProxyPort:[self proxyPort]];
+ [headRequest setProxyType:[self proxyType]];
+ [headRequest setShouldPresentAuthenticationDialog:[self shouldPresentAuthenticationDialog]];
+ [headRequest setShouldPresentProxyAuthenticationDialog:[self shouldPresentProxyAuthenticationDialog]];
+ [headRequest setTimeOutSeconds:[self timeOutSeconds]];
+ [headRequest setUseHTTPVersionOne:[self useHTTPVersionOne]];
+ [headRequest setValidatesSecureCertificate:[self validatesSecureCertificate]];
+ [headRequest setClientCertificateIdentity:clientCertificateIdentity];
+ [headRequest setClientCertificates:[[clientCertificates copy] autorelease]];
+ [headRequest setPACurl:[self PACurl]];
+ [headRequest setShouldPresentCredentialsBeforeChallenge:[self shouldPresentCredentialsBeforeChallenge]];
+ [headRequest setNumberOfTimesToRetryOnTimeout:[self numberOfTimesToRetryOnTimeout]];
+ [headRequest setShouldUseRFC2616RedirectBehaviour:[self shouldUseRFC2616RedirectBehaviour]];
+ [headRequest setShouldAttemptPersistentConnection:[self shouldAttemptPersistentConnection]];
+ [headRequest setPersistentConnectionTimeoutSeconds:[self persistentConnectionTimeoutSeconds]];
+
+ [headRequest setMainRequest:self];
+ [headRequest setRequestMethod:@"HEAD"];
+ return headRequest;
+}
+
+
+#pragma mark upload/download progress
+
+
+- (void)updateProgressIndicators
+{
+ //Only update progress if this isn't a HEAD request used to preset the content-length
+ if (![self mainRequest]) {
+ if ([self showAccurateProgress] || ([self complete] && ![self updatedProgress])) {
+ [self updateUploadProgress];
+ [self updateDownloadProgress];
+ }
+ }
+}
+
+- (id)uploadProgressDelegate
+{
+ [[self cancelledLock] lock];
+ id d = [[uploadProgressDelegate retain] autorelease];
+ [[self cancelledLock] unlock];
+ return d;
+}
+
+- (void)setUploadProgressDelegate:(id)newDelegate
+{
+ [[self cancelledLock] lock];
+ uploadProgressDelegate = newDelegate;
+
+ #if !TARGET_OS_IPHONE
+ // If the uploadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can update it as if it were a UIProgressView
+ double max = 1.0;
+ [ASIHTTPRequest performSelector:@selector(setMaxValue:) onTarget:&uploadProgressDelegate withObject:nil amount:&max callerToRetain:nil];
+ #endif
+ [[self cancelledLock] unlock];
+}
+
+- (id)downloadProgressDelegate
+{
+ [[self cancelledLock] lock];
+ id d = [[downloadProgressDelegate retain] autorelease];
+ [[self cancelledLock] unlock];
+ return d;
+}
+
+- (void)setDownloadProgressDelegate:(id)newDelegate
+{
+ [[self cancelledLock] lock];
+ downloadProgressDelegate = newDelegate;
+
+ #if !TARGET_OS_IPHONE
+ // If the downloadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can update it as if it were a UIProgressView
+ double max = 1.0;
+ [ASIHTTPRequest performSelector:@selector(setMaxValue:) onTarget:&downloadProgressDelegate withObject:nil amount:&max callerToRetain:nil];
+ #endif
+ [[self cancelledLock] unlock];
+}
+
+
+- (void)updateDownloadProgress
+{
+ // We won't update download progress until we've examined the headers, since we might need to authenticate
+ if (![self responseHeaders] || [self needsRedirect] || !([self contentLength] || [self complete])) {
+ return;
+ }
+
+ unsigned long long bytesReadSoFar = [self totalBytesRead]+[self partialDownloadSize];
+ unsigned long long value = 0;
+
+ if ([self showAccurateProgress] && [self contentLength]) {
+ value = bytesReadSoFar-[self lastBytesRead];
+ if (value == 0) {
+ return;
+ }
+ } else {
+ value = 1;
+ [self setUpdatedProgress:YES];
+ }
+ if (!value) {
+ return;
+ }
+
+ [ASIHTTPRequest performSelector:@selector(request:didReceiveBytes:) onTarget:&queue withObject:self amount:&value callerToRetain:self];
+ [ASIHTTPRequest performSelector:@selector(request:didReceiveBytes:) onTarget:&downloadProgressDelegate withObject:self amount:&value callerToRetain:self];
+
+ [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:[self totalBytesRead]+[self partialDownloadSize] ofTotal:[self contentLength]+[self partialDownloadSize]];
+
+ #if NS_BLOCKS_AVAILABLE
+ if (bytesReceivedBlock) {
+ unsigned long long totalSize = [self contentLength] + [self partialDownloadSize];
+ [self performBlockOnMainThread:^{ if (bytesReceivedBlock) { bytesReceivedBlock(value, totalSize); }}];
+ }
+ #endif
+ [self setLastBytesRead:bytesReadSoFar];
+}
+
+- (void)updateUploadProgress
+{
+ if ([self isCancelled] || [self totalBytesSent] == 0) {
+ return;
+ }
+
+ // If this is the first time we've written to the buffer, totalBytesSent will be the size of the buffer (currently seems to be 128KB on both Leopard and iPhone 2.2.1, 32KB on iPhone 3.0)
+ // If request body is less than the buffer size, totalBytesSent will be the total size of the request body
+ // We will remove this from any progress display, as kCFStreamPropertyHTTPRequestBytesWrittenCount does not tell us how much data has actually be written
+ if ([self uploadBufferSize] == 0 && [self totalBytesSent] != [self postLength]) {
+ [self setUploadBufferSize:[self totalBytesSent]];
+ [self incrementUploadSizeBy:-[self uploadBufferSize]];
+ }
+
+ unsigned long long value = 0;
+
+ if ([self showAccurateProgress]) {
+ if ([self totalBytesSent] == [self postLength] || [self lastBytesSent] > 0) {
+ value = [self totalBytesSent]-[self lastBytesSent];
+ } else {
+ return;
+ }
+ } else {
+ value = 1;
+ [self setUpdatedProgress:YES];
+ }
+
+ if (!value) {
+ return;
+ }
+
+ [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&queue withObject:self amount:&value callerToRetain:self];
+ [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&uploadProgressDelegate withObject:self amount:&value callerToRetain:self];
+ [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:[self totalBytesSent]-[self uploadBufferSize] ofTotal:[self postLength]-[self uploadBufferSize]];
+
+ #if NS_BLOCKS_AVAILABLE
+ if(bytesSentBlock){
+ unsigned long long totalSize = [self postLength];
+ [self performBlockOnMainThread:^{ if (bytesSentBlock) { bytesSentBlock(value, totalSize); }}];
+ }
+ #endif
+}
+
+
+- (void)incrementDownloadSizeBy:(long long)length
+{
+ [ASIHTTPRequest performSelector:@selector(request:incrementDownloadSizeBy:) onTarget:&queue withObject:self amount:&length callerToRetain:self];
+ [ASIHTTPRequest performSelector:@selector(request:incrementDownloadSizeBy:) onTarget:&downloadProgressDelegate withObject:self amount:&length callerToRetain:self];
+
+ #if NS_BLOCKS_AVAILABLE
+ if(downloadSizeIncrementedBlock){
+ [self performBlockOnMainThread:^{ if (downloadSizeIncrementedBlock) { downloadSizeIncrementedBlock(length); }}];
+ }
+ #endif
+}
+
+- (void)incrementUploadSizeBy:(long long)length
+{
+ [ASIHTTPRequest performSelector:@selector(request:incrementUploadSizeBy:) onTarget:&queue withObject:self amount:&length callerToRetain:self];
+ [ASIHTTPRequest performSelector:@selector(request:incrementUploadSizeBy:) onTarget:&uploadProgressDelegate withObject:self amount:&length callerToRetain:self];
+
+ #if NS_BLOCKS_AVAILABLE
+ if(uploadSizeIncrementedBlock) {
+ [self performBlockOnMainThread:^{ if (uploadSizeIncrementedBlock) { uploadSizeIncrementedBlock(length); }}];
+ }
+ #endif
+}
+
+
+-(void)removeUploadProgressSoFar
+{
+ long long progressToRemove = -[self totalBytesSent];
+ [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&queue withObject:self amount:&progressToRemove callerToRetain:self];
+ [ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:&uploadProgressDelegate withObject:self amount:&progressToRemove callerToRetain:self];
+ [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:0 ofTotal:[self postLength]];
+
+ #if NS_BLOCKS_AVAILABLE
+ if(bytesSentBlock){
+ unsigned long long totalSize = [self postLength];
+ [self performBlockOnMainThread:^{ if (bytesSentBlock) { bytesSentBlock(progressToRemove, totalSize); }}];
+ }
+ #endif
+}
+
+#if NS_BLOCKS_AVAILABLE
+- (void)performBlockOnMainThread:(ASIBasicBlock)block
+{
+ [self performSelectorOnMainThread:@selector(callBlock:) withObject:[[block copy] autorelease] waitUntilDone:[NSThread isMainThread]];
+}
+
+- (void)callBlock:(ASIBasicBlock)block
+{
+ block();
+}
+#endif
+
+
++ (void)performSelector:(SEL)selector onTarget:(id *)target withObject:(id)object amount:(void *)amount callerToRetain:(id)callerToRetain
+{
+ if ([*target respondsToSelector:selector]) {
+ NSMethodSignature *signature = nil;
+ signature = [*target methodSignatureForSelector:selector];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+
+ [invocation setSelector:selector];
+
+ int argumentNumber = 2;
+
+ // If we got an object parameter, we pass a pointer to the object pointer
+ if (object) {
+ [invocation setArgument:&object atIndex:argumentNumber];
+ argumentNumber++;
+ }
+
+ // For the amount we'll just pass the pointer directly so NSInvocation will call the method using the number itself rather than a pointer to it
+ if (amount) {
+ [invocation setArgument:amount atIndex:argumentNumber];
+ }
+
+ SEL callback = @selector(performInvocation:onTarget:releasingObject:);
+ NSMethodSignature *cbSignature = [ASIHTTPRequest methodSignatureForSelector:callback];
+ NSInvocation *cbInvocation = [NSInvocation invocationWithMethodSignature:cbSignature];
+ [cbInvocation setSelector:callback];
+ [cbInvocation setTarget:self];
+ [cbInvocation setArgument:&invocation atIndex:2];
+ [cbInvocation setArgument:&target atIndex:3];
+ if (callerToRetain) {
+ [cbInvocation setArgument:&callerToRetain atIndex:4];
+ }
+
+ CFRetain(invocation);
+
+ // Used to pass in a request that we must retain until after the call
+ // We're using CFRetain rather than [callerToRetain retain] so things to avoid earthquakes when using garbage collection
+ if (callerToRetain) {
+ CFRetain(callerToRetain);
+ }
+ [cbInvocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:[NSThread isMainThread]];
+ }
+}
+
++ (void)performInvocation:(NSInvocation *)invocation onTarget:(id *)target releasingObject:(id)objectToRelease
+{
+ if (*target && [*target respondsToSelector:[invocation selector]]) {
+ [invocation invokeWithTarget:*target];
+ }
+ CFRelease(invocation);
+ if (objectToRelease) {
+ CFRelease(objectToRelease);
+ }
+}
+
+
++ (void)updateProgressIndicator:(id *)indicator withProgress:(unsigned long long)progress ofTotal:(unsigned long long)total
+{
+ #if TARGET_OS_IPHONE
+ // Cocoa Touch: UIProgressView
+ SEL selector = @selector(setProgress:);
+ float progressAmount = (float)((progress*1.0)/(total*1.0));
+
+ #else
+ // Cocoa: NSProgressIndicator
+ double progressAmount = progressAmount = (progress*1.0)/(total*1.0);
+ SEL selector = @selector(setDoubleValue:);
+ #endif
+
+ if (![*indicator respondsToSelector:selector]) {
+ return;
+ }
+
+ [progressLock lock];
+ [ASIHTTPRequest performSelector:selector onTarget:indicator withObject:nil amount:&progressAmount callerToRetain:nil];
+ [progressLock unlock];
+}
+
+
+#pragma mark talking to delegates / calling blocks
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)requestStarted
+{
+ if ([self error] || [self mainRequest]) {
+ return;
+ }
+ if (delegate && [delegate respondsToSelector:didStartSelector]) {
+ [delegate performSelector:didStartSelector withObject:self];
+ }
+ #if NS_BLOCKS_AVAILABLE
+ if(startedBlock){
+ startedBlock();
+ }
+ #endif
+ if (queue && [queue respondsToSelector:@selector(requestStarted:)]) {
+ [queue performSelector:@selector(requestStarted:) withObject:self];
+ }
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)requestRedirected
+{
+ if ([self error] || [self mainRequest]) {
+ return;
+ }
+
+ if([[self delegate] respondsToSelector:@selector(requestRedirected:)]){
+ [[self delegate] performSelector:@selector(requestRedirected:) withObject:self];
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if(requestRedirectedBlock){
+ requestRedirectedBlock();
+ }
+ #endif
+}
+
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)requestReceivedResponseHeaders:(NSMutableDictionary *)newResponseHeaders
+{
+ if ([self error] || [self mainRequest]) {
+ return;
+ }
+
+ if (delegate && [delegate respondsToSelector:didReceiveResponseHeadersSelector]) {
+ [delegate performSelector:didReceiveResponseHeadersSelector withObject:self withObject:newResponseHeaders];
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if(headersReceivedBlock){
+ headersReceivedBlock(newResponseHeaders);
+ }
+ #endif
+
+ if (queue && [queue respondsToSelector:@selector(request:didReceiveResponseHeaders:)]) {
+ [queue performSelector:@selector(request:didReceiveResponseHeaders:) withObject:self withObject:newResponseHeaders];
+ }
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)requestWillRedirectToURL:(NSURL *)newURL
+{
+ if ([self error] || [self mainRequest]) {
+ return;
+ }
+ if (delegate && [delegate respondsToSelector:willRedirectSelector]) {
+ [delegate performSelector:willRedirectSelector withObject:self withObject:newURL];
+ }
+ if (queue && [queue respondsToSelector:@selector(request:willRedirectToURL:)]) {
+ [queue performSelector:@selector(request:willRedirectToURL:) withObject:self withObject:newURL];
+ }
+}
+
+// Subclasses might override this method to process the result in the same thread
+// If you do this, don't forget to call [super requestFinished] to let the queue / delegate know we're done
+- (void)requestFinished
+{
+#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[STATUS] Request finished: %@",self);
+#endif
+ if ([self error] || [self mainRequest]) {
+ return;
+ }
+ if ([self isPACFileRequest]) {
+ [self reportFinished];
+ } else {
+ [self performSelectorOnMainThread:@selector(reportFinished) withObject:nil waitUntilDone:[NSThread isMainThread]];
+ }
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)reportFinished
+{
+ if (delegate && [delegate respondsToSelector:didFinishSelector]) {
+ [delegate performSelector:didFinishSelector withObject:self];
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if(completionBlock){
+ completionBlock();
+ }
+ #endif
+
+ if (queue && [queue respondsToSelector:@selector(requestFinished:)]) {
+ [queue performSelector:@selector(requestFinished:) withObject:self];
+ }
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)reportFailure
+{
+ if (delegate && [delegate respondsToSelector:didFailSelector]) {
+ [delegate performSelector:didFailSelector withObject:self];
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if(failureBlock){
+ failureBlock();
+ }
+ #endif
+
+ if (queue && [queue respondsToSelector:@selector(requestFailed:)]) {
+ [queue performSelector:@selector(requestFailed:) withObject:self];
+ }
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)passOnReceivedData:(NSData *)data
+{
+ if (delegate && [delegate respondsToSelector:didReceiveDataSelector]) {
+ [delegate performSelector:didReceiveDataSelector withObject:self withObject:data];
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if (dataReceivedBlock) {
+ dataReceivedBlock(data);
+ }
+ #endif
+}
+
+// Subclasses might override this method to perform error handling in the same thread
+// If you do this, don't forget to call [super failWithError:] to let the queue / delegate know we're done
+- (void)failWithError:(NSError *)theError
+{
+#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[STATUS] Request %@: %@",self,(theError == ASIRequestCancelledError ? @"Cancelled" : @"Failed"));
+#endif
+ [self setComplete:YES];
+
+ // Invalidate the current connection so subsequent requests don't attempt to reuse it
+ if (theError && [theError code] != ASIAuthenticationErrorType && [theError code] != ASITooMuchRedirectionErrorType) {
+ [connectionsLock lock];
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Request #%@ failed and will invalidate connection #%@",[self requestID],[[self connectionInfo] objectForKey:@"id"]);
+ #endif
+ [[self connectionInfo] removeObjectForKey:@"request"];
+ [persistentConnectionsPool removeObject:[self connectionInfo]];
+ [connectionsLock unlock];
+ [self destroyReadStream];
+ }
+ if ([self connectionCanBeReused]) {
+ [[self connectionInfo] setObject:[NSDate dateWithTimeIntervalSinceNow:[self persistentConnectionTimeoutSeconds]] forKey:@"expires"];
+ }
+
+ if ([self isCancelled] || [self error]) {
+ return;
+ }
+
+ // If we have cached data, use it and ignore the error when using ASIFallbackToCacheIfLoadFailsCachePolicy
+ if ([self downloadCache] && ([self cachePolicy] & ASIFallbackToCacheIfLoadFailsCachePolicy)) {
+ if ([[self downloadCache] canUseCachedDataForRequest:self]) {
+ [self useDataFromCache];
+ return;
+ }
+ }
+
+
+ [self setError:theError];
+
+ ASIHTTPRequest *failedRequest = self;
+
+ // If this is a HEAD request created by an ASINetworkQueue or compatible queue delegate, make the main request fail
+ if ([self mainRequest]) {
+ failedRequest = [self mainRequest];
+ [failedRequest setError:theError];
+ }
+
+ if ([self isPACFileRequest]) {
+ [failedRequest reportFailure];
+ } else {
+ [failedRequest performSelectorOnMainThread:@selector(reportFailure) withObject:nil waitUntilDone:[NSThread isMainThread]];
+ }
+
+ if (!inProgress)
+ {
+ // if we're not in progress, we can't notify the queue we've finished (doing so can cause a crash later on)
+ // "markAsFinished" will be at the start of main() when we are started
+ return;
+ }
+ [self markAsFinished];
+}
+
+#pragma mark parsing HTTP response headers
+
+- (void)readResponseHeaders
+{
+ [self setAuthenticationNeeded:ASINoAuthenticationNeededYet];
+
+ CFHTTPMessageRef message = (CFHTTPMessageRef)CFReadStreamCopyProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPResponseHeader);
+ if (!message) {
+ return;
+ }
+
+ // Make sure we've received all the headers
+ if (!CFHTTPMessageIsHeaderComplete(message)) {
+ CFRelease(message);
+ return;
+ }
+
+ #if DEBUG_REQUEST_STATUS
+ if ([self totalBytesSent] == [self postLength]) {
+ ASI_DEBUG_LOG(@"[STATUS] Request %@ received response headers",self);
+ }
+ #endif
+
+ [self setResponseHeaders:[NSMakeCollectable(CFHTTPMessageCopyAllHeaderFields(message)) autorelease]];
+ [self setResponseStatusCode:(int)CFHTTPMessageGetResponseStatusCode(message)];
+ [self setResponseStatusMessage:[NSMakeCollectable(CFHTTPMessageCopyResponseStatusLine(message)) autorelease]];
+
+ if ([self downloadCache] && ([[self downloadCache] canUseCachedDataForRequest:self])) {
+
+ // Update the expiry date
+ [[self downloadCache] updateExpiryForRequest:self maxAge:[self secondsToCache]];
+
+ // Read the response from the cache
+ [self useDataFromCache];
+
+ CFRelease(message);
+ return;
+ }
+
+ // Is the server response a challenge for credentials?
+ if ([self responseStatusCode] == 401) {
+ [self setAuthenticationNeeded:ASIHTTPAuthenticationNeeded];
+ } else if ([self responseStatusCode] == 407) {
+ [self setAuthenticationNeeded:ASIProxyAuthenticationNeeded];
+ } else {
+ #if DEBUG_HTTP_AUTHENTICATION
+ if ([self authenticationScheme]) {
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ has passed %@ authentication",self,[self authenticationScheme]);
+ }
+ #endif
+ }
+
+ // Authentication succeeded, or no authentication was required
+ if (![self authenticationNeeded]) {
+
+ // Did we get here without an authentication challenge? (which can happen when shouldPresentCredentialsBeforeChallenge is YES and basic auth was successful)
+ if (!requestAuthentication && [[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic] && [self username] && [self password] && [self useSessionPersistence]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ passed BASIC authentication, and will save credentials in the session store for future use",self);
+ #endif
+
+ NSMutableDictionary *newCredentials = [NSMutableDictionary dictionaryWithCapacity:2];
+ [newCredentials setObject:[self username] forKey:(NSString *)kCFHTTPAuthenticationUsername];
+ [newCredentials setObject:[self password] forKey:(NSString *)kCFHTTPAuthenticationPassword];
+
+ // Store the credentials in the session
+ NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary];
+ [sessionCredentials setObject:newCredentials forKey:@"Credentials"];
+ [sessionCredentials setObject:[self url] forKey:@"URL"];
+ [sessionCredentials setObject:(NSString *)kCFHTTPAuthenticationSchemeBasic forKey:@"AuthenticationScheme"];
+ [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials];
+ }
+ }
+
+ // Read response textEncoding
+ [self parseStringEncodingFromHeaders];
+
+ // Handle cookies
+ NSArray *newCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[self responseHeaders] forURL:[self url]];
+ [self setResponseCookies:newCookies];
+
+ if ([self useCookiePersistence]) {
+
+ // Store cookies in global persistent store
+ [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:newCookies forURL:[self url] mainDocumentURL:nil];
+
+ // We also keep any cookies in the sessionCookies array, so that we have a reference to them if we need to remove them later
+ NSHTTPCookie *cookie;
+ for (cookie in newCookies) {
+ [ASIHTTPRequest addSessionCookie:cookie];
+ }
+ }
+
+ // Do we need to redirect?
+ if (![self willRedirect]) {
+ // See if we got a Content-length header
+ NSString *cLength = [responseHeaders valueForKey:@"Content-Length"];
+ ASIHTTPRequest *theRequest = self;
+ if ([self mainRequest]) {
+ theRequest = [self mainRequest];
+ }
+
+ if (cLength) {
+ unsigned long long length = strtoull([cLength UTF8String], NULL, 0);
+
+ // Workaround for Apache HEAD requests for dynamically generated content returning the wrong Content-Length when using gzip
+ if ([self mainRequest] && [self allowCompressedResponse] && length == 20 && [self showAccurateProgress] && [self shouldResetDownloadProgress]) {
+ [[self mainRequest] setShowAccurateProgress:NO];
+ [[self mainRequest] incrementDownloadSizeBy:1];
+
+ } else {
+ [theRequest setContentLength:length];
+ if ([self showAccurateProgress] && [self shouldResetDownloadProgress]) {
+ [theRequest incrementDownloadSizeBy:[theRequest contentLength]+[theRequest partialDownloadSize]];
+ }
+ }
+
+ } else if ([self showAccurateProgress] && [self shouldResetDownloadProgress]) {
+ [theRequest setShowAccurateProgress:NO];
+ [theRequest incrementDownloadSizeBy:1];
+ }
+ }
+
+ // Handle connection persistence
+ if ([self shouldAttemptPersistentConnection]) {
+
+ NSString *connectionHeader = [[[self responseHeaders] objectForKey:@"Connection"] lowercaseString];
+
+ NSString *httpVersion = [NSMakeCollectable(CFHTTPMessageCopyVersion(message)) autorelease];
+
+ // Don't re-use the connection if the server is HTTP 1.0 and didn't send Connection: Keep-Alive
+ if (![httpVersion isEqualToString:(NSString *)kCFHTTPVersion1_0] || [connectionHeader isEqualToString:@"keep-alive"]) {
+
+ // See if server explicitly told us to close the connection
+ if (![connectionHeader isEqualToString:@"close"]) {
+
+ NSString *keepAliveHeader = [[self responseHeaders] objectForKey:@"Keep-Alive"];
+
+ // If we got a keep alive header, we'll reuse the connection for as long as the server tells us
+ if (keepAliveHeader) {
+ int timeout = 0;
+ int max = 0;
+ NSScanner *scanner = [NSScanner scannerWithString:keepAliveHeader];
+ [scanner scanString:@"timeout=" intoString:NULL];
+ [scanner scanInt:&timeout];
+ [scanner scanUpToString:@"max=" intoString:NULL];
+ [scanner scanString:@"max=" intoString:NULL];
+ [scanner scanInt:&max];
+ if (max > 5) {
+ [self setConnectionCanBeReused:YES];
+ [self setPersistentConnectionTimeoutSeconds:timeout];
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Got a keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]);
+ #endif
+ }
+
+ // Otherwise, we'll assume we can keep this connection open
+ } else {
+ [self setConnectionCanBeReused:YES];
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Got no keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]);
+ #endif
+ }
+ }
+ }
+ }
+
+ CFRelease(message);
+ [self performSelectorOnMainThread:@selector(requestReceivedResponseHeaders:) withObject:[[[self responseHeaders] copy] autorelease] waitUntilDone:[NSThread isMainThread]];
+}
+
+- (BOOL)willRedirect
+{
+ // Do we need to redirect?
+ if (![self shouldRedirect] || ![responseHeaders valueForKey:@"Location"]) {
+ return NO;
+ }
+
+ // Note that ASIHTTPRequest does not currently support 305 Use Proxy
+ int responseCode = [self responseStatusCode];
+ if (responseCode != 301 && responseCode != 302 && responseCode != 303 && responseCode != 307) {
+ return NO;
+ }
+
+ [self performSelectorOnMainThread:@selector(requestRedirected) withObject:nil waitUntilDone:[NSThread isMainThread]];
+
+ // By default, we redirect 301 and 302 response codes as GET requests
+ // According to RFC 2616 this is wrong, but this is what most browsers do, so it's probably what you're expecting to happen
+ // See also:
+ // http://allseeing-i.lighthouseapp.com/projects/27881/tickets/27-302-redirection-issue
+
+ if (responseCode != 307 && (![self shouldUseRFC2616RedirectBehaviour] || responseCode == 303)) {
+ [self setRequestMethod:@"GET"];
+ [self setPostBody:nil];
+ [self setPostLength:0];
+
+ // Perhaps there are other headers we should be preserving, but it's hard to know what we need to keep and what to throw away.
+ NSString *userAgentHeader = [[self requestHeaders] objectForKey:@"User-Agent"];
+ NSString *acceptHeader = [[self requestHeaders] objectForKey:@"Accept"];
+ [self setRequestHeaders:nil];
+ if (userAgentHeader) {
+ [self addRequestHeader:@"User-Agent" value:userAgentHeader];
+ }
+ if (acceptHeader) {
+ [self addRequestHeader:@"Accept" value:acceptHeader];
+ }
+ [self setHaveBuiltRequestHeaders:NO];
+
+ } else {
+ // Force rebuild the cookie header incase we got some new cookies from this request
+ // All other request headers will remain as they are for 301 / 302 redirects
+ [self applyCookieHeader];
+ }
+
+ // Force the redirected request to rebuild the request headers (if not a 303, it will re-use old ones, and add any new ones)
+ [self setRedirectURL:[[NSURL URLWithString:[responseHeaders valueForKey:@"Location"] relativeToURL:[self url]] absoluteURL]];
+ [self setNeedsRedirect:YES];
+
+ // Clear the request cookies
+ // This means manually added cookies will not be added to the redirect request - only those stored in the global persistent store
+ // But, this is probably the safest option - we might be redirecting to a different domain
+ [self setRequestCookies:[NSMutableArray array]];
+
+ #if DEBUG_REQUEST_STATUS
+ ASI_DEBUG_LOG(@"[STATUS] Request will redirect (code: %i): %@",responseCode,self);
+ #endif
+
+ return YES;
+}
+
+- (void)parseStringEncodingFromHeaders
+{
+ // Handle response text encoding
+ NSStringEncoding charset = 0;
+ NSString *mimeType = nil;
+ [[self class] parseMimeType:&mimeType andResponseEncoding:&charset fromContentType:[[self responseHeaders] valueForKey:@"Content-Type"]];
+ if (charset != 0) {
+ [self setResponseEncoding:charset];
+ } else {
+ [self setResponseEncoding:[self defaultResponseEncoding]];
+ }
+}
+
+#pragma mark http authentication
+
+- (void)saveProxyCredentialsToKeychain:(NSDictionary *)newCredentials
+{
+ NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername] password:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationPassword] persistence:NSURLCredentialPersistencePermanent];
+ if (authenticationCredentials) {
+ [ASIHTTPRequest saveCredentials:authenticationCredentials forProxy:[self proxyHost] port:[self proxyPort] realm:[self proxyAuthenticationRealm]];
+ }
+}
+
+
+- (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials
+{
+ NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername] password:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationPassword] persistence:NSURLCredentialPersistencePermanent];
+
+ if (authenticationCredentials) {
+ [ASIHTTPRequest saveCredentials:authenticationCredentials forHost:[[self url] host] port:[[[self url] port] intValue] protocol:[[self url] scheme] realm:[self authenticationRealm]];
+ }
+}
+
+- (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials
+{
+ [self setProxyAuthenticationRetryCount:[self proxyAuthenticationRetryCount]+1];
+
+ if (newCredentials && proxyAuthentication && request) {
+
+ // Apply whatever credentials we've built up to the old request
+ if (CFHTTPMessageApplyCredentialDictionary(request, proxyAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
+
+ //If we have credentials and they're ok, let's save them to the keychain
+ if (useKeychainPersistence) {
+ [self saveProxyCredentialsToKeychain:newCredentials];
+ }
+ if (useSessionPersistence) {
+ NSMutableDictionary *sessionProxyCredentials = [NSMutableDictionary dictionary];
+ [sessionProxyCredentials setObject:(id)proxyAuthentication forKey:@"Authentication"];
+ [sessionProxyCredentials setObject:newCredentials forKey:@"Credentials"];
+ [sessionProxyCredentials setObject:[self proxyHost] forKey:@"Host"];
+ [sessionProxyCredentials setObject:[NSNumber numberWithInt:[self proxyPort]] forKey:@"Port"];
+ [sessionProxyCredentials setObject:[self proxyAuthenticationScheme] forKey:@"AuthenticationScheme"];
+ [[self class] storeProxyAuthenticationCredentialsInSessionStore:sessionProxyCredentials];
+ }
+ [self setProxyCredentials:newCredentials];
+ return YES;
+ } else {
+ [[self class] removeProxyAuthenticationCredentialsFromSessionStore:newCredentials];
+ }
+ }
+ return NO;
+}
+
+- (BOOL)applyCredentials:(NSDictionary *)newCredentials
+{
+ [self setAuthenticationRetryCount:[self authenticationRetryCount]+1];
+
+ if (newCredentials && requestAuthentication && request) {
+ // Apply whatever credentials we've built up to the old request
+ if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
+
+ //If we have credentials and they're ok, let's save them to the keychain
+ if (useKeychainPersistence) {
+ [self saveCredentialsToKeychain:newCredentials];
+ }
+ if (useSessionPersistence) {
+
+ NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary];
+ [sessionCredentials setObject:(id)requestAuthentication forKey:@"Authentication"];
+ [sessionCredentials setObject:newCredentials forKey:@"Credentials"];
+ [sessionCredentials setObject:[self url] forKey:@"URL"];
+ [sessionCredentials setObject:[self authenticationScheme] forKey:@"AuthenticationScheme"];
+ if ([self authenticationRealm]) {
+ [sessionCredentials setObject:[self authenticationRealm] forKey:@"AuthenticationRealm"];
+ }
+ [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials];
+
+ }
+ [self setRequestCredentials:newCredentials];
+ return YES;
+ } else {
+ [[self class] removeAuthenticationCredentialsFromSessionStore:newCredentials];
+ }
+ }
+ return NO;
+}
+
+- (NSMutableDictionary *)findProxyCredentials
+{
+ NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease];
+
+ NSString *user = nil;
+ NSString *pass = nil;
+
+ ASIHTTPRequest *theRequest = [self mainRequest];
+ // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request
+ if ([theRequest proxyUsername] && [theRequest proxyPassword]) {
+ user = [theRequest proxyUsername];
+ pass = [theRequest proxyPassword];
+
+ // Let's try to use the ones set in this object
+ } else if ([self proxyUsername] && [self proxyPassword]) {
+ user = [self proxyUsername];
+ pass = [self proxyPassword];
+ }
+
+ // When we connect to a website using NTLM via a proxy, we will use the main credentials
+ if ((!user || !pass) && [self proxyAuthenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM) {
+ user = [self username];
+ pass = [self password];
+ }
+
+
+
+ // Ok, that didn't work, let's try the keychain
+ // For authenticating proxies, we'll look in the keychain regardless of the value of useKeychainPersistence
+ if ((!user || !pass)) {
+ NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForProxy:[self proxyHost] port:[self proxyPort] protocol:[[self url] scheme] realm:[self proxyAuthenticationRealm]];
+ if (authenticationCredentials) {
+ user = [authenticationCredentials user];
+ pass = [authenticationCredentials password];
+ }
+
+ }
+
+ // Handle NTLM, which requires a domain to be set too
+ if (CFHTTPAuthenticationRequiresAccountDomain(proxyAuthentication)) {
+
+ NSString *ntlmDomain = [self proxyDomain];
+
+ // If we have no domain yet
+ if (!ntlmDomain || [ntlmDomain length] == 0) {
+
+ // Let's try to extract it from the username
+ NSArray* ntlmComponents = [user componentsSeparatedByString:@"\\"];
+ if ([ntlmComponents count] == 2) {
+ ntlmDomain = [ntlmComponents objectAtIndex:0];
+ user = [ntlmComponents objectAtIndex:1];
+
+ // If we are connecting to a website using NTLM, but we are connecting via a proxy, the string we need may be in the domain property
+ } else {
+ ntlmDomain = [self domain];
+ }
+ if (!ntlmDomain) {
+ ntlmDomain = @"";
+ }
+ }
+ [newCredentials setObject:ntlmDomain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain];
+ }
+
+
+ // If we have a username and password, let's apply them to the request and continue
+ if (user && pass) {
+ [newCredentials setObject:user forKey:(NSString *)kCFHTTPAuthenticationUsername];
+ [newCredentials setObject:pass forKey:(NSString *)kCFHTTPAuthenticationPassword];
+ return newCredentials;
+ }
+ return nil;
+}
+
+
+- (NSMutableDictionary *)findCredentials
+{
+ NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease];
+
+ // First, let's look at the url to see if the username and password were included
+ NSString *user = [[self url] user];
+ NSString *pass = [[self url] password];
+
+ if (user && pass) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will use credentials set on its url",self);
+ #endif
+
+ } else {
+
+ // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request
+ if ([self mainRequest] && [[self mainRequest] username] && [[self mainRequest] password]) {
+ user = [[self mainRequest] username];
+ pass = [[self mainRequest] password];
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will use credentials from its parent request",self);
+ #endif
+
+ // Let's try to use the ones set in this object
+ } else if ([self username] && [self password]) {
+ user = [self username];
+ pass = [self password];
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will use username and password properties as credentials",self);
+ #endif
+ }
+ }
+
+ // Ok, that didn't work, let's try the keychain
+ if ((!user || !pass) && useKeychainPersistence) {
+ NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[[self url] host] port:[[[self url] port] intValue] protocol:[[self url] scheme] realm:[self authenticationRealm]];
+ if (authenticationCredentials) {
+ user = [authenticationCredentials user];
+ pass = [authenticationCredentials password];
+ #if DEBUG_HTTP_AUTHENTICATION
+ if (user && pass) {
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will use credentials from the keychain",self);
+ }
+ #endif
+ }
+ }
+
+ // Handle NTLM, which requires a domain to be set too
+ if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) {
+
+ NSString *ntlmDomain = [self domain];
+
+ // If we have no domain yet, let's try to extract it from the username
+ if (!ntlmDomain || [ntlmDomain length] == 0) {
+ ntlmDomain = @"";
+ NSArray* ntlmComponents = [user componentsSeparatedByString:@"\\"];
+ if ([ntlmComponents count] == 2) {
+ ntlmDomain = [ntlmComponents objectAtIndex:0];
+ user = [ntlmComponents objectAtIndex:1];
+ }
+ }
+ [newCredentials setObject:ntlmDomain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain];
+ }
+
+ // If we have a username and password, let's apply them to the request and continue
+ if (user && pass) {
+ [newCredentials setObject:user forKey:(NSString *)kCFHTTPAuthenticationUsername];
+ [newCredentials setObject:pass forKey:(NSString *)kCFHTTPAuthenticationPassword];
+ return newCredentials;
+ }
+ return nil;
+}
+
+// Called by delegate or authentication dialog to resume loading once authentication info has been populated
+- (void)retryUsingSuppliedCredentials
+{
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ received credentials from its delegate or an ASIAuthenticationDialog, will retry",self);
+ #endif
+ //If the url was changed by the delegate, our CFHTTPMessageRef will be NULL and we'll go back to the start
+ if (!request) {
+ [self performSelector:@selector(main) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO];
+ return;
+ }
+ [self performSelector:@selector(attemptToApplyCredentialsAndResume) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO];
+}
+
+// Called by delegate or authentication dialog to cancel authentication
+- (void)cancelAuthentication
+{
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ had authentication cancelled by its delegate or an ASIAuthenticationDialog",self);
+ #endif
+ [self performSelector:@selector(failAuthentication) onThread:[[self class] threadForRequest:self] withObject:nil waitUntilDone:NO];
+}
+
+- (void)failAuthentication
+{
+ [self failWithError:ASIAuthenticationError];
+}
+
+- (BOOL)showProxyAuthenticationDialog
+{
+ if ([self isSynchronous]) {
+ return NO;
+ }
+
+ // Mac authentication dialog coming soon!
+ #if TARGET_OS_IPHONE
+ if ([self shouldPresentProxyAuthenticationDialog]) {
+ [ASIAuthenticationDialog performSelectorOnMainThread:@selector(presentAuthenticationDialogForRequest:) withObject:self waitUntilDone:[NSThread isMainThread]];
+ return YES;
+ }
+ return NO;
+ #else
+ return NO;
+ #endif
+}
+
+
+- (BOOL)willAskDelegateForProxyCredentials
+{
+ if ([self isSynchronous]) {
+ return NO;
+ }
+
+ // If we have a delegate, we'll see if it can handle proxyAuthenticationNeededForRequest:.
+ // Otherwise, we'll try the queue (if this request is part of one) and it will pass the message on to its own delegate
+ id authenticationDelegate = [self delegate];
+ if (!authenticationDelegate) {
+ authenticationDelegate = [self queue];
+ }
+
+ BOOL delegateOrBlockWillHandleAuthentication = NO;
+
+ if ([authenticationDelegate respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
+ delegateOrBlockWillHandleAuthentication = YES;
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if(proxyAuthenticationNeededBlock){
+ delegateOrBlockWillHandleAuthentication = YES;
+ }
+ #endif
+
+ if (delegateOrBlockWillHandleAuthentication) {
+ [self performSelectorOnMainThread:@selector(askDelegateForProxyCredentials) withObject:nil waitUntilDone:NO];
+ }
+
+ return delegateOrBlockWillHandleAuthentication;
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)askDelegateForProxyCredentials
+{
+ id authenticationDelegate = [self delegate];
+ if (!authenticationDelegate) {
+ authenticationDelegate = [self queue];
+ }
+ if ([authenticationDelegate respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
+ [authenticationDelegate performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:self];
+ return;
+ }
+ #if NS_BLOCKS_AVAILABLE
+ if(proxyAuthenticationNeededBlock){
+ proxyAuthenticationNeededBlock();
+ }
+ #endif
+}
+
+
+- (BOOL)willAskDelegateForCredentials
+{
+ if ([self isSynchronous]) {
+ return NO;
+ }
+
+ // If we have a delegate, we'll see if it can handle proxyAuthenticationNeededForRequest:.
+ // Otherwise, we'll try the queue (if this request is part of one) and it will pass the message on to its own delegate
+ id authenticationDelegate = [self delegate];
+ if (!authenticationDelegate) {
+ authenticationDelegate = [self queue];
+ }
+
+ BOOL delegateOrBlockWillHandleAuthentication = NO;
+
+ if ([authenticationDelegate respondsToSelector:@selector(authenticationNeededForRequest:)]) {
+ delegateOrBlockWillHandleAuthentication = YES;
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if (authenticationNeededBlock) {
+ delegateOrBlockWillHandleAuthentication = YES;
+ }
+ #endif
+
+ if (delegateOrBlockWillHandleAuthentication) {
+ [self performSelectorOnMainThread:@selector(askDelegateForCredentials) withObject:nil waitUntilDone:NO];
+ }
+ return delegateOrBlockWillHandleAuthentication;
+}
+
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)askDelegateForCredentials
+{
+ id authenticationDelegate = [self delegate];
+ if (!authenticationDelegate) {
+ authenticationDelegate = [self queue];
+ }
+
+ if ([authenticationDelegate respondsToSelector:@selector(authenticationNeededForRequest:)]) {
+ [authenticationDelegate performSelector:@selector(authenticationNeededForRequest:) withObject:self];
+ return;
+ }
+
+ #if NS_BLOCKS_AVAILABLE
+ if (authenticationNeededBlock) {
+ authenticationNeededBlock();
+ }
+ #endif
+}
+
+- (void)attemptToApplyProxyCredentialsAndResume
+{
+
+ if ([self error] || [self isCancelled]) {
+ return;
+ }
+
+ // Read authentication data
+ if (!proxyAuthentication) {
+ CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty((CFReadStreamRef)[self readStream],kCFStreamPropertyHTTPResponseHeader);
+ proxyAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
+ CFRelease(responseHeader);
+ [self setProxyAuthenticationScheme:[NSMakeCollectable(CFHTTPAuthenticationCopyMethod(proxyAuthentication)) autorelease]];
+ }
+
+ // If we haven't got a CFHTTPAuthenticationRef by now, something is badly wrong, so we'll have to give up
+ if (!proxyAuthentication) {
+ [self cancelLoad];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]];
+ return;
+ }
+
+ // Get the authentication realm
+ [self setProxyAuthenticationRealm:nil];
+ if (!CFHTTPAuthenticationRequiresAccountDomain(proxyAuthentication)) {
+ [self setProxyAuthenticationRealm:[NSMakeCollectable(CFHTTPAuthenticationCopyRealm(proxyAuthentication)) autorelease]];
+ }
+
+ // See if authentication is valid
+ CFStreamError err;
+ if (!CFHTTPAuthenticationIsValid(proxyAuthentication, &err)) {
+
+ CFRelease(proxyAuthentication);
+ proxyAuthentication = NULL;
+
+ // check for bad credentials, so we can give the delegate a chance to replace them
+ if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) {
+
+ // Prevent more than one request from asking for credentials at once
+ [delegateAuthenticationLock lock];
+
+ // We know the credentials we just presented are bad, we should remove them from the session store too
+ [[self class] removeProxyAuthenticationCredentialsFromSessionStore:proxyCredentials];
+ [self setProxyCredentials:nil];
+
+
+ // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us
+ if ([self error] || [self isCancelled]) {
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+
+
+ // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
+ if ([self useSessionPersistence]) {
+ NSDictionary *credentials = [self findSessionProxyAuthenticationCredentials];
+ if (credentials && [self applyProxyCredentials:[credentials objectForKey:@"Credentials"]]) {
+ [delegateAuthenticationLock unlock];
+ [self startRequest];
+ return;
+ }
+ }
+
+ [self setLastActivityTime:nil];
+
+ if ([self willAskDelegateForProxyCredentials]) {
+ [self attemptToApplyProxyCredentialsAndResume];
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+ if ([self showProxyAuthenticationDialog]) {
+ [self attemptToApplyProxyCredentialsAndResume];
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+ [delegateAuthenticationLock unlock];
+ }
+ [self cancelLoad];
+ [self failWithError:ASIAuthenticationError];
+ return;
+ }
+
+ [self cancelLoad];
+
+ if (proxyCredentials) {
+
+ // We use startRequest rather than starting all over again in load request because NTLM requires we reuse the request
+ if ((([self proxyAuthenticationScheme] != (NSString *)kCFHTTPAuthenticationSchemeNTLM) || [self proxyAuthenticationRetryCount] < 2) && [self applyProxyCredentials:proxyCredentials]) {
+ [self startRequest];
+
+ // We've failed NTLM authentication twice, we should assume our credentials are wrong
+ } else if ([self proxyAuthenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM && [self proxyAuthenticationRetryCount] == 2) {
+ [self failWithError:ASIAuthenticationError];
+
+ // Something went wrong, we'll have to give up
+ } else {
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply proxy credentials to request",NSLocalizedDescriptionKey,nil]]];
+ }
+
+ // Are a user name & password needed?
+ } else if (CFHTTPAuthenticationRequiresUserNameAndPassword(proxyAuthentication)) {
+
+ // Prevent more than one request from asking for credentials at once
+ [delegateAuthenticationLock lock];
+
+ // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us
+ if ([self error] || [self isCancelled]) {
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+
+ // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
+ if ([self useSessionPersistence]) {
+ NSDictionary *credentials = [self findSessionProxyAuthenticationCredentials];
+ if (credentials && [self applyProxyCredentials:[credentials objectForKey:@"Credentials"]]) {
+ [delegateAuthenticationLock unlock];
+ [self startRequest];
+ return;
+ }
+ }
+
+ NSMutableDictionary *newCredentials = [self findProxyCredentials];
+
+ //If we have some credentials to use let's apply them to the request and continue
+ if (newCredentials) {
+
+ if ([self applyProxyCredentials:newCredentials]) {
+ [delegateAuthenticationLock unlock];
+ [self startRequest];
+ } else {
+ [delegateAuthenticationLock unlock];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply proxy credentials to request",NSLocalizedDescriptionKey,nil]]];
+ }
+
+ return;
+ }
+
+ if ([self willAskDelegateForProxyCredentials]) {
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+
+ if ([self showProxyAuthenticationDialog]) {
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+ [delegateAuthenticationLock unlock];
+
+ // The delegate isn't interested and we aren't showing the authentication dialog, we'll have to give up
+ [self failWithError:ASIAuthenticationError];
+ return;
+ }
+
+}
+
+- (BOOL)showAuthenticationDialog
+{
+ if ([self isSynchronous]) {
+ return NO;
+ }
+ // Mac authentication dialog coming soon!
+ #if TARGET_OS_IPHONE
+ if ([self shouldPresentAuthenticationDialog]) {
+ [ASIAuthenticationDialog performSelectorOnMainThread:@selector(presentAuthenticationDialogForRequest:) withObject:self waitUntilDone:[NSThread isMainThread]];
+ return YES;
+ }
+ return NO;
+ #else
+ return NO;
+ #endif
+}
+
+- (void)attemptToApplyCredentialsAndResume
+{
+ if ([self error] || [self isCancelled]) {
+ return;
+ }
+
+ // Do we actually need to authenticate with a proxy?
+ if ([self authenticationNeeded] == ASIProxyAuthenticationNeeded) {
+ [self attemptToApplyProxyCredentialsAndResume];
+ return;
+ }
+
+ // Read authentication data
+ if (!requestAuthentication) {
+ CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty((CFReadStreamRef)[self readStream],kCFStreamPropertyHTTPResponseHeader);
+ requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
+ CFRelease(responseHeader);
+ [self setAuthenticationScheme:[NSMakeCollectable(CFHTTPAuthenticationCopyMethod(requestAuthentication)) autorelease]];
+ }
+
+ if (!requestAuthentication) {
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ failed to read authentication information from response headers",self);
+ #endif
+
+ [self cancelLoad];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]];
+ return;
+ }
+
+ // Get the authentication realm
+ [self setAuthenticationRealm:nil];
+ if (!CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) {
+ [self setAuthenticationRealm:[NSMakeCollectable(CFHTTPAuthenticationCopyRealm(requestAuthentication)) autorelease]];
+ }
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ NSString *realm = [self authenticationRealm];
+ if (realm) {
+ realm = [NSString stringWithFormat:@" (Realm: %@)",realm];
+ } else {
+ realm = @"";
+ }
+ if ([self authenticationScheme] != (NSString *)kCFHTTPAuthenticationSchemeNTLM || [self authenticationRetryCount] == 0) {
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ received 401 challenge and must authenticate using %@%@",self,[self authenticationScheme],realm);
+ } else {
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ NTLM handshake step %i",self,[self authenticationRetryCount]+1);
+ }
+ #endif
+
+ // See if authentication is valid
+ CFStreamError err;
+ if (!CFHTTPAuthenticationIsValid(requestAuthentication, &err)) {
+
+ CFRelease(requestAuthentication);
+ requestAuthentication = NULL;
+
+ // check for bad credentials, so we can give the delegate a chance to replace them
+ if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ had bad credentials, will remove them from the session store if they are cached",self);
+ #endif
+
+ // Prevent more than one request from asking for credentials at once
+ [delegateAuthenticationLock lock];
+
+ // We know the credentials we just presented are bad, we should remove them from the session store too
+ [[self class] removeAuthenticationCredentialsFromSessionStore:requestCredentials];
+ [self setRequestCredentials:nil];
+
+ // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us
+ if ([self error] || [self isCancelled]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ failed or was cancelled while waiting to access credentials",self);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+
+ // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
+ if ([self useSessionPersistence]) {
+ NSDictionary *credentials = [self findSessionAuthenticationCredentials];
+ if (credentials && [self applyCredentials:[credentials objectForKey:@"Credentials"]]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will reuse cached credentials from the session (%@)",self,[credentials objectForKey:@"AuthenticationScheme"]);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ [self startRequest];
+ return;
+ }
+ }
+
+ [self setLastActivityTime:nil];
+
+ if ([self willAskDelegateForCredentials]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask its delegate for credentials to use",self);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+ if ([self showAuthenticationDialog]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask ASIAuthenticationDialog for credentials",self);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+ [delegateAuthenticationLock unlock];
+ }
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ has no credentials to present and must give up",self);
+ #endif
+
+ [self cancelLoad];
+ [self failWithError:ASIAuthenticationError];
+ return;
+ }
+
+ [self cancelLoad];
+
+ if (requestCredentials) {
+
+ if ((([self authenticationScheme] != (NSString *)kCFHTTPAuthenticationSchemeNTLM) || [self authenticationRetryCount] < 2) && [self applyCredentials:requestCredentials]) {
+ [self startRequest];
+
+ // We've failed NTLM authentication twice, we should assume our credentials are wrong
+ } else if ([self authenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM && [self authenticationRetryCount ] == 2) {
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ has failed NTLM authentication",self);
+ #endif
+
+ [self failWithError:ASIAuthenticationError];
+
+ } else {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ had credentials and they were not marked as bad, but we got a 401 all the same.",self);
+ #endif
+
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]];
+ }
+
+ // Are a user name & password needed?
+ } else if (CFHTTPAuthenticationRequiresUserNameAndPassword(requestAuthentication)) {
+
+ // Prevent more than one request from asking for credentials at once
+ [delegateAuthenticationLock lock];
+
+ // If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us
+ if ([self error] || [self isCancelled]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ failed or was cancelled while waiting to access credentials",self);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+
+ // Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
+ if ([self useSessionPersistence]) {
+ NSDictionary *credentials = [self findSessionAuthenticationCredentials];
+ if (credentials && [self applyCredentials:[credentials objectForKey:@"Credentials"]]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will reuse cached credentials from the session (%@)",self,[credentials objectForKey:@"AuthenticationScheme"]);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ [self startRequest];
+ return;
+ }
+ }
+
+
+ NSMutableDictionary *newCredentials = [self findCredentials];
+
+ //If we have some credentials to use let's apply them to the request and continue
+ if (newCredentials) {
+
+ if ([self applyCredentials:newCredentials]) {
+ [delegateAuthenticationLock unlock];
+ [self startRequest];
+ } else {
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ failed to apply credentials",self);
+ #endif
+ [delegateAuthenticationLock unlock];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]];
+ }
+ return;
+ }
+ if ([self willAskDelegateForCredentials]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask its delegate for credentials to use",self);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+ if ([self showAuthenticationDialog]) {
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ will ask ASIAuthenticationDialog for credentials",self);
+ #endif
+
+ [delegateAuthenticationLock unlock];
+ return;
+ }
+
+ #if DEBUG_HTTP_AUTHENTICATION
+ ASI_DEBUG_LOG(@"[AUTH] Request %@ has no credentials to present and must give up",self);
+ #endif
+ [delegateAuthenticationLock unlock];
+ [self failWithError:ASIAuthenticationError];
+ return;
+ }
+
+}
+
+- (void)addBasicAuthenticationHeaderWithUsername:(NSString *)theUsername andPassword:(NSString *)thePassword
+{
+ [self addRequestHeader:@"Authorization" value:[NSString stringWithFormat:@"Basic %@",[ASIHTTPRequest base64forData:[[NSString stringWithFormat:@"%@:%@",theUsername,thePassword] dataUsingEncoding:NSUTF8StringEncoding]]]];
+ [self setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic];
+
+}
+
+
+#pragma mark stream status handlers
+
+- (void)handleNetworkEvent:(CFStreamEventType)type
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ [[self cancelledLock] lock];
+
+ if ([self complete] || [self isCancelled]) {
+ [[self cancelledLock] unlock];
+ [pool drain];
+ return;
+ }
+
+ CFRetain(self);
+
+ // Dispatch the stream events.
+ switch (type) {
+ case kCFStreamEventHasBytesAvailable:
+ [self handleBytesAvailable];
+ break;
+
+ case kCFStreamEventEndEncountered:
+ [self handleStreamComplete];
+ break;
+
+ case kCFStreamEventErrorOccurred:
+ [self handleStreamError];
+ break;
+
+ default:
+ break;
+ }
+
+ [self performThrottling];
+
+ [[self cancelledLock] unlock];
+
+ if ([self downloadComplete] && [self needsRedirect]) {
+ if (![self willAskDelegateToConfirmRedirect]) {
+ [self performRedirect];
+ }
+ } else if ([self downloadComplete] && [self authenticationNeeded]) {
+ [self attemptToApplyCredentialsAndResume];
+ }
+
+ CFRelease(self);
+ [pool drain];
+}
+
+- (BOOL)willAskDelegateToConfirmRedirect
+{
+ // We must lock to ensure delegate / queue aren't changed while we check them
+ [[self cancelledLock] lock];
+
+ // Here we perform an initial check to see if either the delegate or the queue wants to be asked about the redirect, because if not we should redirect straight away
+ // We will check again on the main thread later
+ BOOL needToAskDelegateAboutRedirect = (([self delegate] && [[self delegate] respondsToSelector:[self willRedirectSelector]]) || ([self queue] && [[self queue] respondsToSelector:@selector(request:willRedirectToURL:)]));
+
+ [[self cancelledLock] unlock];
+
+ // Either the delegate or the queue's delegate is interested in being told when we are about to redirect
+ if (needToAskDelegateAboutRedirect) {
+ NSURL *newURL = [[[self redirectURL] copy] autorelease];
+ [self setRedirectURL:nil];
+ [self performSelectorOnMainThread:@selector(requestWillRedirectToURL:) withObject:newURL waitUntilDone:[NSThread isMainThread]];
+ return true;
+ }
+ return false;
+}
+
+- (void)handleBytesAvailable
+{
+ if (![self responseHeaders]) {
+ [self readResponseHeaders];
+ }
+
+ // If we've cancelled the load part way through (for example, after deciding to use a cached version)
+ if ([self complete]) {
+ return;
+ }
+
+ // In certain (presumably very rare) circumstances, handleBytesAvailable seems to be called when there isn't actually any data available
+ // We'll check that there is actually data available to prevent blocking on CFReadStreamRead()
+ // So far, I've only seen this in the stress tests, so it might never happen in real-world situations.
+ if (!CFReadStreamHasBytesAvailable((CFReadStreamRef)[self readStream])) {
+ return;
+ }
+
+ long long bufferSize = 16384;
+ if (contentLength > 262144) {
+ bufferSize = 262144;
+ } else if (contentLength > 65536) {
+ bufferSize = 65536;
+ }
+
+ // Reduce the buffer size if we're receiving data too quickly when bandwidth throttling is active
+ // This just augments the throttling done in measureBandwidthUsage to reduce the amount we go over the limit
+
+ if ([[self class] isBandwidthThrottled]) {
+ [bandwidthThrottlingLock lock];
+ if (maxBandwidthPerSecond > 0) {
+ long long maxiumumSize = (long long)maxBandwidthPerSecond-(long long)bandwidthUsedInLastSecond;
+ if (maxiumumSize < 0) {
+ // We aren't supposed to read any more data right now, but we'll read a single byte anyway so the CFNetwork's buffer isn't full
+ bufferSize = 1;
+ } else if (maxiumumSize/4 < bufferSize) {
+ // We were going to fetch more data that we should be allowed, so we'll reduce the size of our read
+ bufferSize = maxiumumSize/4;
+ }
+ }
+ if (bufferSize < 1) {
+ bufferSize = 1;
+ }
+ [bandwidthThrottlingLock unlock];
+ }
+
+
+ UInt8 buffer[bufferSize];
+ NSInteger bytesRead = [[self readStream] read:buffer maxLength:sizeof(buffer)];
+
+ // Less than zero is an error
+ if (bytesRead < 0) {
+ [self handleStreamError];
+
+ // If zero bytes were read, wait for the EOF to come.
+ } else if (bytesRead) {
+
+ // If we are inflating the response on the fly
+ NSData *inflatedData = nil;
+ if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) {
+ if (![self dataDecompressor]) {
+ [self setDataDecompressor:[ASIDataDecompressor decompressor]];
+ }
+ NSError *err = nil;
+ inflatedData = [[self dataDecompressor] uncompressBytes:buffer length:bytesRead error:&err];
+ if (err) {
+ [self failWithError:err];
+ return;
+ }
+ }
+
+ [self setTotalBytesRead:[self totalBytesRead]+bytesRead];
+ [self setLastActivityTime:[NSDate date]];
+
+ // For bandwidth measurement / throttling
+ [ASIHTTPRequest incrementBandwidthUsedInLastSecond:bytesRead];
+
+ // If we need to redirect, and have automatic redirect on, and might be resuming a download, let's do nothing with the content
+ if ([self needsRedirect] && [self shouldRedirect] && [self allowResumeForFileDownloads]) {
+ return;
+ }
+
+ BOOL dataWillBeHandledExternally = NO;
+ if ([[self delegate] respondsToSelector:[self didReceiveDataSelector]]) {
+ dataWillBeHandledExternally = YES;
+ }
+ #if NS_BLOCKS_AVAILABLE
+ if (dataReceivedBlock) {
+ dataWillBeHandledExternally = YES;
+ }
+ #endif
+ // Does the delegate want to handle the data manually?
+ if (dataWillBeHandledExternally) {
+
+ NSData *data = nil;
+ if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) {
+ data = inflatedData;
+ } else {
+ data = [NSData dataWithBytes:buffer length:bytesRead];
+ }
+ [self performSelectorOnMainThread:@selector(passOnReceivedData:) withObject:data waitUntilDone:[NSThread isMainThread]];
+
+ // Are we downloading to a file?
+ } else if ([self downloadDestinationPath]) {
+ BOOL append = NO;
+ if (![self fileDownloadOutputStream]) {
+ if (![self temporaryFileDownloadPath]) {
+ [self setTemporaryFileDownloadPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]];
+ } else if ([self allowResumeForFileDownloads] && [[self requestHeaders] objectForKey:@"Range"]) {
+ if ([[self responseHeaders] objectForKey:@"Content-Range"]) {
+ append = YES;
+ } else {
+ [self incrementDownloadSizeBy:-[self partialDownloadSize]];
+ [self setPartialDownloadSize:0];
+ }
+ }
+
+ [self setFileDownloadOutputStream:[[[NSOutputStream alloc] initToFileAtPath:[self temporaryFileDownloadPath] append:append] autorelease]];
+ [[self fileDownloadOutputStream] open];
+
+ }
+ [[self fileDownloadOutputStream] write:buffer maxLength:bytesRead];
+
+ if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) {
+
+ if (![self inflatedFileDownloadOutputStream]) {
+ if (![self temporaryUncompressedDataDownloadPath]) {
+ [self setTemporaryUncompressedDataDownloadPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]];
+ }
+
+ [self setInflatedFileDownloadOutputStream:[[[NSOutputStream alloc] initToFileAtPath:[self temporaryUncompressedDataDownloadPath] append:append] autorelease]];
+ [[self inflatedFileDownloadOutputStream] open];
+ }
+
+ [[self inflatedFileDownloadOutputStream] write:[inflatedData bytes] maxLength:[inflatedData length]];
+ }
+
+
+ //Otherwise, let's add the data to our in-memory store
+ } else {
+ if ([self isResponseCompressed] && ![self shouldWaitToInflateCompressedResponses]) {
+ [rawResponseData appendData:inflatedData];
+ } else {
+ [rawResponseData appendBytes:buffer length:bytesRead];
+ }
+ }
+ }
+}
+
+- (void)handleStreamComplete
+{
+
+#if DEBUG_REQUEST_STATUS
+ ASI_DEBUG_LOG(@"[STATUS] Request %@ finished downloading data (%qu bytes)",self, [self totalBytesRead]);
+#endif
+ [self setStatusTimer:nil];
+ [self setDownloadComplete:YES];
+
+ if (![self responseHeaders]) {
+ [self readResponseHeaders];
+ }
+
+ [progressLock lock];
+ // Find out how much data we've uploaded so far
+ [self setLastBytesSent:totalBytesSent];
+ [self setTotalBytesSent:[[NSMakeCollectable(CFReadStreamCopyProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPRequestBytesWrittenCount)) autorelease] unsignedLongLongValue]];
+ [self setComplete:YES];
+ if (![self contentLength]) {
+ [self setContentLength:[self totalBytesRead]];
+ }
+ [self updateProgressIndicators];
+
+
+ [[self postBodyReadStream] close];
+ [self setPostBodyReadStream:nil];
+
+ [self setDataDecompressor:nil];
+
+ NSError *fileError = nil;
+
+ // Delete up the request body temporary file, if it exists
+ if ([self didCreateTemporaryPostDataFile] && ![self authenticationNeeded]) {
+ [self removeTemporaryUploadFile];
+ [self removeTemporaryCompressedUploadFile];
+ }
+
+ // Close the output stream as we're done writing to the file
+ if ([self temporaryFileDownloadPath]) {
+
+ [[self fileDownloadOutputStream] close];
+ [self setFileDownloadOutputStream:nil];
+
+ [[self inflatedFileDownloadOutputStream] close];
+ [self setInflatedFileDownloadOutputStream:nil];
+
+ // If we are going to redirect and we are resuming, let's ignore this download
+ if ([self shouldRedirect] && [self needsRedirect] && [self allowResumeForFileDownloads]) {
+
+ } else if ([self isResponseCompressed]) {
+
+ // Decompress the file directly to the destination path
+ if ([self shouldWaitToInflateCompressedResponses]) {
+ [ASIDataDecompressor uncompressDataFromFile:[self temporaryFileDownloadPath] toFile:[self downloadDestinationPath] error:&fileError];
+
+ // Response should already have been inflated, move the temporary file to the destination path
+ } else {
+ NSError *moveError = nil;
+ [[[[NSFileManager alloc] init] autorelease] moveItemAtPath:[self temporaryUncompressedDataDownloadPath] toPath:[self downloadDestinationPath] error:&moveError];
+ if (moveError) {
+ fileError = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to move file from '%@' to '%@'",[self temporaryFileDownloadPath],[self downloadDestinationPath]],NSLocalizedDescriptionKey,moveError,NSUnderlyingErrorKey,nil]];
+ }
+ [self setTemporaryUncompressedDataDownloadPath:nil];
+
+ }
+ [self removeTemporaryDownloadFile];
+
+ } else {
+
+ //Remove any file at the destination path
+ NSError *moveError = nil;
+ if (![[self class] removeFileAtPath:[self downloadDestinationPath] error:&moveError]) {
+ fileError = moveError;
+
+ }
+
+ //Move the temporary file to the destination path
+ if (!fileError) {
+ [[[[NSFileManager alloc] init] autorelease] moveItemAtPath:[self temporaryFileDownloadPath] toPath:[self downloadDestinationPath] error:&moveError];
+ if (moveError) {
+ fileError = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to move file from '%@' to '%@'",[self temporaryFileDownloadPath],[self downloadDestinationPath]],NSLocalizedDescriptionKey,moveError,NSUnderlyingErrorKey,nil]];
+ }
+ [self setTemporaryFileDownloadPath:nil];
+ }
+
+ }
+ }
+
+ // Save to the cache
+ if ([self downloadCache] && ![self didUseCachedResponse]) {
+ [[self downloadCache] storeResponseForRequest:self maxAge:[self secondsToCache]];
+ }
+
+ [progressLock unlock];
+
+
+ [connectionsLock lock];
+ if (![self connectionCanBeReused]) {
+ [self unscheduleReadStream];
+ }
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ if ([self requestID]) {
+ ASI_DEBUG_LOG(@"[CONNECTION] Request #%@ finished using connection #%@",[self requestID], [[self connectionInfo] objectForKey:@"id"]);
+ }
+ #endif
+ [[self connectionInfo] removeObjectForKey:@"request"];
+ [[self connectionInfo] setObject:[NSDate dateWithTimeIntervalSinceNow:[self persistentConnectionTimeoutSeconds]] forKey:@"expires"];
+ [connectionsLock unlock];
+
+ if (![self authenticationNeeded]) {
+ [self destroyReadStream];
+ }
+
+
+ if (![self needsRedirect] && ![self authenticationNeeded] && ![self didUseCachedResponse]) {
+
+ if (fileError) {
+ [self failWithError:fileError];
+ } else {
+ [self requestFinished];
+ }
+
+ [self markAsFinished];
+
+ // If request has asked delegate or ASIAuthenticationDialog for credentials
+ } else if ([self authenticationNeeded]) {
+ // Do nothing.
+ }
+
+}
+
+- (void)markAsFinished
+{
+ // Autoreleased requests may well be dealloced here otherwise
+ CFRetain(self);
+
+ // dealloc won't be called when running with GC, so we'll clean these up now
+ if (request) {
+ CFRelease(request);
+ request = nil;
+ }
+ if (requestAuthentication) {
+ CFRelease(requestAuthentication);
+ requestAuthentication = nil;
+ }
+ if (proxyAuthentication) {
+ CFRelease(proxyAuthentication);
+ proxyAuthentication = nil;
+ }
+
+ BOOL wasInProgress = inProgress;
+ BOOL wasFinished = finished;
+
+ if (!wasFinished)
+ [self willChangeValueForKey:@"isFinished"];
+ if (wasInProgress)
+ [self willChangeValueForKey:@"isExecuting"];
+
+ [self setInProgress:NO];
+ finished = YES;
+
+ if (wasInProgress)
+ [self didChangeValueForKey:@"isExecuting"];
+ if (!wasFinished)
+ [self didChangeValueForKey:@"isFinished"];
+
+ #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
+ if ([ASIHTTPRequest isMultitaskingSupported] && [self shouldContinueWhenAppEntersBackground]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (backgroundTask != UIBackgroundTaskInvalid) {
+ [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
+ backgroundTask = UIBackgroundTaskInvalid;
+ }
+ });
+ }
+ #endif
+ CFRelease(self);
+}
+
+- (void)useDataFromCache
+{
+ NSDictionary *headers = [[self downloadCache] cachedResponseHeadersForURL:[self url]];
+ NSString *dataPath = [[self downloadCache] pathToCachedResponseDataForURL:[self url]];
+
+ ASIHTTPRequest *theRequest = self;
+ if ([self mainRequest]) {
+ theRequest = [self mainRequest];
+ }
+
+ if (headers && dataPath) {
+
+ [self setResponseStatusCode:[[headers objectForKey:@"X-ASIHTTPRequest-Response-Status-Code"] intValue]];
+ [self setDidUseCachedResponse:YES];
+ [theRequest setResponseHeaders:headers];
+
+ if ([theRequest downloadDestinationPath]) {
+ [theRequest setDownloadDestinationPath:dataPath];
+ } else {
+ [theRequest setRawResponseData:[NSMutableData dataWithData:[[self downloadCache] cachedResponseDataForURL:[self url]]]];
+ }
+ [theRequest setContentLength:[[[self responseHeaders] objectForKey:@"Content-Length"] longLongValue]];
+ [theRequest setTotalBytesRead:[self contentLength]];
+
+ [theRequest parseStringEncodingFromHeaders];
+
+ [theRequest setResponseCookies:[NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:[self url]]];
+
+ // See if we need to redirect
+ if ([self willRedirect]) {
+ if (![self willAskDelegateToConfirmRedirect]) {
+ [self performRedirect];
+ }
+ return;
+ }
+ }
+
+ [theRequest setComplete:YES];
+ [theRequest setDownloadComplete:YES];
+
+ // If we're pulling data from the cache without contacting the server at all, we won't have set originalURL yet
+ if ([self redirectCount] == 0) {
+ [theRequest setOriginalURL:[theRequest url]];
+ }
+
+ [theRequest updateProgressIndicators];
+ [theRequest requestFinished];
+ [theRequest markAsFinished];
+ if ([self mainRequest]) {
+ [self markAsFinished];
+ }
+}
+
+- (BOOL)retryUsingNewConnection
+{
+ if ([self retryCount] == 0) {
+
+ [self setWillRetryRequest:YES];
+ [self cancelLoad];
+ [self setWillRetryRequest:NO];
+
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Request attempted to use connection #%@, but it has been closed - will retry with a new connection", [[self connectionInfo] objectForKey:@"id"]);
+ #endif
+ [connectionsLock lock];
+ [[self connectionInfo] removeObjectForKey:@"request"];
+ [persistentConnectionsPool removeObject:[self connectionInfo]];
+ [self setConnectionInfo:nil];
+ [connectionsLock unlock];
+ [self setRetryCount:[self retryCount]+1];
+ [self startRequest];
+ return YES;
+ }
+ #if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Request attempted to use connection #%@, but it has been closed - we have already retried with a new connection, so we must give up", [[self connectionInfo] objectForKey:@"id"]);
+ #endif
+ return NO;
+}
+
+- (void)handleStreamError
+
+{
+ NSError *underlyingError = [NSMakeCollectable(CFReadStreamCopyError((CFReadStreamRef)[self readStream])) autorelease];
+
+ if (![self error]) { // We may already have handled this error
+
+ // First, check for a 'socket not connected', 'broken pipe' or 'connection lost' error
+ // This may occur when we've attempted to reuse a connection that should have been closed
+ // If we get this, we need to retry the request
+ // We'll only do this once - if it happens again on retry, we'll give up
+ // -1005 = kCFURLErrorNetworkConnectionLost - this doesn't seem to be declared on Mac OS 10.5
+ if (([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain] && ([underlyingError code] == ENOTCONN || [underlyingError code] == EPIPE))
+ || ([[underlyingError domain] isEqualToString:(NSString *)kCFErrorDomainCFNetwork] && [underlyingError code] == -1005)) {
+ if ([self retryUsingNewConnection]) {
+ return;
+ }
+ }
+
+ NSString *reason = @"A connection failure occurred";
+
+ // We'll use a custom error message for SSL errors, but you should always check underlying error if you want more details
+ // For some reason SecureTransport.h doesn't seem to be available on iphone, so error codes hard-coded
+ // Also, iPhone seems to handle errors differently from Mac OS X - a self-signed certificate returns a different error code on each platform, so we'll just provide a general error
+ if ([[underlyingError domain] isEqualToString:NSOSStatusErrorDomain]) {
+ if ([underlyingError code] <= -9800 && [underlyingError code] >= -9818) {
+ reason = [NSString stringWithFormat:@"%@: SSL problem (Possible causes may include a bad/expired/self-signed certificate, clock set to wrong date)",reason];
+ }
+ }
+ [self cancelLoad];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIConnectionFailureErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:reason,NSLocalizedDescriptionKey,underlyingError,NSUnderlyingErrorKey,nil]]];
+ } else {
+ [self cancelLoad];
+ }
+ [self checkRequestStatus];
+}
+
+#pragma mark managing the read stream
+
+- (void)destroyReadStream
+{
+ if ([self readStream]) {
+ [self unscheduleReadStream];
+ if (![self connectionCanBeReused]) {
+ [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
+ [[self readStream] close];
+ }
+ [self setReadStream:nil];
+ }
+}
+
+- (void)scheduleReadStream
+{
+ if ([self readStream] && ![self readStreamIsScheduled]) {
+
+ [connectionsLock lock];
+ runningRequestCount++;
+ if (shouldUpdateNetworkActivityIndicator) {
+ [[self class] showNetworkActivityIndicator];
+ }
+ [connectionsLock unlock];
+
+ // Reset the timeout
+ [self setLastActivityTime:[NSDate date]];
+ CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
+ CFReadStreamSetClient((CFReadStreamRef)[self readStream], kNetworkEvents, ReadStreamClientCallBack, &ctxt);
+ [[self readStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
+ [self setReadStreamIsScheduled:YES];
+ }
+}
+
+
+- (void)unscheduleReadStream
+{
+ if ([self readStream] && [self readStreamIsScheduled]) {
+
+ [connectionsLock lock];
+ runningRequestCount--;
+ if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) {
+ // This call will wait half a second before turning off the indicator
+ // This can prevent flicker when you have a single request finish and then immediately start another request
+ // We run this on the main thread because we have no guarantee this thread will have a runloop in 0.5 seconds time
+ // We don't bother the cancel this call if we start a new request, because we'll check if requests are running before we hide it
+ [[self class] performSelectorOnMainThread:@selector(hideNetworkActivityIndicatorAfterDelay) withObject:nil waitUntilDone:[NSThread isMainThread]];
+ }
+ [connectionsLock unlock];
+
+ CFReadStreamSetClient((CFReadStreamRef)[self readStream], kCFStreamEventNone, NULL, NULL);
+ [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
+ [self setReadStreamIsScheduled:NO];
+ }
+}
+
+#pragma mark cleanup
+
+- (BOOL)removeTemporaryDownloadFile
+{
+ NSError *err = nil;
+ if ([self temporaryFileDownloadPath]) {
+ if (![[self class] removeFileAtPath:[self temporaryFileDownloadPath] error:&err]) {
+ [self failWithError:err];
+ }
+ [self setTemporaryFileDownloadPath:nil];
+ }
+ return (!err);
+}
+
+- (BOOL)removeTemporaryUncompressedDownloadFile
+{
+ NSError *err = nil;
+ if ([self temporaryUncompressedDataDownloadPath]) {
+ if (![[self class] removeFileAtPath:[self temporaryUncompressedDataDownloadPath] error:&err]) {
+ [self failWithError:err];
+ }
+ [self setTemporaryUncompressedDataDownloadPath:nil];
+ }
+ return (!err);
+}
+
+- (BOOL)removeTemporaryUploadFile
+{
+ NSError *err = nil;
+ if ([self postBodyFilePath]) {
+ if (![[self class] removeFileAtPath:[self postBodyFilePath] error:&err]) {
+ [self failWithError:err];
+ }
+ [self setPostBodyFilePath:nil];
+ }
+ return (!err);
+}
+
+- (BOOL)removeTemporaryCompressedUploadFile
+{
+ NSError *err = nil;
+ if ([self compressedPostBodyFilePath]) {
+ if (![[self class] removeFileAtPath:[self compressedPostBodyFilePath] error:&err]) {
+ [self failWithError:err];
+ }
+ [self setCompressedPostBodyFilePath:nil];
+ }
+ return (!err);
+}
+
++ (BOOL)removeFileAtPath:(NSString *)path error:(NSError **)err
+{
+ NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
+
+ if ([fileManager fileExistsAtPath:path]) {
+ NSError *removeError = nil;
+ [fileManager removeItemAtPath:path error:&removeError];
+ if (removeError) {
+ if (err) {
+ *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to delete file at path '%@'",path],NSLocalizedDescriptionKey,removeError,NSUnderlyingErrorKey,nil]];
+ }
+ return NO;
+ }
+ }
+ return YES;
+}
+
+#pragma mark Proxies
+
+- (BOOL)configureProxies
+{
+ // Have details of the proxy been set on this request
+ if (![self isPACFileRequest] && (![self proxyHost] && ![self proxyPort])) {
+
+ // If not, we need to figure out what they'll be
+ NSArray *proxies = nil;
+
+ // Have we been given a proxy auto config file?
+ if ([self PACurl]) {
+
+ // If yes, we'll need to fetch the PAC file asynchronously, so we stop this request to wait until we have the proxy details.
+ [self fetchPACFile];
+ return NO;
+
+ // Detect proxy settings and apply them
+ } else {
+
+#if TARGET_OS_IPHONE
+ NSDictionary *proxySettings = [NSMakeCollectable(CFNetworkCopySystemProxySettings()) autorelease];
+#else
+ NSDictionary *proxySettings = [NSMakeCollectable(SCDynamicStoreCopyProxies(NULL)) autorelease];
+#endif
+
+ proxies = [NSMakeCollectable(CFNetworkCopyProxiesForURL((CFURLRef)[self url], (CFDictionaryRef)proxySettings)) autorelease];
+
+ // Now check to see if the proxy settings contained a PAC url, we need to run the script to get the real list of proxies if so
+ NSDictionary *settings = [proxies objectAtIndex:0];
+ if ([settings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]) {
+ [self setPACurl:[settings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]];
+ [self fetchPACFile];
+ return NO;
+ }
+ }
+
+ if (!proxies) {
+ [self setReadStream:nil];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to obtain information on proxy servers needed for request",NSLocalizedDescriptionKey,nil]]];
+ return NO;
+ }
+ // I don't really understand why the dictionary returned by CFNetworkCopyProxiesForURL uses different key names from CFNetworkCopySystemProxySettings/SCDynamicStoreCopyProxies
+ // and why its key names are documented while those we actually need to use don't seem to be (passing the kCF* keys doesn't seem to work)
+ if ([proxies count] > 0) {
+ NSDictionary *settings = [proxies objectAtIndex:0];
+ [self setProxyHost:[settings objectForKey:(NSString *)kCFProxyHostNameKey]];
+ [self setProxyPort:[[settings objectForKey:(NSString *)kCFProxyPortNumberKey] intValue]];
+ [self setProxyType:[settings objectForKey:(NSString *)kCFProxyTypeKey]];
+ }
+ }
+ return YES;
+}
+
+
+
+// Attempts to download a PAC (Proxy Auto-Configuration) file
+// PAC files at file://, http:// and https:// addresses are supported
+- (void)fetchPACFile
+{
+ // For file:// urls, we'll use an async NSInputStream (ASIHTTPRequest does not support file:// urls)
+ if ([[self PACurl] isFileURL]) {
+ NSInputStream *stream = [[[NSInputStream alloc] initWithFileAtPath:[[self PACurl] path]] autorelease];
+ [self setPACFileReadStream:stream];
+ [stream setDelegate:(id)self];
+ [stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
+ [stream open];
+ // If it takes more than timeOutSeconds to read the PAC, we'll just give up and assume no proxies
+ // We won't bother to handle cases where the first part of the PAC is read within timeOutSeconds, but the whole thing takes longer
+ // Either our PAC file is in easy reach, or it's going to slow things down to the point that it's probably better requests fail
+ [self performSelector:@selector(timeOutPACRead) withObject:nil afterDelay:[self timeOutSeconds]];
+ return;
+ }
+
+ NSString *scheme = [[[self PACurl] scheme] lowercaseString];
+ if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
+ // Don't know how to read data from this URL, we'll have to give up
+ // We'll simply assume no proxies, and start the request as normal
+ [self startRequest];
+ return;
+ }
+
+ // Create an ASIHTTPRequest to fetch the PAC file
+ ASIHTTPRequest *PACRequest = [ASIHTTPRequest requestWithURL:[self PACurl]];
+
+ // Will prevent this request attempting to configure proxy settings for itself
+ [PACRequest setIsPACFileRequest:YES];
+
+ [PACRequest setTimeOutSeconds:[self timeOutSeconds]];
+
+ // If we're a synchronous request, we'll download the PAC file synchronously
+ if ([self isSynchronous]) {
+ [PACRequest startSynchronous];
+ if (![PACRequest error] && [PACRequest responseString]) {
+ [self runPACScript:[PACRequest responseString]];
+ }
+ [self startRequest];
+ return;
+ }
+
+ [self setPACFileRequest:PACRequest];
+
+ // Force this request to run before others in the shared queue
+ [PACRequest setQueuePriority:NSOperationQueuePriorityHigh];
+
+ // We'll treat failure to download the PAC file the same as success - if we were unable to fetch a PAC file, we proceed as if we have no proxy server and let this request fail itself if necessary
+ [PACRequest setDelegate:self];
+ [PACRequest setDidFinishSelector:@selector(finishedDownloadingPACFile:)];
+ [PACRequest setDidFailSelector:@selector(finishedDownloadingPACFile:)];
+ [PACRequest startAsynchronous];
+
+ // Temporarily increase the number of operations in the shared queue to give our request a chance to run
+ [connectionsLock lock];
+ [sharedQueue setMaxConcurrentOperationCount:[sharedQueue maxConcurrentOperationCount]+1];
+ [connectionsLock unlock];
+}
+
+// Called as we read the PAC file from a file:// url
+- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
+{
+ if (![self PACFileReadStream]) {
+ return;
+ }
+ if (eventCode == NSStreamEventHasBytesAvailable) {
+
+ if (![self PACFileData]) {
+ [self setPACFileData:[NSMutableData data]];
+ }
+ // If your PAC file is larger than 16KB, you're just being cruel.
+ uint8_t buf[16384];
+ NSInteger len = [(NSInputStream *)stream read:buf maxLength:16384];
+ if (len) {
+ [[self PACFileData] appendBytes:(const void *)buf length:len];
+ }
+
+ } else if (eventCode == NSStreamEventErrorOccurred || eventCode == NSStreamEventEndEncountered) {
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeOutPACRead) object:nil];
+
+ [stream close];
+ [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
+ [self setPACFileReadStream:nil];
+
+ if (eventCode == NSStreamEventEndEncountered) {
+ // It sounds as though we have no idea what encoding a PAC file will use
+ static NSStringEncoding encodingsToTry[2] = {NSUTF8StringEncoding,NSISOLatin1StringEncoding};
+ NSUInteger i;
+ for (i=0; i<2; i++) {
+ NSString *pacScript = [[[NSString alloc] initWithBytes:[[self PACFileData] bytes] length:[[self PACFileData] length] encoding:encodingsToTry[i]] autorelease];
+ if (pacScript) {
+ [self runPACScript:pacScript];
+ break;
+ }
+ }
+ }
+ [self setPACFileData:nil];
+ [self startRequest];
+ }
+}
+
+// Called if it takes longer than timeOutSeconds to read the whole PAC file (when reading from a file:// url)
+- (void)timeOutPACRead
+{
+ [self stream:[self PACFileReadStream] handleEvent:NSStreamEventErrorOccurred];
+}
+
+// Runs the downloaded PAC script
+- (void)runPACScript:(NSString *)script
+{
+ if (script) {
+ // From: http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html
+ // Work around <rdar://problem/5530166>. This dummy call to
+ // CFNetworkCopyProxiesForURL initialise some state within CFNetwork
+ // that is required by CFNetworkCopyProxiesForAutoConfigurationScript.
+ CFRelease(CFNetworkCopyProxiesForURL((CFURLRef)[self url], NULL));
+
+ // Obtain the list of proxies by running the autoconfiguration script
+ CFErrorRef err = NULL;
+ NSArray *proxies = [NSMakeCollectable(CFNetworkCopyProxiesForAutoConfigurationScript((CFStringRef)script,(CFURLRef)[self url], &err)) autorelease];
+ if (!err && [proxies count] > 0) {
+ NSDictionary *settings = [proxies objectAtIndex:0];
+ [self setProxyHost:[settings objectForKey:(NSString *)kCFProxyHostNameKey]];
+ [self setProxyPort:[[settings objectForKey:(NSString *)kCFProxyPortNumberKey] intValue]];
+ [self setProxyType:[settings objectForKey:(NSString *)kCFProxyTypeKey]];
+ }
+ }
+}
+
+// Called if we successfully downloaded a PAC file from a webserver
+- (void)finishedDownloadingPACFile:(ASIHTTPRequest *)theRequest
+{
+ if (![theRequest error] && [theRequest responseString]) {
+ [self runPACScript:[theRequest responseString]];
+ }
+
+ // Set the shared queue's maxConcurrentOperationCount back to normal
+ [connectionsLock lock];
+ [sharedQueue setMaxConcurrentOperationCount:[sharedQueue maxConcurrentOperationCount]-1];
+ [connectionsLock unlock];
+
+ // We no longer need our PAC file request
+ [self setPACFileRequest:nil];
+
+ // Start the request
+ [self startRequest];
+}
+
+
+#pragma mark persistent connections
+
+- (NSNumber *)connectionID
+{
+ return [[self connectionInfo] objectForKey:@"id"];
+}
+
++ (void)expirePersistentConnections
+{
+ [connectionsLock lock];
+ NSUInteger i;
+ for (i=0; i<[persistentConnectionsPool count]; i++) {
+ NSDictionary *existingConnection = [persistentConnectionsPool objectAtIndex:i];
+ if (![existingConnection objectForKey:@"request"] && [[existingConnection objectForKey:@"expires"] timeIntervalSinceNow] <= 0) {
+#if DEBUG_PERSISTENT_CONNECTIONS
+ ASI_DEBUG_LOG(@"[CONNECTION] Closing connection #%i because it has expired",[[existingConnection objectForKey:@"id"] intValue]);
+#endif
+ NSInputStream *stream = [existingConnection objectForKey:@"stream"];
+ if (stream) {
+ [stream close];
+ }
+ [persistentConnectionsPool removeObject:existingConnection];
+ i--;
+ }
+ }
+ [connectionsLock unlock];
+}
+
+#pragma mark NSCopying
+- (id)copyWithZone:(NSZone *)zone
+{
+ // Don't forget - this will return a retained copy!
+ ASIHTTPRequest *newRequest = [[[self class] alloc] initWithURL:[self url]];
+ [newRequest setDelegate:[self delegate]];
+ [newRequest setRequestMethod:[self requestMethod]];
+ [newRequest setPostBody:[self postBody]];
+ [newRequest setShouldStreamPostDataFromDisk:[self shouldStreamPostDataFromDisk]];
+ [newRequest setPostBodyFilePath:[self postBodyFilePath]];
+ [newRequest setRequestHeaders:[[[self requestHeaders] mutableCopyWithZone:zone] autorelease]];
+ [newRequest setRequestCookies:[[[self requestCookies] mutableCopyWithZone:zone] autorelease]];
+ [newRequest setUseCookiePersistence:[self useCookiePersistence]];
+ [newRequest setUseKeychainPersistence:[self useKeychainPersistence]];
+ [newRequest setUseSessionPersistence:[self useSessionPersistence]];
+ [newRequest setAllowCompressedResponse:[self allowCompressedResponse]];
+ [newRequest setDownloadDestinationPath:[self downloadDestinationPath]];
+ [newRequest setTemporaryFileDownloadPath:[self temporaryFileDownloadPath]];
+ [newRequest setUsername:[self username]];
+ [newRequest setPassword:[self password]];
+ [newRequest setDomain:[self domain]];
+ [newRequest setProxyUsername:[self proxyUsername]];
+ [newRequest setProxyPassword:[self proxyPassword]];
+ [newRequest setProxyDomain:[self proxyDomain]];
+ [newRequest setProxyHost:[self proxyHost]];
+ [newRequest setProxyPort:[self proxyPort]];
+ [newRequest setProxyType:[self proxyType]];
+ [newRequest setUploadProgressDelegate:[self uploadProgressDelegate]];
+ [newRequest setDownloadProgressDelegate:[self downloadProgressDelegate]];
+ [newRequest setShouldPresentAuthenticationDialog:[self shouldPresentAuthenticationDialog]];
+ [newRequest setShouldPresentProxyAuthenticationDialog:[self shouldPresentProxyAuthenticationDialog]];
+ [newRequest setPostLength:[self postLength]];
+ [newRequest setHaveBuiltPostBody:[self haveBuiltPostBody]];
+ [newRequest setDidStartSelector:[self didStartSelector]];
+ [newRequest setDidFinishSelector:[self didFinishSelector]];
+ [newRequest setDidFailSelector:[self didFailSelector]];
+ [newRequest setTimeOutSeconds:[self timeOutSeconds]];
+ [newRequest setShouldResetDownloadProgress:[self shouldResetDownloadProgress]];
+ [newRequest setShouldResetUploadProgress:[self shouldResetUploadProgress]];
+ [newRequest setShowAccurateProgress:[self showAccurateProgress]];
+ [newRequest setDefaultResponseEncoding:[self defaultResponseEncoding]];
+ [newRequest setAllowResumeForFileDownloads:[self allowResumeForFileDownloads]];
+ [newRequest setUserInfo:[[[self userInfo] copyWithZone:zone] autorelease]];
+ [newRequest setTag:[self tag]];
+ [newRequest setUseHTTPVersionOne:[self useHTTPVersionOne]];
+ [newRequest setShouldRedirect:[self shouldRedirect]];
+ [newRequest setValidatesSecureCertificate:[self validatesSecureCertificate]];
+ [newRequest setClientCertificateIdentity:clientCertificateIdentity];
+ [newRequest setClientCertificates:[[clientCertificates copy] autorelease]];
+ [newRequest setPACurl:[self PACurl]];
+ [newRequest setShouldPresentCredentialsBeforeChallenge:[self shouldPresentCredentialsBeforeChallenge]];
+ [newRequest setNumberOfTimesToRetryOnTimeout:[self numberOfTimesToRetryOnTimeout]];
+ [newRequest setShouldUseRFC2616RedirectBehaviour:[self shouldUseRFC2616RedirectBehaviour]];
+ [newRequest setShouldAttemptPersistentConnection:[self shouldAttemptPersistentConnection]];
+ [newRequest setPersistentConnectionTimeoutSeconds:[self persistentConnectionTimeoutSeconds]];
+ [newRequest setAuthenticationScheme:[self authenticationScheme]];
+ return newRequest;
+}
+
+#pragma mark default time out
+
++ (NSTimeInterval)defaultTimeOutSeconds
+{
+ return defaultTimeOutSeconds;
+}
+
++ (void)setDefaultTimeOutSeconds:(NSTimeInterval)newTimeOutSeconds
+{
+ defaultTimeOutSeconds = newTimeOutSeconds;
+}
+
+
+#pragma mark client certificate
+
+- (void)setClientCertificateIdentity:(SecIdentityRef)anIdentity {
+ if(clientCertificateIdentity) {
+ CFRelease(clientCertificateIdentity);
+ }
+
+ clientCertificateIdentity = anIdentity;
+
+ if (clientCertificateIdentity) {
+ CFRetain(clientCertificateIdentity);
+ }
+}
+
+
+#pragma mark session credentials
+
++ (NSMutableArray *)sessionProxyCredentialsStore
+{
+ [sessionCredentialsLock lock];
+ if (!sessionProxyCredentialsStore) {
+ sessionProxyCredentialsStore = [[NSMutableArray alloc] init];
+ }
+ [sessionCredentialsLock unlock];
+ return sessionProxyCredentialsStore;
+}
+
++ (NSMutableArray *)sessionCredentialsStore
+{
+ [sessionCredentialsLock lock];
+ if (!sessionCredentialsStore) {
+ sessionCredentialsStore = [[NSMutableArray alloc] init];
+ }
+ [sessionCredentialsLock unlock];
+ return sessionCredentialsStore;
+}
+
++ (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials
+{
+ [sessionCredentialsLock lock];
+ [self removeProxyAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
+ [[[self class] sessionProxyCredentialsStore] addObject:credentials];
+ [sessionCredentialsLock unlock];
+}
+
++ (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials
+{
+ [sessionCredentialsLock lock];
+ [self removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
+ [[[self class] sessionCredentialsStore] addObject:credentials];
+ [sessionCredentialsLock unlock];
+}
+
++ (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials
+{
+ [sessionCredentialsLock lock];
+ NSMutableArray *sessionCredentialsList = [[self class] sessionProxyCredentialsStore];
+ NSUInteger i;
+ for (i=0; i<[sessionCredentialsList count]; i++) {
+ NSDictionary *theCredentials = [sessionCredentialsList objectAtIndex:i];
+ if ([theCredentials objectForKey:@"Credentials"] == credentials) {
+ [sessionCredentialsList removeObjectAtIndex:i];
+ [sessionCredentialsLock unlock];
+ return;
+ }
+ }
+ [sessionCredentialsLock unlock];
+}
+
++ (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials
+{
+ [sessionCredentialsLock lock];
+ NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore];
+ NSUInteger i;
+ for (i=0; i<[sessionCredentialsList count]; i++) {
+ NSDictionary *theCredentials = [sessionCredentialsList objectAtIndex:i];
+ if ([theCredentials objectForKey:@"Credentials"] == credentials) {
+ [sessionCredentialsList removeObjectAtIndex:i];
+ [sessionCredentialsLock unlock];
+ return;
+ }
+ }
+ [sessionCredentialsLock unlock];
+}
+
+- (NSDictionary *)findSessionProxyAuthenticationCredentials
+{
+ [sessionCredentialsLock lock];
+ NSMutableArray *sessionCredentialsList = [[self class] sessionProxyCredentialsStore];
+ for (NSDictionary *theCredentials in sessionCredentialsList) {
+ if ([[theCredentials objectForKey:@"Host"] isEqualToString:[self proxyHost]] && [[theCredentials objectForKey:@"Port"] intValue] == [self proxyPort]) {
+ [sessionCredentialsLock unlock];
+ return theCredentials;
+ }
+ }
+ [sessionCredentialsLock unlock];
+ return nil;
+}
+
+
+- (NSDictionary *)findSessionAuthenticationCredentials
+{
+ [sessionCredentialsLock lock];
+ NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore];
+ NSURL *requestURL = [self url];
+
+ BOOL haveFoundExactMatch;
+ NSDictionary *closeMatch = nil;
+
+ // Loop through all the cached credentials we have, looking for the best match for this request
+ for (NSDictionary *theCredentials in sessionCredentialsList) {
+
+ haveFoundExactMatch = NO;
+ NSURL *cachedCredentialsURL = [theCredentials objectForKey:@"URL"];
+
+ // Find an exact match (same url)
+ if ([cachedCredentialsURL isEqual:[self url]]) {
+ haveFoundExactMatch = YES;
+
+ // This is not an exact match for the url, and we already have a close match we can use
+ } else if (closeMatch) {
+ continue;
+
+ // Find a close match (same host, scheme and port)
+ } else if ([[cachedCredentialsURL host] isEqualToString:[requestURL host]] && ([cachedCredentialsURL port] == [requestURL port] || ([requestURL port] && [[cachedCredentialsURL port] isEqualToNumber:[requestURL port]])) && [[cachedCredentialsURL scheme] isEqualToString:[requestURL scheme]]) {
+ } else {
+ continue;
+ }
+
+ // Just a sanity check to ensure we never choose credentials from a different realm. Can't really do more than that, as either this request or the stored credentials may not have a realm when the other does
+ if ([self authenticationRealm] && ([theCredentials objectForKey:@"AuthenticationRealm"] && ![[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) {
+ continue;
+ }
+
+ // If we have a username and password set on the request, check that they are the same as the cached ones
+ if ([self username] && [self password]) {
+ NSDictionary *usernameAndPassword = [theCredentials objectForKey:@"Credentials"];
+ NSString *storedUsername = [usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername];
+ NSString *storedPassword = [usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword];
+ if (![storedUsername isEqualToString:[self username]] || ![storedPassword isEqualToString:[self password]]) {
+ continue;
+ }
+ }
+
+ // If we have an exact match for the url, use those credentials
+ if (haveFoundExactMatch) {
+ [sessionCredentialsLock unlock];
+ return theCredentials;
+ }
+
+ // We have no exact match, let's remember that we have a good match for this server, and we'll use it at the end if we don't find an exact match
+ closeMatch = theCredentials;
+ }
+ [sessionCredentialsLock unlock];
+
+ // Return credentials that matched on host, port and scheme, or nil if we didn't find any
+ return closeMatch;
+}
+
+#pragma mark keychain storage
+
++ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
+{
+ NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
+ [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credentials forProtectionSpace:protectionSpace];
+}
+
++ (void)saveCredentials:(NSURLCredential *)credentials forProxy:(NSString *)host port:(int)port realm:(NSString *)realm
+{
+ NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithProxyHost:host port:port type:NSURLProtectionSpaceHTTPProxy realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
+ [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credentials forProtectionSpace:protectionSpace];
+}
+
++ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
+{
+ NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
+ return [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace];
+}
+
++ (NSURLCredential *)savedCredentialsForProxy:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
+{
+ NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithProxyHost:host port:port type:NSURLProtectionSpaceHTTPProxy realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
+ return [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace];
+}
+
++ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
+{
+ NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
+ NSURLCredential *credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace];
+ if (credential) {
+ [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:credential forProtectionSpace:protectionSpace];
+ }
+}
+
++ (void)removeCredentialsForProxy:(NSString *)host port:(int)port realm:(NSString *)realm
+{
+ NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithProxyHost:host port:port type:NSURLProtectionSpaceHTTPProxy realm:realm authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
+ NSURLCredential *credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace];
+ if (credential) {
+ [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:credential forProtectionSpace:protectionSpace];
+ }
+}
+
++ (NSMutableArray *)sessionCookies
+{
+ [sessionCookiesLock lock];
+ if (!sessionCookies) {
+ [ASIHTTPRequest setSessionCookies:[NSMutableArray array]];
+ }
+ NSMutableArray *cookies = [[sessionCookies retain] autorelease];
+ [sessionCookiesLock unlock];
+ return cookies;
+}
+
++ (void)setSessionCookies:(NSMutableArray *)newSessionCookies
+{
+ [sessionCookiesLock lock];
+ // Remove existing cookies from the persistent store
+ for (NSHTTPCookie *cookie in sessionCookies) {
+ [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
+ }
+ [sessionCookies release];
+ sessionCookies = [newSessionCookies retain];
+ [sessionCookiesLock unlock];
+}
+
++ (void)addSessionCookie:(NSHTTPCookie *)newCookie
+{
+ [sessionCookiesLock lock];
+ NSHTTPCookie *cookie;
+ NSUInteger i;
+ NSUInteger max = [[ASIHTTPRequest sessionCookies] count];
+ for (i=0; i<max; i++) {
+ cookie = [[ASIHTTPRequest sessionCookies] objectAtIndex:i];
+ if ([[cookie domain] isEqualToString:[newCookie domain]] && [[cookie path] isEqualToString:[newCookie path]] && [[cookie name] isEqualToString:[newCookie name]]) {
+ [[ASIHTTPRequest sessionCookies] removeObjectAtIndex:i];
+ break;
+ }
+ }
+ [[ASIHTTPRequest sessionCookies] addObject:newCookie];
+ [sessionCookiesLock unlock];
+}
+
+// Dump all session data (authentication and cookies)
++ (void)clearSession
+{
+ [sessionCredentialsLock lock];
+ [[[self class] sessionCredentialsStore] removeAllObjects];
+ [sessionCredentialsLock unlock];
+ [[self class] setSessionCookies:nil];
+ [[[self class] defaultCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
+}
+
+#pragma mark get user agent
+
++ (NSString *)defaultUserAgentString
+{
+ @synchronized (self) {
+
+ if (!defaultUserAgent) {
+
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+
+ // Attempt to find a name for this application
+ NSString *appName = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
+ if (!appName) {
+ appName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
+ }
+
+ NSData *latin1Data = [appName dataUsingEncoding:NSUTF8StringEncoding];
+ appName = [[[NSString alloc] initWithData:latin1Data encoding:NSISOLatin1StringEncoding] autorelease];
+
+ // If we couldn't find one, we'll give up (and ASIHTTPRequest will use the standard CFNetwork user agent)
+ if (!appName) {
+ return nil;
+ }
+
+ NSString *appVersion = nil;
+ NSString *marketingVersionNumber = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ NSString *developmentVersionNumber = [bundle objectForInfoDictionaryKey:@"CFBundleVersion"];
+ if (marketingVersionNumber && developmentVersionNumber) {
+ if ([marketingVersionNumber isEqualToString:developmentVersionNumber]) {
+ appVersion = marketingVersionNumber;
+ } else {
+ appVersion = [NSString stringWithFormat:@"%@ rv:%@",marketingVersionNumber,developmentVersionNumber];
+ }
+ } else {
+ appVersion = (marketingVersionNumber ? marketingVersionNumber : developmentVersionNumber);
+ }
+
+ NSString *deviceName;
+ NSString *OSName;
+ NSString *OSVersion;
+ NSString *locale = [[NSLocale currentLocale] localeIdentifier];
+
+ #if TARGET_OS_IPHONE
+ UIDevice *device = [UIDevice currentDevice];
+ deviceName = [device model];
+ OSName = [device systemName];
+ OSVersion = [device systemVersion];
+
+ #else
+ deviceName = @"Macintosh";
+ OSName = @"Mac OS X";
+
+ // From http://www.cocoadev.com/index.pl?DeterminingOSVersion
+ // We won't bother to check for systems prior to 10.4, since ASIHTTPRequest only works on 10.5+
+ OSErr err;
+ SInt32 versionMajor, versionMinor, versionBugFix;
+ err = Gestalt(gestaltSystemVersionMajor, &versionMajor);
+ if (err != noErr) return nil;
+ err = Gestalt(gestaltSystemVersionMinor, &versionMinor);
+ if (err != noErr) return nil;
+ err = Gestalt(gestaltSystemVersionBugFix, &versionBugFix);
+ if (err != noErr) return nil;
+ OSVersion = [NSString stringWithFormat:@"%u.%u.%u", versionMajor, versionMinor, versionBugFix];
+ #endif
+
+ // Takes the form "My Application 1.0 (Macintosh; Mac OS X 10.5.7; en_GB)"
+ [self setDefaultUserAgentString:[NSString stringWithFormat:@"%@ %@ (%@; %@ %@; %@)", appName, appVersion, deviceName, OSName, OSVersion, locale]];
+ }
+ return [[defaultUserAgent retain] autorelease];
+ }
+ return nil;
+}
+
++ (void)setDefaultUserAgentString:(NSString *)agent
+{
+ @synchronized (self) {
+ if (defaultUserAgent == agent) {
+ return;
+ }
+ [defaultUserAgent release];
+ defaultUserAgent = [agent copy];
+ }
+}
+
+
+#pragma mark mime-type detection
+
++ (NSString *)mimeTypeForFileAtPath:(NSString *)path
+{
+ if (![[[[NSFileManager alloc] init] autorelease] fileExistsAtPath:path]) {
+ return nil;
+ }
+ // Borrowed from http://stackoverflow.com/questions/2439020/wheres-the-iphone-mime-type-database
+ CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL);
+ CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
+ CFRelease(UTI);
+ if (!MIMEType) {
+ return @"application/octet-stream";
+ }
+ return [NSMakeCollectable(MIMEType) autorelease];
+}
+
+#pragma mark bandwidth measurement / throttling
+
+- (void)performThrottling
+{
+ if (![self readStream]) {
+ return;
+ }
+ [ASIHTTPRequest measureBandwidthUsage];
+ if ([ASIHTTPRequest isBandwidthThrottled]) {
+ [bandwidthThrottlingLock lock];
+ // Handle throttling
+ if (throttleWakeUpTime) {
+ if ([throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] > 0) {
+ if ([self readStreamIsScheduled]) {
+ [self unscheduleReadStream];
+ #if DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[THROTTLING] Sleeping request %@ until after %@",self,throttleWakeUpTime);
+ #endif
+ }
+ } else {
+ if (![self readStreamIsScheduled]) {
+ [self scheduleReadStream];
+ #if DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[THROTTLING] Waking up request %@",self);
+ #endif
+ }
+ }
+ }
+ [bandwidthThrottlingLock unlock];
+
+ // Bandwidth throttling must have been turned off since we last looked, let's re-schedule the stream
+ } else if (![self readStreamIsScheduled]) {
+ [self scheduleReadStream];
+ }
+}
+
++ (BOOL)isBandwidthThrottled
+{
+#if TARGET_OS_IPHONE
+ [bandwidthThrottlingLock lock];
+
+ BOOL throttle = isBandwidthThrottled || (!shouldThrottleBandwidthForWWANOnly && (maxBandwidthPerSecond > 0));
+ [bandwidthThrottlingLock unlock];
+ return throttle;
+#else
+ [bandwidthThrottlingLock lock];
+ BOOL throttle = (maxBandwidthPerSecond > 0);
+ [bandwidthThrottlingLock unlock];
+ return throttle;
+#endif
+}
+
++ (unsigned long)maxBandwidthPerSecond
+{
+ [bandwidthThrottlingLock lock];
+ unsigned long amount = maxBandwidthPerSecond;
+ [bandwidthThrottlingLock unlock];
+ return amount;
+}
+
++ (void)setMaxBandwidthPerSecond:(unsigned long)bytes
+{
+ [bandwidthThrottlingLock lock];
+ maxBandwidthPerSecond = bytes;
+ [bandwidthThrottlingLock unlock];
+}
+
++ (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes
+{
+ [bandwidthThrottlingLock lock];
+ bandwidthUsedInLastSecond += bytes;
+ [bandwidthThrottlingLock unlock];
+}
+
++ (void)recordBandwidthUsage
+{
+ if (bandwidthUsedInLastSecond == 0) {
+ [bandwidthUsageTracker removeAllObjects];
+ } else {
+ NSTimeInterval interval = [bandwidthMeasurementDate timeIntervalSinceNow];
+ while ((interval < 0 || [bandwidthUsageTracker count] > 5) && [bandwidthUsageTracker count] > 0) {
+ [bandwidthUsageTracker removeObjectAtIndex:0];
+ interval++;
+ }
+ }
+ #if DEBUG_THROTTLING
+ ASI_DEBUG_LOG(@"[THROTTLING] ===Used: %u bytes of bandwidth in last measurement period===",bandwidthUsedInLastSecond);
+ #endif
+ [bandwidthUsageTracker addObject:[NSNumber numberWithUnsignedLong:bandwidthUsedInLastSecond]];
+ [bandwidthMeasurementDate release];
+ bandwidthMeasurementDate = [[NSDate dateWithTimeIntervalSinceNow:1] retain];
+ bandwidthUsedInLastSecond = 0;
+
+ NSUInteger measurements = [bandwidthUsageTracker count];
+ unsigned long totalBytes = 0;
+ for (NSNumber *bytes in bandwidthUsageTracker) {
+ totalBytes += [bytes unsignedLongValue];
+ }
+ averageBandwidthUsedPerSecond = totalBytes/measurements;
+}
+
++ (unsigned long)averageBandwidthUsedPerSecond
+{
+ [bandwidthThrottlingLock lock];
+ unsigned long amount = averageBandwidthUsedPerSecond;
+ [bandwidthThrottlingLock unlock];
+ return amount;
+}
+
++ (void)measureBandwidthUsage
+{
+ // Other requests may have to wait for this lock if we're sleeping, but this is fine, since in that case we already know they shouldn't be sending or receiving data
+ [bandwidthThrottlingLock lock];
+
+ if (!bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < -0) {
+ [ASIHTTPRequest recordBandwidthUsage];
+ }
+
+ // Are we performing bandwidth throttling?
+ if (
+ #if TARGET_OS_IPHONE
+ isBandwidthThrottled || (!shouldThrottleBandwidthForWWANOnly && (maxBandwidthPerSecond))
+ #else
+ maxBandwidthPerSecond
+ #endif
+ ) {
+ // How much data can we still send or receive this second?
+ long long bytesRemaining = (long long)maxBandwidthPerSecond - (long long)bandwidthUsedInLastSecond;
+
+ // Have we used up our allowance?
+ if (bytesRemaining < 0) {
+
+ // Yes, put this request to sleep until a second is up, with extra added punishment sleeping time for being very naughty (we have used more bandwidth than we were allowed)
+ double extraSleepyTime = (-bytesRemaining/(maxBandwidthPerSecond*1.0));
+ [throttleWakeUpTime release];
+ throttleWakeUpTime = [[NSDate alloc] initWithTimeInterval:extraSleepyTime sinceDate:bandwidthMeasurementDate];
+ }
+ }
+ [bandwidthThrottlingLock unlock];
+}
+
++ (unsigned long)maxUploadReadLength
+{
+ [bandwidthThrottlingLock lock];
+
+ // We'll split our bandwidth allowance into 4 (which is the default for an ASINetworkQueue's max concurrent operations count) to give all running requests a fighting chance of reading data this cycle
+ long long toRead = maxBandwidthPerSecond/4;
+ if (maxBandwidthPerSecond > 0 && (bandwidthUsedInLastSecond + toRead > maxBandwidthPerSecond)) {
+ toRead = (long long)maxBandwidthPerSecond-(long long)bandwidthUsedInLastSecond;
+ if (toRead < 0) {
+ toRead = 0;
+ }
+ }
+
+ if (toRead == 0 || !bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < -0) {
+ [throttleWakeUpTime release];
+ throttleWakeUpTime = [bandwidthMeasurementDate retain];
+ }
+ [bandwidthThrottlingLock unlock];
+ return (unsigned long)toRead;
+}
+
+
+#if TARGET_OS_IPHONE
++ (void)setShouldThrottleBandwidthForWWAN:(BOOL)throttle
+{
+ if (throttle) {
+ [ASIHTTPRequest throttleBandwidthForWWANUsingLimit:ASIWWANBandwidthThrottleAmount];
+ } else {
+ [ASIHTTPRequest unsubscribeFromNetworkReachabilityNotifications];
+ [ASIHTTPRequest setMaxBandwidthPerSecond:0];
+ [bandwidthThrottlingLock lock];
+ isBandwidthThrottled = NO;
+ shouldThrottleBandwidthForWWANOnly = NO;
+ [bandwidthThrottlingLock unlock];
+ }
+}
+
++ (void)throttleBandwidthForWWANUsingLimit:(unsigned long)limit
+{
+ [bandwidthThrottlingLock lock];
+ shouldThrottleBandwidthForWWANOnly = YES;
+ maxBandwidthPerSecond = limit;
+ [ASIHTTPRequest registerForNetworkReachabilityNotifications];
+ [bandwidthThrottlingLock unlock];
+ [ASIHTTPRequest reachabilityChanged:nil];
+}
+
+#pragma mark reachability
+
++ (void)registerForNetworkReachabilityNotifications
+{
+ [[Reachability reachabilityForInternetConnection] startNotifier];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
+}
+
+
++ (void)unsubscribeFromNetworkReachabilityNotifications
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
+}
+
++ (BOOL)isNetworkReachableViaWWAN
+{
+ return ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == ReachableViaWWAN);
+}
+
++ (void)reachabilityChanged:(NSNotification *)note
+{
+ [bandwidthThrottlingLock lock];
+ isBandwidthThrottled = [ASIHTTPRequest isNetworkReachableViaWWAN];
+ [bandwidthThrottlingLock unlock];
+}
+#endif
+
+#pragma mark queue
+
+// Returns the shared queue
++ (NSOperationQueue *)sharedQueue
+{
+ return [[sharedQueue retain] autorelease];
+}
+
+#pragma mark cache
+
++ (void)setDefaultCache:(id <ASICacheDelegate>)cache
+{
+ @synchronized (self) {
+ [cache retain];
+ [defaultCache release];
+ defaultCache = cache;
+ }
+}
+
++ (id <ASICacheDelegate>)defaultCache
+{
+ @synchronized(self) {
+ return [[defaultCache retain] autorelease];
+ }
+ return nil;
+}
+
+
+#pragma mark network activity
+
++ (BOOL)isNetworkInUse
+{
+ [connectionsLock lock];
+ BOOL inUse = (runningRequestCount > 0);
+ [connectionsLock unlock];
+ return inUse;
+}
+
++ (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate
+{
+ [connectionsLock lock];
+ shouldUpdateNetworkActivityIndicator = shouldUpdate;
+ [connectionsLock unlock];
+}
+
++ (void)showNetworkActivityIndicator
+{
+#if TARGET_OS_IPHONE
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
+#endif
+}
+
++ (void)hideNetworkActivityIndicator
+{
+#if TARGET_OS_IPHONE
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+#endif
+}
+
+
+/* Always called on main thread */
++ (void)hideNetworkActivityIndicatorAfterDelay
+{
+ [self performSelector:@selector(hideNetworkActivityIndicatorIfNeeeded) withObject:nil afterDelay:0.5];
+}
+
++ (void)hideNetworkActivityIndicatorIfNeeeded
+{
+ [connectionsLock lock];
+ if (runningRequestCount == 0) {
+ [self hideNetworkActivityIndicator];
+ }
+ [connectionsLock unlock];
+}
+
+
+#pragma mark threading behaviour
+
+// In the default implementation, all requests run in a single background thread
+// Advanced users only: Override this method in a subclass for a different threading behaviour
+// Eg: return [NSThread mainThread] to run all requests in the main thread
+// Alternatively, you can create a thread on demand, or manage a pool of threads
+// Threads returned by this method will need to run the runloop in default mode (eg CFRunLoopRun())
+// Requests will stop the runloop when they complete
+// If you have multiple requests sharing the thread or you want to re-use the thread, you'll need to restart the runloop
++ (NSThread *)threadForRequest:(ASIHTTPRequest *)request
+{
+ if (networkThread == nil) {
+ @synchronized(self) {
+ if (networkThread == nil) {
+ networkThread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequests) object:nil];
+ [networkThread start];
+ }
+ }
+ }
+ return networkThread;
+}
+
++ (void)runRequests
+{
+ // Should keep the runloop from exiting
+ CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+ CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
+
+ BOOL runAlways = YES; // Introduced to cheat Static Analyzer
+ while (runAlways) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
+ [pool drain];
+ }
+
+ // Should never be called, but anyway
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
+ CFRelease(source);
+}
+
+#pragma mark miscellany
+
+#if TARGET_OS_IPHONE
++ (BOOL)isMultitaskingSupported
+{
+ BOOL multiTaskingSupported = NO;
+ if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) {
+ multiTaskingSupported = [(id)[UIDevice currentDevice] isMultitaskingSupported];
+ }
+ return multiTaskingSupported;
+}
+#endif
+
+// From: http://www.cocoadev.com/index.pl?BaseSixtyFour
+
++ (NSString*)base64forData:(NSData*)theData {
+
+ const uint8_t* input = (const uint8_t*)[theData bytes];
+ NSInteger length = [theData length];
+
+ static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+ NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
+ uint8_t* output = (uint8_t*)data.mutableBytes;
+
+ NSInteger i,i2;
+ for (i=0; i < length; i += 3) {
+ NSInteger value = 0;
+ for (i2=0; i2<3; i2++) {
+ value <<= 8;
+ if (i+i2 < length) {
+ value |= (0xFF & input[i+i2]);
+ }
+ }
+
+ NSInteger theIndex = (i / 3) * 4;
+ output[theIndex + 0] = table[(value >> 18) & 0x3F];
+ output[theIndex + 1] = table[(value >> 12) & 0x3F];
+ output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
+ output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
+ }
+
+ return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
+}
+
++ (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
+{
+ NSDictionary *responseHeaders = [request responseHeaders];
+
+ // If we weren't given a custom max-age, lets look for one in the response headers
+ if (!maxAge) {
+ NSString *cacheControl = [[responseHeaders objectForKey:@"Cache-Control"] lowercaseString];
+ if (cacheControl) {
+ NSScanner *scanner = [NSScanner scannerWithString:cacheControl];
+ [scanner scanUpToString:@"max-age" intoString:NULL];
+ if ([scanner scanString:@"max-age" intoString:NULL]) {
+ [scanner scanString:@"=" intoString:NULL];
+ [scanner scanDouble:&maxAge];
+ }
+ }
+ }
+
+ // RFC 2612 says max-age must override any Expires header
+ if (maxAge) {
+ return [[NSDate date] addTimeInterval:maxAge];
+ } else {
+ NSString *expires = [responseHeaders objectForKey:@"Expires"];
+ if (expires) {
+ return [ASIHTTPRequest dateFromRFC1123String:expires];
+ }
+ }
+ return nil;
+}
+
+// Based on hints from http://stackoverflow.com/questions/1850824/parsing-a-rfc-822-date-with-nsdateformatter
++ (NSDate *)dateFromRFC1123String:(NSString *)string
+{
+ NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
+ [formatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
+ // Does the string include a week day?
+ NSString *day = @"";
+ if ([string rangeOfString:@","].location != NSNotFound) {
+ day = @"EEE, ";
+ }
+ // Does the string include seconds?
+ NSString *seconds = @"";
+ if ([[string componentsSeparatedByString:@":"] count] == 3) {
+ seconds = @":ss";
+ }
+ [formatter setDateFormat:[NSString stringWithFormat:@"%@dd MMM yyyy HH:mm%@ z",day,seconds]];
+ return [formatter dateFromString:string];
+}
+
++ (void)parseMimeType:(NSString **)mimeType andResponseEncoding:(NSStringEncoding *)stringEncoding fromContentType:(NSString *)contentType
+{
+ if (!contentType) {
+ return;
+ }
+ NSScanner *charsetScanner = [NSScanner scannerWithString: contentType];
+ if (![charsetScanner scanUpToString:@";" intoString:mimeType] || [charsetScanner scanLocation] == [contentType length]) {
+ *mimeType = [contentType stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ return;
+ }
+ *mimeType = [*mimeType stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ NSString *charsetSeparator = @"charset=";
+ NSString *IANAEncoding = nil;
+
+ if ([charsetScanner scanUpToString: charsetSeparator intoString: NULL] && [charsetScanner scanLocation] < [contentType length]) {
+ [charsetScanner setScanLocation: [charsetScanner scanLocation] + [charsetSeparator length]];
+ [charsetScanner scanUpToString: @";" intoString: &IANAEncoding];
+ }
+
+ if (IANAEncoding) {
+ CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)IANAEncoding);
+ if (cfEncoding != kCFStringEncodingInvalidId) {
+ *stringEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark blocks
+#if NS_BLOCKS_AVAILABLE
+- (void)setStartedBlock:(ASIBasicBlock)aStartedBlock
+{
+ [startedBlock release];
+ startedBlock = [aStartedBlock copy];
+}
+
+- (void)setHeadersReceivedBlock:(ASIHeadersBlock)aReceivedBlock
+{
+ [headersReceivedBlock release];
+ headersReceivedBlock = [aReceivedBlock copy];
+}
+
+- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock
+{
+ [completionBlock release];
+ completionBlock = [aCompletionBlock copy];
+}
+
+- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock
+{
+ [failureBlock release];
+ failureBlock = [aFailedBlock copy];
+}
+
+- (void)setBytesReceivedBlock:(ASIProgressBlock)aBytesReceivedBlock
+{
+ [bytesReceivedBlock release];
+ bytesReceivedBlock = [aBytesReceivedBlock copy];
+}
+
+- (void)setBytesSentBlock:(ASIProgressBlock)aBytesSentBlock
+{
+ [bytesSentBlock release];
+ bytesSentBlock = [aBytesSentBlock copy];
+}
+
+- (void)setDownloadSizeIncrementedBlock:(ASISizeBlock)aDownloadSizeIncrementedBlock{
+ [downloadSizeIncrementedBlock release];
+ downloadSizeIncrementedBlock = [aDownloadSizeIncrementedBlock copy];
+}
+
+- (void)setUploadSizeIncrementedBlock:(ASISizeBlock)anUploadSizeIncrementedBlock
+{
+ [uploadSizeIncrementedBlock release];
+ uploadSizeIncrementedBlock = [anUploadSizeIncrementedBlock copy];
+}
+
+- (void)setDataReceivedBlock:(ASIDataBlock)aReceivedBlock
+{
+ [dataReceivedBlock release];
+ dataReceivedBlock = [aReceivedBlock copy];
+}
+
+- (void)setAuthenticationNeededBlock:(ASIBasicBlock)anAuthenticationBlock
+{
+ [authenticationNeededBlock release];
+ authenticationNeededBlock = [anAuthenticationBlock copy];
+}
+- (void)setProxyAuthenticationNeededBlock:(ASIBasicBlock)aProxyAuthenticationBlock
+{
+ [proxyAuthenticationNeededBlock release];
+ proxyAuthenticationNeededBlock = [aProxyAuthenticationBlock copy];
+}
+- (void)setRequestRedirectedBlock:(ASIBasicBlock)aRedirectBlock
+{
+ [requestRedirectedBlock release];
+ requestRedirectedBlock = [aRedirectBlock copy];
+}
+#endif
+
+#pragma mark ===
+
+@synthesize username;
+@synthesize password;
+@synthesize userAgentString;
+@synthesize domain;
+@synthesize proxyUsername;
+@synthesize proxyPassword;
+@synthesize proxyDomain;
+@synthesize url;
+@synthesize originalURL;
+@synthesize delegate;
+@synthesize queue;
+@synthesize uploadProgressDelegate;
+@synthesize downloadProgressDelegate;
+@synthesize useKeychainPersistence;
+@synthesize useSessionPersistence;
+@synthesize useCookiePersistence;
+@synthesize downloadDestinationPath;
+@synthesize temporaryFileDownloadPath;
+@synthesize temporaryUncompressedDataDownloadPath;
+@synthesize didStartSelector;
+@synthesize didReceiveResponseHeadersSelector;
+@synthesize willRedirectSelector;
+@synthesize didFinishSelector;
+@synthesize didFailSelector;
+@synthesize didReceiveDataSelector;
+@synthesize authenticationRealm;
+@synthesize proxyAuthenticationRealm;
+@synthesize error;
+@synthesize complete;
+@synthesize requestHeaders;
+@synthesize responseHeaders;
+@synthesize responseCookies;
+@synthesize requestCookies;
+@synthesize requestCredentials;
+@synthesize responseStatusCode;
+@synthesize rawResponseData;
+@synthesize lastActivityTime;
+@synthesize timeOutSeconds;
+@synthesize requestMethod;
+@synthesize postBody;
+@synthesize compressedPostBody;
+@synthesize contentLength;
+@synthesize partialDownloadSize;
+@synthesize postLength;
+@synthesize shouldResetDownloadProgress;
+@synthesize shouldResetUploadProgress;
+@synthesize mainRequest;
+@synthesize totalBytesRead;
+@synthesize totalBytesSent;
+@synthesize showAccurateProgress;
+@synthesize uploadBufferSize;
+@synthesize defaultResponseEncoding;
+@synthesize responseEncoding;
+@synthesize allowCompressedResponse;
+@synthesize allowResumeForFileDownloads;
+@synthesize userInfo;
+@synthesize tag;
+@synthesize postBodyFilePath;
+@synthesize compressedPostBodyFilePath;
+@synthesize postBodyWriteStream;
+@synthesize postBodyReadStream;
+@synthesize shouldStreamPostDataFromDisk;
+@synthesize didCreateTemporaryPostDataFile;
+@synthesize useHTTPVersionOne;
+@synthesize lastBytesRead;
+@synthesize lastBytesSent;
+@synthesize cancelledLock;
+@synthesize haveBuiltPostBody;
+@synthesize fileDownloadOutputStream;
+@synthesize inflatedFileDownloadOutputStream;
+@synthesize authenticationRetryCount;
+@synthesize proxyAuthenticationRetryCount;
+@synthesize updatedProgress;
+@synthesize shouldRedirect;
+@synthesize validatesSecureCertificate;
+@synthesize needsRedirect;
+@synthesize redirectCount;
+@synthesize shouldCompressRequestBody;
+@synthesize proxyCredentials;
+@synthesize proxyHost;
+@synthesize proxyPort;
+@synthesize proxyType;
+@synthesize PACurl;
+@synthesize authenticationScheme;
+@synthesize proxyAuthenticationScheme;
+@synthesize shouldPresentAuthenticationDialog;
+@synthesize shouldPresentProxyAuthenticationDialog;
+@synthesize authenticationNeeded;
+@synthesize responseStatusMessage;
+@synthesize shouldPresentCredentialsBeforeChallenge;
+@synthesize haveBuiltRequestHeaders;
+@synthesize inProgress;
+@synthesize numberOfTimesToRetryOnTimeout;
+@synthesize retryCount;
+@synthesize willRetryRequest;
+@synthesize shouldAttemptPersistentConnection;
+@synthesize persistentConnectionTimeoutSeconds;
+@synthesize connectionCanBeReused;
+@synthesize connectionInfo;
+@synthesize readStream;
+@synthesize readStreamIsScheduled;
+@synthesize shouldUseRFC2616RedirectBehaviour;
+@synthesize downloadComplete;
+@synthesize requestID;
+@synthesize runLoopMode;
+@synthesize statusTimer;
+@synthesize downloadCache;
+@synthesize cachePolicy;
+@synthesize cacheStoragePolicy;
+@synthesize didUseCachedResponse;
+@synthesize secondsToCache;
+@synthesize clientCertificates;
+@synthesize redirectURL;
+#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
+@synthesize shouldContinueWhenAppEntersBackground;
+#endif
+@synthesize dataDecompressor;
+@synthesize shouldWaitToInflateCompressedResponses;
+
+@synthesize isPACFileRequest;
+@synthesize PACFileRequest;
+@synthesize PACFileReadStream;
+@synthesize PACFileData;
+
+@synthesize isSynchronous;
+@end
diff --git a/samples/objective-c/Foo.h b/samples/objective-c/Foo.h
new file mode 100644
index 0000000..95fbbf5
--- /dev/null
+++ b/samples/objective-c/Foo.h
@@ -0,0 +1,8 @@
+#import <Foundation/Foundation.h>
+
+
+@interface Foo : NSObject {
+
+}
+
+@end
diff --git a/samples/objective-c/Foo.m b/samples/objective-c/Foo.m
new file mode 100644
index 0000000..f11b1bc
--- /dev/null
+++ b/samples/objective-c/Foo.m
@@ -0,0 +1,6 @@
+#import "Foo.h"
+
+
+@implementation Foo
+
+@end
diff --git a/samples/objective-c/FooAppDelegate.h b/samples/objective-c/FooAppDelegate.h
new file mode 100644
index 0000000..24531b2
--- /dev/null
+++ b/samples/objective-c/FooAppDelegate.h
@@ -0,0 +1,10 @@
+#import <Cocoa/Cocoa.h>
+
+@interface FooAppDelegate : NSObject <NSApplicationDelegate> {
+@private
+ NSWindow *window;
+}
+
+@property (assign) IBOutlet NSWindow *window;
+
+@end
diff --git a/samples/objective-c/FooAppDelegate.m b/samples/objective-c/FooAppDelegate.m
new file mode 100644
index 0000000..7e8710b
--- /dev/null
+++ b/samples/objective-c/FooAppDelegate.m
@@ -0,0 +1,12 @@
+#import "FooAppDelegate.h"
+
+@implementation FooAppDelegate
+
+@synthesize window;
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ // Insert code here to initialize your application
+}
+
+@end
diff --git a/samples/objective-c/JSONKit.h b/samples/objective-c/JSONKit.h
new file mode 100644
index 0000000..71bd0c3
--- /dev/null
+++ b/samples/objective-c/JSONKit.h
@@ -0,0 +1,251 @@
+//
+// JSONKit.h
+// http://github.com/johnezang/JSONKit
+// Dual licensed under either the terms of the BSD License, or alternatively
+// under the terms of the Apache License, Version 2.0, as specified below.
+//
+
+/*
+ Copyright (c) 2011, John Engelhart
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the Zang Industries nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ Copyright 2011 John Engelhart
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <TargetConditionals.h>
+#include <AvailabilityMacros.h>
+
+#ifdef __OBJC__
+#import <Foundation/NSArray.h>
+#import <Foundation/NSData.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSError.h>
+#import <Foundation/NSObjCRuntime.h>
+#import <Foundation/NSString.h>
+#endif // __OBJC__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// For Mac OS X < 10.5.
+#ifndef NSINTEGER_DEFINED
+#define NSINTEGER_DEFINED
+#if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+#define NSIntegerMin LONG_MIN
+#define NSIntegerMax LONG_MAX
+#define NSUIntegerMax ULONG_MAX
+#else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
+typedef int NSInteger;
+typedef unsigned int NSUInteger;
+#define NSIntegerMin INT_MIN
+#define NSIntegerMax INT_MAX
+#define NSUIntegerMax UINT_MAX
+#endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
+#endif // NSINTEGER_DEFINED
+
+
+#ifndef _JSONKIT_H_
+#define _JSONKIT_H_
+
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465)
+#define JK_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
+#else
+#define JK_DEPRECATED_ATTRIBUTE
+#endif
+
+#define JSONKIT_VERSION_MAJOR 1
+#define JSONKIT_VERSION_MINOR 4
+
+typedef NSUInteger JKFlags;
+
+/*
+ JKParseOptionComments : Allow C style // and /_* ... *_/ (without a _, obviously) comments in JSON.
+ JKParseOptionUnicodeNewlines : Allow Unicode recommended (?:\r\n|[\n\v\f\r\x85\p{Zl}\p{Zp}]) newlines.
+ JKParseOptionLooseUnicode : Normally the decoder will stop with an error at any malformed Unicode.
+ This option allows JSON with malformed Unicode to be parsed without reporting an error.
+ Any malformed Unicode is replaced with \uFFFD, or "REPLACEMENT CHARACTER".
+ */
+
+enum {
+ JKParseOptionNone = 0,
+ JKParseOptionStrict = 0,
+ JKParseOptionComments = (1 << 0),
+ JKParseOptionUnicodeNewlines = (1 << 1),
+ JKParseOptionLooseUnicode = (1 << 2),
+ JKParseOptionPermitTextAfterValidJSON = (1 << 3),
+ JKParseOptionValidFlags = (JKParseOptionComments | JKParseOptionUnicodeNewlines | JKParseOptionLooseUnicode | JKParseOptionPermitTextAfterValidJSON),
+};
+typedef JKFlags JKParseOptionFlags;
+
+enum {
+ JKSerializeOptionNone = 0,
+ JKSerializeOptionPretty = (1 << 0),
+ JKSerializeOptionEscapeUnicode = (1 << 1),
+ JKSerializeOptionEscapeForwardSlashes = (1 << 4),
+ JKSerializeOptionValidFlags = (JKSerializeOptionPretty | JKSerializeOptionEscapeUnicode | JKSerializeOptionEscapeForwardSlashes),
+};
+typedef JKFlags JKSerializeOptionFlags;
+
+#ifdef __OBJC__
+
+typedef struct JKParseState JKParseState; // Opaque internal, private type.
+
+// As a general rule of thumb, if you use a method that doesn't accept a JKParseOptionFlags argument, it defaults to JKParseOptionStrict
+
+@interface JSONDecoder : NSObject {
+ JKParseState *parseState;
+}
++ (id)decoder;
++ (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (void)clearCache;
+
+// The parse... methods were deprecated in v1.4 in favor of the v1.4 objectWith... methods.
+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
+// The NSData MUST be UTF8 encoded JSON.
+- (id)parseJSONData:(NSData *)jsonData JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData: instead.
+- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error JK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
+
+// Methods that return immutable collection objects.
+- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
+- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
+// The NSData MUST be UTF8 encoded JSON.
+- (id)objectWithData:(NSData *)jsonData;
+- (id)objectWithData:(NSData *)jsonData error:(NSError **)error;
+
+// Methods that return mutable collection objects.
+- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
+- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
+// The NSData MUST be UTF8 encoded JSON.
+- (id)mutableObjectWithData:(NSData *)jsonData;
+- (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error;
+
+@end
+
+////////////
+#pragma mark Deserializing methods
+////////////
+
+@interface NSString (JSONKitDeserializing)
+- (id)objectFromJSONString;
+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
+- (id)mutableObjectFromJSONString;
+- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
+@end
+
+@interface NSData (JSONKitDeserializing)
+// The NSData MUST be UTF8 encoded JSON.
+- (id)objectFromJSONData;
+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
+- (id)mutableObjectFromJSONData;
+- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
+- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error;
+@end
+
+////////////
+#pragma mark Serializing methods
+////////////
+
+@interface NSString (JSONKitSerializing)
+// Convenience methods for those that need to serialize the receiving NSString (i.e., instead of having to serialize a NSArray with a single NSString, you can "serialize to JSON" just the NSString).
+// Normally, a string that is serialized to JSON has quotation marks surrounding it, which you may or may not want when serializing a single string, and can be controlled with includeQuotes:
+// includeQuotes:YES `a "test"...` -> `"a \"test\"..."`
+// includeQuotes:NO `a "test"...` -> `a \"test\"...`
+- (NSData *)JSONData; // Invokes JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
+- (NSString *)JSONString; // Invokes JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
+@end
+
+@interface NSArray (JSONKitSerializing)
+- (NSData *)JSONData;
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
+- (NSString *)JSONString;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
+@end
+
+@interface NSDictionary (JSONKitSerializing)
+- (NSData *)JSONData;
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
+- (NSString *)JSONString;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
+@end
+
+#ifdef __BLOCKS__
+
+@interface NSArray (JSONKitSerializingBlockAdditions)
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
+@end
+
+@interface NSDictionary (JSONKitSerializingBlockAdditions)
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
+@end
+
+#endif
+
+
+#endif // __OBJC__
+
+#endif // _JSONKIT_H_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/samples/objective-c/JSONKit.m b/samples/objective-c/JSONKit.m
new file mode 100644
index 0000000..76a04f0
--- /dev/null
+++ b/samples/objective-c/JSONKit.m
@@ -0,0 +1,3059 @@
+//
+// JSONKit.m
+// http://github.com/johnezang/JSONKit
+// Dual licensed under either the terms of the BSD License, or alternatively
+// under the terms of the Apache License, Version 2.0, as specified below.
+//
+
+/*
+ Copyright (c) 2011, John Engelhart
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the Zang Industries nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ Copyright 2011 John Engelhart
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+
+/*
+ Acknowledgments:
+
+ The bulk of the UTF8 / UTF32 conversion and verification comes
+ from ConvertUTF.[hc]. It has been modified from the original sources.
+
+ The original sources were obtained from http://www.unicode.org/.
+ However, the web site no longer seems to host the files. Instead,
+ the Unicode FAQ http://www.unicode.org/faq//utf_bom.html#gen4
+ points to International Components for Unicode (ICU)
+ http://site.icu-project.org/ as an example of how to write a UTF
+ converter.
+
+ The decision to use the ConvertUTF.[ch] code was made to leverage
+ "proven" code. Hopefully the local modifications are bug free.
+
+ The code in isValidCodePoint() is derived from the ICU code in
+ utf.h for the macros U_IS_UNICODE_NONCHAR and U_IS_UNICODE_CHAR.
+
+ From the original ConvertUTF.[ch]:
+
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/errno.h>
+#include <math.h>
+#include <limits.h>
+#include <objc/runtime.h>
+
+#import "JSONKit.h"
+
+//#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFDictionary.h>
+#include <CoreFoundation/CFNumber.h>
+
+//#import <Foundation/Foundation.h>
+#import <Foundation/NSArray.h>
+#import <Foundation/NSAutoreleasePool.h>
+#import <Foundation/NSData.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSException.h>
+#import <Foundation/NSNull.h>
+#import <Foundation/NSObjCRuntime.h>
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#ifdef JK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS
+#warning As of JSONKit v1.4, JK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS is no longer required. It is no longer a valid option.
+#endif
+
+#ifdef __OBJC_GC__
+#error JSONKit does not support Objective-C Garbage Collection
+#endif
+
+#if __has_feature(objc_arc)
+#error JSONKit does not support Objective-C Automatic Reference Counting (ARC)
+#endif
+
+// The following checks are really nothing more than sanity checks.
+// JSONKit technically has a few problems from a "strictly C99 conforming" standpoint, though they are of the pedantic nitpicking variety.
+// In practice, though, for the compilers and architectures we can reasonably expect this code to be compiled for, these pedantic nitpicks aren't really a problem.
+// Since we're limited as to what we can do with pre-processor #if checks, these checks are not nearly as through as they should be.
+
+#if (UINT_MAX != 0xffffffffU) || (INT_MIN != (-0x7fffffff-1)) || (ULLONG_MAX != 0xffffffffffffffffULL) || (LLONG_MIN != (-0x7fffffffffffffffLL-1LL))
+#error JSONKit requires the C 'int' and 'long long' types to be 32 and 64 bits respectively.
+#endif
+
+#if !defined(__LP64__) && ((UINT_MAX != ULONG_MAX) || (INT_MAX != LONG_MAX) || (INT_MIN != LONG_MIN) || (WORD_BIT != LONG_BIT))
+#error JSONKit requires the C 'int' and 'long' types to be the same on 32-bit architectures.
+#endif
+
+// Cocoa / Foundation uses NS*Integer as the type for a lot of arguments. We make sure that NS*Integer is something we are expecting and is reasonably compatible with size_t / ssize_t
+
+#if (NSUIntegerMax != ULONG_MAX) || (NSIntegerMax != LONG_MAX) || (NSIntegerMin != LONG_MIN)
+#error JSONKit requires NSInteger and NSUInteger to be the same size as the C 'long' type.
+#endif
+
+#if (NSUIntegerMax != SIZE_MAX) || (NSIntegerMax != SSIZE_MAX)
+#error JSONKit requires NSInteger and NSUInteger to be the same size as the C 'size_t' type.
+#endif
+
+
+// For DJB hash.
+#define JK_HASH_INIT (1402737925UL)
+
+// Use __builtin_clz() instead of trailingBytesForUTF8[] table lookup.
+#define JK_FAST_TRAILING_BYTES
+
+// JK_CACHE_SLOTS must be a power of 2. Default size is 1024 slots.
+#define JK_CACHE_SLOTS_BITS (10)
+#define JK_CACHE_SLOTS (1UL << JK_CACHE_SLOTS_BITS)
+// JK_CACHE_PROBES is the number of probe attempts.
+#define JK_CACHE_PROBES (4UL)
+// JK_INIT_CACHE_AGE must be < (1 << AGE) - 1, where AGE is sizeof(typeof(AGE)) * 8.
+#define JK_INIT_CACHE_AGE (0)
+
+// JK_TOKENBUFFER_SIZE is the default stack size for the temporary buffer used to hold "non-simple" strings (i.e., contains \ escapes)
+#define JK_TOKENBUFFER_SIZE (1024UL * 2UL)
+
+// JK_STACK_OBJS is the default number of spaces reserved on the stack for temporarily storing pointers to Obj-C objects before they can be transferred to a NSArray / NSDictionary.
+#define JK_STACK_OBJS (1024UL * 1UL)
+
+#define JK_JSONBUFFER_SIZE (1024UL * 4UL)
+#define JK_UTF8BUFFER_SIZE (1024UL * 16UL)
+
+#define JK_ENCODE_CACHE_SLOTS (1024UL)
+
+
+#if defined (__GNUC__) && (__GNUC__ >= 4)
+#define JK_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__))
+#define JK_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect))
+#define JK_EXPECT_T(cond) JK_EXPECTED(cond, 1U)
+#define JK_EXPECT_F(cond) JK_EXPECTED(cond, 0U)
+#define JK_PREFETCH(ptr) __builtin_prefetch(ptr)
+#else // defined (__GNUC__) && (__GNUC__ >= 4)
+#define JK_ATTRIBUTES(attr, ...)
+#define JK_EXPECTED(cond, expect) (cond)
+#define JK_EXPECT_T(cond) (cond)
+#define JK_EXPECT_F(cond) (cond)
+#define JK_PREFETCH(ptr)
+#endif // defined (__GNUC__) && (__GNUC__ >= 4)
+
+#define JK_STATIC_INLINE static __inline__ JK_ATTRIBUTES(always_inline)
+#define JK_ALIGNED(arg) JK_ATTRIBUTES(aligned(arg))
+#define JK_UNUSED_ARG JK_ATTRIBUTES(unused)
+#define JK_WARN_UNUSED JK_ATTRIBUTES(warn_unused_result)
+#define JK_WARN_UNUSED_CONST JK_ATTRIBUTES(warn_unused_result, const)
+#define JK_WARN_UNUSED_PURE JK_ATTRIBUTES(warn_unused_result, pure)
+#define JK_WARN_UNUSED_SENTINEL JK_ATTRIBUTES(warn_unused_result, sentinel)
+#define JK_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(nonnull(arg, ##__VA_ARGS__))
+#define JK_WARN_UNUSED_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(warn_unused_result, nonnull(arg, ##__VA_ARGS__))
+#define JK_WARN_UNUSED_CONST_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(warn_unused_result, const, nonnull(arg, ##__VA_ARGS__))
+#define JK_WARN_UNUSED_PURE_NONNULL_ARGS(arg, ...) JK_ATTRIBUTES(warn_unused_result, pure, nonnull(arg, ##__VA_ARGS__))
+
+#if defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
+#define JK_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) JK_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__), alloc_size(as))
+#else // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
+#define JK_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) JK_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__))
+#endif // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
+
+
+@class JKArray, JKDictionaryEnumerator, JKDictionary;
+
+enum {
+ JSONNumberStateStart = 0,
+ JSONNumberStateFinished = 1,
+ JSONNumberStateError = 2,
+ JSONNumberStateWholeNumberStart = 3,
+ JSONNumberStateWholeNumberMinus = 4,
+ JSONNumberStateWholeNumberZero = 5,
+ JSONNumberStateWholeNumber = 6,
+ JSONNumberStatePeriod = 7,
+ JSONNumberStateFractionalNumberStart = 8,
+ JSONNumberStateFractionalNumber = 9,
+ JSONNumberStateExponentStart = 10,
+ JSONNumberStateExponentPlusMinus = 11,
+ JSONNumberStateExponent = 12,
+};
+
+enum {
+ JSONStringStateStart = 0,
+ JSONStringStateParsing = 1,
+ JSONStringStateFinished = 2,
+ JSONStringStateError = 3,
+ JSONStringStateEscape = 4,
+ JSONStringStateEscapedUnicode1 = 5,
+ JSONStringStateEscapedUnicode2 = 6,
+ JSONStringStateEscapedUnicode3 = 7,
+ JSONStringStateEscapedUnicode4 = 8,
+ JSONStringStateEscapedUnicodeSurrogate1 = 9,
+ JSONStringStateEscapedUnicodeSurrogate2 = 10,
+ JSONStringStateEscapedUnicodeSurrogate3 = 11,
+ JSONStringStateEscapedUnicodeSurrogate4 = 12,
+ JSONStringStateEscapedNeedEscapeForSurrogate = 13,
+ JSONStringStateEscapedNeedEscapedUForSurrogate = 14,
+};
+
+enum {
+ JKParseAcceptValue = (1 << 0),
+ JKParseAcceptComma = (1 << 1),
+ JKParseAcceptEnd = (1 << 2),
+ JKParseAcceptValueOrEnd = (JKParseAcceptValue | JKParseAcceptEnd),
+ JKParseAcceptCommaOrEnd = (JKParseAcceptComma | JKParseAcceptEnd),
+};
+
+enum {
+ JKClassUnknown = 0,
+ JKClassString = 1,
+ JKClassNumber = 2,
+ JKClassArray = 3,
+ JKClassDictionary = 4,
+ JKClassNull = 5,
+};
+
+enum {
+ JKManagedBufferOnStack = 1,
+ JKManagedBufferOnHeap = 2,
+ JKManagedBufferLocationMask = (0x3),
+ JKManagedBufferLocationShift = (0),
+
+ JKManagedBufferMustFree = (1 << 2),
+};
+typedef JKFlags JKManagedBufferFlags;
+
+enum {
+ JKObjectStackOnStack = 1,
+ JKObjectStackOnHeap = 2,
+ JKObjectStackLocationMask = (0x3),
+ JKObjectStackLocationShift = (0),
+
+ JKObjectStackMustFree = (1 << 2),
+};
+typedef JKFlags JKObjectStackFlags;
+
+enum {
+ JKTokenTypeInvalid = 0,
+ JKTokenTypeNumber = 1,
+ JKTokenTypeString = 2,
+ JKTokenTypeObjectBegin = 3,
+ JKTokenTypeObjectEnd = 4,
+ JKTokenTypeArrayBegin = 5,
+ JKTokenTypeArrayEnd = 6,
+ JKTokenTypeSeparator = 7,
+ JKTokenTypeComma = 8,
+ JKTokenTypeTrue = 9,
+ JKTokenTypeFalse = 10,
+ JKTokenTypeNull = 11,
+ JKTokenTypeWhiteSpace = 12,
+};
+typedef NSUInteger JKTokenType;
+
+// These are prime numbers to assist with hash slot probing.
+enum {
+ JKValueTypeNone = 0,
+ JKValueTypeString = 5,
+ JKValueTypeLongLong = 7,
+ JKValueTypeUnsignedLongLong = 11,
+ JKValueTypeDouble = 13,
+};
+typedef NSUInteger JKValueType;
+
+enum {
+ JKEncodeOptionAsData = 1,
+ JKEncodeOptionAsString = 2,
+ JKEncodeOptionAsTypeMask = 0x7,
+ JKEncodeOptionCollectionObj = (1 << 3),
+ JKEncodeOptionStringObj = (1 << 4),
+ JKEncodeOptionStringObjTrimQuotes = (1 << 5),
+
+};
+typedef NSUInteger JKEncodeOptionType;
+
+typedef NSUInteger JKHash;
+
+typedef struct JKTokenCacheItem JKTokenCacheItem;
+typedef struct JKTokenCache JKTokenCache;
+typedef struct JKTokenValue JKTokenValue;
+typedef struct JKParseToken JKParseToken;
+typedef struct JKPtrRange JKPtrRange;
+typedef struct JKObjectStack JKObjectStack;
+typedef struct JKBuffer JKBuffer;
+typedef struct JKConstBuffer JKConstBuffer;
+typedef struct JKConstPtrRange JKConstPtrRange;
+typedef struct JKRange JKRange;
+typedef struct JKManagedBuffer JKManagedBuffer;
+typedef struct JKFastClassLookup JKFastClassLookup;
+typedef struct JKEncodeCache JKEncodeCache;
+typedef struct JKEncodeState JKEncodeState;
+typedef struct JKObjCImpCache JKObjCImpCache;
+typedef struct JKHashTableEntry JKHashTableEntry;
+
+typedef id (*NSNumberAllocImp)(id receiver, SEL selector);
+typedef id (*NSNumberInitWithUnsignedLongLongImp)(id receiver, SEL selector, unsigned long long value);
+typedef id (*JKClassFormatterIMP)(id receiver, SEL selector, id object);
+#ifdef __BLOCKS__
+typedef id (^JKClassFormatterBlock)(id formatObject);
+#endif
+
+
+struct JKPtrRange {
+ unsigned char *ptr;
+ size_t length;
+};
+
+struct JKConstPtrRange {
+ const unsigned char *ptr;
+ size_t length;
+};
+
+struct JKRange {
+ size_t location, length;
+};
+
+struct JKManagedBuffer {
+ JKPtrRange bytes;
+ JKManagedBufferFlags flags;
+ size_t roundSizeUpToMultipleOf;
+};
+
+struct JKObjectStack {
+ void **objects, **keys;
+ CFHashCode *cfHashes;
+ size_t count, index, roundSizeUpToMultipleOf;
+ JKObjectStackFlags flags;
+};
+
+struct JKBuffer {
+ JKPtrRange bytes;
+};
+
+struct JKConstBuffer {
+ JKConstPtrRange bytes;
+};
+
+struct JKTokenValue {
+ JKConstPtrRange ptrRange;
+ JKValueType type;
+ JKHash hash;
+ union {
+ long long longLongValue;
+ unsigned long long unsignedLongLongValue;
+ double doubleValue;
+ } number;
+ JKTokenCacheItem *cacheItem;
+};
+
+struct JKParseToken {
+ JKConstPtrRange tokenPtrRange;
+ JKTokenType type;
+ JKTokenValue value;
+ JKManagedBuffer tokenBuffer;
+};
+
+struct JKTokenCacheItem {
+ void *object;
+ JKHash hash;
+ CFHashCode cfHash;
+ size_t size;
+ unsigned char *bytes;
+ JKValueType type;
+};
+
+struct JKTokenCache {
+ JKTokenCacheItem *items;
+ size_t count;
+ unsigned int prng_lfsr;
+ unsigned char age[JK_CACHE_SLOTS];
+};
+
+struct JKObjCImpCache {
+ Class NSNumberClass;
+ NSNumberAllocImp NSNumberAlloc;
+ NSNumberInitWithUnsignedLongLongImp NSNumberInitWithUnsignedLongLong;
+};
+
+struct JKParseState {
+ JKParseOptionFlags parseOptionFlags;
+ JKConstBuffer stringBuffer;
+ size_t atIndex, lineNumber, lineStartIndex;
+ size_t prev_atIndex, prev_lineNumber, prev_lineStartIndex;
+ JKParseToken token;
+ JKObjectStack objectStack;
+ JKTokenCache cache;
+ JKObjCImpCache objCImpCache;
+ NSError *error;
+ int errorIsPrev;
+ BOOL mutableCollections;
+};
+
+struct JKFastClassLookup {
+ void *stringClass;
+ void *numberClass;
+ void *arrayClass;
+ void *dictionaryClass;
+ void *nullClass;
+};
+
+struct JKEncodeCache {
+ id object;
+ size_t offset;
+ size_t length;
+};
+
+struct JKEncodeState {
+ JKManagedBuffer utf8ConversionBuffer;
+ JKManagedBuffer stringBuffer;
+ size_t atIndex;
+ JKFastClassLookup fastClassLookup;
+ JKEncodeCache cache[JK_ENCODE_CACHE_SLOTS];
+ JKSerializeOptionFlags serializeOptionFlags;
+ JKEncodeOptionType encodeOption;
+ size_t depth;
+ NSError *error;
+ id classFormatterDelegate;
+ SEL classFormatterSelector;
+ JKClassFormatterIMP classFormatterIMP;
+#ifdef __BLOCKS__
+ JKClassFormatterBlock classFormatterBlock;
+#endif
+};
+
+// This is a JSONKit private class.
+@interface JKSerializer : NSObject {
+ JKEncodeState *encodeState;
+}
+
+#ifdef __BLOCKS__
+#define JKSERIALIZER_BLOCKS_PROTO id(^)(id object)
+#else
+#define JKSERIALIZER_BLOCKS_PROTO id
+#endif
+
++ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
+- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
+- (void)releaseState;
+
+@end
+
+struct JKHashTableEntry {
+ NSUInteger keyHash;
+ id key, object;
+};
+
+
+typedef uint32_t UTF32; /* at least 32 bits */
+typedef uint16_t UTF16; /* at least 16 bits */
+typedef uint8_t UTF8; /* typically 8 bits */
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+
+#if !defined(JK_FAST_TRAILING_BYTES)
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+#endif
+
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+#define JK_AT_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->atIndex]))
+#define JK_END_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->stringBuffer.bytes.length]))
+
+
+static JKArray *_JKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection);
+static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex);
+static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger objectIndex, id newObject);
+static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex);
+
+
+static NSUInteger _JKDictionaryCapacityForCount(NSUInteger count);
+static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection);
+static JKHashTableEntry *_JKDictionaryHashEntry(JKDictionary *dictionary);
+static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary);
+static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary);
+static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry);
+static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object);
+static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey);
+
+
+static void _JSONDecoderCleanup(JSONDecoder *decoder);
+
+static id _NSStringObjectFromJSONString(NSString *jsonString, JKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection);
+
+
+static void jk_managedBuffer_release(JKManagedBuffer *managedBuffer);
+static void jk_managedBuffer_setToStackBuffer(JKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length);
+static unsigned char *jk_managedBuffer_resize(JKManagedBuffer *managedBuffer, size_t newSize);
+static void jk_objectStack_release(JKObjectStack *objectStack);
+static void jk_objectStack_setToStackBuffer(JKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count);
+static int jk_objectStack_resize(JKObjectStack *objectStack, size_t newCount);
+
+static void jk_error(JKParseState *parseState, NSString *format, ...);
+static int jk_parse_string(JKParseState *parseState);
+static int jk_parse_number(JKParseState *parseState);
+static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr);
+JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState);
+JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState);
+static int jk_parse_next_token(JKParseState *parseState);
+static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String);
+static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex);
+static void *jk_parse_dictionary(JKParseState *parseState);
+static void *jk_parse_array(JKParseState *parseState);
+static void *jk_object_for_token(JKParseState *parseState);
+static void *jk_cachedObjects(JKParseState *parseState);
+JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState);
+JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy);
+
+
+static void jk_encode_error(JKEncodeState *encodeState, NSString *format, ...);
+static int jk_encode_printf(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...);
+static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format);
+static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState);
+static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format);
+static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format);
+static int jk_encode_writen(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length);
+JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr);
+JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object);
+static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr);
+
+#define jk_encode_write1(es, dc, f) (JK_EXPECT_F(_jk_encode_prettyPrint) ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f))
+
+
+JK_STATIC_INLINE size_t jk_min(size_t a, size_t b);
+JK_STATIC_INLINE size_t jk_max(size_t a, size_t b);
+JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c);
+
+// JSONKit v1.4 used both a JKArray : NSArray and JKMutableArray : NSMutableArray, and the same for the dictionary collection type.
+// However, Louis Gerbarg (via cocoa-dev) pointed out that Cocoa / Core Foundation actually implements only a single class that inherits from the
+// mutable version, and keeps an ivar bit for whether or not that instance is mutable. This means that the immutable versions of the collection
+// classes receive the mutating methods, but this is handled by having those methods throw an exception when the ivar bit is set to immutable.
+// We adopt the same strategy here. It's both cleaner and gets rid of the method swizzling hackery used in JSONKit v1.4.
+
+
+// This is a workaround for issue #23 https://github.com/johnezang/JSONKit/pull/23
+// Basically, there seem to be a problem with using +load in static libraries on iOS. However, __attribute__ ((constructor)) does work correctly.
+// Since we do not require anything "special" that +load provides, and we can accomplish the same thing using __attribute__ ((constructor)), the +load logic was moved here.
+
+static Class _JKArrayClass = NULL;
+static size_t _JKArrayInstanceSize = 0UL;
+static Class _JKDictionaryClass = NULL;
+static size_t _JKDictionaryInstanceSize = 0UL;
+
+// For JSONDecoder...
+static Class _jk_NSNumberClass = NULL;
+static NSNumberAllocImp _jk_NSNumberAllocImp = NULL;
+static NSNumberInitWithUnsignedLongLongImp _jk_NSNumberInitWithUnsignedLongLongImp = NULL;
+
+extern void jk_collectionClassLoadTimeInitialization(void) __attribute__ ((constructor));
+
+void jk_collectionClassLoadTimeInitialization(void) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at load time initialization may be less than ideal.
+
+ _JKArrayClass = objc_getClass("JKArray");
+ _JKArrayInstanceSize = jk_max(16UL, class_getInstanceSize(_JKArrayClass));
+
+ _JKDictionaryClass = objc_getClass("JKDictionary");
+ _JKDictionaryInstanceSize = jk_max(16UL, class_getInstanceSize(_JKDictionaryClass));
+
+ // For JSONDecoder...
+ _jk_NSNumberClass = [NSNumber class];
+ _jk_NSNumberAllocImp = (NSNumberAllocImp)[NSNumber methodForSelector:@selector(alloc)];
+
+ // Hacktacular. Need to do it this way due to the nature of class clusters.
+ id temp_NSNumber = [NSNumber alloc];
+ _jk_NSNumberInitWithUnsignedLongLongImp = (NSNumberInitWithUnsignedLongLongImp)[temp_NSNumber methodForSelector:@selector(initWithUnsignedLongLong:)];
+ [[temp_NSNumber init] release];
+ temp_NSNumber = NULL;
+
+ [pool release]; pool = NULL;
+}
+
+
+#pragma mark -
+@interface JKArray : NSMutableArray <NSCopying, NSMutableCopying, NSFastEnumeration> {
+ id *objects;
+ NSUInteger count, capacity, mutations;
+}
+@end
+
+@implementation JKArray
+
++ (id)allocWithZone:(NSZone *)zone
+{
+#pragma unused(zone)
+ [NSException raise:NSInvalidArgumentException format:@"*** - [%@ %@]: The %@ class is private to JSONKit and should not be used in this fashion.", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])];
+ return(NULL);
+}
+
+static JKArray *_JKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection) {
+ NSCParameterAssert((objects != NULL) && (_JKArrayClass != NULL) && (_JKArrayInstanceSize > 0UL));
+ JKArray *array = NULL;
+ if(JK_EXPECT_T((array = (JKArray *)calloc(1UL, _JKArrayInstanceSize)) != NULL)) { // Directly allocate the JKArray instance via calloc.
+ array->isa = _JKArrayClass;
+ if((array = [array init]) == NULL) { return(NULL); }
+ array->capacity = count;
+ array->count = count;
+ if(JK_EXPECT_F((array->objects = (id *)malloc(sizeof(id) * array->capacity)) == NULL)) { [array autorelease]; return(NULL); }
+ memcpy(array->objects, objects, array->capacity * sizeof(id));
+ array->mutations = (mutableCollection == NO) ? 0UL : 1UL;
+ }
+ return(array);
+}
+
+// Note: The caller is responsible for -retaining the object that is to be added.
+static void _JKArrayInsertObjectAtIndex(JKArray *array, id newObject, NSUInteger objectIndex) {
+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex <= array->count) && (newObject != NULL));
+ if(!((array != NULL) && (array->objects != NULL) && (objectIndex <= array->count) && (newObject != NULL))) { [newObject autorelease]; return; }
+ if((array->count + 1UL) >= array->capacity) {
+ id *newObjects = NULL;
+ if((newObjects = (id *)realloc(array->objects, sizeof(id) * (array->capacity + 16UL))) == NULL) { [NSException raise:NSMallocException format:@"Unable to resize objects array."]; }
+ array->objects = newObjects;
+ array->capacity += 16UL;
+ memset(&array->objects[array->count], 0, sizeof(id) * (array->capacity - array->count));
+ }
+ array->count++;
+ if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex + 1UL], &array->objects[objectIndex], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[objectIndex] = NULL; }
+ array->objects[objectIndex] = newObject;
+}
+
+// Note: The caller is responsible for -retaining the object that is to be added.
+static void _JKArrayReplaceObjectAtIndexWithObject(JKArray *array, NSUInteger objectIndex, id newObject) {
+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL));
+ if(!((array != NULL) && (array->objects != NULL) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL))) { [newObject autorelease]; return; }
+ CFRelease(array->objects[objectIndex]);
+ array->objects[objectIndex] = NULL;
+ array->objects[objectIndex] = newObject;
+}
+
+static void _JKArrayRemoveObjectAtIndex(JKArray *array, NSUInteger objectIndex) {
+ NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL));
+ if(!((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL))) { return; }
+ CFRelease(array->objects[objectIndex]);
+ array->objects[objectIndex] = NULL;
+ if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex], &array->objects[objectIndex + 1UL], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[array->count - 1UL] = NULL; }
+ array->count--;
+}
+
+- (void)dealloc
+{
+ if(JK_EXPECT_T(objects != NULL)) {
+ NSUInteger atObject = 0UL;
+ for(atObject = 0UL; atObject < count; atObject++) { if(JK_EXPECT_T(objects[atObject] != NULL)) { CFRelease(objects[atObject]); objects[atObject] = NULL; } }
+ free(objects); objects = NULL;
+ }
+
+ [super dealloc];
+}
+
+- (NSUInteger)count
+{
+ NSParameterAssert((objects != NULL) && (count <= capacity));
+ return(count);
+}
+
+- (void)getObjects:(id *)objectsPtr range:(NSRange)range
+{
+ NSParameterAssert((objects != NULL) && (count <= capacity));
+ if((objectsPtr == NULL) && (NSMaxRange(range) > 0UL)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: pointer to objects array is NULL but range length is %lu", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSMaxRange(range)]; }
+ if((range.location > count) || (NSMaxRange(range) > count)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSMaxRange(range), count]; }
+ memcpy(objectsPtr, objects + range.location, range.length * sizeof(id));
+}
+
+- (id)objectAtIndex:(NSUInteger)objectIndex
+{
+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count]; }
+ NSParameterAssert((objects != NULL) && (count <= capacity) && (objects[objectIndex] != NULL));
+ return(objects[objectIndex]);
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
+{
+ NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (objects != NULL) && (count <= capacity));
+ if(JK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
+ if(JK_EXPECT_F(state->state >= count)) { return(0UL); }
+
+ NSUInteger enumeratedCount = 0UL;
+ while(JK_EXPECT_T(enumeratedCount < len) && JK_EXPECT_T(state->state < count)) { NSParameterAssert(objects[state->state] != NULL); stackbuf[enumeratedCount++] = objects[state->state++]; }
+
+ return(enumeratedCount);
+}
+
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)objectIndex
+{
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(objectIndex > count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count + 1UL]; }
+#ifdef __clang_analyzer__
+ [anObject retain]; // Stupid clang analyzer... Issue #19.
+#else
+ anObject = [anObject retain];
+#endif
+ _JKArrayInsertObjectAtIndex(self, anObject, objectIndex);
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
+}
+
+- (void)removeObjectAtIndex:(NSUInteger)objectIndex
+{
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count]; }
+ _JKArrayRemoveObjectAtIndex(self, objectIndex);
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
+}
+
+- (void)replaceObjectAtIndex:(NSUInteger)objectIndex withObject:(id)anObject
+{
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%lu) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count]; }
+#ifdef __clang_analyzer__
+ [anObject retain]; // Stupid clang analyzer... Issue #19.
+#else
+ anObject = [anObject retain];
+#endif
+ _JKArrayReplaceObjectAtIndexWithObject(self, objectIndex, anObject);
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ NSParameterAssert((objects != NULL) && (count <= capacity));
+ return((mutations == 0UL) ? [self retain] : [(NSArray *)[NSArray allocWithZone:zone] initWithObjects:objects count:count]);
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone
+{
+ NSParameterAssert((objects != NULL) && (count <= capacity));
+ return([(NSMutableArray *)[NSMutableArray allocWithZone:zone] initWithObjects:objects count:count]);
+}
+
+@end
+
+
+#pragma mark -
+@interface JKDictionaryEnumerator : NSEnumerator {
+ id collection;
+ NSUInteger nextObject;
+}
+
+- (id)initWithJKDictionary:(JKDictionary *)initDictionary;
+- (NSArray *)allObjects;
+- (id)nextObject;
+
+@end
+
+@implementation JKDictionaryEnumerator
+
+- (id)initWithJKDictionary:(JKDictionary *)initDictionary
+{
+ NSParameterAssert(initDictionary != NULL);
+ if((self = [super init]) == NULL) { return(NULL); }
+ if((collection = (id)CFRetain(initDictionary)) == NULL) { [self autorelease]; return(NULL); }
+ return(self);
+}
+
+- (void)dealloc
+{
+ if(collection != NULL) { CFRelease(collection); collection = NULL; }
+ [super dealloc];
+}
+
+- (NSArray *)allObjects
+{
+ NSParameterAssert(collection != NULL);
+ NSUInteger count = [(NSDictionary *)collection count], atObject = 0UL;
+ id objects[count];
+
+ while((objects[atObject] = [self nextObject]) != NULL) { NSParameterAssert(atObject < count); atObject++; }
+
+ return([NSArray arrayWithObjects:objects count:atObject]);
+}
+
+- (id)nextObject
+{
+ NSParameterAssert((collection != NULL) && (_JKDictionaryHashEntry(collection) != NULL));
+ JKHashTableEntry *entry = _JKDictionaryHashEntry(collection);
+ NSUInteger capacity = _JKDictionaryCapacity(collection);
+ id returnObject = NULL;
+
+ if(entry != NULL) { while((nextObject < capacity) && ((returnObject = entry[nextObject++].key) == NULL)) { /* ... */ } }
+
+ return(returnObject);
+}
+
+@end
+
+#pragma mark -
+@interface JKDictionary : NSMutableDictionary <NSCopying, NSMutableCopying, NSFastEnumeration> {
+ NSUInteger count, capacity, mutations;
+ JKHashTableEntry *entry;
+}
+@end
+
+@implementation JKDictionary
+
++ (id)allocWithZone:(NSZone *)zone
+{
+#pragma unused(zone)
+ [NSException raise:NSInvalidArgumentException format:@"*** - [%@ %@]: The %@ class is private to JSONKit and should not be used in this fashion.", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])];
+ return(NULL);
+}
+
+// These values are taken from Core Foundation CF-550 CFBasicHash.m. As a bonus, they align very well with our JKHashTableEntry struct too.
+static const NSUInteger jk_dictionaryCapacities[] = {
+ 0UL, 3UL, 7UL, 13UL, 23UL, 41UL, 71UL, 127UL, 191UL, 251UL, 383UL, 631UL, 1087UL, 1723UL,
+ 2803UL, 4523UL, 7351UL, 11959UL, 19447UL, 31231UL, 50683UL, 81919UL, 132607UL,
+ 214519UL, 346607UL, 561109UL, 907759UL, 1468927UL, 2376191UL, 3845119UL,
+ 6221311UL, 10066421UL, 16287743UL, 26354171UL, 42641881UL, 68996069UL,
+ 111638519UL, 180634607UL, 292272623UL, 472907251UL
+};
+
+static NSUInteger _JKDictionaryCapacityForCount(NSUInteger count) {
+ NSUInteger bottom = 0UL, top = sizeof(jk_dictionaryCapacities) / sizeof(NSUInteger), mid = 0UL, tableSize = lround(floor((count) * 1.33));
+ while(top > bottom) { mid = (top + bottom) / 2UL; if(jk_dictionaryCapacities[mid] < tableSize) { bottom = mid + 1UL; } else { top = mid; } }
+ return(jk_dictionaryCapacities[bottom]);
+}
+
+static void _JKDictionaryResizeIfNeccessary(JKDictionary *dictionary) {
+ NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
+
+ NSUInteger capacityForCount = 0UL;
+ if(dictionary->capacity < (capacityForCount = _JKDictionaryCapacityForCount(dictionary->count + 1UL))) { // resize
+ NSUInteger oldCapacity = dictionary->capacity;
+#ifndef NS_BLOCK_ASSERTIONS
+ NSUInteger oldCount = dictionary->count;
+#endif
+ JKHashTableEntry *oldEntry = dictionary->entry;
+ if(JK_EXPECT_F((dictionary->entry = (JKHashTableEntry *)calloc(1UL, sizeof(JKHashTableEntry) * capacityForCount)) == NULL)) { [NSException raise:NSMallocException format:@"Unable to allocate memory for hash table."]; }
+ dictionary->capacity = capacityForCount;
+ dictionary->count = 0UL;
+
+ NSUInteger idx = 0UL;
+ for(idx = 0UL; idx < oldCapacity; idx++) { if(oldEntry[idx].key != NULL) { _JKDictionaryAddObject(dictionary, oldEntry[idx].keyHash, oldEntry[idx].key, oldEntry[idx].object); oldEntry[idx].keyHash = 0UL; oldEntry[idx].key = NULL; oldEntry[idx].object = NULL; } }
+ NSCParameterAssert((oldCount == dictionary->count));
+ free(oldEntry); oldEntry = NULL;
+ }
+}
+
+static JKDictionary *_JKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection) {
+ NSCParameterAssert((keys != NULL) && (keyHashes != NULL) && (objects != NULL) && (_JKDictionaryClass != NULL) && (_JKDictionaryInstanceSize > 0UL));
+ JKDictionary *dictionary = NULL;
+ if(JK_EXPECT_T((dictionary = (JKDictionary *)calloc(1UL, _JKDictionaryInstanceSize)) != NULL)) { // Directly allocate the JKDictionary instance via calloc.
+ dictionary->isa = _JKDictionaryClass;
+ if((dictionary = [dictionary init]) == NULL) { return(NULL); }
+ dictionary->capacity = _JKDictionaryCapacityForCount(count);
+ dictionary->count = 0UL;
+
+ if(JK_EXPECT_F((dictionary->entry = (JKHashTableEntry *)calloc(1UL, sizeof(JKHashTableEntry) * dictionary->capacity)) == NULL)) { [dictionary autorelease]; return(NULL); }
+
+ NSUInteger idx = 0UL;
+ for(idx = 0UL; idx < count; idx++) { _JKDictionaryAddObject(dictionary, keyHashes[idx], keys[idx], objects[idx]); }
+
+ dictionary->mutations = (mutableCollection == NO) ? 0UL : 1UL;
+ }
+ return(dictionary);
+}
+
+- (void)dealloc
+{
+ if(JK_EXPECT_T(entry != NULL)) {
+ NSUInteger atEntry = 0UL;
+ for(atEntry = 0UL; atEntry < capacity; atEntry++) {
+ if(JK_EXPECT_T(entry[atEntry].key != NULL)) { CFRelease(entry[atEntry].key); entry[atEntry].key = NULL; }
+ if(JK_EXPECT_T(entry[atEntry].object != NULL)) { CFRelease(entry[atEntry].object); entry[atEntry].object = NULL; }
+ }
+
+ free(entry); entry = NULL;
+ }
+
+ [super dealloc];
+}
+
+static JKHashTableEntry *_JKDictionaryHashEntry(JKDictionary *dictionary) {
+ NSCParameterAssert(dictionary != NULL);
+ return(dictionary->entry);
+}
+
+static NSUInteger _JKDictionaryCapacity(JKDictionary *dictionary) {
+ NSCParameterAssert(dictionary != NULL);
+ return(dictionary->capacity);
+}
+
+static void _JKDictionaryRemoveObjectWithEntry(JKDictionary *dictionary, JKHashTableEntry *entry) {
+ NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL) && (dictionary->count <= dictionary->capacity));
+ CFRelease(entry->key); entry->key = NULL;
+ CFRelease(entry->object); entry->object = NULL;
+ entry->keyHash = 0UL;
+ dictionary->count--;
+ // In order for certain invariants that are used to speed up the search for a particular key, we need to "re-add" all the entries in the hash table following this entry until we hit a NULL entry.
+ NSUInteger removeIdx = entry - dictionary->entry, idx = 0UL;
+ NSCParameterAssert((removeIdx < dictionary->capacity));
+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
+ NSUInteger entryIdx = (removeIdx + idx + 1UL) % dictionary->capacity;
+ JKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
+ if(atEntry->key == NULL) { break; }
+ NSUInteger keyHash = atEntry->keyHash;
+ id key = atEntry->key, object = atEntry->object;
+ NSCParameterAssert(object != NULL);
+ atEntry->keyHash = 0UL;
+ atEntry->key = NULL;
+ atEntry->object = NULL;
+ NSUInteger addKeyEntry = keyHash % dictionary->capacity, addIdx = 0UL;
+ for(addIdx = 0UL; addIdx < dictionary->capacity; addIdx++) {
+ JKHashTableEntry *atAddEntry = &dictionary->entry[((addKeyEntry + addIdx) % dictionary->capacity)];
+ if(JK_EXPECT_T(atAddEntry->key == NULL)) { NSCParameterAssert((atAddEntry->keyHash == 0UL) && (atAddEntry->object == NULL)); atAddEntry->key = key; atAddEntry->object = object; atAddEntry->keyHash = keyHash; break; }
+ }
+ }
+}
+
+static void _JKDictionaryAddObject(JKDictionary *dictionary, NSUInteger keyHash, id key, id object) {
+ NSCParameterAssert((dictionary != NULL) && (key != NULL) && (object != NULL) && (dictionary->count < dictionary->capacity) && (dictionary->entry != NULL));
+ NSUInteger keyEntry = keyHash % dictionary->capacity, idx = 0UL;
+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
+ NSUInteger entryIdx = (keyEntry + idx) % dictionary->capacity;
+ JKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
+ if(JK_EXPECT_F(atEntry->keyHash == keyHash) && JK_EXPECT_T(atEntry->key != NULL) && (JK_EXPECT_F(key == atEntry->key) || JK_EXPECT_F(CFEqual(atEntry->key, key)))) { _JKDictionaryRemoveObjectWithEntry(dictionary, atEntry); }
+ if(JK_EXPECT_T(atEntry->key == NULL)) { NSCParameterAssert((atEntry->keyHash == 0UL) && (atEntry->object == NULL)); atEntry->key = key; atEntry->object = object; atEntry->keyHash = keyHash; dictionary->count++; return; }
+ }
+
+ // We should never get here. If we do, we -release the key / object because it's our responsibility.
+ CFRelease(key);
+ CFRelease(object);
+}
+
+- (NSUInteger)count
+{
+ return(count);
+}
+
+static JKHashTableEntry *_JKDictionaryHashTableEntryForKey(JKDictionary *dictionary, id aKey) {
+ NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
+ if((aKey == NULL) || (dictionary->capacity == 0UL)) { return(NULL); }
+ NSUInteger keyHash = CFHash(aKey), keyEntry = (keyHash % dictionary->capacity), idx = 0UL;
+ JKHashTableEntry *atEntry = NULL;
+ for(idx = 0UL; idx < dictionary->capacity; idx++) {
+ atEntry = &dictionary->entry[(keyEntry + idx) % dictionary->capacity];
+ if(JK_EXPECT_T(atEntry->keyHash == keyHash) && JK_EXPECT_T(atEntry->key != NULL) && ((atEntry->key == aKey) || CFEqual(atEntry->key, aKey))) { NSCParameterAssert(atEntry->object != NULL); return(atEntry); break; }
+ if(JK_EXPECT_F(atEntry->key == NULL)) { NSCParameterAssert(atEntry->object == NULL); return(NULL); break; } // If the key was in the table, we would have found it by now.
+ }
+ return(NULL);
+}
+
+- (id)objectForKey:(id)aKey
+{
+ NSParameterAssert((entry != NULL) && (count <= capacity));
+ JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey);
+ return((entryForKey != NULL) ? entryForKey->object : NULL);
+}
+
+- (void)getObjects:(id *)objects andKeys:(id *)keys
+{
+ NSParameterAssert((entry != NULL) && (count <= capacity));
+ NSUInteger atEntry = 0UL; NSUInteger arrayIdx = 0UL;
+ for(atEntry = 0UL; atEntry < capacity; atEntry++) {
+ if(JK_EXPECT_T(entry[atEntry].key != NULL)) {
+ NSCParameterAssert((entry[atEntry].object != NULL) && (arrayIdx < count));
+ if(JK_EXPECT_T(keys != NULL)) { keys[arrayIdx] = entry[atEntry].key; }
+ if(JK_EXPECT_T(objects != NULL)) { objects[arrayIdx] = entry[atEntry].object; }
+ arrayIdx++;
+ }
+ }
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
+{
+ NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (entry != NULL) && (count <= capacity));
+ if(JK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
+ if(JK_EXPECT_F(state->state >= capacity)) { return(0UL); }
+
+ NSUInteger enumeratedCount = 0UL;
+ while(JK_EXPECT_T(enumeratedCount < len) && JK_EXPECT_T(state->state < capacity)) { if(JK_EXPECT_T(entry[state->state].key != NULL)) { stackbuf[enumeratedCount++] = entry[state->state].key; } state->state++; }
+
+ return(enumeratedCount);
+}
+
+- (NSEnumerator *)keyEnumerator
+{
+ return([[[JKDictionaryEnumerator alloc] initWithJKDictionary:self] autorelease]);
+}
+
+- (void)setObject:(id)anObject forKey:(id)aKey
+{
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; }
+
+ _JKDictionaryResizeIfNeccessary(self);
+#ifndef __clang_analyzer__
+ aKey = [aKey copy]; // Why on earth would clang complain that this -copy "might leak",
+ anObject = [anObject retain]; // but this -retain doesn't!?
+#endif // __clang_analyzer__
+ _JKDictionaryAddObject(self, CFHash(aKey), aKey, anObject);
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
+}
+
+- (void)removeObjectForKey:(id)aKey
+{
+ if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to remove nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
+ JKHashTableEntry *entryForKey = _JKDictionaryHashTableEntryForKey(self, aKey);
+ if(entryForKey != NULL) {
+ _JKDictionaryRemoveObjectWithEntry(self, entryForKey);
+ mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
+ }
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ NSParameterAssert((entry != NULL) && (count <= capacity));
+ return((mutations == 0UL) ? [self retain] : [[NSDictionary allocWithZone:zone] initWithDictionary:self]);
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone
+{
+ NSParameterAssert((entry != NULL) && (count <= capacity));
+ return([[NSMutableDictionary allocWithZone:zone] initWithDictionary:self]);
+}
+
+@end
+
+
+
+#pragma mark -
+
+JK_STATIC_INLINE size_t jk_min(size_t a, size_t b) { return((a < b) ? a : b); }
+JK_STATIC_INLINE size_t jk_max(size_t a, size_t b) { return((a > b) ? a : b); }
+
+JK_STATIC_INLINE JKHash jk_calculateHash(JKHash currentHash, unsigned char c) { return((((currentHash << 5) + currentHash) + (c - 29)) ^ (currentHash >> 19)); }
+
+
+static void jk_error(JKParseState *parseState, NSString *format, ...) {
+ NSCParameterAssert((parseState != NULL) && (format != NULL));
+
+ va_list varArgsList;
+ va_start(varArgsList, format);
+ NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
+ va_end(varArgsList);
+
+#if 0
+ const unsigned char *lineStart = parseState->stringBuffer.bytes.ptr + parseState->lineStartIndex;
+ const unsigned char *lineEnd = lineStart;
+ const unsigned char *atCharacterPtr = NULL;
+
+ for(atCharacterPtr = lineStart; atCharacterPtr < JK_END_STRING_PTR(parseState); atCharacterPtr++) { lineEnd = atCharacterPtr; if(jk_parse_is_newline(parseState, atCharacterPtr)) { break; } }
+
+ NSString *lineString = @"", *carretString = @"";
+ if(lineStart < JK_END_STRING_PTR(parseState)) {
+ lineString = [[[NSString alloc] initWithBytes:lineStart length:(lineEnd - lineStart) encoding:NSUTF8StringEncoding] autorelease];
+ carretString = [NSString stringWithFormat:@"%*.*s^", (int)(parseState->atIndex - parseState->lineStartIndex), (int)(parseState->atIndex - parseState->lineStartIndex), " "];
+ }
+#endif
+
+ if(parseState->error == NULL) {
+ parseState->error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ formatString, NSLocalizedDescriptionKey,
+ [NSNumber numberWithUnsignedLong:parseState->atIndex], @"JKAtIndexKey",
+ [NSNumber numberWithUnsignedLong:parseState->lineNumber], @"JKLineNumberKey",
+ //lineString, @"JKErrorLine0Key",
+ //carretString, @"JKErrorLine1Key",
+ NULL]];
+ }
+}
+
+#pragma mark -
+#pragma mark Buffer and Object Stack management functions
+
+static void jk_managedBuffer_release(JKManagedBuffer *managedBuffer) {
+ if((managedBuffer->flags & JKManagedBufferMustFree)) {
+ if(managedBuffer->bytes.ptr != NULL) { free(managedBuffer->bytes.ptr); managedBuffer->bytes.ptr = NULL; }
+ managedBuffer->flags &= ~JKManagedBufferMustFree;
+ }
+
+ managedBuffer->bytes.ptr = NULL;
+ managedBuffer->bytes.length = 0UL;
+ managedBuffer->flags &= ~JKManagedBufferLocationMask;
+}
+
+static void jk_managedBuffer_setToStackBuffer(JKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length) {
+ jk_managedBuffer_release(managedBuffer);
+ managedBuffer->bytes.ptr = ptr;
+ managedBuffer->bytes.length = length;
+ managedBuffer->flags = (managedBuffer->flags & ~JKManagedBufferLocationMask) | JKManagedBufferOnStack;
+}
+
+static unsigned char *jk_managedBuffer_resize(JKManagedBuffer *managedBuffer, size_t newSize) {
+ size_t roundedUpNewSize = newSize;
+
+ if(managedBuffer->roundSizeUpToMultipleOf > 0UL) { roundedUpNewSize = newSize + ((managedBuffer->roundSizeUpToMultipleOf - (newSize % managedBuffer->roundSizeUpToMultipleOf)) % managedBuffer->roundSizeUpToMultipleOf); }
+
+ if((roundedUpNewSize != managedBuffer->bytes.length) && (roundedUpNewSize > managedBuffer->bytes.length)) {
+ if((managedBuffer->flags & JKManagedBufferLocationMask) == JKManagedBufferOnStack) {
+ NSCParameterAssert((managedBuffer->flags & JKManagedBufferMustFree) == 0);
+ unsigned char *newBuffer = NULL, *oldBuffer = managedBuffer->bytes.ptr;
+
+ if((newBuffer = (unsigned char *)malloc(roundedUpNewSize)) == NULL) { return(NULL); }
+ memcpy(newBuffer, oldBuffer, jk_min(managedBuffer->bytes.length, roundedUpNewSize));
+ managedBuffer->flags = (managedBuffer->flags & ~JKManagedBufferLocationMask) | (JKManagedBufferOnHeap | JKManagedBufferMustFree);
+ managedBuffer->bytes.ptr = newBuffer;
+ managedBuffer->bytes.length = roundedUpNewSize;
+ } else {
+ NSCParameterAssert(((managedBuffer->flags & JKManagedBufferMustFree) != 0) && ((managedBuffer->flags & JKManagedBufferLocationMask) == JKManagedBufferOnHeap));
+ if((managedBuffer->bytes.ptr = (unsigned char *)reallocf(managedBuffer->bytes.ptr, roundedUpNewSize)) == NULL) { return(NULL); }
+ managedBuffer->bytes.length = roundedUpNewSize;
+ }
+ }
+
+ return(managedBuffer->bytes.ptr);
+}
+
+
+
+static void jk_objectStack_release(JKObjectStack *objectStack) {
+ NSCParameterAssert(objectStack != NULL);
+
+ NSCParameterAssert(objectStack->index <= objectStack->count);
+ size_t atIndex = 0UL;
+ for(atIndex = 0UL; atIndex < objectStack->index; atIndex++) {
+ if(objectStack->objects[atIndex] != NULL) { CFRelease(objectStack->objects[atIndex]); objectStack->objects[atIndex] = NULL; }
+ if(objectStack->keys[atIndex] != NULL) { CFRelease(objectStack->keys[atIndex]); objectStack->keys[atIndex] = NULL; }
+ }
+ objectStack->index = 0UL;
+
+ if(objectStack->flags & JKObjectStackMustFree) {
+ NSCParameterAssert((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap);
+ if(objectStack->objects != NULL) { free(objectStack->objects); objectStack->objects = NULL; }
+ if(objectStack->keys != NULL) { free(objectStack->keys); objectStack->keys = NULL; }
+ if(objectStack->cfHashes != NULL) { free(objectStack->cfHashes); objectStack->cfHashes = NULL; }
+ objectStack->flags &= ~JKObjectStackMustFree;
+ }
+
+ objectStack->objects = NULL;
+ objectStack->keys = NULL;
+ objectStack->cfHashes = NULL;
+
+ objectStack->count = 0UL;
+ objectStack->flags &= ~JKObjectStackLocationMask;
+}
+
+static void jk_objectStack_setToStackBuffer(JKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count) {
+ NSCParameterAssert((objectStack != NULL) && (objects != NULL) && (keys != NULL) && (cfHashes != NULL) && (count > 0UL));
+ jk_objectStack_release(objectStack);
+ objectStack->objects = objects;
+ objectStack->keys = keys;
+ objectStack->cfHashes = cfHashes;
+ objectStack->count = count;
+ objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | JKObjectStackOnStack;
+#ifndef NS_BLOCK_ASSERTIONS
+ size_t idx;
+ for(idx = 0UL; idx < objectStack->count; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
+#endif
+}
+
+static int jk_objectStack_resize(JKObjectStack *objectStack, size_t newCount) {
+ size_t roundedUpNewCount = newCount;
+ int returnCode = 0;
+
+ void **newObjects = NULL, **newKeys = NULL;
+ CFHashCode *newCFHashes = NULL;
+
+ if(objectStack->roundSizeUpToMultipleOf > 0UL) { roundedUpNewCount = newCount + ((objectStack->roundSizeUpToMultipleOf - (newCount % objectStack->roundSizeUpToMultipleOf)) % objectStack->roundSizeUpToMultipleOf); }
+
+ if((roundedUpNewCount != objectStack->count) && (roundedUpNewCount > objectStack->count)) {
+ if((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnStack) {
+ NSCParameterAssert((objectStack->flags & JKObjectStackMustFree) == 0);
+
+ if((newObjects = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
+ memcpy(newObjects, objectStack->objects, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
+ if((newKeys = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
+ memcpy(newKeys, objectStack->keys, jk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
+
+ if((newCFHashes = (CFHashCode *)calloc(1UL, roundedUpNewCount * sizeof(CFHashCode))) == NULL) { returnCode = 1; goto errorExit; }
+ memcpy(newCFHashes, objectStack->cfHashes, jk_min(objectStack->count, roundedUpNewCount) * sizeof(CFHashCode));
+
+ objectStack->flags = (objectStack->flags & ~JKObjectStackLocationMask) | (JKObjectStackOnHeap | JKObjectStackMustFree);
+ objectStack->objects = newObjects; newObjects = NULL;
+ objectStack->keys = newKeys; newKeys = NULL;
+ objectStack->cfHashes = newCFHashes; newCFHashes = NULL;
+ objectStack->count = roundedUpNewCount;
+ } else {
+ NSCParameterAssert(((objectStack->flags & JKObjectStackMustFree) != 0) && ((objectStack->flags & JKObjectStackLocationMask) == JKObjectStackOnHeap));
+ if((newObjects = (void ** )realloc(objectStack->objects, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->objects = newObjects; newObjects = NULL; } else { returnCode = 1; goto errorExit; }
+ if((newKeys = (void ** )realloc(objectStack->keys, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->keys = newKeys; newKeys = NULL; } else { returnCode = 1; goto errorExit; }
+ if((newCFHashes = (CFHashCode *)realloc(objectStack->cfHashes, roundedUpNewCount * sizeof(CFHashCode))) != NULL) { objectStack->cfHashes = newCFHashes; newCFHashes = NULL; } else { returnCode = 1; goto errorExit; }
+
+#ifndef NS_BLOCK_ASSERTIONS
+ size_t idx;
+ for(idx = objectStack->count; idx < roundedUpNewCount; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
+#endif
+ objectStack->count = roundedUpNewCount;
+ }
+ }
+
+ errorExit:
+ if(newObjects != NULL) { free(newObjects); newObjects = NULL; }
+ if(newKeys != NULL) { free(newKeys); newKeys = NULL; }
+ if(newCFHashes != NULL) { free(newCFHashes); newCFHashes = NULL; }
+
+ return(returnCode);
+}
+
+////////////
+#pragma mark -
+#pragma mark Unicode related functions
+
+JK_STATIC_INLINE ConversionResult isValidCodePoint(UTF32 *u32CodePoint) {
+ ConversionResult result = conversionOK;
+ UTF32 ch = *u32CodePoint;
+
+ if(JK_EXPECT_F(ch >= UNI_SUR_HIGH_START) && (JK_EXPECT_T(ch <= UNI_SUR_LOW_END))) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
+ if(JK_EXPECT_F(ch >= 0xFDD0U) && (JK_EXPECT_F(ch <= 0xFDEFU) || JK_EXPECT_F((ch & 0xFFFEU) == 0xFFFEU)) && JK_EXPECT_T(ch <= 0x10FFFFU)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
+ if(JK_EXPECT_F(ch == 0U)) { result = sourceIllegal; ch = UNI_REPLACEMENT_CHAR; goto finished; }
+
+ finished:
+ *u32CodePoint = ch;
+ return(result);
+}
+
+
+static int isLegalUTF8(const UTF8 *source, size_t length) {
+ const UTF8 *srcptr = source + length;
+ UTF8 a;
+
+ switch(length) {
+ default: return(0); // Everything else falls through when "true"...
+ case 4: if(JK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
+ case 3: if(JK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
+ case 2: if(JK_EXPECT_F( (a = (*--srcptr)) > 0xBF )) { return(0); }
+
+ switch(*source) { // no fall-through in this inner switch
+ case 0xE0: if(JK_EXPECT_F(a < 0xA0)) { return(0); } break;
+ case 0xED: if(JK_EXPECT_F(a > 0x9F)) { return(0); } break;
+ case 0xF0: if(JK_EXPECT_F(a < 0x90)) { return(0); } break;
+ case 0xF4: if(JK_EXPECT_F(a > 0x8F)) { return(0); } break;
+ default: if(JK_EXPECT_F(a < 0x80)) { return(0); }
+ }
+
+ case 1: if(JK_EXPECT_F((JK_EXPECT_T(*source < 0xC2)) && JK_EXPECT_F(*source >= 0x80))) { return(0); }
+ }
+
+ if(JK_EXPECT_F(*source > 0xF4)) { return(0); }
+
+ return(1);
+}
+
+static ConversionResult ConvertSingleCodePointInUTF8(const UTF8 *sourceStart, const UTF8 *sourceEnd, UTF8 const **nextUTF8, UTF32 *convertedUTF32) {
+ ConversionResult result = conversionOK;
+ const UTF8 *source = sourceStart;
+ UTF32 ch = 0UL;
+
+#if !defined(JK_FAST_TRAILING_BYTES)
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+#else
+ unsigned short extraBytesToRead = __builtin_clz(((*source)^0xff) << 25);
+#endif
+
+ if(JK_EXPECT_F((source + extraBytesToRead + 1) > sourceEnd) || JK_EXPECT_F(!isLegalUTF8(source, extraBytesToRead + 1))) {
+ source++;
+ while((source < sourceEnd) && (((*source) & 0xc0) == 0x80) && ((source - sourceStart) < (extraBytesToRead + 1))) { source++; }
+ NSCParameterAssert(source <= sourceEnd);
+ result = ((source < sourceEnd) && (((*source) & 0xc0) != 0x80)) ? sourceIllegal : ((sourceStart + extraBytesToRead + 1) > sourceEnd) ? sourceExhausted : sourceIllegal;
+ ch = UNI_REPLACEMENT_CHAR;
+ goto finished;
+ }
+
+ switch(extraBytesToRead) { // The cases all fall through.
+ case 5: ch += *source++; ch <<= 6;
+ case 4: ch += *source++; ch <<= 6;
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ result = isValidCodePoint(&ch);
+
+ finished:
+ *nextUTF8 = source;
+ *convertedUTF32 = ch;
+
+ return(result);
+}
+
+
+static ConversionResult ConvertUTF32toUTF8 (UTF32 u32CodePoint, UTF8 **targetStart, UTF8 *targetEnd) {
+ const UTF32 byteMask = 0xBF, byteMark = 0x80;
+ ConversionResult result = conversionOK;
+ UTF8 *target = *targetStart;
+ UTF32 ch = u32CodePoint;
+ unsigned short bytesToWrite = 0;
+
+ result = isValidCodePoint(&ch);
+
+ // Figure out how many bytes the result will require. Turn any illegally large UTF32 things (> Plane 17) into replacement chars.
+ if(ch < (UTF32)0x80) { bytesToWrite = 1; }
+ else if(ch < (UTF32)0x800) { bytesToWrite = 2; }
+ else if(ch < (UTF32)0x10000) { bytesToWrite = 3; }
+ else if(ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; }
+ else { bytesToWrite = 3; ch = UNI_REPLACEMENT_CHAR; result = sourceIllegal; }
+
+ target += bytesToWrite;
+ if (target > targetEnd) { target -= bytesToWrite; result = targetExhausted; goto finished; }
+
+ switch (bytesToWrite) { // note: everything falls through.
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+ }
+
+ target += bytesToWrite;
+
+ finished:
+ *targetStart = target;
+ return(result);
+}
+
+JK_STATIC_INLINE int jk_string_add_unicodeCodePoint(JKParseState *parseState, uint32_t unicodeCodePoint, size_t *tokenBufferIdx, JKHash *stringHash) {
+ UTF8 *u8s = &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx];
+ ConversionResult result;
+
+ if((result = ConvertUTF32toUTF8(unicodeCodePoint, &u8s, (parseState->token.tokenBuffer.bytes.ptr + parseState->token.tokenBuffer.bytes.length))) != conversionOK) { if(result == targetExhausted) { return(1); } }
+ size_t utf8len = u8s - &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx], nextIdx = (*tokenBufferIdx) + utf8len;
+
+ while(*tokenBufferIdx < nextIdx) { *stringHash = jk_calculateHash(*stringHash, parseState->token.tokenBuffer.bytes.ptr[(*tokenBufferIdx)++]); }
+
+ return(0);
+}
+
+////////////
+#pragma mark -
+#pragma mark Decoding / parsing / deserializing functions
+
+static int jk_parse_string(JKParseState *parseState) {
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
+ const unsigned char *stringStart = JK_AT_STRING_PTR(parseState) + 1;
+ const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
+ const unsigned char *atStringCharacter = stringStart;
+ unsigned char *tokenBuffer = parseState->token.tokenBuffer.bytes.ptr;
+ size_t tokenStartIndex = parseState->atIndex;
+ size_t tokenBufferIdx = 0UL;
+
+ int onlySimpleString = 1, stringState = JSONStringStateStart;
+ uint16_t escapedUnicode1 = 0U, escapedUnicode2 = 0U;
+ uint32_t escapedUnicodeCodePoint = 0U;
+ JKHash stringHash = JK_HASH_INIT;
+
+ while(1) {
+ unsigned long currentChar;
+
+ if(JK_EXPECT_F(atStringCharacter == endOfBuffer)) { /* XXX Add error message */ stringState = JSONStringStateError; goto finishedParsing; }
+
+ if(JK_EXPECT_F((currentChar = *atStringCharacter++) >= 0x80UL)) {
+ const unsigned char *nextValidCharacter = NULL;
+ UTF32 u32ch = 0U;
+ ConversionResult result;
+
+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter - 1, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { goto switchToSlowPath; }
+ stringHash = jk_calculateHash(stringHash, currentChar);
+ while(atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)); stringHash = jk_calculateHash(stringHash, *atStringCharacter++); }
+ continue;
+ } else {
+ if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; goto finishedParsing; }
+
+ if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) {
+ switchToSlowPath:
+ onlySimpleString = 0;
+ stringState = JSONStringStateParsing;
+ tokenBufferIdx = (atStringCharacter - stringStart) - 1L;
+ if(JK_EXPECT_F((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length)) { if((tokenBuffer = jk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { jk_error(parseState, @"Internal error: Unable to resize temporary buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; } }
+ memcpy(tokenBuffer, stringStart, tokenBufferIdx);
+ goto slowMatch;
+ }
+
+ if(JK_EXPECT_F(currentChar < 0x20UL)) { jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing; }
+
+ stringHash = jk_calculateHash(stringHash, currentChar);
+ }
+ }
+
+ slowMatch:
+
+ for(atStringCharacter = (stringStart + ((atStringCharacter - stringStart) - 1L)); (atStringCharacter < endOfBuffer) && (tokenBufferIdx < parseState->token.tokenBuffer.bytes.length); atStringCharacter++) {
+ if((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length) { if((tokenBuffer = jk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { jk_error(parseState, @"Internal error: Unable to resize temporary buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; } }
+
+ NSCParameterAssert(tokenBufferIdx < parseState->token.tokenBuffer.bytes.length);
+
+ unsigned long currentChar = (*atStringCharacter), escapedChar;
+
+ if(JK_EXPECT_T(stringState == JSONStringStateParsing)) {
+ if(JK_EXPECT_T(currentChar >= 0x20UL)) {
+ if(JK_EXPECT_T(currentChar < (unsigned long)0x80)) { // Not a UTF8 sequence
+ if(JK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = JSONStringStateFinished; atStringCharacter++; goto finishedParsing; }
+ if(JK_EXPECT_F(currentChar == (unsigned long)'\\')) { stringState = JSONStringStateEscape; continue; }
+ stringHash = jk_calculateHash(stringHash, currentChar);
+ tokenBuffer[tokenBufferIdx++] = currentChar;
+ continue;
+ } else { // UTF8 sequence
+ const unsigned char *nextValidCharacter = NULL;
+ UTF32 u32ch = 0U;
+ ConversionResult result;
+
+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(atStringCharacter, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) {
+ if((result == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal UTF8 sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
+ if(result == sourceExhausted) { jk_error(parseState, @"End of buffer reached while parsing UTF8 in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; }
+ if(jk_string_add_unicodeCodePoint(parseState, u32ch, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; }
+ atStringCharacter = nextValidCharacter - 1;
+ continue;
+ } else {
+ while(atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = jk_calculateHash(stringHash, *atStringCharacter++); }
+ atStringCharacter--;
+ continue;
+ }
+ }
+ } else { // currentChar < 0x20
+ jk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = JSONStringStateError; goto finishedParsing;
+ }
+
+ } else { // stringState != JSONStringStateParsing
+ int isSurrogate = 1;
+
+ switch(stringState) {
+ case JSONStringStateEscape:
+ switch(currentChar) {
+ case 'u': escapedUnicode1 = 0U; escapedUnicode2 = 0U; escapedUnicodeCodePoint = 0U; stringState = JSONStringStateEscapedUnicode1; break;
+
+ case 'b': escapedChar = '\b'; goto parsedEscapedChar;
+ case 'f': escapedChar = '\f'; goto parsedEscapedChar;
+ case 'n': escapedChar = '\n'; goto parsedEscapedChar;
+ case 'r': escapedChar = '\r'; goto parsedEscapedChar;
+ case 't': escapedChar = '\t'; goto parsedEscapedChar;
+ case '\\': escapedChar = '\\'; goto parsedEscapedChar;
+ case '/': escapedChar = '/'; goto parsedEscapedChar;
+ case '"': escapedChar = '"'; goto parsedEscapedChar;
+
+ parsedEscapedChar:
+ stringState = JSONStringStateParsing;
+ stringHash = jk_calculateHash(stringHash, escapedChar);
+ tokenBuffer[tokenBufferIdx++] = escapedChar;
+ break;
+
+ default: jk_error(parseState, @"Invalid escape sequence found in \"\" string."); stringState = JSONStringStateError; goto finishedParsing; break;
+ }
+ break;
+
+ case JSONStringStateEscapedUnicode1:
+ case JSONStringStateEscapedUnicode2:
+ case JSONStringStateEscapedUnicode3:
+ case JSONStringStateEscapedUnicode4: isSurrogate = 0;
+ case JSONStringStateEscapedUnicodeSurrogate1:
+ case JSONStringStateEscapedUnicodeSurrogate2:
+ case JSONStringStateEscapedUnicodeSurrogate3:
+ case JSONStringStateEscapedUnicodeSurrogate4:
+ {
+ uint16_t hexValue = 0U;
+
+ switch(currentChar) {
+ case '0' ... '9': hexValue = currentChar - '0'; goto parsedHex;
+ case 'a' ... 'f': hexValue = (currentChar - 'a') + 10U; goto parsedHex;
+ case 'A' ... 'F': hexValue = (currentChar - 'A') + 10U; goto parsedHex;
+
+ parsedHex:
+ if(!isSurrogate) { escapedUnicode1 = (escapedUnicode1 << 4) | hexValue; } else { escapedUnicode2 = (escapedUnicode2 << 4) | hexValue; }
+
+ if(stringState == JSONStringStateEscapedUnicode4) {
+ if(((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xE000U))) {
+ if((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xDC00U)) { stringState = JSONStringStateEscapedNeedEscapeForSurrogate; }
+ else if((escapedUnicode1 >= 0xDC00U) && (escapedUnicode1 < 0xE000U)) {
+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = UNI_REPLACEMENT_CHAR; }
+ else { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
+ }
+ }
+ else { escapedUnicodeCodePoint = escapedUnicode1; }
+ }
+
+ if(stringState == JSONStringStateEscapedUnicodeSurrogate4) {
+ if((escapedUnicode2 < 0xdc00) || (escapedUnicode2 > 0xdfff)) {
+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = UNI_REPLACEMENT_CHAR; }
+ else { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
+ }
+ else { escapedUnicodeCodePoint = ((escapedUnicode1 - 0xd800) * 0x400) + (escapedUnicode2 - 0xdc00) + 0x10000; }
+ }
+
+ if((stringState == JSONStringStateEscapedUnicode4) || (stringState == JSONStringStateEscapedUnicodeSurrogate4)) {
+ if((isValidCodePoint(&escapedUnicodeCodePoint) == sourceIllegal) && ((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0)) { jk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
+ stringState = JSONStringStateParsing;
+ if(jk_string_add_unicodeCodePoint(parseState, escapedUnicodeCodePoint, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; }
+ }
+ else if((stringState >= JSONStringStateEscapedUnicode1) && (stringState <= JSONStringStateEscapedUnicodeSurrogate4)) { stringState++; }
+ break;
+
+ default: jk_error(parseState, @"Unexpected character found in \\u Unicode escape sequence. Found '%c', expected [0-9a-fA-F].", currentChar); stringState = JSONStringStateError; goto finishedParsing; break;
+ }
+ }
+ break;
+
+ case JSONStringStateEscapedNeedEscapeForSurrogate:
+ if(currentChar == '\\') { stringState = JSONStringStateEscapedNeedEscapedUForSurrogate; }
+ else {
+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0) { jk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
+ else { stringState = JSONStringStateParsing; atStringCharacter--; if(jk_string_add_unicodeCodePoint(parseState, UNI_REPLACEMENT_CHAR, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; } }
+ }
+ break;
+
+ case JSONStringStateEscapedNeedEscapedUForSurrogate:
+ if(currentChar == 'u') { stringState = JSONStringStateEscapedUnicodeSurrogate1; }
+ else {
+ if((parseState->parseOptionFlags & JKParseOptionLooseUnicode) == 0) { jk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = JSONStringStateError; goto finishedParsing; }
+ else { stringState = JSONStringStateParsing; atStringCharacter -= 2; if(jk_string_add_unicodeCodePoint(parseState, UNI_REPLACEMENT_CHAR, &tokenBufferIdx, &stringHash)) { jk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; } }
+ }
+ break;
+
+ default: jk_error(parseState, @"Internal error: Unknown stringState. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = JSONStringStateError; goto finishedParsing; break;
+ }
+ }
+ }
+
+finishedParsing:
+
+ if(JK_EXPECT_T(stringState == JSONStringStateFinished)) {
+ NSCParameterAssert((parseState->stringBuffer.bytes.ptr + tokenStartIndex) < atStringCharacter);
+
+ parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + tokenStartIndex;
+ parseState->token.tokenPtrRange.length = (atStringCharacter - parseState->token.tokenPtrRange.ptr);
+
+ if(JK_EXPECT_T(onlySimpleString)) {
+ NSCParameterAssert(((parseState->token.tokenPtrRange.ptr + 1) < endOfBuffer) && (parseState->token.tokenPtrRange.length >= 2UL) && (((parseState->token.tokenPtrRange.ptr + 1) + (parseState->token.tokenPtrRange.length - 2)) < endOfBuffer));
+ parseState->token.value.ptrRange.ptr = parseState->token.tokenPtrRange.ptr + 1;
+ parseState->token.value.ptrRange.length = parseState->token.tokenPtrRange.length - 2UL;
+ } else {
+ parseState->token.value.ptrRange.ptr = parseState->token.tokenBuffer.bytes.ptr;
+ parseState->token.value.ptrRange.length = tokenBufferIdx;
+ }
+
+ parseState->token.value.hash = stringHash;
+ parseState->token.value.type = JKValueTypeString;
+ parseState->atIndex = (atStringCharacter - parseState->stringBuffer.bytes.ptr);
+ }
+
+ if(JK_EXPECT_F(stringState != JSONStringStateFinished)) { jk_error(parseState, @"Invalid string."); }
+ return(JK_EXPECT_T(stringState == JSONStringStateFinished) ? 0 : 1);
+}
+
+static int jk_parse_number(JKParseState *parseState) {
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
+ const unsigned char *numberStart = JK_AT_STRING_PTR(parseState);
+ const unsigned char *endOfBuffer = JK_END_STRING_PTR(parseState);
+ const unsigned char *atNumberCharacter = NULL;
+ int numberState = JSONNumberStateWholeNumberStart, isFloatingPoint = 0, isNegative = 0, backup = 0;
+ size_t startingIndex = parseState->atIndex;
+
+ for(atNumberCharacter = numberStart; (JK_EXPECT_T(atNumberCharacter < endOfBuffer)) && (JK_EXPECT_T(!(JK_EXPECT_F(numberState == JSONNumberStateFinished) || JK_EXPECT_F(numberState == JSONNumberStateError)))); atNumberCharacter++) {
+ unsigned long currentChar = (unsigned long)(*atNumberCharacter), lowerCaseCC = currentChar | 0x20UL;
+
+ switch(numberState) {
+ case JSONNumberStateWholeNumberStart: if (currentChar == '-') { numberState = JSONNumberStateWholeNumberMinus; isNegative = 1; break; }
+ case JSONNumberStateWholeNumberMinus: if (currentChar == '0') { numberState = JSONNumberStateWholeNumberZero; break; }
+ else if( (currentChar >= '1') && (currentChar <= '9')) { numberState = JSONNumberStateWholeNumber; break; }
+ else { /* XXX Add error message */ numberState = JSONNumberStateError; break; }
+ case JSONNumberStateExponentStart: if( (currentChar == '+') || (currentChar == '-')) { numberState = JSONNumberStateExponentPlusMinus; break; }
+ case JSONNumberStateFractionalNumberStart:
+ case JSONNumberStateExponentPlusMinus:if(!((currentChar >= '0') && (currentChar <= '9'))) { /* XXX Add error message */ numberState = JSONNumberStateError; break; }
+ else { if(numberState == JSONNumberStateFractionalNumberStart) { numberState = JSONNumberStateFractionalNumber; }
+ else { numberState = JSONNumberStateExponent; } break; }
+ case JSONNumberStateWholeNumberZero:
+ case JSONNumberStateWholeNumber: if (currentChar == '.') { numberState = JSONNumberStateFractionalNumberStart; isFloatingPoint = 1; break; }
+ case JSONNumberStateFractionalNumber: if (lowerCaseCC == 'e') { numberState = JSONNumberStateExponentStart; isFloatingPoint = 1; break; }
+ case JSONNumberStateExponent: if(!((currentChar >= '0') && (currentChar <= '9')) || (numberState == JSONNumberStateWholeNumberZero)) { numberState = JSONNumberStateFinished; backup = 1; break; }
+ break;
+ default: /* XXX Add error message */ numberState = JSONNumberStateError; break;
+ }
+ }
+
+ parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + startingIndex;
+ parseState->token.tokenPtrRange.length = (atNumberCharacter - parseState->token.tokenPtrRange.ptr) - backup;
+ parseState->atIndex = (parseState->token.tokenPtrRange.ptr + parseState->token.tokenPtrRange.length) - parseState->stringBuffer.bytes.ptr;
+
+ if(JK_EXPECT_T(numberState == JSONNumberStateFinished)) {
+ unsigned char numberTempBuf[parseState->token.tokenPtrRange.length + 4UL];
+ unsigned char *endOfNumber = NULL;
+
+ memcpy(numberTempBuf, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length);
+ numberTempBuf[parseState->token.tokenPtrRange.length] = 0;
+
+ errno = 0;
+
+ // Treat "-0" as a floating point number, which is capable of representing negative zeros.
+ if(JK_EXPECT_F(parseState->token.tokenPtrRange.length == 2UL) && JK_EXPECT_F(numberTempBuf[1] == '0') && JK_EXPECT_F(isNegative)) { isFloatingPoint = 1; }
+
+ if(isFloatingPoint) {
+ parseState->token.value.number.doubleValue = strtod((const char *)numberTempBuf, (char **)&endOfNumber); // strtod is documented to return U+2261 (identical to) 0.0 on an underflow error (along with setting errno to ERANGE).
+ parseState->token.value.type = JKValueTypeDouble;
+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.doubleValue;
+ parseState->token.value.ptrRange.length = sizeof(double);
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type);
+ } else {
+ if(isNegative) {
+ parseState->token.value.number.longLongValue = strtoll((const char *)numberTempBuf, (char **)&endOfNumber, 10);
+ parseState->token.value.type = JKValueTypeLongLong;
+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.longLongValue;
+ parseState->token.value.ptrRange.length = sizeof(long long);
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.longLongValue;
+ } else {
+ parseState->token.value.number.unsignedLongLongValue = strtoull((const char *)numberTempBuf, (char **)&endOfNumber, 10);
+ parseState->token.value.type = JKValueTypeUnsignedLongLong;
+ parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.unsignedLongLongValue;
+ parseState->token.value.ptrRange.length = sizeof(unsigned long long);
+ parseState->token.value.hash = (JK_HASH_INIT + parseState->token.value.type) + (JKHash)parseState->token.value.number.unsignedLongLongValue;
+ }
+ }
+
+ if(JK_EXPECT_F(errno != 0)) {
+ numberState = JSONNumberStateError;
+ if(errno == ERANGE) {
+ switch(parseState->token.value.type) {
+ case JKValueTypeDouble: jk_error(parseState, @"The value '%s' could not be represented as a 'double' due to %s.", numberTempBuf, (parseState->token.value.number.doubleValue == 0.0) ? "underflow" : "overflow"); break; // see above for == 0.0.
+ case JKValueTypeLongLong: jk_error(parseState, @"The value '%s' exceeded the minimum value that could be represented: %lld.", numberTempBuf, parseState->token.value.number.longLongValue); break;
+ case JKValueTypeUnsignedLongLong: jk_error(parseState, @"The value '%s' exceeded the maximum value that could be represented: %llu.", numberTempBuf, parseState->token.value.number.unsignedLongLongValue); break;
+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
+ }
+ }
+ }
+ if(JK_EXPECT_F(endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length]) && JK_EXPECT_F(numberState != JSONNumberStateError)) { numberState = JSONNumberStateError; jk_error(parseState, @"The conversion function did not consume all of the number tokens characters."); }
+
+ size_t hashIndex = 0UL;
+ for(hashIndex = 0UL; hashIndex < parseState->token.value.ptrRange.length; hashIndex++) { parseState->token.value.hash = jk_calculateHash(parseState->token.value.hash, parseState->token.value.ptrRange.ptr[hashIndex]); }
+ }
+
+ if(JK_EXPECT_F(numberState != JSONNumberStateFinished)) { jk_error(parseState, @"Invalid number."); }
+ return(JK_EXPECT_T((numberState == JSONNumberStateFinished)) ? 0 : 1);
+}
+
+JK_STATIC_INLINE void jk_set_parsed_token(JKParseState *parseState, const unsigned char *ptr, size_t length, JKTokenType type, size_t advanceBy) {
+ parseState->token.tokenPtrRange.ptr = ptr;
+ parseState->token.tokenPtrRange.length = length;
+ parseState->token.type = type;
+ parseState->atIndex += advanceBy;
+}
+
+static size_t jk_parse_is_newline(JKParseState *parseState, const unsigned char *atCharacterPtr) {
+ NSCParameterAssert((parseState != NULL) && (atCharacterPtr != NULL) && (atCharacterPtr >= parseState->stringBuffer.bytes.ptr) && (atCharacterPtr < JK_END_STRING_PTR(parseState)));
+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
+
+ if(JK_EXPECT_F(atCharacterPtr >= endOfStringPtr)) { return(0UL); }
+
+ if(JK_EXPECT_F((*(atCharacterPtr + 0)) == '\n')) { return(1UL); }
+ if(JK_EXPECT_F((*(atCharacterPtr + 0)) == '\r')) { if((JK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr)) && ((*(atCharacterPtr + 1)) == '\n')) { return(2UL); } return(1UL); }
+ if(parseState->parseOptionFlags & JKParseOptionUnicodeNewlines) {
+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == 0xc2)) && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x85))) { return(2UL); }
+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == 0xe2)) && (((atCharacterPtr + 2) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x80) && (((*(atCharacterPtr + 2)) == 0xa8) || ((*(atCharacterPtr + 2)) == 0xa9)))) { return(3UL); }
+ }
+
+ return(0UL);
+}
+
+JK_STATIC_INLINE int jk_parse_skip_newline(JKParseState *parseState) {
+ size_t newlineAdvanceAtIndex = 0UL;
+ if(JK_EXPECT_F((newlineAdvanceAtIndex = jk_parse_is_newline(parseState, JK_AT_STRING_PTR(parseState))) > 0UL)) { parseState->lineNumber++; parseState->atIndex += (newlineAdvanceAtIndex - 1UL); parseState->lineStartIndex = parseState->atIndex + 1UL; return(1); }
+ return(0);
+}
+
+JK_STATIC_INLINE void jk_parse_skip_whitespace(JKParseState *parseState) {
+#ifndef __clang_analyzer__
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
+ const unsigned char *atCharacterPtr = NULL;
+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
+
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
+ if(((*(atCharacterPtr + 0)) == ' ') || ((*(atCharacterPtr + 0)) == '\t')) { continue; }
+ if(jk_parse_skip_newline(parseState)) { continue; }
+ if(parseState->parseOptionFlags & JKParseOptionComments) {
+ if((JK_EXPECT_F((*(atCharacterPtr + 0)) == '/')) && (JK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr))) {
+ if((*(atCharacterPtr + 1)) == '/') {
+ parseState->atIndex++;
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) { if(jk_parse_skip_newline(parseState)) { break; } }
+ continue;
+ }
+ if((*(atCharacterPtr + 1)) == '*') {
+ parseState->atIndex++;
+ for(atCharacterPtr = JK_AT_STRING_PTR(parseState); (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
+ if(jk_parse_skip_newline(parseState)) { continue; }
+ if(((*(atCharacterPtr + 0)) == '*') && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == '/'))) { parseState->atIndex++; break; }
+ }
+ continue;
+ }
+ }
+ }
+ break;
+ }
+#endif
+}
+
+static int jk_parse_next_token(JKParseState *parseState) {
+ NSCParameterAssert((parseState != NULL) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
+ const unsigned char *atCharacterPtr = NULL;
+ const unsigned char *endOfStringPtr = JK_END_STRING_PTR(parseState);
+ unsigned char currentCharacter = 0U;
+ int stopParsing = 0;
+
+ parseState->prev_atIndex = parseState->atIndex;
+ parseState->prev_lineNumber = parseState->lineNumber;
+ parseState->prev_lineStartIndex = parseState->lineStartIndex;
+
+ jk_parse_skip_whitespace(parseState);
+
+ if((JK_AT_STRING_PTR(parseState) == endOfStringPtr)) { stopParsing = 1; }
+
+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((atCharacterPtr = JK_AT_STRING_PTR(parseState)) < endOfStringPtr))) {
+ currentCharacter = *atCharacterPtr;
+
+ if(JK_EXPECT_T(currentCharacter == '"')) { if(JK_EXPECT_T((stopParsing = jk_parse_string(parseState)) == 0)) { jk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, JKTokenTypeString, 0UL); } }
+ else if(JK_EXPECT_T(currentCharacter == ':')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeSeparator, 1UL); }
+ else if(JK_EXPECT_T(currentCharacter == ',')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeComma, 1UL); }
+ else if((JK_EXPECT_T(currentCharacter >= '0') && JK_EXPECT_T(currentCharacter <= '9')) || JK_EXPECT_T(currentCharacter == '-')) { if(JK_EXPECT_T((stopParsing = jk_parse_number(parseState)) == 0)) { jk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, JKTokenTypeNumber, 0UL); } }
+ else if(JK_EXPECT_T(currentCharacter == '{')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeObjectBegin, 1UL); }
+ else if(JK_EXPECT_T(currentCharacter == '}')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeObjectEnd, 1UL); }
+ else if(JK_EXPECT_T(currentCharacter == '[')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeArrayBegin, 1UL); }
+ else if(JK_EXPECT_T(currentCharacter == ']')) { jk_set_parsed_token(parseState, atCharacterPtr, 1UL, JKTokenTypeArrayEnd, 1UL); }
+
+ else if(JK_EXPECT_T(currentCharacter == 't')) { if(!((JK_EXPECT_T((atCharacterPtr + 4UL) < endOfStringPtr)) && (JK_EXPECT_T(atCharacterPtr[1] == 'r')) && (JK_EXPECT_T(atCharacterPtr[2] == 'u')) && (JK_EXPECT_T(atCharacterPtr[3] == 'e')))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeTrue, 4UL); } }
+ else if(JK_EXPECT_T(currentCharacter == 'f')) { if(!((JK_EXPECT_T((atCharacterPtr + 5UL) < endOfStringPtr)) && (JK_EXPECT_T(atCharacterPtr[1] == 'a')) && (JK_EXPECT_T(atCharacterPtr[2] == 'l')) && (JK_EXPECT_T(atCharacterPtr[3] == 's')) && (JK_EXPECT_T(atCharacterPtr[4] == 'e')))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 5UL, JKTokenTypeFalse, 5UL); } }
+ else if(JK_EXPECT_T(currentCharacter == 'n')) { if(!((JK_EXPECT_T((atCharacterPtr + 4UL) < endOfStringPtr)) && (JK_EXPECT_T(atCharacterPtr[1] == 'u')) && (JK_EXPECT_T(atCharacterPtr[2] == 'l')) && (JK_EXPECT_T(atCharacterPtr[3] == 'l')))) { stopParsing = 1; /* XXX Add error message */ } else { jk_set_parsed_token(parseState, atCharacterPtr, 4UL, JKTokenTypeNull, 4UL); } }
+ else { stopParsing = 1; /* XXX Add error message */ }
+ }
+
+ if(JK_EXPECT_F(stopParsing)) { jk_error(parseState, @"Unexpected token, wanted '{', '}', '[', ']', ',', ':', 'true', 'false', 'null', '\"STRING\"', 'NUMBER'."); }
+ return(stopParsing);
+}
+
+static void jk_error_parse_accept_or3(JKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String) {
+ NSString *acceptStrings[16];
+ int acceptIdx = 0;
+ if(state & JKParseAcceptValue) { acceptStrings[acceptIdx++] = or1String; }
+ if(state & JKParseAcceptComma) { acceptStrings[acceptIdx++] = or2String; }
+ if(state & JKParseAcceptEnd) { acceptStrings[acceptIdx++] = or3String; }
+ if(acceptIdx == 1) { jk_error(parseState, @"Expected %@, not '%*.*s'", acceptStrings[0], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
+ else if(acceptIdx == 2) { jk_error(parseState, @"Expected %@ or %@, not '%*.*s'", acceptStrings[0], acceptStrings[1], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
+ else if(acceptIdx == 3) { jk_error(parseState, @"Expected %@, %@, or %@, not '%*.*s", acceptStrings[0], acceptStrings[1], acceptStrings[2], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
+}
+
+static void *jk_parse_array(JKParseState *parseState) {
+ size_t startingObjectIndex = parseState->objectStack.index;
+ int arrayState = JKParseAcceptValueOrEnd, stopParsing = 0;
+ void *parsedArray = NULL;
+
+ while(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
+ if(JK_EXPECT_F(parseState->objectStack.index > (parseState->objectStack.count - 4UL))) { if(jk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { jk_error(parseState, @"Internal error: [array] objectsIndex > %zu, resize failed? %@ line %#ld", (parseState->objectStack.count - 4UL), [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break; } }
+
+ if(JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)) {
+ void *object = NULL;
+#ifndef NS_BLOCK_ASSERTIONS
+ parseState->objectStack.objects[parseState->objectStack.index] = NULL;
+ parseState->objectStack.keys [parseState->objectStack.index] = NULL;
+#endif
+ switch(parseState->token.type) {
+ case JKTokenTypeNumber:
+ case JKTokenTypeString:
+ case JKTokenTypeTrue:
+ case JKTokenTypeFalse:
+ case JKTokenTypeNull:
+ case JKTokenTypeArrayBegin:
+ case JKTokenTypeObjectBegin:
+ if(JK_EXPECT_F((arrayState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
+ if(JK_EXPECT_F((object = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Object == NULL"); stopParsing = 1; break; } else { parseState->objectStack.objects[parseState->objectStack.index++] = object; arrayState = JKParseAcceptCommaOrEnd; }
+ break;
+ case JKTokenTypeArrayEnd: if(JK_EXPECT_T(arrayState & JKParseAcceptEnd)) { NSCParameterAssert(parseState->objectStack.index >= startingObjectIndex); parsedArray = (void *)_JKArrayCreate((id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections); } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ']'."); } stopParsing = 1; break;
+ case JKTokenTypeComma: if(JK_EXPECT_T(arrayState & JKParseAcceptComma)) { arrayState = JKParseAcceptValue; } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, arrayState, @"a value", @"a comma", @"a ']'"); stopParsing = 1; break;
+ }
+ }
+ }
+
+ if(JK_EXPECT_F(parsedArray == NULL)) { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { if(parseState->objectStack.objects[idx] != NULL) { CFRelease(parseState->objectStack.objects[idx]); parseState->objectStack.objects[idx] = NULL; } } }
+#if !defined(NS_BLOCK_ASSERTIONS)
+ else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
+#endif
+
+ parseState->objectStack.index = startingObjectIndex;
+ return(parsedArray);
+}
+
+static void *jk_create_dictionary(JKParseState *parseState, size_t startingObjectIndex) {
+ void *parsedDictionary = NULL;
+
+ parseState->objectStack.index--;
+
+ parsedDictionary = _JKDictionaryCreate((id *)&parseState->objectStack.keys[startingObjectIndex], (NSUInteger *)&parseState->objectStack.cfHashes[startingObjectIndex], (id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections);
+
+ return(parsedDictionary);
+}
+
+static void *jk_parse_dictionary(JKParseState *parseState) {
+ size_t startingObjectIndex = parseState->objectStack.index;
+ int dictState = JKParseAcceptValueOrEnd, stopParsing = 0;
+ void *parsedDictionary = NULL;
+
+ while(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
+ if(JK_EXPECT_F(parseState->objectStack.index > (parseState->objectStack.count - 4UL))) { if(jk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { jk_error(parseState, @"Internal error: [dictionary] objectsIndex > %zu, resize failed? %@ line #%ld", (parseState->objectStack.count - 4UL), [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break; } }
+
+ size_t objectStackIndex = parseState->objectStack.index++;
+ parseState->objectStack.keys[objectStackIndex] = NULL;
+ parseState->objectStack.objects[objectStackIndex] = NULL;
+ void *key = NULL, *object = NULL;
+
+ if(JK_EXPECT_T((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)))) {
+ switch(parseState->token.type) {
+ case JKTokenTypeString:
+ if(JK_EXPECT_F((dictState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected string."); stopParsing = 1; break; }
+ if(JK_EXPECT_F((key = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Key == NULL."); stopParsing = 1; break; }
+ else {
+ parseState->objectStack.keys[objectStackIndex] = key;
+ if(JK_EXPECT_T(parseState->token.value.cacheItem != NULL)) { if(JK_EXPECT_F(parseState->token.value.cacheItem->cfHash == 0UL)) { parseState->token.value.cacheItem->cfHash = CFHash(key); } parseState->objectStack.cfHashes[objectStackIndex] = parseState->token.value.cacheItem->cfHash; }
+ else { parseState->objectStack.cfHashes[objectStackIndex] = CFHash(key); }
+ }
+ break;
+
+ case JKTokenTypeObjectEnd: if((JK_EXPECT_T(dictState & JKParseAcceptEnd))) { NSCParameterAssert(parseState->objectStack.index >= startingObjectIndex); parsedDictionary = jk_create_dictionary(parseState, startingObjectIndex); } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected '}'."); } stopParsing = 1; break;
+ case JKTokenTypeComma: if((JK_EXPECT_T(dictState & JKParseAcceptComma))) { dictState = JKParseAcceptValue; parseState->objectStack.index--; continue; } else { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
+
+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, dictState, @"a \"STRING\"", @"a comma", @"a '}'"); stopParsing = 1; break;
+ }
+ }
+
+ if(JK_EXPECT_T(stopParsing == 0)) {
+ if(JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0)) { if(JK_EXPECT_F(parseState->token.type != JKTokenTypeSeparator)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Expected ':'."); stopParsing = 1; } }
+ }
+
+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0))) {
+ switch(parseState->token.type) {
+ case JKTokenTypeNumber:
+ case JKTokenTypeString:
+ case JKTokenTypeTrue:
+ case JKTokenTypeFalse:
+ case JKTokenTypeNull:
+ case JKTokenTypeArrayBegin:
+ case JKTokenTypeObjectBegin:
+ if(JK_EXPECT_F((dictState & JKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; jk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
+ if(JK_EXPECT_F((object = jk_object_for_token(parseState)) == NULL)) { jk_error(parseState, @"Internal error: Object == NULL."); stopParsing = 1; break; } else { parseState->objectStack.objects[objectStackIndex] = object; dictState = JKParseAcceptCommaOrEnd; }
+ break;
+ default: parseState->errorIsPrev = 1; jk_error_parse_accept_or3(parseState, dictState, @"a value", @"a comma", @"a '}'"); stopParsing = 1; break;
+ }
+ }
+ }
+
+ if(JK_EXPECT_F(parsedDictionary == NULL)) { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { if(parseState->objectStack.keys[idx] != NULL) { CFRelease(parseState->objectStack.keys[idx]); parseState->objectStack.keys[idx] = NULL; } if(parseState->objectStack.objects[idx] != NULL) { CFRelease(parseState->objectStack.objects[idx]); parseState->objectStack.objects[idx] = NULL; } } }
+#if !defined(NS_BLOCK_ASSERTIONS)
+ else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
+#endif
+
+ parseState->objectStack.index = startingObjectIndex;
+ return(parsedDictionary);
+}
+
+static id json_parse_it(JKParseState *parseState) {
+ id parsedObject = NULL;
+ int stopParsing = 0;
+
+ while((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length))) {
+ if((JK_EXPECT_T(stopParsing == 0)) && (JK_EXPECT_T((stopParsing = jk_parse_next_token(parseState)) == 0))) {
+ switch(parseState->token.type) {
+ case JKTokenTypeArrayBegin:
+ case JKTokenTypeObjectBegin: parsedObject = [(id)jk_object_for_token(parseState) autorelease]; stopParsing = 1; break;
+ default: jk_error(parseState, @"Expected either '[' or '{'."); stopParsing = 1; break;
+ }
+ }
+ }
+
+ NSCParameterAssert((parseState->objectStack.index == 0) && (JK_AT_STRING_PTR(parseState) <= JK_END_STRING_PTR(parseState)));
+
+ if((parsedObject == NULL) && (JK_AT_STRING_PTR(parseState) == JK_END_STRING_PTR(parseState))) { jk_error(parseState, @"Reached the end of the buffer."); }
+ if(parsedObject == NULL) { jk_error(parseState, @"Unable to parse JSON."); }
+
+ if((parsedObject != NULL) && (JK_AT_STRING_PTR(parseState) < JK_END_STRING_PTR(parseState))) {
+ jk_parse_skip_whitespace(parseState);
+ if((parsedObject != NULL) && ((parseState->parseOptionFlags & JKParseOptionPermitTextAfterValidJSON) == 0) && (JK_AT_STRING_PTR(parseState) < JK_END_STRING_PTR(parseState))) {
+ jk_error(parseState, @"A valid JSON object was parsed but there were additional non-white-space characters remaining.");
+ parsedObject = NULL;
+ }
+ }
+
+ return(parsedObject);
+}
+
+////////////
+#pragma mark -
+#pragma mark Object cache
+
+// This uses a Galois Linear Feedback Shift Register (LFSR) PRNG to pick which item in the cache to age. It has a period of (2^32)-1.
+// NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value. The LFSR is initalized to 1 in -initWithParseOptions:
+JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
+ NSCParameterAssert((parseState != NULL) && (parseState->cache.prng_lfsr != 0U));
+ parseState->cache.prng_lfsr = (parseState->cache.prng_lfsr >> 1) ^ ((0U - (parseState->cache.prng_lfsr & 1U)) & 0x80200003U);
+ parseState->cache.age[parseState->cache.prng_lfsr & (parseState->cache.count - 1UL)] >>= 1;
+}
+
+// The object cache is nothing more than a hash table with open addressing collision resolution that is bounded by JK_CACHE_PROBES attempts.
+//
+// The hash table is a linear C array of JKTokenCacheItem. The terms "item" and "bucket" are synonymous with the index in to the cache array, i.e. cache.items[bucket].
+//
+// Items in the cache have an age associated with them. An items age is incremented using saturating unsigned arithmetic and decremeted using unsigned right shifts.
+// Thus, an items age is managed using an AIMD policy- additive increase, multiplicative decrease. All age calculations and manipulations are branchless.
+// The primitive C type MUST be unsigned. It is currently a "char", which allows (at a minimum and in practice) 8 bits.
+//
+// A "useable bucket" is a bucket that is not in use (never populated), or has an age == 0.
+//
+// When an item is found in the cache, it's age is incremented.
+// If a useable bucket hasn't been found, the current item (bucket) is aged along with two random items.
+//
+// If a value is not found in the cache, and no useable bucket has been found, that value is not added to the cache.
+
+static void *jk_cachedObjects(JKParseState *parseState) {
+ unsigned long bucket = parseState->token.value.hash & (parseState->cache.count - 1UL), setBucket = 0UL, useableBucket = 0UL, x = 0UL;
+ void *parsedAtom = NULL;
+
+ if(JK_EXPECT_F(parseState->token.value.ptrRange.length == 0UL) && JK_EXPECT_T(parseState->token.value.type == JKValueTypeString)) { return(@""); }
+
+ for(x = 0UL; x < JK_CACHE_PROBES; x++) {
+ if(JK_EXPECT_F(parseState->cache.items[bucket].object == NULL)) { setBucket = 1UL; useableBucket = bucket; break; }
+
+ if((JK_EXPECT_T(parseState->cache.items[bucket].hash == parseState->token.value.hash)) && (JK_EXPECT_T(parseState->cache.items[bucket].size == parseState->token.value.ptrRange.length)) && (JK_EXPECT_T(parseState->cache.items[bucket].type == parseState->token.value.type)) && (JK_EXPECT_T(parseState->cache.items[bucket].bytes != NULL)) && (JK_EXPECT_T(memcmp(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length) == 0U))) {
+ parseState->cache.age[bucket] = (((uint32_t)parseState->cache.age[bucket]) + 1U) - (((((uint32_t)parseState->cache.age[bucket]) + 1U) >> 31) ^ 1U);
+ parseState->token.value.cacheItem = &parseState->cache.items[bucket];
+ NSCParameterAssert(parseState->cache.items[bucket].object != NULL);
+ return((void *)CFRetain(parseState->cache.items[bucket].object));
+ } else {
+ if(JK_EXPECT_F(setBucket == 0UL) && JK_EXPECT_F(parseState->cache.age[bucket] == 0U)) { setBucket = 1UL; useableBucket = bucket; }
+ if(JK_EXPECT_F(setBucket == 0UL)) { parseState->cache.age[bucket] >>= 1; jk_cache_age(parseState); jk_cache_age(parseState); }
+ // This is the open addressing function. The values length and type are used as a form of "double hashing" to distribute values with the same effective value hash across different object cache buckets.
+ // The values type is a prime number that is relatively coprime to the other primes in the set of value types and the number of hash table buckets.
+ bucket = (parseState->token.value.hash + (parseState->token.value.ptrRange.length * (x + 1UL)) + (parseState->token.value.type * (x + 1UL)) + (3UL * (x + 1UL))) & (parseState->cache.count - 1UL);
+ }
+ }
+
+ switch(parseState->token.value.type) {
+ case JKValueTypeString: parsedAtom = (void *)CFStringCreateWithBytes(NULL, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length, kCFStringEncodingUTF8, 0); break;
+ case JKValueTypeLongLong: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.longLongValue); break;
+ case JKValueTypeUnsignedLongLong:
+ if(parseState->token.value.number.unsignedLongLongValue <= LLONG_MAX) { parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.unsignedLongLongValue); }
+ else { parsedAtom = (void *)parseState->objCImpCache.NSNumberInitWithUnsignedLongLong(parseState->objCImpCache.NSNumberAlloc(parseState->objCImpCache.NSNumberClass, @selector(alloc)), @selector(initWithUnsignedLongLong:), parseState->token.value.number.unsignedLongLongValue); }
+ break;
+ case JKValueTypeDouble: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberDoubleType, &parseState->token.value.number.doubleValue); break;
+ default: jk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
+ }
+
+ if(JK_EXPECT_T(setBucket) && (JK_EXPECT_T(parsedAtom != NULL))) {
+ bucket = useableBucket;
+ if(JK_EXPECT_T((parseState->cache.items[bucket].object != NULL))) { CFRelease(parseState->cache.items[bucket].object); parseState->cache.items[bucket].object = NULL; }
+
+ if(JK_EXPECT_T((parseState->cache.items[bucket].bytes = (unsigned char *)reallocf(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.length)) != NULL)) {
+ memcpy(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length);
+ parseState->cache.items[bucket].object = (void *)CFRetain(parsedAtom);
+ parseState->cache.items[bucket].hash = parseState->token.value.hash;
+ parseState->cache.items[bucket].cfHash = 0UL;
+ parseState->cache.items[bucket].size = parseState->token.value.ptrRange.length;
+ parseState->cache.items[bucket].type = parseState->token.value.type;
+ parseState->token.value.cacheItem = &parseState->cache.items[bucket];
+ parseState->cache.age[bucket] = JK_INIT_CACHE_AGE;
+ } else { // The realloc failed, so clear the appropriate fields.
+ parseState->cache.items[bucket].hash = 0UL;
+ parseState->cache.items[bucket].cfHash = 0UL;
+ parseState->cache.items[bucket].size = 0UL;
+ parseState->cache.items[bucket].type = 0UL;
+ }
+ }
+
+ return(parsedAtom);
+}
+
+
+static void *jk_object_for_token(JKParseState *parseState) {
+ void *parsedAtom = NULL;
+
+ parseState->token.value.cacheItem = NULL;
+ switch(parseState->token.type) {
+ case JKTokenTypeString: parsedAtom = jk_cachedObjects(parseState); break;
+ case JKTokenTypeNumber: parsedAtom = jk_cachedObjects(parseState); break;
+ case JKTokenTypeObjectBegin: parsedAtom = jk_parse_dictionary(parseState); break;
+ case JKTokenTypeArrayBegin: parsedAtom = jk_parse_array(parseState); break;
+ case JKTokenTypeTrue: parsedAtom = (void *)kCFBooleanTrue; break;
+ case JKTokenTypeFalse: parsedAtom = (void *)kCFBooleanFalse; break;
+ case JKTokenTypeNull: parsedAtom = (void *)kCFNull; break;
+ default: jk_error(parseState, @"Internal error: Unknown token type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
+ }
+
+ return(parsedAtom);
+}
+
+#pragma mark -
+@implementation JSONDecoder
+
++ (id)decoder
+{
+ return([self decoderWithParseOptions:JKParseOptionStrict]);
+}
+
++ (id)decoderWithParseOptions:(JKParseOptionFlags)parseOptionFlags
+{
+ return([[[self alloc] initWithParseOptions:parseOptionFlags] autorelease]);
+}
+
+- (id)init
+{
+ return([self initWithParseOptions:JKParseOptionStrict]);
+}
+
+- (id)initWithParseOptions:(JKParseOptionFlags)parseOptionFlags
+{
+ if((self = [super init]) == NULL) { return(NULL); }
+
+ if(parseOptionFlags & ~JKParseOptionValidFlags) { [self autorelease]; [NSException raise:NSInvalidArgumentException format:@"Invalid parse options."]; }
+
+ if((parseState = (JKParseState *)calloc(1UL, sizeof(JKParseState))) == NULL) { goto errorExit; }
+
+ parseState->parseOptionFlags = parseOptionFlags;
+
+ parseState->token.tokenBuffer.roundSizeUpToMultipleOf = 4096UL;
+ parseState->objectStack.roundSizeUpToMultipleOf = 2048UL;
+
+ parseState->objCImpCache.NSNumberClass = _jk_NSNumberClass;
+ parseState->objCImpCache.NSNumberAlloc = _jk_NSNumberAllocImp;
+ parseState->objCImpCache.NSNumberInitWithUnsignedLongLong = _jk_NSNumberInitWithUnsignedLongLongImp;
+
+ parseState->cache.prng_lfsr = 1U;
+ parseState->cache.count = JK_CACHE_SLOTS;
+ if((parseState->cache.items = (JKTokenCacheItem *)calloc(1UL, sizeof(JKTokenCacheItem) * parseState->cache.count)) == NULL) { goto errorExit; }
+
+ return(self);
+
+ errorExit:
+ if(self) { [self autorelease]; self = NULL; }
+ return(NULL);
+}
+
+// This is here primarily to support the NSString and NSData convenience functions so the autoreleased JSONDecoder can release most of its resources before the pool pops.
+static void _JSONDecoderCleanup(JSONDecoder *decoder) {
+ if((decoder != NULL) && (decoder->parseState != NULL)) {
+ jk_managedBuffer_release(&decoder->parseState->token.tokenBuffer);
+ jk_objectStack_release(&decoder->parseState->objectStack);
+
+ [decoder clearCache];
+ if(decoder->parseState->cache.items != NULL) { free(decoder->parseState->cache.items); decoder->parseState->cache.items = NULL; }
+
+ free(decoder->parseState); decoder->parseState = NULL;
+ }
+}
+
+- (void)dealloc
+{
+ _JSONDecoderCleanup(self);
+ [super dealloc];
+}
+
+- (void)clearCache
+{
+ if(JK_EXPECT_T(parseState != NULL)) {
+ if(JK_EXPECT_T(parseState->cache.items != NULL)) {
+ size_t idx = 0UL;
+ for(idx = 0UL; idx < parseState->cache.count; idx++) {
+ if(JK_EXPECT_T(parseState->cache.items[idx].object != NULL)) { CFRelease(parseState->cache.items[idx].object); parseState->cache.items[idx].object = NULL; }
+ if(JK_EXPECT_T(parseState->cache.items[idx].bytes != NULL)) { free(parseState->cache.items[idx].bytes); parseState->cache.items[idx].bytes = NULL; }
+ memset(&parseState->cache.items[idx], 0, sizeof(JKTokenCacheItem));
+ parseState->cache.age[idx] = 0U;
+ }
+ }
+ }
+}
+
+// This needs to be completely rewritten.
+static id _JKParseUTF8String(JKParseState *parseState, BOOL mutableCollections, const unsigned char *string, size_t length, NSError **error) {
+ NSCParameterAssert((parseState != NULL) && (string != NULL) && (parseState->cache.prng_lfsr != 0U));
+ parseState->stringBuffer.bytes.ptr = string;
+ parseState->stringBuffer.bytes.length = length;
+ parseState->atIndex = 0UL;
+ parseState->lineNumber = 1UL;
+ parseState->lineStartIndex = 0UL;
+ parseState->prev_atIndex = 0UL;
+ parseState->prev_lineNumber = 1UL;
+ parseState->prev_lineStartIndex = 0UL;
+ parseState->error = NULL;
+ parseState->errorIsPrev = 0;
+ parseState->mutableCollections = (mutableCollections == NO) ? NO : YES;
+
+ unsigned char stackTokenBuffer[JK_TOKENBUFFER_SIZE] JK_ALIGNED(64);
+ jk_managedBuffer_setToStackBuffer(&parseState->token.tokenBuffer, stackTokenBuffer, sizeof(stackTokenBuffer));
+
+ void *stackObjects [JK_STACK_OBJS] JK_ALIGNED(64);
+ void *stackKeys [JK_STACK_OBJS] JK_ALIGNED(64);
+ CFHashCode stackCFHashes[JK_STACK_OBJS] JK_ALIGNED(64);
+ jk_objectStack_setToStackBuffer(&parseState->objectStack, stackObjects, stackKeys, stackCFHashes, JK_STACK_OBJS);
+
+ id parsedJSON = json_parse_it(parseState);
+
+ if((error != NULL) && (parseState->error != NULL)) { *error = parseState->error; }
+
+ jk_managedBuffer_release(&parseState->token.tokenBuffer);
+ jk_objectStack_release(&parseState->objectStack);
+
+ parseState->stringBuffer.bytes.ptr = NULL;
+ parseState->stringBuffer.bytes.length = 0UL;
+ parseState->atIndex = 0UL;
+ parseState->lineNumber = 1UL;
+ parseState->lineStartIndex = 0UL;
+ parseState->prev_atIndex = 0UL;
+ parseState->prev_lineNumber = 1UL;
+ parseState->prev_lineStartIndex = 0UL;
+ parseState->error = NULL;
+ parseState->errorIsPrev = 0;
+ parseState->mutableCollections = NO;
+
+ return(parsedJSON);
+}
+
+////////////
+#pragma mark Deprecated as of v1.4
+////////////
+
+// Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length
+{
+ return([self objectWithUTF8String:string length:length error:NULL]);
+}
+
+// Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
+- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error
+{
+ return([self objectWithUTF8String:string length:length error:error]);
+}
+
+// Deprecated in JSONKit v1.4. Use objectWithData: instead.
+- (id)parseJSONData:(NSData *)jsonData
+{
+ return([self objectWithData:jsonData error:NULL]);
+}
+
+// Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
+- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error
+{
+ return([self objectWithData:jsonData error:error]);
+}
+
+////////////
+#pragma mark Methods that return immutable collection objects
+////////////
+
+- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
+{
+ return([self objectWithUTF8String:string length:length error:NULL]);
+}
+
+- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
+{
+ if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
+ if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
+
+ return(_JKParseUTF8String(parseState, NO, string, (size_t)length, error));
+}
+
+- (id)objectWithData:(NSData *)jsonData
+{
+ return([self objectWithData:jsonData error:NULL]);
+}
+
+- (id)objectWithData:(NSData *)jsonData error:(NSError **)error
+{
+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
+ return([self objectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
+}
+
+////////////
+#pragma mark Methods that return mutable collection objects
+////////////
+
+- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
+{
+ return([self mutableObjectWithUTF8String:string length:length error:NULL]);
+}
+
+- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
+{
+ if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
+ if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
+
+ return(_JKParseUTF8String(parseState, YES, string, (size_t)length, error));
+}
+
+- (id)mutableObjectWithData:(NSData *)jsonData
+{
+ return([self mutableObjectWithData:jsonData error:NULL]);
+}
+
+- (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error
+{
+ if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
+ return([self mutableObjectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
+}
+
+@end
+
+/*
+ The NSString and NSData convenience methods need a little bit of explanation.
+
+ Prior to JSONKit v1.4, the NSString -objectFromJSONStringWithParseOptions:error: method looked like
+
+ const unsigned char *utf8String = (const unsigned char *)[self UTF8String];
+ if(utf8String == NULL) { return(NULL); }
+ size_t utf8Length = strlen((const char *)utf8String);
+ return([[JSONDecoder decoderWithParseOptions:parseOptionFlags] parseUTF8String:utf8String length:utf8Length error:error]);
+
+ This changed with v1.4 to a more complicated method. The reason for this is to keep the amount of memory that is
+ allocated, but not yet freed because it is dependent on the autorelease pool to pop before it can be reclaimed.
+
+ In the simpler v1.3 code, this included all the bytes used to store the -UTF8String along with the JSONDecoder and all its overhead.
+
+ Now we use an autoreleased CFMutableData that is sized to the UTF8 length of the NSString in question and is used to hold the UTF8
+ conversion of said string.
+
+ Once parsed, the CFMutableData has its length set to 0. This should, hopefully, allow the CFMutableData to realloc and/or free
+ the buffer.
+
+ Another change made was a slight modification to JSONDecoder so that most of the cleanup work that was done in -dealloc was moved
+ to a private, internal function. These convenience routines keep the pointer to the autoreleased JSONDecoder and calls
+ _JSONDecoderCleanup() to early release the decoders resources since we already know that particular decoder is not going to be used
+ again.
+
+ If everything goes smoothly, this will most likely result in perhaps a few hundred bytes that are allocated but waiting for the
+ autorelease pool to pop. This is compared to the thousands and easily hundreds of thousands of bytes that would have been in
+ autorelease limbo. It's more complicated for us, but a win for the user.
+
+ Autorelease objects are used in case things don't go smoothly. By having them autoreleased, we effectively guarantee that our
+ requirement to -release the object is always met, not matter what goes wrong. The downside is having a an object or two in
+ autorelease limbo, but we've done our best to minimize that impact, so it all balances out.
+ */
+
+@implementation NSString (JSONKitDeserializing)
+
+static id _NSStringObjectFromJSONString(NSString *jsonString, JKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection) {
+ id returnObject = NULL;
+ CFMutableDataRef mutableData = NULL;
+ JSONDecoder *decoder = NULL;
+
+ CFIndex stringLength = CFStringGetLength((CFStringRef)jsonString);
+ NSUInteger stringUTF8Length = [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+ if((mutableData = (CFMutableDataRef)[(id)CFDataCreateMutable(NULL, (NSUInteger)stringUTF8Length) autorelease]) != NULL) {
+ UInt8 *utf8String = CFDataGetMutableBytePtr(mutableData);
+ CFIndex usedBytes = 0L, convertedCount = 0L;
+
+ convertedCount = CFStringGetBytes((CFStringRef)jsonString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, utf8String, (NSUInteger)stringUTF8Length, &usedBytes);
+ if(JK_EXPECT_F(convertedCount != stringLength) || JK_EXPECT_F(usedBytes < 0L)) { if(error != NULL) { *error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:[NSDictionary dictionaryWithObject:@"An error occurred converting the contents of a NSString to UTF8." forKey:NSLocalizedDescriptionKey]]; } goto exitNow; }
+
+ if(mutableCollection == NO) { returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
+ else { returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
+ }
+
+exitNow:
+ if(mutableData != NULL) { CFDataSetLength(mutableData, 0L); }
+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
+ return(returnObject);
+}
+
+- (id)objectFromJSONString
+{
+ return([self objectFromJSONStringWithParseOptions:JKParseOptionStrict error:NULL]);
+}
+
+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
+{
+ return([self objectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
+}
+
+- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
+{
+ return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, NO));
+}
+
+
+- (id)mutableObjectFromJSONString
+{
+ return([self mutableObjectFromJSONStringWithParseOptions:JKParseOptionStrict error:NULL]);
+}
+
+- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags
+{
+ return([self mutableObjectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
+}
+
+- (id)mutableObjectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
+{
+ return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, YES));
+}
+
+@end
+
+@implementation NSData (JSONKitDeserializing)
+
+- (id)objectFromJSONData
+{
+ return([self objectFromJSONDataWithParseOptions:JKParseOptionStrict error:NULL]);
+}
+
+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags
+{
+ return([self objectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
+}
+
+- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
+{
+ JSONDecoder *decoder = NULL;
+ id returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithData:self error:error];
+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
+ return(returnObject);
+}
+
+- (id)mutableObjectFromJSONData
+{
+ return([self mutableObjectFromJSONDataWithParseOptions:JKParseOptionStrict error:NULL]);
+}
+
+- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags
+{
+ return([self mutableObjectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
+}
+
+- (id)mutableObjectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags error:(NSError **)error
+{
+ JSONDecoder *decoder = NULL;
+ id returnObject = [(decoder = [JSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithData:self error:error];
+ if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
+ return(returnObject);
+}
+
+
+@end
+
+////////////
+#pragma mark -
+#pragma mark Encoding / deserializing functions
+
+static void jk_encode_error(JKEncodeState *encodeState, NSString *format, ...) {
+ NSCParameterAssert((encodeState != NULL) && (format != NULL));
+
+ va_list varArgsList;
+ va_start(varArgsList, format);
+ NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
+ va_end(varArgsList);
+
+ if(encodeState->error == NULL) {
+ encodeState->error = [NSError errorWithDomain:@"JKErrorDomain" code:-1L userInfo:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ formatString, NSLocalizedDescriptionKey,
+ NULL]];
+ }
+}
+
+JK_STATIC_INLINE void jk_encode_updateCache(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object) {
+ NSCParameterAssert(encodeState != NULL);
+ if(JK_EXPECT_T(cacheSlot != NULL)) {
+ NSCParameterAssert((object != NULL) && (startingAtIndex <= encodeState->atIndex));
+ cacheSlot->object = object;
+ cacheSlot->offset = startingAtIndex;
+ cacheSlot->length = (size_t)(encodeState->atIndex - startingAtIndex);
+ }
+}
+
+static int jk_encode_printf(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...) {
+ va_list varArgsList, varArgsListCopy;
+ va_start(varArgsList, format);
+ va_copy(varArgsListCopy, varArgsList);
+
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
+
+ ssize_t formattedStringLength = 0L;
+ int returnValue = 0;
+
+ if(JK_EXPECT_T((formattedStringLength = vsnprintf((char *)&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex], (encodeState->stringBuffer.bytes.length - encodeState->atIndex), format, varArgsList)) >= (ssize_t)(encodeState->stringBuffer.bytes.length - encodeState->atIndex))) {
+ NSCParameterAssert(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length));
+ if(JK_EXPECT_F(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (formattedStringLength * 2UL)+ 4096UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); returnValue = 1; goto exitNow; }
+ if(JK_EXPECT_F((formattedStringLength = vsnprintf((char *)&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex], (encodeState->stringBuffer.bytes.length - encodeState->atIndex), format, varArgsListCopy)) >= (ssize_t)(encodeState->stringBuffer.bytes.length - encodeState->atIndex))) { jk_encode_error(encodeState, @"vsnprintf failed unexpectedly."); returnValue = 1; goto exitNow; }
+ }
+
+exitNow:
+ va_end(varArgsList);
+ va_end(varArgsListCopy);
+ if(JK_EXPECT_T(returnValue == 0)) { encodeState->atIndex += formattedStringLength; jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object); }
+ return(returnValue);
+}
+
+static int jk_encode_write(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format) {
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
+ if(JK_EXPECT_F(((encodeState->atIndex + strlen(format) + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + strlen(format) + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+
+ size_t formatIdx = 0UL;
+ for(formatIdx = 0UL; format[formatIdx] != 0; formatIdx++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[formatIdx]; }
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
+ return(0);
+}
+
+static int jk_encode_writePrettyPrintWhiteSpace(JKEncodeState *encodeState) {
+ NSCParameterAssert((encodeState != NULL) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) != 0UL));
+ if(JK_EXPECT_F((encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_T(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 4096UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\n';
+ size_t depthWhiteSpace = 0UL;
+ for(depthWhiteSpace = 0UL; depthWhiteSpace < (encodeState->depth * 2UL); depthWhiteSpace++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
+ return(0);
+}
+
+static int jk_encode_write1slow(JKEncodeState *encodeState, ssize_t depthChange, const char *format) {
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (format != NULL) && ((depthChange >= -1L) && (depthChange <= 1L)) && ((encodeState->depth == 0UL) ? (depthChange >= 0L) : 1) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) != 0UL));
+ if(JK_EXPECT_F((encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 4096UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+ encodeState->depth += depthChange;
+ if(JK_EXPECT_T(format[0] == ':')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
+ else {
+ if(JK_EXPECT_F(depthChange == -1L)) { if(JK_EXPECT_F(jk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0];
+ if(JK_EXPECT_T(depthChange != -1L)) { if(JK_EXPECT_F(jk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
+ }
+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
+ return(0);
+}
+
+static int jk_encode_write1fast(JKEncodeState *encodeState, ssize_t depthChange JK_UNUSED_ARG, const char *format) {
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && ((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0UL));
+ if(JK_EXPECT_T((encodeState->atIndex + 4UL) < encodeState->stringBuffer.bytes.length)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; }
+ else { return(jk_encode_write(encodeState, NULL, 0UL, NULL, format)); }
+ return(0);
+}
+
+static int jk_encode_writen(JKEncodeState *encodeState, JKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length) {
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex));
+ if(JK_EXPECT_F((encodeState->stringBuffer.bytes.length - encodeState->atIndex) < (length + 4UL))) { if(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + 4096UL + length) == NULL) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); } }
+ memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, format, length);
+ encodeState->atIndex += length;
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
+ return(0);
+}
+
+JK_STATIC_INLINE JKHash jk_encode_object_hash(void *objectPtr) {
+ return( ( (((JKHash)objectPtr) >> 21) ^ (((JKHash)objectPtr) >> 9) ) + (((JKHash)objectPtr) >> 4) );
+}
+
+static int jk_encode_add_atom_to_buffer(JKEncodeState *encodeState, void *objectPtr) {
+ NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (objectPtr != NULL));
+
+ id object = (id)objectPtr, encodeCacheObject = object;
+ int isClass = JKClassUnknown;
+ size_t startingAtIndex = encodeState->atIndex;
+
+ JKHash objectHash = jk_encode_object_hash(objectPtr);
+ JKEncodeCache *cacheSlot = &encodeState->cache[objectHash % JK_ENCODE_CACHE_SLOTS];
+
+ if(JK_EXPECT_T(cacheSlot->object == object)) {
+ NSCParameterAssert((cacheSlot->object != NULL) &&
+ (cacheSlot->offset < encodeState->atIndex) && ((cacheSlot->offset + cacheSlot->length) < encodeState->atIndex) &&
+ (cacheSlot->offset < encodeState->stringBuffer.bytes.length) && ((cacheSlot->offset + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)));
+ if(JK_EXPECT_F(((encodeState->atIndex + cacheSlot->length + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + cacheSlot->length + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+ NSCParameterAssert(((encodeState->atIndex + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
+ ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
+ ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->atIndex)));
+ memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, encodeState->stringBuffer.bytes.ptr + cacheSlot->offset, cacheSlot->length);
+ encodeState->atIndex += cacheSlot->length;
+ return(0);
+ }
+
+ // When we encounter a class that we do not handle, and we have either a delegate or block that the user supplied to format unsupported classes,
+ // we "re-run" the object check. However, we re-run the object check exactly ONCE. If the user supplies an object that isn't one of the
+ // supported classes, we fail the second time (i.e., double fault error).
+ BOOL rerunningAfterClassFormatter = NO;
+ rerunAfterClassFormatter:;
+
+ // XXX XXX XXX XXX
+ //
+ // We need to work around a bug in 10.7, which breaks ABI compatibility with Objective-C going back not just to 10.0, but OpenStep and even NextStep.
+ //
+ // It has long been documented that "the very first thing that a pointer to an Objective-C object "points to" is a pointer to that objects class".
+ //
+ // This is euphemistically called "tagged pointers". There are a number of highly technical problems with this, most involving long passages from
+ // the C standard(s). In short, one can make a strong case, couched from the perspective of the C standard(s), that that 10.7 "tagged pointers" are
+ // fundamentally Wrong and Broken, and should have never been implemented. Assuming those points are glossed over, because the change is very clearly
+ // breaking ABI compatibility, this should have resulted in a minimum of a "minimum version required" bump in various shared libraries to prevent
+ // causes code that used to work just fine to suddenly break without warning.
+ //
+ // In fact, the C standard says that the hack below is "undefined behavior"- there is no requirement that the 10.7 tagged pointer hack of setting the
+ // "lower, unused bits" must be preserved when casting the result to an integer type, but this "works" because for most architectures
+ // `sizeof(long) == sizeof(void *)` and the compiler uses the same representation for both. (note: this is informal, not meant to be
+ // normative or pedantically correct).
+ //
+ // In other words, while this "works" for now, technically the compiler is not obligated to do "what we want", and a later version of the compiler
+ // is not required in any way to produce the same results or behavior that earlier versions of the compiler did for the statement below.
+ //
+ // Fan-fucking-tastic.
+ //
+ // Why not just use `object_getClass()`? Because `object->isa` reduces to (typically) a *single* instruction. Calling `object_getClass()` requires
+ // that the compiler potentially spill registers, establish a function call frame / environment, and finally execute a "jump subroutine" instruction.
+ // Then, the called subroutine must spend half a dozen instructions in its prolog, however many instructions doing whatever it does, then half a dozen
+ // instructions in its prolog. One instruction compared to dozens, maybe a hundred instructions.
+ //
+ // Yes, that's one to two orders of magnitude difference. Which is compelling in its own right. When going for performance, you're often happy with
+ // gains in the two to three percent range.
+ //
+ // XXX XXX XXX XXX
+
+ BOOL workAroundMacOSXABIBreakingBug = NO;
+ if(JK_EXPECT_F(((NSUInteger)object) & 0x1)) { workAroundMacOSXABIBreakingBug = YES; goto slowClassLookup; }
+
+ if(JK_EXPECT_T(object->isa == encodeState->fastClassLookup.stringClass)) { isClass = JKClassString; }
+ else if(JK_EXPECT_T(object->isa == encodeState->fastClassLookup.numberClass)) { isClass = JKClassNumber; }
+ else if(JK_EXPECT_T(object->isa == encodeState->fastClassLookup.dictionaryClass)) { isClass = JKClassDictionary; }
+ else if(JK_EXPECT_T(object->isa == encodeState->fastClassLookup.arrayClass)) { isClass = JKClassArray; }
+ else if(JK_EXPECT_T(object->isa == encodeState->fastClassLookup.nullClass)) { isClass = JKClassNull; }
+ else {
+ slowClassLookup:
+ if(JK_EXPECT_T([object isKindOfClass:[NSString class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.stringClass = object->isa; } isClass = JKClassString; }
+ else if(JK_EXPECT_T([object isKindOfClass:[NSNumber class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.numberClass = object->isa; } isClass = JKClassNumber; }
+ else if(JK_EXPECT_T([object isKindOfClass:[NSDictionary class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.dictionaryClass = object->isa; } isClass = JKClassDictionary; }
+ else if(JK_EXPECT_T([object isKindOfClass:[NSArray class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.arrayClass = object->isa; } isClass = JKClassArray; }
+ else if(JK_EXPECT_T([object isKindOfClass:[NSNull class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.nullClass = object->isa; } isClass = JKClassNull; }
+ else {
+ if((rerunningAfterClassFormatter == NO) && (
+#ifdef __BLOCKS__
+ ((encodeState->classFormatterBlock) && ((object = encodeState->classFormatterBlock(object)) != NULL)) ||
+#endif
+ ((encodeState->classFormatterIMP) && ((object = encodeState->classFormatterIMP(encodeState->classFormatterDelegate, encodeState->classFormatterSelector, object)) != NULL)) )) { rerunningAfterClassFormatter = YES; goto rerunAfterClassFormatter; }
+
+ if(rerunningAfterClassFormatter == NO) { jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([encodeCacheObject class])); return(1); }
+ else { jk_encode_error(encodeState, @"Unable to serialize object class %@ that was returned by the unsupported class formatter. Original object class was %@.", (object == NULL) ? @"NULL" : NSStringFromClass([object class]), NSStringFromClass([encodeCacheObject class])); return(1); }
+ }
+ }
+
+ // This is here for the benefit of the optimizer. It allows the optimizer to do loop invariant code motion for the JKClassArray
+ // and JKClassDictionary cases when printing simple, single characters via jk_encode_write(), which is actually a macro:
+ // #define jk_encode_write1(es, dc, f) (_jk_encode_prettyPrint ? jk_encode_write1slow(es, dc, f) : jk_encode_write1fast(es, dc, f))
+ int _jk_encode_prettyPrint = JK_EXPECT_T((encodeState->serializeOptionFlags & JKSerializeOptionPretty) == 0) ? 0 : 1;
+
+ switch(isClass) {
+ case JKClassString:
+ {
+ {
+ const unsigned char *cStringPtr = (const unsigned char *)CFStringGetCStringPtr((CFStringRef)object, kCFStringEncodingMacRoman);
+ if(cStringPtr != NULL) {
+ const unsigned char *utf8String = cStringPtr;
+ size_t utf8Idx = 0UL;
+
+ CFIndex stringLength = CFStringGetLength((CFStringRef)object);
+ if(JK_EXPECT_F(((encodeState->atIndex + (stringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length)) && JK_EXPECT_F((jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (stringLength * 2UL) + 1024UL) == NULL))) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
+ for(utf8Idx = 0UL; utf8String[utf8Idx] != 0U; utf8Idx++) {
+ NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
+ if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U)) { encodeState->atIndex = startingAtIndex; goto slowUTF8Path; }
+ if(JK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
+ switch(utf8String[utf8Idx]) {
+ case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
+ case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
+ case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
+ case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
+ case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
+ default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
+ }
+ } else {
+ if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\') || (JK_EXPECT_F(encodeState->serializeOptionFlags & JKSerializeOptionEscapeForwardSlashes) && JK_EXPECT_F(utf8String[utf8Idx] == '/'))) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
+ }
+ }
+ NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
+ return(0);
+ }
+ }
+
+ slowUTF8Path:
+ {
+ CFIndex stringLength = CFStringGetLength((CFStringRef)object);
+ CFIndex maxStringUTF8Length = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 32L;
+
+ if(JK_EXPECT_F((size_t)maxStringUTF8Length > encodeState->utf8ConversionBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->utf8ConversionBuffer, maxStringUTF8Length + 1024UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+
+ CFIndex usedBytes = 0L, convertedCount = 0L;
+ convertedCount = CFStringGetBytes((CFStringRef)object, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, encodeState->utf8ConversionBuffer.bytes.ptr, encodeState->utf8ConversionBuffer.bytes.length - 16L, &usedBytes);
+ if(JK_EXPECT_F(convertedCount != stringLength) || JK_EXPECT_F(usedBytes < 0L)) { jk_encode_error(encodeState, @"An error occurred converting the contents of a NSString to UTF8."); return(1); }
+
+ if(JK_EXPECT_F((encodeState->atIndex + (maxStringUTF8Length * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length) && JK_EXPECT_F(jk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (maxStringUTF8Length * 2UL) + 1024UL) == NULL)) { jk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
+
+ const unsigned char *utf8String = encodeState->utf8ConversionBuffer.bytes.ptr;
+
+ size_t utf8Idx = 0UL;
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
+ for(utf8Idx = 0UL; utf8Idx < (size_t)usedBytes; utf8Idx++) {
+ NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
+ NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
+ NSCParameterAssert((CFIndex)utf8Idx < usedBytes);
+ if(JK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
+ switch(utf8String[utf8Idx]) {
+ case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
+ case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
+ case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
+ case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
+ case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
+ default: if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
+ }
+ } else {
+ if(JK_EXPECT_F(utf8String[utf8Idx] >= 0x80U) && (encodeState->serializeOptionFlags & JKSerializeOptionEscapeUnicode)) {
+ const unsigned char *nextValidCharacter = NULL;
+ UTF32 u32ch = 0U;
+ ConversionResult result;
+
+ if(JK_EXPECT_F((result = ConvertSingleCodePointInUTF8(&utf8String[utf8Idx], &utf8String[usedBytes], (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { jk_encode_error(encodeState, @"Error converting UTF8."); return(1); }
+ else {
+ utf8Idx = (nextValidCharacter - utf8String) - 1UL;
+ if(JK_EXPECT_T(u32ch <= 0xffffU)) { if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", u32ch))) { return(1); } }
+ else { if(JK_EXPECT_F(jk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x\\u%4.4x", (0xd7c0U + (u32ch >> 10)), (0xdc00U + (u32ch & 0x3ffU))))) { return(1); } }
+ }
+ } else {
+ if(JK_EXPECT_F(utf8String[utf8Idx] == '\"') || JK_EXPECT_F(utf8String[utf8Idx] == '\\') || (JK_EXPECT_F(encodeState->serializeOptionFlags & JKSerializeOptionEscapeForwardSlashes) && JK_EXPECT_F(utf8String[utf8Idx] == '/'))) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
+ encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
+ }
+ }
+ }
+ NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
+ if(JK_EXPECT_T((encodeState->encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
+ jk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
+ return(0);
+ }
+ }
+ break;
+
+ case JKClassNumber:
+ {
+ if(object == (id)kCFBooleanTrue) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "true", 4UL)); }
+ else if(object == (id)kCFBooleanFalse) { return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "false", 5UL)); }
+
+ const char *objCType = [object objCType];
+ char anum[256], *aptr = &anum[255];
+ int isNegative = 0;
+ unsigned long long ullv;
+ long long llv;
+
+ if(JK_EXPECT_F(objCType == NULL) || JK_EXPECT_F(objCType[0] == 0) || JK_EXPECT_F(objCType[1] != 0)) { jk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%s'", (objCType == NULL) ? "<NULL>" : objCType); return(1); }
+
+ switch(objCType[0]) {
+ case 'c': case 'i': case 's': case 'l': case 'q':
+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &llv))) {
+ if(llv < 0LL) { ullv = -llv; isNegative = 1; } else { ullv = llv; isNegative = 0; }
+ goto convertNumber;
+ } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
+ break;
+ case 'C': case 'I': case 'S': case 'L': case 'Q': case 'B':
+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &ullv))) {
+ convertNumber:
+ if(JK_EXPECT_F(ullv < 10ULL)) { *--aptr = ullv + '0'; } else { while(JK_EXPECT_T(ullv > 0ULL)) { *--aptr = (ullv % 10ULL) + '0'; ullv /= 10ULL; NSCParameterAssert(aptr > anum); } }
+ if(isNegative) { *--aptr = '-'; }
+ NSCParameterAssert(aptr > anum);
+ return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, aptr, &anum[255] - aptr));
+ } else { jk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
+ break;
+ case 'f': case 'd':
+ {
+ double dv;
+ if(JK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberDoubleType, &dv))) {
+ if(JK_EXPECT_F(!isfinite(dv))) { jk_encode_error(encodeState, @"Floating point values must be finite. JSON does not support NaN or Infinity."); return(1); }
+ return(jk_encode_printf(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "%.17g", dv));
+ } else { jk_encode_error(encodeState, @"Unable to get floating point value from number object."); return(1); }
+ }
+ break;
+ default: jk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%c' / 0x%2.2x", objCType[0], objCType[0]); return(1); break;
+ }
+ }
+ break;
+
+ case JKClassArray:
+ {
+ int printComma = 0;
+ CFIndex arrayCount = CFArrayGetCount((CFArrayRef)object), idx = 0L;
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "["))) { return(1); }
+ if(JK_EXPECT_F(arrayCount > 1020L)) {
+ for(id arrayObject in object) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, arrayObject))) { return(1); } }
+ } else {
+ void *objects[1024];
+ CFArrayGetValues((CFArrayRef)object, CFRangeMake(0L, arrayCount), (const void **)objects);
+ for(idx = 0L; idx < arrayCount; idx++) { if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); } }
+ }
+ return(jk_encode_write1(encodeState, -1L, "]"));
+ }
+ break;
+
+ case JKClassDictionary:
+ {
+ int printComma = 0;
+ CFIndex dictionaryCount = CFDictionaryGetCount((CFDictionaryRef)object), idx = 0L;
+ id enumerateObject = JK_EXPECT_F(_jk_encode_prettyPrint) ? [[object allKeys] sortedArrayUsingSelector:@selector(compare:)] : object;
+
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 1L, "{"))) { return(1); }
+ if(JK_EXPECT_F(_jk_encode_prettyPrint) || JK_EXPECT_F(dictionaryCount > 1020L)) {
+ for(id keyObject in enumerateObject) {
+ if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } }
+ printComma = 1;
+ if(JK_EXPECT_F((keyObject->isa != encodeState->fastClassLookup.stringClass)) && JK_EXPECT_F(([keyObject isKindOfClass:[NSString class]] == NO))) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); }
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keyObject))) { return(1); }
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); }
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, (void *)CFDictionaryGetValue((CFDictionaryRef)object, keyObject)))) { return(1); }
+ }
+ } else {
+ void *keys[1024], *objects[1024];
+ CFDictionaryGetKeysAndValues((CFDictionaryRef)object, (const void **)keys, (const void **)objects);
+ for(idx = 0L; idx < dictionaryCount; idx++) {
+ if(JK_EXPECT_T(printComma)) { if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ","))) { return(1); } }
+ printComma = 1;
+ if(JK_EXPECT_F(((id)keys[idx])->isa != encodeState->fastClassLookup.stringClass) && JK_EXPECT_F([(id)keys[idx] isKindOfClass:[NSString class]] == NO)) { jk_encode_error(encodeState, @"Key must be a string object."); return(1); }
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, keys[idx]))) { return(1); }
+ if(JK_EXPECT_F(jk_encode_write1(encodeState, 0L, ":"))) { return(1); }
+ if(JK_EXPECT_F(jk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); }
+ }
+ }
+ return(jk_encode_write1(encodeState, -1L, "}"));
+ }
+ break;
+
+ case JKClassNull: return(jk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "null", 4UL)); break;
+
+ default: jk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([object class])); return(1); break;
+ }
+
+ return(0);
+}
+
+
+@implementation JKSerializer
+
++ (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
+{
+ return([[[[self alloc] init] autorelease] serializeObject:object options:optionFlags encodeOption:encodeOption block:block delegate:delegate selector:selector error:error]);
+}
+
+- (id)serializeObject:(id)object options:(JKSerializeOptionFlags)optionFlags encodeOption:(JKEncodeOptionType)encodeOption block:(JKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
+{
+#ifndef __BLOCKS__
+#pragma unused(block)
+#endif
+ NSParameterAssert((object != NULL) && (encodeState == NULL) && ((delegate != NULL) ? (block == NULL) : 1) && ((block != NULL) ? (delegate == NULL) : 1) &&
+ (((encodeOption & JKEncodeOptionCollectionObj) != 0UL) ? (((encodeOption & JKEncodeOptionStringObj) == 0UL) && ((encodeOption & JKEncodeOptionStringObjTrimQuotes) == 0UL)) : 1) &&
+ (((encodeOption & JKEncodeOptionStringObj) != 0UL) ? ((encodeOption & JKEncodeOptionCollectionObj) == 0UL) : 1));
+
+ id returnObject = NULL;
+
+ if(encodeState != NULL) { [self releaseState]; }
+ if((encodeState = (struct JKEncodeState *)calloc(1UL, sizeof(JKEncodeState))) == NULL) { [NSException raise:NSMallocException format:@"Unable to allocate state structure."]; return(NULL); }
+
+ if((error != NULL) && (*error != NULL)) { *error = NULL; }
+
+ if(delegate != NULL) {
+ if(selector == NULL) { [NSException raise:NSInvalidArgumentException format:@"The delegate argument is not NULL, but the selector argument is NULL."]; }
+ if([delegate respondsToSelector:selector] == NO) { [NSException raise:NSInvalidArgumentException format:@"The serializeUnsupportedClassesUsingDelegate: delegate does not respond to the selector argument."]; }
+ encodeState->classFormatterDelegate = delegate;
+ encodeState->classFormatterSelector = selector;
+ encodeState->classFormatterIMP = (JKClassFormatterIMP)[delegate methodForSelector:selector];
+ NSCParameterAssert(encodeState->classFormatterIMP != NULL);
+ }
+
+#ifdef __BLOCKS__
+ encodeState->classFormatterBlock = block;
+#endif
+ encodeState->serializeOptionFlags = optionFlags;
+ encodeState->encodeOption = encodeOption;
+ encodeState->stringBuffer.roundSizeUpToMultipleOf = (1024UL * 32UL);
+ encodeState->utf8ConversionBuffer.roundSizeUpToMultipleOf = 4096UL;
+
+ unsigned char stackJSONBuffer[JK_JSONBUFFER_SIZE] JK_ALIGNED(64);
+ jk_managedBuffer_setToStackBuffer(&encodeState->stringBuffer, stackJSONBuffer, sizeof(stackJSONBuffer));
+
+ unsigned char stackUTF8Buffer[JK_UTF8BUFFER_SIZE] JK_ALIGNED(64);
+ jk_managedBuffer_setToStackBuffer(&encodeState->utf8ConversionBuffer, stackUTF8Buffer, sizeof(stackUTF8Buffer));
+
+ if(((encodeOption & JKEncodeOptionCollectionObj) != 0UL) && (([object isKindOfClass:[NSArray class]] == NO) && ([object isKindOfClass:[NSDictionary class]] == NO))) { jk_encode_error(encodeState, @"Unable to serialize object class %@, expected a NSArray or NSDictionary.", NSStringFromClass([object class])); goto errorExit; }
+ if(((encodeOption & JKEncodeOptionStringObj) != 0UL) && ([object isKindOfClass:[NSString class]] == NO)) { jk_encode_error(encodeState, @"Unable to serialize object class %@, expected a NSString.", NSStringFromClass([object class])); goto errorExit; }
+
+ if(jk_encode_add_atom_to_buffer(encodeState, object) == 0) {
+ BOOL stackBuffer = ((encodeState->stringBuffer.flags & JKManagedBufferMustFree) == 0UL) ? YES : NO;
+
+ if((encodeState->atIndex < 2UL))
+ if((stackBuffer == NO) && ((encodeState->stringBuffer.bytes.ptr = (unsigned char *)reallocf(encodeState->stringBuffer.bytes.ptr, encodeState->atIndex + 16UL)) == NULL)) { jk_encode_error(encodeState, @"Unable to realloc buffer"); goto errorExit; }
+
+ switch((encodeOption & JKEncodeOptionAsTypeMask)) {
+ case JKEncodeOptionAsData:
+ if(stackBuffer == YES) { if((returnObject = [(id)CFDataCreate( NULL, encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSData object"); } }
+ else { if((returnObject = [(id)CFDataCreateWithBytesNoCopy( NULL, encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, NULL) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSData object"); } }
+ break;
+
+ case JKEncodeOptionAsString:
+ if(stackBuffer == YES) { if((returnObject = [(id)CFStringCreateWithBytes( NULL, (const UInt8 *)encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, kCFStringEncodingUTF8, NO) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSString object"); } }
+ else { if((returnObject = [(id)CFStringCreateWithBytesNoCopy(NULL, (const UInt8 *)encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, kCFStringEncodingUTF8, NO, NULL) autorelease]) == NULL) { jk_encode_error(encodeState, @"Unable to create NSString object"); } }
+ break;
+
+ default: jk_encode_error(encodeState, @"Unknown encode as type."); break;
+ }
+
+ if((returnObject != NULL) && (stackBuffer == NO)) { encodeState->stringBuffer.flags &= ~JKManagedBufferMustFree; encodeState->stringBuffer.bytes.ptr = NULL; encodeState->stringBuffer.bytes.length = 0UL; }
+ }
+
+errorExit:
+ if((encodeState != NULL) && (error != NULL) && (encodeState->error != NULL)) { *error = encodeState->error; encodeState->error = NULL; }
+ [self releaseState];
+
+ return(returnObject);
+}
+
+- (void)releaseState
+{
+ if(encodeState != NULL) {
+ jk_managedBuffer_release(&encodeState->stringBuffer);
+ jk_managedBuffer_release(&encodeState->utf8ConversionBuffer);
+ free(encodeState); encodeState = NULL;
+ }
+}
+
+- (void)dealloc
+{
+ [self releaseState];
+ [super dealloc];
+}
+
+@end
+
+@implementation NSString (JSONKitSerializing)
+
+////////////
+#pragma mark Methods for serializing a single NSString.
+////////////
+
+// Useful for those who need to serialize just a NSString. Otherwise you would have to do something like [NSArray arrayWithObject:stringToBeJSONSerialized], serializing the array, and then chopping of the extra ^\[.*\]$ square brackets.
+
+// NSData returning methods...
+
+- (NSData *)JSONData
+{
+ return([self JSONDataWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]);
+}
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
+}
+
+// NSString returning methods...
+
+- (NSString *)JSONString
+{
+ return([self JSONStringWithOptions:JKSerializeOptionNone includeQuotes:YES error:NULL]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | ((includeQuotes == NO) ? JKEncodeOptionStringObjTrimQuotes : 0UL) | JKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
+}
+
+@end
+
+@implementation NSArray (JSONKitSerializing)
+
+// NSData returning methods...
+
+- (NSData *)JSONData
+{
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
+}
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
+}
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
+}
+
+// NSString returning methods...
+
+- (NSString *)JSONString
+{
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
+}
+
+@end
+
+@implementation NSDictionary (JSONKitSerializing)
+
+// NSData returning methods...
+
+- (NSData *)JSONData
+{
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
+}
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
+}
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
+}
+
+// NSString returning methods...
+
+- (NSString *)JSONString
+{
+ return([JKSerializer serializeObject:self options:JKSerializeOptionNone encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
+}
+
+@end
+
+
+#ifdef __BLOCKS__
+
+@implementation NSArray (JSONKitSerializingBlockAdditions)
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
+}
+
+@end
+
+@implementation NSDictionary (JSONKitSerializingBlockAdditions)
+
+- (NSData *)JSONDataWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsData | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
+}
+
+- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
+{
+ return([JKSerializer serializeObject:self options:serializeOptions encodeOption:(JKEncodeOptionAsString | JKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
+}
+
+@end
+
+#endif // __BLOCKS__
+
diff --git a/samples/objective-c/MainMenuViewController.h b/samples/objective-c/MainMenuViewController.h
new file mode 100644
index 0000000..f646771
--- /dev/null
+++ b/samples/objective-c/MainMenuViewController.h
@@ -0,0 +1,21 @@
+//
+// Copyright 2009-2011 Facebook
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+@interface MainMenuViewController : TTTableViewController {
+
+}
+
+@end
diff --git a/samples/objective-c/MainMenuViewController.m b/samples/objective-c/MainMenuViewController.m
new file mode 100644
index 0000000..c97318b
--- /dev/null
+++ b/samples/objective-c/MainMenuViewController.m
@@ -0,0 +1,157 @@
+//
+// Copyright 2009-2011 Facebook
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "MainMenuViewController.h"
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation MainMenuViewController
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+ if (self = [super initWithNibName:nil bundle:nil]) {
+ self.title = @"Style Catalog";
+ //self.variableHeightRows = YES;
+ self.tableViewStyle = UITableViewStyleGrouped;
+
+ self.dataSource =
+ [TTSectionedDataSource dataSourceWithObjects:
+ @"Text Styles",
+ [TTTableTextItem itemWithText:@"Link Text"
+ URL:@"tt://styles/linkText:/text"],
+ [TTTableTextItem itemWithText:@"Mini Badge"
+ URL:@"tt://styles/miniBadge/text"],
+ [TTTableTextItem itemWithText:@"Badge"
+ URL:@"tt://styles/badge/text"],
+ [TTTableTextItem itemWithText:@"Large Badge"
+ URL:@"tt://styles/largeBadge/text"],
+
+ @"Views",
+ [TTTableTextItem itemWithText:@"Post Text Editor"
+ URL:@"tt://styles/postTextEditor/view"],
+ [TTTableTextItem itemWithText:@"Photo Caption"
+ URL:@"tt://styles/photoCaption/view"],
+ [TTTableTextItem itemWithText:@"Photo Status Label"
+ URL:@"tt://styles/photoStatusLabel/view"],
+ [TTTableTextItem itemWithText:@"Page Dot"
+ URL:@"tt://styles/pageDot:/view"],
+ [TTTableTextItem itemWithText:@"Highlighted Link"
+ URL:@"tt://styles/linkHighlighted/view"],
+ [TTTableTextItem itemWithText:@"Table Header"
+ URL:@"tt://styles/tableHeader/view"],
+ [TTTableTextItem itemWithText:@"Picker Cell"
+ URL:@"tt://styles/pickerCell:/view"],
+ [TTTableTextItem itemWithText:@"Search Table Shadow"
+ URL:@"tt://styles/searchTableShadow/view"],
+ [TTTableTextItem itemWithText:@"Black Bezel"
+ URL:@"tt://styles/blackBezel/view"],
+ [TTTableTextItem itemWithText:@"White Bezel"
+ URL:@"tt://styles/whiteBezel/view"],
+ [TTTableTextItem itemWithText:@"Black Banner"
+ URL:@"tt://styles/blackBanner/view"],
+ [TTTableTextItem itemWithText:@"Tab Bar"
+ URL:@"tt://styles/tabBar/view"],
+ [TTTableTextItem itemWithText:@"Tab Strip"
+ URL:@"tt://styles/tabStrip/view"],
+
+ @"Tab Grid",
+ [TTTableTextItem itemWithText:@"Tab Grid"
+ URL:@"tt://styles/tabGrid/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Top Left"
+ URL:@"tt://styles/tabGridTabTopLeft:/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Top Right"
+ URL:@"tt://styles/tabGridTabTopRight:/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Bottom Right"
+ URL:@"tt://styles/tabGridTabBottomRight:/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Bottom Left"
+ URL:@"tt://styles/tabGridTabBottomLeft:/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Left"
+ URL:@"tt://styles/tabGridTabLeft:/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Right"
+ URL:@"tt://styles/tabGridTabRight:/view"],
+ [TTTableTextItem itemWithText:@"Tab Grid Center"
+ URL:@"tt://styles/tabGridTabCenter:/view"],
+
+ @"Tabs",
+ [TTTableTextItem itemWithText:@"Tab"
+ URL:@"tt://styles/tab:/view"],
+ [TTTableTextItem itemWithText:@"Round Tab"
+ URL:@"tt://styles/tabRound:/view"],
+ [TTTableTextItem itemWithText:@"Tab Left Overflow"
+ URL:@"tt://styles/tabOverflowLeft/view"],
+ [TTTableTextItem itemWithText:@"Tab Right Overflow"
+ URL:@"tt://styles/tabOverflowRight/view"],
+
+ @"Images",
+ [TTTableTextItem itemWithText:@"Thumb View"
+ URL:@"tt://styles/thumbView:/image"],
+
+ @"Launcher",
+ [TTTableTextItem itemWithText:@"Launcher Button"
+ URL:@"tt://styles/launcherButton:/image"],
+ [TTTableTextItem itemWithText:@"Launcher Close Button"
+ URL:@"tt://styles/launcherCloseButton:/view"],
+
+ @"Text Bar",
+ [TTTableTextItem itemWithText:@"Text Bar"
+ URL:@"tt://styles/textBar/view"],
+ [TTTableTextItem itemWithText:@"Text Bar Footer"
+ URL:@"tt://styles/textBarFooter/view"],
+ [TTTableTextItem itemWithText:@"Text Bar Text Field"
+ URL:@"tt://styles/textBarTextField/view"],
+ [TTTableTextItem itemWithText:@"Text Bar Post Button"
+ URL:@"tt://styles/textBarPostButton:/text"],
+
+ @"Toolbars",
+ [TTTableTextItem itemWithText:@"Toolbar Button"
+ URL:@"tt://styles/toolbarButton:/view"],
+ [TTTableTextItem itemWithText:@"Toolbar Back Button"
+ URL:@"tt://styles/toolbarBackButton:/view"],
+ [TTTableTextItem itemWithText:@"Toolbar Forward Button"
+ URL:@"tt://styles/toolbarForwardButton:/view"],
+ [TTTableTextItem itemWithText:@"Toolbar Round Button"
+ URL:@"tt://styles/toolbarRoundButton:/view"],
+ [TTTableTextItem itemWithText:@"Black Toolbar Button"
+ URL:@"tt://styles/blackToolbarButton:/view"],
+ [TTTableTextItem itemWithText:@"Gray Toolbar Button"
+ URL:@"tt://styles/grayToolbarButton:/view"],
+ [TTTableTextItem itemWithText:@"Black Toolbar Forward Button"
+ URL:@"tt://styles/blackToolbarForwardButton:/view"],
+ [TTTableTextItem itemWithText:@"Black Toolbar Round Button"
+ URL:@"tt://styles/blackToolbarRoundButton:/view"],
+
+ @"Search",
+ [TTTableTextItem itemWithText:@"Search Text Field"
+ URL:@"tt://styles/searchTextField/view"],
+ [TTTableTextItem itemWithText:@"Search Bar"
+ URL:@"tt://styles/searchBar/view"],
+ [TTTableTextItem itemWithText:@"Search Bar Bottom"
+ URL:@"tt://styles/searchBarBottom/view"],
+ [TTTableTextItem itemWithText:@"Black Search Bar"
+ URL:@"tt://styles/blackSearchBar/view"],
+
+ nil];
+ }
+
+ return self;
+}
+
+
+@end
+
diff --git a/samples/objective-c/PlaygroundViewController.h b/samples/objective-c/PlaygroundViewController.h
new file mode 100644
index 0000000..9c94c56
--- /dev/null
+++ b/samples/objective-c/PlaygroundViewController.h
@@ -0,0 +1,22 @@
+//
+// Copyright 2009-2011 Facebook
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+@interface PlaygroundViewController : UIViewController {
+ UIScrollView* _scrollView;
+}
+
+@end
diff --git a/samples/objective-c/PlaygroundViewController.m b/samples/objective-c/PlaygroundViewController.m
new file mode 100644
index 0000000..5d52d91
--- /dev/null
+++ b/samples/objective-c/PlaygroundViewController.m
@@ -0,0 +1,200 @@
+//
+// Copyright 2009-2011 Facebook
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "PlaygroundViewController.h"
+
+#import <Three20Core/NSDataAdditions.h>
+
+static const CGFloat kFramePadding = 10;
+static const CGFloat kElementSpacing = 5;
+static const CGFloat kGroupSpacing = 10;
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation PlaygroundViewController
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (CGFloat) addHeader:(NSString*)text yOffset:(CGFloat)yOffset {
+ UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
+ label.text = text;
+ label.font = [UIFont systemFontOfSize:20];
+ label.numberOfLines = 0;
+
+ CGRect frame = label.frame;
+ frame.origin.x = kFramePadding;
+ frame.origin.y = yOffset;
+ frame.size.width = 320 - kFramePadding * 2;
+ frame.size.height = [text sizeWithFont:label.font
+ constrainedToSize:CGSizeMake(frame.size.width, 10000)].height;
+ label.frame = frame;
+
+ [_scrollView addSubview:label];
+
+ yOffset += label.frame.size.height + kElementSpacing;
+
+ TT_RELEASE_SAFELY(label);
+
+ return yOffset;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (CGFloat) addText:(NSString*)text yOffset:(CGFloat)yOffset {
+ UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
+ label.text = text;
+ label.numberOfLines = 0;
+
+ CGRect frame = label.frame;
+ frame.origin.x = kFramePadding;
+ frame.origin.y = yOffset;
+ frame.size.width = 320 - kFramePadding * 2;
+ frame.size.height = [text sizeWithFont:label.font
+ constrainedToSize:CGSizeMake(frame.size.width, 10000)].height;
+ label.frame = frame;
+
+ [_scrollView addSubview:label];
+
+ yOffset += label.frame.size.height + kElementSpacing;
+
+ TT_RELEASE_SAFELY(label);
+
+ return yOffset;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void) loadView {
+ [super loadView];
+
+ _scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
+ _scrollView.autoresizingMask =
+ UIViewAutoresizingFlexibleWidth
+ | UIViewAutoresizingFlexibleHeight;
+
+ [self.view addSubview:_scrollView];
+
+ CGFloat yOffset = kFramePadding;
+
+ yOffset = [self addHeader:NSLocalizedString(@"TTDebug", @"") yOffset:yOffset];
+
+ {
+ UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+ [button setTitle:NSLocalizedString(@"Debug test", @"") forState:UIControlStateNormal];
+ [button addTarget: self
+ action: @selector(debugTestAction)
+ forControlEvents: UIControlEventTouchUpInside];
+ [button sizeToFit];
+
+ CGRect frame = button.frame;
+ frame.origin.x = kFramePadding;
+ frame.origin.y = yOffset;
+ button.frame = frame;
+
+ [_scrollView addSubview:button];
+
+ yOffset += frame.size.height;
+ }
+
+ yOffset += kGroupSpacing;
+
+ yOffset = [self addHeader:NSLocalizedString(@"TTGlobalCoreLocale", @"") yOffset:yOffset];
+ yOffset = [self addText:[NSString stringWithFormat:NSLocalizedString(@"Current locale: %@", @""),
+ [TTCurrentLocale()
+ displayNameForKey:NSLocaleIdentifier
+ value:[TTCurrentLocale() localeIdentifier]]]
+ yOffset:yOffset];
+ yOffset += kGroupSpacing;
+
+ yOffset = [self addHeader:NSLocalizedString(@"TTGlobalCorePaths", @"") yOffset:yOffset];
+ yOffset = [self addText:[NSString stringWithFormat:NSLocalizedString(@"Bundle path: %@", @""),
+ TTPathForBundleResource(@"Icon.png")]
+ yOffset:yOffset];
+ yOffset = [self addText:[NSString stringWithFormat:NSLocalizedString(@"Document path: %@", @""),
+ TTPathForDocumentsResource(@"document.pdf")]
+ yOffset:yOffset];
+ yOffset += kGroupSpacing;
+
+ yOffset = [self addHeader:NSLocalizedString(@"NSDataAdditions", @"") yOffset:yOffset];
+ yOffset = [self addText:[NSString stringWithFormat:NSLocalizedString(@"MD5 Hash of \"Three20\": %@", @""),
+ [[@"Three20" dataUsingEncoding:NSUTF8StringEncoding] md5Hash]]
+ yOffset:yOffset];
+ yOffset += kGroupSpacing;
+
+ [_scrollView setContentSize:CGSizeMake(320, yOffset)];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void) viewDidUnload {
+ [super viewDidUnload];
+
+ TT_RELEASE_SAFELY(_scrollView);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void) viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+
+ [_scrollView flashScrollIndicators];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void) debugTestAction {
+#ifdef DEBUG
+ NSLog(@"Three20 debug logging is currently...ON");
+#else
+ NSLog(@"Three20 debug logging is currently...OFF");
+#endif
+
+ // This will print the current method name.
+ TTDPRINTMETHODNAME();
+
+ TTDPRINT(@"Showing TTDPRINT.");
+ TTDPRINT(@"-----------------");
+ TTDPRINT(@"Showing TTD log levels <= %d", TTMAXLOGLEVEL);
+ TTDERROR(@"This is TTDERROR, level %d.", TTLOGLEVEL_ERROR);
+ TTDWARNING(@"This is TTDWARNING, level %d.", TTLOGLEVEL_WARNING);
+ TTDINFO(@"This is TTDINFO, level %d.", TTLOGLEVEL_INFO);
+
+ TTDPRINT(@"");
+ TTDPRINT(@"Showing TTDCONDITIONLOG.");
+ TTDPRINT(@"------------------------");
+ TTDCONDITIONLOG(true, @"This will always display because the condition is \"true\"");
+ TTDCONDITIONLOG(false, @"This will never display because the condition is \"false\"");
+ TTDCONDITIONLOG(rand()%2, @"This will randomly display because the condition is \"rand()%2\"");
+
+ TTDPRINT(@"");
+ TTDPRINT(@"Showing TTDASSERT.");
+ TTDPRINT(@"------------------");
+ // Should do nothing at all.
+ TTDASSERT(true);
+
+ // This will jump you into the debugger in the simulator.
+ // Note that this isn't a crash! Simply the equivalent of setting
+ // a breakpoint in the debugger, but programmatically. These TTDASSERTs
+ // will be completely stripped away from your final product, assuming
+ // you don't declare the DEBUG preprocessor macro (and you shouldn't in
+ // your final product).
+ TTDASSERT(false);
+}
+
+
+@end
diff --git a/samples/objective-c/SBJsonParser.h b/samples/objective-c/SBJsonParser.h
new file mode 100644
index 0000000..751122f
--- /dev/null
+++ b/samples/objective-c/SBJsonParser.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2009 Stig Brautaset. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its contributors may be used
+ to endorse or promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+
+/**
+ @brief Parse JSON Strings and NSData objects
+
+ This uses SBJsonStreamParser internally.
+
+ @see @ref objc2json
+
+ */
+
+@interface SBJsonParser : NSObject
+
+/**
+ @brief The maximum recursing depth.
+
+ Defaults to 32. If the input is nested deeper than this the input will be deemed to be
+ malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can
+ turn off this security feature by setting the maxDepth value to 0.
+ */
+@property NSUInteger maxDepth;
+
+/**
+ @brief Description of parse error
+
+ This method returns the trace of the last method that failed.
+ You need to check the return value of the call you're making to figure out
+ if the call actually failed, before you know call this method.
+
+ @return A string describing the error encountered, or nil if no error occured.
+
+ */
+@property(copy) NSString *error;
+
+/**
+ @brief Return the object represented by the given NSData object.
+
+ The data *must* be UTF8 encoded.
+
+ @param data An NSData containing UTF8 encoded data to parse.
+ @return The NSArray or NSDictionary represented by the object, or nil if an error occured.
+
+ */
+- (id)objectWithData:(NSData*)data;
+
+/**
+ @brief Return the object represented by the given string
+
+ This method converts its input to an NSData object containing UTF8 and calls -objectWithData: with it.
+
+ @return The NSArray or NSDictionary represented by the object, or nil if an error occured.
+ */
+- (id)objectWithString:(NSString *)repr;
+
+/**
+ @brief Return the object represented by the given string
+
+ This method calls objectWithString: internally. If an error occurs, and if @p error
+ is not nil, it creates an NSError object and returns this through its second argument.
+
+ @param jsonText the json string to parse
+ @param error pointer to an NSError object to populate on error
+
+ @return The NSArray or NSDictionary represented by the object, or nil if an error occured.
+ */
+
+- (id)objectWithString:(NSString*)jsonText
+ error:(NSError**)error;
+
+@end
+
+
diff --git a/samples/objective-c/SBJsonParser.m b/samples/objective-c/SBJsonParser.m
new file mode 100644
index 0000000..d1b4b1e
--- /dev/null
+++ b/samples/objective-c/SBJsonParser.m
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2009,2010 Stig Brautaset. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its contributors may be used
+ to endorse or promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "SBJsonParser.h"
+#import "SBJsonStreamParser.h"
+#import "SBJsonStreamParserAdapter.h"
+#import "SBJsonStreamParserAccumulator.h"
+
+@implementation SBJsonParser
+
+@synthesize maxDepth;
+@synthesize error;
+
+- (id)init {
+ self = [super init];
+ if (self)
+ self.maxDepth = 32u;
+ return self;
+}
+
+
+#pragma mark Methods
+
+- (id)objectWithData:(NSData *)data {
+
+ if (!data) {
+ self.error = @"Input was 'nil'";
+ return nil;
+ }
+
+ SBJsonStreamParserAccumulator *accumulator = [[SBJsonStreamParserAccumulator alloc] init];
+
+ SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter alloc] init];
+ adapter.delegate = accumulator;
+
+ SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init];
+ parser.maxDepth = self.maxDepth;
+ parser.delegate = adapter;
+
+ switch ([parser parse:data]) {
+ case SBJsonStreamParserComplete:
+ return accumulator.value;
+ break;
+
+ case SBJsonStreamParserWaitingForData:
+ self.error = @"Unexpected end of input";
+ break;
+
+ case SBJsonStreamParserError:
+ self.error = parser.error;
+ break;
+ }
+
+ return nil;
+}
+
+- (id)objectWithString:(NSString *)repr {
+ return [self objectWithData:[repr dataUsingEncoding:NSUTF8StringEncoding]];
+}
+
+- (id)objectWithString:(NSString*)repr error:(NSError**)error_ {
+ id tmp = [self objectWithString:repr];
+ if (tmp)
+ return tmp;
+
+ if (error_) {
+ NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil];
+ *error_ = [NSError errorWithDomain:@"org.brautaset.SBJsonParser.ErrorDomain" code:0 userInfo:ui];
+ }
+
+ return nil;
+}
+
+@end
diff --git a/samples/objective-c/StyleViewController.h b/samples/objective-c/StyleViewController.h
new file mode 100644
index 0000000..c9d0093
--- /dev/null
+++ b/samples/objective-c/StyleViewController.h
@@ -0,0 +1,26 @@
+//
+// Copyright 2009-2011 Facebook
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+@interface StyleViewController : TTViewController {
+@private
+ TTStyle* _style;
+ TTStyle* _styleHighlight;
+ TTStyle* _styleDisabled;
+ TTStyle* _styleSelected;
+ NSString* _styleType;
+}
+
+@end
diff --git a/samples/objective-c/StyleViewController.m b/samples/objective-c/StyleViewController.m
new file mode 100644
index 0000000..7018b5e
--- /dev/null
+++ b/samples/objective-c/StyleViewController.m
@@ -0,0 +1,185 @@
+//
+// Copyright 2009-2011 Facebook
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "StyleViewController.h"
+
+#import "StyleView.h"
+
+NSString* kTextStyleType = @"text";
+NSString* kViewStyleType = @"view";
+NSString* kImageStyleType = @"image";
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation StyleViewController
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (id)initWithStyleName:(NSString*)name styleType:(NSString*)styleType {
+ if (self = [super initWithNibName:nil bundle:nil]) {
+ self.title = name;
+
+ _style = [[[TTStyleSheet globalStyleSheet] styleWithSelector:name] retain];
+ _styleHighlight = [[[TTStyleSheet globalStyleSheet]
+ styleWithSelector: name
+ forState: UIControlStateHighlighted] retain];
+ _styleDisabled = [[[TTStyleSheet globalStyleSheet]
+ styleWithSelector: name
+ forState: UIControlStateDisabled] retain];
+ _styleSelected = [[[TTStyleSheet globalStyleSheet]
+ styleWithSelector: name
+ forState: UIControlStateSelected] retain];
+
+ _styleType = [styleType copy];
+ }
+
+ return self;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)dealloc {
+ TT_RELEASE_SAFELY(_style);
+ TT_RELEASE_SAFELY(_styleHighlight);
+ TT_RELEASE_SAFELY(_styleDisabled);
+ TT_RELEASE_SAFELY(_styleSelected);
+ TT_RELEASE_SAFELY(_styleType);
+
+ [super dealloc];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark UIViewController
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)addTextView:(NSString*)title frame:(CGRect)frame style:(TTStyle*)style {
+ CGRect textFrame = TTRectInset(frame, UIEdgeInsetsMake(20, 20, 20, 20));
+ StyleView* text = [[StyleView alloc]
+ initWithFrame:textFrame];
+ text.text = title;
+ TTStyleContext* context = [[TTStyleContext alloc] init];
+ context.frame = frame;
+ context.delegate = text;
+ context.font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
+ CGSize size = [style addToSize:CGSizeZero context:context];
+ TT_RELEASE_SAFELY(context);
+
+ size.width += 20;
+ size.height += 20;
+ textFrame.size = size;
+ text.frame = textFrame;
+
+ text.style = style;
+ text.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:1 alpha:1];
+ text.autoresizingMask =
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
+
+ [self.view addSubview:text];
+ TT_RELEASE_SAFELY(text);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)addView:(CGRect)frame style:(TTStyle*)style {
+ CGRect viewFrame = TTRectInset(frame, UIEdgeInsetsMake(20, 20, 20, 20));
+ StyleView* view = [[StyleView alloc]
+ initWithFrame:viewFrame];
+
+ view.style = style;
+ view.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:1 alpha:1];
+ view.autoresizingMask =
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
+
+ [self.view addSubview:view];
+ TT_RELEASE_SAFELY(view);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)addImageView:(CGRect)frame style:(TTStyle*)style {
+ CGRect viewFrame = TTRectInset(frame, UIEdgeInsetsMake(20, 20, 20, 20));
+ TTImageView* view = [[TTImageView alloc]
+ initWithFrame:viewFrame];
+
+ view.urlPath = @"bundle://Icon.png";
+ view.style = style;
+ view.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:1 alpha:1];
+ view.autoresizingMask =
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
+ CGRect imageFrame = view.frame;
+ imageFrame.size = view.image.size;
+ view.frame = imageFrame;
+
+ [self.view addSubview:view];
+ TT_RELEASE_SAFELY(view);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)loadView {
+ [super loadView];
+
+ CGRect frame = self.view.bounds;
+ frame.size.height /= 4;
+
+ if ([_styleType isEqualToString:kTextStyleType]) {
+ [self addTextView:@"UIControlStateNormal" frame:frame style:_style];
+
+ frame.origin.y += frame.size.height;
+ [self addTextView:@"UIControlStateHighlighted" frame:frame style:_styleHighlight];
+
+ frame.origin.y += frame.size.height;
+ [self addTextView:@"UIControlStateDisabled" frame:frame style:_styleDisabled];
+
+ frame.origin.y += frame.size.height;
+ [self addTextView:@"UIControlStateSelected" frame:frame style:_styleSelected];
+
+ } else if ([_styleType isEqualToString:kViewStyleType]) {
+ [self addView:frame style:_style];
+
+ frame.origin.y += frame.size.height;
+ [self addView:frame style:_styleHighlight];
+
+ frame.origin.y += frame.size.height;
+ [self addView:frame style:_styleDisabled];
+
+ frame.origin.y += frame.size.height;
+ [self addView:frame style:_styleSelected];
+
+ } else if ([_styleType isEqualToString:kImageStyleType]) {
+ [self addImageView:frame style:_style];
+
+ frame.origin.y += frame.size.height;
+ [self addImageView:frame style:_styleHighlight];
+
+ frame.origin.y += frame.size.height;
+ [self addImageView:frame style:_styleDisabled];
+
+ frame.origin.y += frame.size.height;
+ [self addImageView:frame style:_styleSelected];
+
+ }
+}
+
+
+@end
+
diff --git a/samples/objective-c/TUITableView.h b/samples/objective-c/TUITableView.h
new file mode 100644
index 0000000..d7036c5
--- /dev/null
+++ b/samples/objective-c/TUITableView.h
@@ -0,0 +1,217 @@
+/*
+ Copyright 2011 Twitter, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this work except in compliance with the License.
+ You may obtain a copy of the License in the LICENSE file, or at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "TUIScrollView.h"
+#import "TUIFastIndexPath.h"
+
+typedef enum {
+ TUITableViewStylePlain, // regular table view
+ TUITableViewStyleGrouped, // grouped table view—headers stick to the top of the table view and scroll with it
+} TUITableViewStyle;
+
+typedef enum {
+ TUITableViewScrollPositionNone,
+ TUITableViewScrollPositionTop,
+ TUITableViewScrollPositionMiddle,
+ TUITableViewScrollPositionBottom,
+ TUITableViewScrollPositionToVisible, // currently the only supported arg
+} TUITableViewScrollPosition;
+
+typedef enum {
+ TUITableViewInsertionMethodBeforeIndex = NSOrderedAscending,
+ TUITableViewInsertionMethodAtIndex = NSOrderedSame,
+ TUITableViewInsertionMethodAfterIndex = NSOrderedDescending
+} TUITableViewInsertionMethod;
+
+@class TUITableViewCell;
+@protocol TUITableViewDataSource;
+
+@class TUITableView;
+
+@protocol TUITableViewDelegate<NSObject, TUIScrollViewDelegate>
+
+- (CGFloat)tableView:(TUITableView *)tableView heightForRowAtIndexPath:(TUIFastIndexPath *)indexPath;
+
+@optional
+
+- (void)tableView:(TUITableView *)tableView willDisplayCell:(TUITableViewCell *)cell forRowAtIndexPath:(TUIFastIndexPath *)indexPath; // called after the cell's frame has been set but before it's added as a subview
+- (void)tableView:(TUITableView *)tableView didSelectRowAtIndexPath:(TUIFastIndexPath *)indexPath; // happens on left/right mouse down, key up/down
+- (void)tableView:(TUITableView *)tableView didDeselectRowAtIndexPath:(TUIFastIndexPath *)indexPath;
+- (void)tableView:(TUITableView *)tableView didClickRowAtIndexPath:(TUIFastIndexPath *)indexPath withEvent:(NSEvent *)event; // happens on left/right mouse up (can look at clickCount)
+
+- (BOOL)tableView:(TUITableView*)tableView shouldSelectRowAtIndexPath:(TUIFastIndexPath*)indexPath forEvent:(NSEvent*)event; // YES, if not implemented
+- (NSMenu *)tableView:(TUITableView *)tableView menuForRowAtIndexPath:(TUIFastIndexPath *)indexPath withEvent:(NSEvent *)event;
+
+// the following are good places to update or restore state (such as selection) when the table data reloads
+- (void)tableViewWillReloadData:(TUITableView *)tableView;
+- (void)tableViewDidReloadData:(TUITableView *)tableView;
+
+// the following is optional for row reordering
+- (TUIFastIndexPath *)tableView:(TUITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(TUIFastIndexPath *)fromPath toProposedIndexPath:(TUIFastIndexPath *)proposedPath;
+
+@end
+
+@interface TUITableView : TUIScrollView
+{
+ TUITableViewStyle _style;
+ __unsafe_unretained id <TUITableViewDataSource> _dataSource; // weak
+ NSArray * _sectionInfo;
+
+ TUIView * _pullDownView;
+ TUIView * _headerView;
+
+ CGSize _lastSize;
+ CGFloat _contentHeight;
+
+ NSMutableIndexSet * _visibleSectionHeaders;
+ NSMutableDictionary * _visibleItems;
+ NSMutableDictionary * _reusableTableCells;
+
+ TUIFastIndexPath * _selectedIndexPath;
+ TUIFastIndexPath * _indexPathShouldBeFirstResponder;
+ NSInteger _futureMakeFirstResponderToken;
+ TUIFastIndexPath * _keepVisibleIndexPathForReload;
+ CGFloat _relativeOffsetForReload;
+
+ // drag-to-reorder state
+ TUITableViewCell * _dragToReorderCell;
+ CGPoint _currentDragToReorderLocation;
+ CGPoint _currentDragToReorderMouseOffset;
+ TUIFastIndexPath * _currentDragToReorderIndexPath;
+ TUITableViewInsertionMethod _currentDragToReorderInsertionMethod;
+ TUIFastIndexPath * _previousDragToReorderIndexPath;
+ TUITableViewInsertionMethod _previousDragToReorderInsertionMethod;
+
+ struct {
+ unsigned int animateSelectionChanges:1;
+ unsigned int forceSaveScrollPosition:1;
+ unsigned int derepeaterEnabled:1;
+ unsigned int layoutSubviewsReentrancyGuard:1;
+ unsigned int didFirstLayout:1;
+ unsigned int dataSourceNumberOfSectionsInTableView:1;
+ unsigned int delegateTableViewWillDisplayCellForRowAtIndexPath:1;
+ unsigned int maintainContentOffsetAfterReload:1;
+ } _tableFlags;
+
+}
+
+- (id)initWithFrame:(CGRect)frame style:(TUITableViewStyle)style; // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
+
+@property (nonatomic,unsafe_unretained) id <TUITableViewDataSource> dataSource;
+@property (nonatomic,unsafe_unretained) id <TUITableViewDelegate> delegate;
+
+@property (readwrite, assign) BOOL animateSelectionChanges;
+@property (nonatomic, assign) BOOL maintainContentOffsetAfterReload;
+
+- (void)reloadData;
+
+/**
+ The table view itself has mechanisms for maintaining scroll position. During a live resize the table view should automatically "do the right thing". This method may be useful during a reload if you want to stay in the same spot. Use it instead of -reloadData.
+ */
+- (void)reloadDataMaintainingVisibleIndexPath:(TUIFastIndexPath *)indexPath relativeOffset:(CGFloat)relativeOffset;
+
+// Forces a re-calculation and re-layout of the table. This is most useful for animating the relayout. It is potentially _more_ expensive than -reloadData since it has to allow for animating.
+- (void)reloadLayout;
+
+- (NSInteger)numberOfSections;
+- (NSInteger)numberOfRowsInSection:(NSInteger)section;
+
+- (CGRect)rectForHeaderOfSection:(NSInteger)section;
+- (CGRect)rectForSection:(NSInteger)section;
+- (CGRect)rectForRowAtIndexPath:(TUIFastIndexPath *)indexPath;
+
+- (NSIndexSet *)indexesOfSectionsInRect:(CGRect)rect;
+- (NSIndexSet *)indexesOfSectionHeadersInRect:(CGRect)rect;
+- (TUIFastIndexPath *)indexPathForCell:(TUITableViewCell *)cell; // returns nil if cell is not visible
+- (NSArray *)indexPathsForRowsInRect:(CGRect)rect; // returns nil if rect not valid
+- (TUIFastIndexPath *)indexPathForRowAtPoint:(CGPoint)point;
+- (TUIFastIndexPath *)indexPathForRowAtVerticalOffset:(CGFloat)offset;
+- (NSInteger)indexOfSectionWithHeaderAtPoint:(CGPoint)point;
+- (NSInteger)indexOfSectionWithHeaderAtVerticalOffset:(CGFloat)offset;
+
+- (void)enumerateIndexPathsUsingBlock:(void (^)(TUIFastIndexPath *indexPath, BOOL *stop))block;
+- (void)enumerateIndexPathsWithOptions:(NSEnumerationOptions)options usingBlock:(void (^)(TUIFastIndexPath *indexPath, BOOL *stop))block;
+- (void)enumerateIndexPathsFromIndexPath:(TUIFastIndexPath *)fromIndexPath toIndexPath:(TUIFastIndexPath *)toIndexPath withOptions:(NSEnumerationOptions)options usingBlock:(void (^)(TUIFastIndexPath *indexPath, BOOL *stop))block;
+
+- (TUIView *)headerViewForSection:(NSInteger)section;
+- (TUITableViewCell *)cellForRowAtIndexPath:(TUIFastIndexPath *)indexPath; // returns nil if cell is not visible or index path is out of range
+- (NSArray *)visibleCells; // no particular order
+- (NSArray *)sortedVisibleCells; // top to bottom
+- (NSArray *)indexPathsForVisibleRows;
+
+- (void)scrollToRowAtIndexPath:(TUIFastIndexPath *)indexPath atScrollPosition:(TUITableViewScrollPosition)scrollPosition animated:(BOOL)animated;
+
+- (TUIFastIndexPath *)indexPathForSelectedRow; // return nil or index path representing section and row of selection.
+- (TUIFastIndexPath *)indexPathForFirstRow;
+- (TUIFastIndexPath *)indexPathForLastRow;
+
+- (void)selectRowAtIndexPath:(TUIFastIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(TUITableViewScrollPosition)scrollPosition;
+- (void)deselectRowAtIndexPath:(TUIFastIndexPath *)indexPath animated:(BOOL)animated;
+
+/**
+ Above the top cell, only visible if you pull down (if you have scroll bouncing enabled)
+ */
+@property (nonatomic, strong) TUIView *pullDownView;
+
+- (BOOL)pullDownViewIsVisible;
+
+@property (nonatomic, strong) TUIView *headerView;
+
+/**
+ Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.
+ */
+- (TUITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
+
+@end
+
+@protocol TUITableViewDataSource<NSObject>
+
+@required
+
+- (NSInteger)tableView:(TUITableView *)table numberOfRowsInSection:(NSInteger)section;
+
+- (TUITableViewCell *)tableView:(TUITableView *)tableView cellForRowAtIndexPath:(TUIFastIndexPath *)indexPath;
+
+@optional
+
+- (TUIView *)tableView:(TUITableView *)tableView headerViewForSection:(NSInteger)section;
+
+// the following are required to support row reordering
+- (BOOL)tableView:(TUITableView *)tableView canMoveRowAtIndexPath:(TUIFastIndexPath *)indexPath;
+- (void)tableView:(TUITableView *)tableView moveRowAtIndexPath:(TUIFastIndexPath *)fromIndexPath toIndexPath:(TUIFastIndexPath *)toIndexPath;
+
+// the following are required to support row reordering
+- (BOOL)tableView:(TUITableView *)tableView canMoveRowAtIndexPath:(TUIFastIndexPath *)indexPath;
+- (void)tableView:(TUITableView *)tableView moveRowAtIndexPath:(TUIFastIndexPath *)fromIndexPath toIndexPath:(TUIFastIndexPath *)toIndexPath;
+
+/**
+ Default is 1 if not implemented
+ */
+- (NSInteger)numberOfSectionsInTableView:(TUITableView *)tableView;
+
+@end
+
+@interface NSIndexPath (TUITableView)
+
++ (NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section;
+
+@property(nonatomic,readonly) NSUInteger section;
+@property(nonatomic,readonly) NSUInteger row;
+
+@end
+
+#import "TUITableViewCell.h"
+#import "TUITableView+Derepeater.h"
diff --git a/samples/objective-c/TUITableView.m b/samples/objective-c/TUITableView.m
new file mode 100644
index 0000000..0ec17d7
--- /dev/null
+++ b/samples/objective-c/TUITableView.m
@@ -0,0 +1,1248 @@
+/*
+ Copyright 2011 Twitter, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this work except in compliance with the License.
+ You may obtain a copy of the License in the LICENSE file, or at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "TUITableView.h"
+#import "TUITableView+Cell.h"
+#import "TUITableViewSectionHeader.h"
+#import "TUINSView.h"
+
+// header views need to be above the cells at all times
+#define HEADER_Z_POSITION 1000
+
+typedef struct {
+ CGFloat offset; // from beginning of section
+ CGFloat height;
+} TUITableViewRowInfo;
+
+@interface TUITableViewSection : NSObject
+{
+ __unsafe_unretained TUITableView *_tableView; // weak
+ TUIView *_headerView; // Not reusable (similar to UITableView)
+ NSInteger sectionIndex;
+ NSUInteger numberOfRows;
+ CGFloat sectionHeight;
+ CGFloat sectionOffset;
+ TUITableViewRowInfo *rowInfo;
+}
+
+@property (strong, readonly) TUIView *headerView;
+@property (nonatomic, assign) CGFloat sectionOffset;
+@property (readonly) NSInteger sectionIndex;
+
+@end
+
+@implementation TUITableViewSection
+
+@synthesize sectionOffset;
+@synthesize sectionIndex;
+
+- (id)initWithNumberOfRows:(NSUInteger)n sectionIndex:(NSInteger)s tableView:(TUITableView *)t
+{
+ if((self = [super init])){
+ _tableView = t;
+ sectionIndex = s;
+ numberOfRows = n;
+ rowInfo = calloc(n, sizeof(TUITableViewRowInfo));
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ if(rowInfo) free(rowInfo);
+}
+
+- (NSUInteger)numberOfRows
+{
+ return numberOfRows;
+}
+
+- (void)_setupRowHeights
+{
+ sectionHeight = 0.0;
+
+ TUIView *header;
+ if((header = self.headerView) != nil) {
+ sectionHeight += roundf(header.frame.size.height);
+ }
+
+ for(int i = 0; i < numberOfRows; ++i) {
+ CGFloat h = roundf([_tableView.delegate tableView:_tableView heightForRowAtIndexPath:[TUIFastIndexPath indexPathForRow:i inSection:sectionIndex]]);
+ rowInfo[i].offset = sectionHeight;
+ rowInfo[i].height = h;
+ sectionHeight += h;
+ }
+
+}
+
+- (CGFloat)rowHeight:(NSInteger)i
+{
+ if(i >= 0 && i < numberOfRows) {
+ return rowInfo[i].height;
+ }
+ return 0.0;
+}
+
+- (CGFloat)sectionRowOffset:(NSInteger)i
+{
+ if(i >= 0 && i < numberOfRows){
+ return rowInfo[i].offset;
+ }
+ return 0.0;
+}
+
+- (CGFloat)tableRowOffset:(NSInteger)i
+{
+ return sectionOffset + [self sectionRowOffset:i];
+}
+
+- (CGFloat)sectionHeight
+{
+ return sectionHeight;
+}
+
+- (CGFloat)headerHeight
+{
+ return (self.headerView != nil) ? self.headerView.frame.size.height : 0;
+}
+
+/**
+ * @brief Obtain the section header view.
+ *
+ * The section header view is created lazily via the data source when this
+ * method is first called.
+ *
+ * @return section header view
+ */
+- (TUIView *)headerView
+{
+ if(_headerView == nil) {
+ if(_tableView.dataSource != nil && [_tableView.dataSource respondsToSelector:@selector(tableView:headerViewForSection:)]){
+ _headerView = [_tableView.dataSource tableView:_tableView headerViewForSection:sectionIndex];
+ _headerView.autoresizingMask = TUIViewAutoresizingFlexibleWidth;
+ _headerView.layer.zPosition = HEADER_Z_POSITION;
+ }
+ }
+ return _headerView;
+}
+
+@end
+
+@interface TUITableView (Private)
+- (void)_updateSectionInfo;
+- (void)_updateDerepeaterViews;
+@end
+
+@implementation TUITableView
+
+@synthesize pullDownView=_pullDownView;
+@synthesize headerView=_headerView;
+
+- (id)initWithFrame:(CGRect)frame style:(TUITableViewStyle)style
+{
+ if((self = [super initWithFrame:frame])) {
+ _style = style;
+ _reusableTableCells = [[NSMutableDictionary alloc] init];
+ _visibleSectionHeaders = [[NSMutableIndexSet alloc] init];
+ _visibleItems = [[NSMutableDictionary alloc] init];
+ _tableFlags.animateSelectionChanges = 1;
+ }
+ return self;
+}
+
+- (id)initWithFrame:(CGRect)frame
+{
+ return [self initWithFrame:frame style:TUITableViewStylePlain];
+}
+
+
+- (id<TUITableViewDelegate>)delegate
+{
+ return (id<TUITableViewDelegate>)[super delegate];
+}
+
+- (void)setDelegate:(id<TUITableViewDelegate>)d
+{
+ _tableFlags.delegateTableViewWillDisplayCellForRowAtIndexPath = [d respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:)];
+ [super setDelegate:d]; // must call super
+}
+
+- (id<TUITableViewDataSource>)dataSource
+{
+ return _dataSource;
+}
+
+- (void)setDataSource:(id<TUITableViewDataSource>)d
+{
+ _dataSource = d;
+ _tableFlags.dataSourceNumberOfSectionsInTableView = [_dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
+}
+
+- (BOOL)animateSelectionChanges
+{
+ return _tableFlags.animateSelectionChanges;
+}
+
+- (void)setAnimateSelectionChanges:(BOOL)a
+{
+ _tableFlags.animateSelectionChanges = a;
+}
+
+- (NSInteger)numberOfSections
+{
+ return [_sectionInfo count];
+}
+
+- (NSInteger)numberOfRowsInSection:(NSInteger)section
+{
+ return [[_sectionInfo objectAtIndex:section] numberOfRows];
+}
+
+- (CGRect)rectForHeaderOfSection:(NSInteger)section {
+ if(section >= 0 && section < [_sectionInfo count]){
+ TUITableViewSection *s = [_sectionInfo objectAtIndex:section];
+ CGFloat offset = [s sectionOffset];
+ CGFloat height = [s headerHeight];
+ CGFloat y = _contentHeight - offset - height;
+ return CGRectMake(0, y, self.bounds.size.width, height);
+ }
+ return CGRectZero;
+}
+
+- (CGRect)rectForSection:(NSInteger)section
+{
+ if(section >= 0 && section < [_sectionInfo count]){
+ TUITableViewSection *s = [_sectionInfo objectAtIndex:section];
+ CGFloat offset = [s sectionOffset];
+ CGFloat height = [s sectionHeight];
+ CGFloat y = _contentHeight - offset - height;
+ return CGRectMake(0, y, self.bounds.size.width, height);
+ }
+ return CGRectZero;
+}
+
+- (CGRect)rectForRowAtIndexPath:(TUIFastIndexPath *)indexPath
+{
+ NSInteger section = indexPath.section;
+ NSInteger row = indexPath.row;
+ if(section >= 0 && section < [_sectionInfo count]) {
+ TUITableViewSection *s = [_sectionInfo objectAtIndex:section];
+ CGFloat offset = [s tableRowOffset:row];
+ CGFloat height = [s rowHeight:row];
+ CGFloat y = _contentHeight - offset - height;
+ return CGRectMake(0, y, self.bounds.size.width, height);
+ }
+ return CGRectZero;
+}
+
+/**
+ * @brief Update section info
+ *
+ * The previous section info is released and new section info is created.
+ */
+- (void)_updateSectionInfo {
+
+ if(_sectionInfo != nil){
+
+ // remove any visible headers, they should be re-added when the table is laid out
+ for(TUITableViewSection *section in _sectionInfo){
+ TUIView *headerView;
+ if((headerView = [section headerView]) != nil){
+ [headerView removeFromSuperview];
+ }
+ }
+
+ // clear visible section headers
+ [_visibleSectionHeaders removeAllIndexes];
+ // clear the section info array
+ _sectionInfo = nil;
+ }
+
+ NSInteger numberOfSections = 1;
+ if(_tableFlags.dataSourceNumberOfSectionsInTableView){
+ numberOfSections = [_dataSource numberOfSectionsInTableView:self];
+ }
+
+ NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:numberOfSections];
+
+ CGFloat offset = [_headerView bounds].size.height - self.contentInset.top*2;
+ for(int s = 0; s < numberOfSections; ++s) {
+ TUITableViewSection *section = [[TUITableViewSection alloc] initWithNumberOfRows:[_dataSource tableView:self numberOfRowsInSection:s] sectionIndex:s tableView:self];
+ [section _setupRowHeights];
+ section.sectionOffset = offset;
+ offset += [section sectionHeight];
+ [sections addObject:section];
+ }
+
+ _contentHeight = offset - self.contentInset.bottom;
+ _sectionInfo = sections;
+
+}
+
+- (void)_enqueueReusableCell:(TUITableViewCell *)cell
+{
+ NSString *identifier = cell.reuseIdentifier;
+
+ if(!identifier)
+ return;
+
+ NSMutableArray *array = [_reusableTableCells objectForKey:identifier];
+ if(!array) {
+ array = [[NSMutableArray alloc] init];
+ [_reusableTableCells setObject:array forKey:identifier];
+ }
+ [array addObject:cell];
+}
+
+- (TUITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
+{
+ if(!identifier)
+ return nil;
+
+ NSMutableArray *array = [_reusableTableCells objectForKey:identifier];
+ if(array) {
+ TUITableViewCell *c = [array lastObject];
+ if(c) {
+ [array removeLastObject];
+ [c prepareForReuse];
+ return c;
+ }
+ }
+ return nil;
+}
+
+/**
+ * @brief Obtain the header view for the specified section
+ *
+ * If the section has no header, nil is returned.
+ *
+ * @param section the section
+ * @return section header
+ */
+- (TUIView *)headerViewForSection:(NSInteger)section {
+ if(section >= 0 && section < [_sectionInfo count]){
+ return [(TUITableViewSection *)[_sectionInfo objectAtIndex:section] headerView];
+ }else{
+ return nil;
+ }
+}
+
+- (TUITableViewCell *)cellForRowAtIndexPath:(TUIFastIndexPath *)indexPath // returns nil if cell is not visible or index path is out of range
+{
+ return [_visibleItems objectForKey:indexPath];
+}
+
+- (NSArray *)visibleCells
+{
+ return [_visibleItems allValues];
+}
+
+static NSInteger SortCells(TUITableViewCell *a, TUITableViewCell *b, void *ctx)
+{
+ if(a.frame.origin.y > b.frame.origin.y)
+ return NSOrderedAscending;
+ return NSOrderedDescending;
+}
+
+- (NSArray *)sortedVisibleCells
+{
+ NSArray *v = [self visibleCells];
+ return [v sortedArrayUsingComparator:(NSComparator)^NSComparisonResult(TUITableViewCell *a, TUITableViewCell *b) {
+ if(a.frame.origin.y > b.frame.origin.y)
+ return NSOrderedAscending;
+ return NSOrderedDescending;
+ }];
+}
+
+#define INDEX_PATHS_FOR_VISIBLE_ROWS [_visibleItems allKeys]
+
+- (NSArray *)indexPathsForVisibleRows
+{
+ return INDEX_PATHS_FOR_VISIBLE_ROWS;
+}
+
+- (TUIFastIndexPath *)indexPathForCell:(TUITableViewCell *)c
+{
+ for(TUIFastIndexPath *i in _visibleItems) {
+ TUITableViewCell *cell = [_visibleItems objectForKey:i];
+ if(cell == c)
+ return i;
+ }
+ return nil;
+}
+
+/**
+ * @brief Obtain the indexes of sections which intersect @p rect.
+ *
+ * @param rect the rect
+ * @return intersecting sections
+ */
+- (NSIndexSet *)indexesOfSectionsInRect:(CGRect)rect
+{
+ NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
+
+ for(int i = 0; i < [_sectionInfo count]; i++) {
+ if(CGRectIntersectsRect([self rectForSection:i], rect)){
+ [indexes addIndex:i];
+ }
+ }
+
+ return indexes;
+}
+
+/**
+ * @brief Obtain the indexes of sections whose header views intersect @p rect.
+ *
+ * @param rect the rect
+ * @return intersecting sections
+ */
+- (NSIndexSet *)indexesOfSectionHeadersInRect:(CGRect)rect
+{
+ NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
+
+ for(int i = 0; i < [_sectionInfo count]; i++) {
+ if(CGRectIntersectsRect([self rectForHeaderOfSection:i], rect)){
+ [indexes addIndex:i];
+ }
+ }
+
+ return indexes;
+}
+
+- (NSArray *)indexPathsForRowsInRect:(CGRect)rect
+{
+ NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:50];
+ NSInteger sectionIndex = 0;
+ for(TUITableViewSection *section in _sectionInfo) {
+ NSInteger numberOfRows = [section numberOfRows];
+ for(NSInteger row = 0; row < numberOfRows; ++row) {
+ TUIFastIndexPath *indexPath = [TUIFastIndexPath indexPathForRow:row inSection:sectionIndex];
+ CGRect cellRect = [self rectForRowAtIndexPath:indexPath];
+ if(CGRectIntersectsRect(cellRect, rect)) {
+ [indexPaths addObject:indexPath];
+ } else {
+ // not visible
+ }
+ }
+ ++sectionIndex;
+ }
+ return indexPaths;
+}
+
+/**
+ * @brief Obtain the index path of the row at the specified point
+ *
+ * If the point is not valid or no row exists at that point, nil is
+ * returned.
+ *
+ * @param point location in the table view
+ * @return index path of the row at @p point
+ */
+- (TUIFastIndexPath *)indexPathForRowAtPoint:(CGPoint)point {
+
+ NSInteger sectionIndex = 0;
+ for(TUITableViewSection *section in _sectionInfo){
+ for(NSInteger row = 0; row < [section numberOfRows]; row++){
+ TUIFastIndexPath *indexPath = [TUIFastIndexPath indexPathForRow:row inSection:sectionIndex];
+ CGRect cellRect = [self rectForRowAtIndexPath:indexPath];
+ if(CGRectContainsPoint(cellRect, point)){
+ return indexPath;
+ }
+ }
+ ++sectionIndex;
+ }
+
+ return nil;
+}
+
+/**
+ * @brief Obtain the index path of the row at the specified y-coordinate offset
+ *
+ * Unlike #indexPathForRowAtPoint:, this method does not consider the x-coordinate.
+ * If the offset is not valid or no row exists at that offset, nil is returned.
+ *
+ * @param offset y-coordinate offset in the table view
+ * @return index path of the row at @p offset
+ */
+- (TUIFastIndexPath *)indexPathForRowAtVerticalOffset:(CGFloat)offset {
+
+ NSInteger sectionIndex = 0;
+ for(TUITableViewSection *section in _sectionInfo){
+ for(NSInteger row = 0; row < [section numberOfRows]; row++){
+ TUIFastIndexPath *indexPath = [TUIFastIndexPath indexPathForRow:row inSection:sectionIndex];
+ CGRect cellRect = [self rectForRowAtIndexPath:indexPath];
+ if(offset >= cellRect.origin.y && offset <= (cellRect.origin.y + cellRect.size.height)){
+ return indexPath;
+ }
+ }
+ ++sectionIndex;
+ }
+
+ return nil;
+}
+
+/**
+ * @brief Obtain the index of a section whose header is at the specified point
+ *
+ * If the point is not valid or no header exists at that point, a negative value
+ * is returned.
+ *
+ * @param point location in the table view
+ * @return index of the section whose header is at @p point
+ */
+- (NSInteger)indexOfSectionWithHeaderAtPoint:(CGPoint)point {
+
+ NSInteger sectionIndex = 0;
+ for(TUITableViewSection *section in _sectionInfo){
+ TUIView *headerView;
+ if((headerView = section.headerView) != nil){
+ CGFloat offset = [section sectionOffset];
+ CGFloat height = [section headerHeight];
+ CGFloat y = _contentHeight - offset - height;
+ CGRect frame = CGRectMake(0, y, self.bounds.size.width, height);
+ if(point.y > frame.origin.y && point.y < (frame.origin.y + frame.size.height)){
+ return sectionIndex;
+ }
+ }
+ sectionIndex++;
+ }
+
+ return -1;
+}
+
+/**
+ * @brief Obtain the index of a section whose header is at the specified y-coordinate offset
+ *
+ * Unlike #indexOfSectionWithHeaderAtPoint:, this method does not consider the x-coordinate.
+ * If the offset is not valid or no header exists at that offset, a negative value
+ * is returned.
+ *
+ * @param offset y-coordinate offset in the table view
+ * @return index of the section whose header is at @p offset
+ */
+- (NSInteger)indexOfSectionWithHeaderAtVerticalOffset:(CGFloat)offset {
+
+ NSInteger sectionIndex = 0;
+ for(TUITableViewSection *section in _sectionInfo){
+ TUIView *headerView;
+ if((headerView = section.headerView) != nil){
+ CGFloat offset = [section sectionOffset];
+ CGFloat height = [section headerHeight];
+ CGFloat y = _contentHeight - offset - height;
+ CGRect frame = CGRectMake(0, y, self.bounds.size.width, height);
+ if(offset >= frame.origin.y && offset <= (frame.origin.y + frame.size.height)){
+ return sectionIndex;
+ }
+ }
+ sectionIndex++;
+ }
+
+ return -1;
+}
+
+/**
+ * @brief Enumerate index paths
+ * @see #enumerateIndexPathsFromIndexPath:toIndexPath:withOptions:usingBlock:
+ */
+- (void)enumerateIndexPathsUsingBlock:(void (^)(TUIFastIndexPath *indexPath, BOOL *stop))block {
+ [self enumerateIndexPathsFromIndexPath:nil toIndexPath:nil withOptions:0 usingBlock:block];
+}
+
+/**
+ * @brief Enumerate index paths
+ * @see #enumerateIndexPathsFromIndexPath:toIndexPath:withOptions:usingBlock:
+ */
+- (void)enumerateIndexPathsWithOptions:(NSEnumerationOptions)options usingBlock:(void (^)(TUIFastIndexPath *indexPath, BOOL *stop))block {
+ [self enumerateIndexPathsFromIndexPath:nil toIndexPath:nil withOptions:options usingBlock:block];
+}
+
+/**
+ * @brief Enumerate index paths
+ *
+ * The provided block is repeatedly invoked with each valid index path between
+ * the specified bounds. Both bounding index paths are inclusive.
+ *
+ * @param fromIndexPath the index path to begin enumerating at or nil to begin at the first index path
+ * @param toIndexPath the index path to stop enumerating at or nil to stop at the last index path
+ * @param options enumeration options (not currently supported; pass 0)
+ * @param block the block to enumerate with
+ */
+- (void)enumerateIndexPathsFromIndexPath:(TUIFastIndexPath *)fromIndexPath toIndexPath:(TUIFastIndexPath *)toIndexPath withOptions:(NSEnumerationOptions)options usingBlock:(void (^)(TUIFastIndexPath *indexPath, BOOL *stop))block {
+ NSInteger sectionLowerBound = (fromIndexPath != nil) ? fromIndexPath.section : 0;
+ NSInteger sectionUpperBound = (toIndexPath != nil) ? toIndexPath.section : [self numberOfSections] - 1;
+ NSInteger rowLowerBound = (fromIndexPath != nil) ? fromIndexPath.row : 0;
+ NSInteger rowUpperBound = (toIndexPath != nil) ? toIndexPath.row : -1;
+
+ NSInteger irow = rowLowerBound; // start at the lower bound row for the first iteration...
+ for(NSInteger i = sectionLowerBound; i < [self numberOfSections] && i <= sectionUpperBound /* inclusive */; i++){
+ NSInteger rowCount = [self numberOfRowsInSection:i];
+ for(NSInteger j = irow; j < rowCount && j <= ((rowUpperBound < 0 || i < sectionUpperBound) ? rowCount - 1 : rowUpperBound) /* inclusive */; j++){
+ BOOL stop = FALSE;
+ block([TUIFastIndexPath indexPathForRow:j inSection:i], &stop);
+ if(stop) return;
+ }
+ irow = 0; // ...then use zero for subsequent iterations
+ }
+
+}
+
+- (TUIFastIndexPath *)_topVisibleIndexPath
+{
+ TUIFastIndexPath *topVisibleIndex = nil;
+ NSArray *v = [INDEX_PATHS_FOR_VISIBLE_ROWS sortedArrayUsingSelector:@selector(compare:)];
+ if([v count])
+ topVisibleIndex = [v objectAtIndex:0];
+ return topVisibleIndex;
+}
+
+- (void)setFrame:(CGRect)f
+{
+ _tableFlags.forceSaveScrollPosition = 1;
+ [super setFrame:f];
+}
+
+- (void)setContentOffset:(CGPoint)p
+{
+ _tableFlags.didFirstLayout = 1; // prevent the auto-scroll-to-top during the first layout
+ [super setContentOffset:p];
+
+ // if we're currently dragging we need to update the drag operation since the content under
+ // the mouse has moved; we just call update again with the last mouse location
+ if([self __isDraggingCell]){
+ [self __updateDraggingCell:_dragToReorderCell offset:_currentDragToReorderMouseOffset location:_currentDragToReorderLocation];
+ }
+
+}
+
+- (void)setPullDownView:(TUIView *)p
+{
+ [_pullDownView removeFromSuperview];
+
+ _pullDownView = p;
+
+ [self addSubview:_pullDownView];
+ _pullDownView.hidden = YES;
+}
+
+- (void)setHeaderView:(TUIView *)h
+{
+ [_headerView removeFromSuperview];
+
+ _headerView = h;
+
+ [self addSubview:_headerView];
+ _headerView.hidden = YES;
+}
+
+- (BOOL)_preLayoutCells
+{
+ CGRect bounds = self.bounds;
+
+ if(!_sectionInfo || !CGSizeEqualToSize(bounds.size, _lastSize)) {
+
+ // save scroll position
+ CGFloat previousOffset = 0.0f;
+ TUIFastIndexPath *savedIndexPath = nil;
+ CGFloat relativeOffset = 0.0;
+ if(_tableFlags.maintainContentOffsetAfterReload) {
+ previousOffset = self.contentSize.height + self.contentOffset.y;
+ } else {
+ if(_tableFlags.forceSaveScrollPosition || [self.nsView inLiveResize]) {
+ _tableFlags.forceSaveScrollPosition = 0;
+ NSArray *a = [INDEX_PATHS_FOR_VISIBLE_ROWS sortedArrayUsingSelector:@selector(compare:)];
+ if([a count]) {
+ savedIndexPath = [a objectAtIndex:0];
+ CGRect v = [self visibleRect];
+ CGRect r = [self rectForRowAtIndexPath:savedIndexPath];
+ relativeOffset = ((v.origin.y + v.size.height) - (r.origin.y + r.size.height));
+ relativeOffset += (_lastSize.height - bounds.size.height);
+ }
+ } else if(_keepVisibleIndexPathForReload) {
+ savedIndexPath = _keepVisibleIndexPathForReload;
+ relativeOffset = _relativeOffsetForReload;
+ _keepVisibleIndexPathForReload = nil;
+ }
+ }
+
+ [self _updateSectionInfo]; // clean up any previous section info and recreate it
+ self.contentSize = CGSizeMake(self.bounds.size.width, _contentHeight);
+
+ _lastSize = bounds.size;
+
+ if(!_tableFlags.didFirstLayout) {
+ _tableFlags.didFirstLayout = 1;
+ [self scrollToTopAnimated:NO];
+ }
+
+ // restore scroll position
+ if(_tableFlags.maintainContentOffsetAfterReload) {
+ CGFloat newOffset = previousOffset - self.contentSize.height;
+ self.contentOffset = CGPointMake(self.contentOffset.x, newOffset);
+ } else {
+ if(savedIndexPath) {
+ CGRect v = [self visibleRect];
+ CGRect r = [self rectForRowAtIndexPath:savedIndexPath];
+ r.origin.y -= (v.size.height - r.size.height);
+ r.size.height += (v.size.height - r.size.height);
+
+ r.origin.y += relativeOffset;
+
+ [self scrollRectToVisible:r animated:NO];
+ }
+ }
+
+ return YES; // needs visible cells to be redisplayed
+ }
+
+ return NO; // just need to do the recycling
+}
+
+/**
+ * @brief Layout header views for sections which have one.
+ */
+- (void)_layoutSectionHeaders:(BOOL)visibleHeadersNeedRelayout
+{
+ CGRect visible = [self visibleRect];
+ NSIndexSet *oldIndexes = _visibleSectionHeaders;
+ NSIndexSet *newIndexes = [self indexesOfSectionsInRect:visible];
+
+ NSMutableIndexSet *toRemove = [oldIndexes mutableCopy];
+ [toRemove removeIndexes:newIndexes];
+ NSMutableIndexSet *toAdd = [newIndexes mutableCopy];
+ [toAdd removeIndexes:oldIndexes];
+
+ // update the placement of all visible headers
+ __block TUIView *pinnedHeader = nil;
+ [newIndexes enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
+ if(index < [_sectionInfo count]) {
+ TUITableViewSection *section = [_sectionInfo objectAtIndex:index];
+ if(section.headerView != nil) {
+ CGRect headerFrame = [self rectForHeaderOfSection:index];
+
+ if(_style == TUITableViewStyleGrouped) {
+ // check if this header needs to be pinned
+ if(CGRectGetMaxY(headerFrame) > CGRectGetMaxY(visible)) {
+ headerFrame.origin.y = CGRectGetMaxY(visible) - headerFrame.size.height;
+ pinnedHeader = section.headerView;
+ // if the header is a TUITableViewSectionHeader notify it of it's pinned state
+ if([section.headerView isKindOfClass:[TUITableViewSectionHeader class]]){
+ ((TUITableViewSectionHeader *)section.headerView).pinnedToViewport = TRUE;
+ }
+ }else if((pinnedHeader != nil) && (CGRectGetMaxY(headerFrame) > pinnedHeader.frame.origin.y)) {
+ // this header is intersecting with the pinned header, so we push the pinned header upwards.
+ CGRect pinnedHeaderFrame = pinnedHeader.frame;
+ pinnedHeaderFrame.origin.y = CGRectGetMaxY(headerFrame);
+ pinnedHeader.frame = pinnedHeaderFrame;
+ // if the header is a TUITableViewSectionHeader notify it of it's pinned state
+ if([section.headerView isKindOfClass:[TUITableViewSectionHeader class]]){
+ ((TUITableViewSectionHeader *)section.headerView).pinnedToViewport = FALSE;
+ }
+ }else{
+ // if the header is a TUITableViewSectionHeader notify it of it's pinned state
+ if([section.headerView isKindOfClass:[TUITableViewSectionHeader class]]){
+ ((TUITableViewSectionHeader *)section.headerView).pinnedToViewport = FALSE;
+ }
+ }
+ }
+
+ section.headerView.frame = headerFrame;
+ [section.headerView setNeedsLayout];
+
+ if(section.headerView.superview == nil){
+ [self addSubview:section.headerView];
+ }
+
+ }
+ }
+ [_visibleSectionHeaders addIndex:index];
+ }];
+
+ // remove offscreen headers
+ [toRemove enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
+ if(index < [_sectionInfo count]) {
+ TUITableViewSection *section = [_sectionInfo objectAtIndex:index];
+ if(section.headerView != nil) {
+ [section.headerView removeFromSuperview];
+ }
+ }
+ [_visibleSectionHeaders removeIndex:index];
+ }];
+
+}
+
+- (void)_layoutCells:(BOOL)visibleCellsNeedRelayout
+{
+
+ if(visibleCellsNeedRelayout) {
+ // update remaining visible cells if needed
+ for(TUIFastIndexPath *i in _visibleItems) {
+ TUITableViewCell *cell = [_visibleItems objectForKey:i];
+ cell.frame = [self rectForRowAtIndexPath:i];
+ cell.layer.zPosition = 0;
+ [cell setNeedsLayout];
+ }
+ }
+
+ CGRect visible = [self visibleRect];
+
+ // Example:
+ // old: 0 1 2 3 4 5 6 7
+ // new: 2 3 4 5 6 7 8 9
+ // to remove: 0 1
+ // to add: 8 9
+
+ NSArray *oldVisibleIndexPaths = INDEX_PATHS_FOR_VISIBLE_ROWS;
+ NSArray *newVisibleIndexPaths = [self indexPathsForRowsInRect:visible];
+
+ NSMutableArray *indexPathsToRemove = [oldVisibleIndexPaths mutableCopy];
+ [indexPathsToRemove removeObjectsInArray:newVisibleIndexPaths];
+
+ NSMutableArray *indexPathsToAdd = [newVisibleIndexPaths mutableCopy];
+ [indexPathsToAdd removeObjectsInArray:oldVisibleIndexPaths];
+
+ // remove offscreen cells
+ for(TUIFastIndexPath *i in indexPathsToRemove) {
+ TUITableViewCell *cell = [self cellForRowAtIndexPath:i];
+ // don't reuse the dragged cell
+ if(_dragToReorderCell == nil || ![cell isEqual:_dragToReorderCell]){
+ [self _enqueueReusableCell:cell];
+ [cell removeFromSuperview];
+ [_visibleItems removeObjectForKey:i];
+ }
+ }
+
+ // add new cells
+ for(TUIFastIndexPath *i in indexPathsToAdd) {
+ if([_visibleItems objectForKey:i]) {
+ NSLog(@"!!! Warning: already have a cell in place for index path %@\n\n\n", i);
+ } else {
+ TUITableViewCell *cell = [_dataSource tableView:self cellForRowAtIndexPath:i];
+ [self.nsView invalidateHoverForView:cell];
+
+ cell.frame = [self rectForRowAtIndexPath:i];
+ cell.layer.zPosition = 0;
+
+ [cell setNeedsLayout];
+ [cell prepareForDisplay];
+
+ if([i isEqual:_selectedIndexPath]) {
+ [cell setSelected:YES animated:NO];
+ } else {
+ [cell setSelected:NO animated:NO];
+ }
+
+ if(_tableFlags.delegateTableViewWillDisplayCellForRowAtIndexPath) {
+ [_delegate tableView:self willDisplayCell:cell forRowAtIndexPath:i];
+ }
+
+ [self addSubview:cell];
+
+ if([_indexPathShouldBeFirstResponder isEqual:i]) {
+ // only make cells first responder if they accept it
+ if([cell acceptsFirstResponder]){
+ [self.nsWindow makeFirstResponderIfNotAlreadyInResponderChain:cell withFutureRequestToken:_futureMakeFirstResponderToken];
+ }
+ _indexPathShouldBeFirstResponder = nil;
+ }
+
+ [_visibleItems setObject:cell forKey:i];
+ }
+ }
+
+ // if we have a dragged cell, make sure it's on top of the newly added cells
+ if([indexPathsToAdd count] > 0 && _dragToReorderCell != nil){
+ [[_dragToReorderCell superview] bringSubviewToFront:_dragToReorderCell];
+ }
+
+ if(_headerView) {
+ CGSize s = self.contentSize;
+ CGRect headerViewRect = CGRectMake(0, s.height - _headerView.frame.size.height, visible.size.width, _headerView.frame.size.height);
+ if(CGRectIntersectsRect(headerViewRect, visible)) {
+ _headerView.frame = headerViewRect;
+ [_headerView setNeedsLayout];
+
+ if(_headerView.hidden) {
+ // show
+ _headerView.hidden = NO;
+ }
+ } else {
+ if(!_headerView.hidden) {
+ _headerView.hidden = YES;
+ }
+ }
+ }
+
+ if(_pullDownView) {
+ CGSize s = self.contentSize;
+ CGRect pullDownRect = CGRectMake(0, s.height, visible.size.width, _pullDownView.frame.size.height);
+ if([self pullDownViewIsVisible]) {
+ if(_pullDownView.hidden) {
+ // show
+ _pullDownView.frame = pullDownRect;
+ _pullDownView.hidden = NO;
+ }
+ } else {
+ if(!_pullDownView.hidden) {
+ _pullDownView.hidden = YES;
+ }
+ }
+ }
+}
+
+- (BOOL)pullDownViewIsVisible
+{
+ if(_pullDownView) {
+ CGSize s = self.contentSize;
+ CGRect visible = [self visibleRect];
+ CGRect pullDownRect = CGRectMake(0, s.height, visible.size.width, _pullDownView.frame.size.height);
+ return CGRectIntersectsRect(pullDownRect, visible);
+ }
+ return NO;
+}
+
+- (void)reloadDataMaintainingVisibleIndexPath:(TUIFastIndexPath *)indexPath relativeOffset:(CGFloat)relativeOffset
+{
+ _keepVisibleIndexPathForReload = indexPath;
+ _relativeOffsetForReload = relativeOffset;
+ [self reloadData];
+}
+
+- (void)reloadData
+{
+
+ // notify our delegate we're about to reload the table
+ if(self.delegate != nil && [self.delegate respondsToSelector:@selector(tableViewWillReloadData:)]){
+ [self.delegate tableViewWillReloadData:self];
+ }
+
+ _selectedIndexPath = nil;
+
+ // need to recycle all visible cells, have them be regenerated on layoutSubviews
+ // because the same cells might have different content
+ for(TUIFastIndexPath *i in _visibleItems) {
+ TUITableViewCell *cell = [_visibleItems objectForKey:i];
+ [self _enqueueReusableCell:cell];
+ [cell removeFromSuperview];
+ }
+
+ // if we have a dragged cell, clear it
+ _dragToReorderCell = nil;
+
+ // clear visible cells
+ [_visibleItems removeAllObjects];
+
+ // remove any visible headers, they should be re-added when the table is laid out
+ for(TUITableViewSection *section in _sectionInfo){
+ TUIView *headerView;
+ if((headerView = [section headerView]) != nil){
+ [headerView removeFromSuperview];
+ }
+ }
+
+ // clear visible section headers
+ [_visibleSectionHeaders removeAllIndexes];
+
+ _sectionInfo = nil; // will be regenerated on next layout
+
+ [self layoutSubviews];
+
+ // notify our delegate the table view has been reloaded
+ if(self.delegate != nil && [self.delegate respondsToSelector:@selector(tableViewDidReloadData:)]){
+ [self.delegate tableViewDidReloadData:self];
+ }
+
+}
+
+- (void)layoutSubviews
+{
+ if(!_tableFlags.layoutSubviewsReentrancyGuard) {
+ _tableFlags.layoutSubviewsReentrancyGuard = 1;
+
+ [TUIView setAnimationsEnabled:NO block:^{
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+
+ BOOL visibleCellsNeedRelayout = [self _preLayoutCells];
+ [super layoutSubviews]; // this will munge with the contentOffset
+ [self _layoutSectionHeaders:visibleCellsNeedRelayout];
+ [self _layoutCells:visibleCellsNeedRelayout];
+
+ if(_tableFlags.derepeaterEnabled)
+ [self _updateDerepeaterViews];
+
+ [CATransaction commit];
+ }];
+
+ _tableFlags.layoutSubviewsReentrancyGuard = 0;
+ } else {
+// NSLog(@"trying to nest...");
+ }
+}
+
+- (void)reloadLayout
+{
+ _sectionInfo = nil; // will be regenerated on next layout
+
+ [self _preLayoutCells];
+ [super layoutSubviews]; // this will munge with the contentOffset
+ [self _layoutSectionHeaders:YES];
+ [self _layoutCells:YES];
+}
+
+- (void)scrollToRowAtIndexPath:(TUIFastIndexPath *)indexPath atScrollPosition:(TUITableViewScrollPosition)scrollPosition animated:(BOOL)animated
+{
+ CGRect v = [self visibleRect];
+ CGRect r = [self rectForRowAtIndexPath:indexPath];
+
+ // when the target index path section has a header view, add its height to
+ // the height of our row to prevent the selected row from being overlapped
+ // by the pinned header
+ TUIView *headerView;
+ if((headerView = [self headerViewForSection:indexPath.section]) != nil){
+ CGRect headerFrame = [self rectForHeaderOfSection:indexPath.section];
+ r.size.height += headerFrame.size.height;
+ }
+
+ switch(scrollPosition) {
+ case TUITableViewScrollPositionNone:
+ // do nothing
+ break;
+ case TUITableViewScrollPositionTop:
+ r.origin.y -= (v.size.height - r.size.height);
+ r.size.height += (v.size.height - r.size.height);
+ [self scrollRectToVisible:r animated:animated];
+ break;
+ case TUITableViewScrollPositionToVisible:
+ default:
+ [self scrollRectToVisible:r animated:animated];
+ break;
+ }
+
+}
+
+- (TUIFastIndexPath *)indexPathForSelectedRow
+{
+ return _selectedIndexPath;
+}
+
+- (TUIFastIndexPath *)indexPathForFirstRow
+{
+ return [TUIFastIndexPath indexPathForRow:0 inSection:0];
+}
+
+- (TUIFastIndexPath *)indexPathForLastRow
+{
+ NSInteger sec = [self numberOfSections] - 1;
+ NSInteger row = [self numberOfRowsInSection:sec] - 1;
+ return [TUIFastIndexPath indexPathForRow:row inSection:sec];
+}
+
+- (void)_makeRowAtIndexPathFirstResponder:(TUIFastIndexPath *)indexPath
+{
+ TUITableViewCell *cell = [self cellForRowAtIndexPath:indexPath];
+ // only cells that accept first responder should be made first responder
+ if(cell && [cell acceptsFirstResponder]) {
+ [self.nsWindow makeFirstResponderIfNotAlreadyInResponderChain:cell];
+ } else {
+ _indexPathShouldBeFirstResponder = indexPath;
+ _futureMakeFirstResponderToken = [self.nsWindow futureMakeFirstResponderRequestToken];
+ }
+}
+
+- (void)selectRowAtIndexPath:(TUIFastIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(TUITableViewScrollPosition)scrollPosition
+{
+ TUIFastIndexPath *oldIndexPath = [self indexPathForSelectedRow];
+// if([indexPath isEqual:oldIndexPath]) {
+// // just scroll to visible
+// } else {
+ [self deselectRowAtIndexPath:[self indexPathForSelectedRow] animated:animated];
+
+ TUITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]; // may be nil
+ [cell setSelected:YES animated:animated];
+ // should already be nil
+ _selectedIndexPath = indexPath;
+ [cell setNeedsDisplay];
+
+ // only notify when the selection actually changes
+ if([self.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]){
+ [self.delegate tableView:self didSelectRowAtIndexPath:indexPath];
+ }
+// }
+
+ NSResponder *firstResponder = [self.nsWindow firstResponder];
+ if(firstResponder == self || firstResponder == [self cellForRowAtIndexPath:oldIndexPath]) {
+ // only make cell first responder if the table view already is first responder
+ [self _makeRowAtIndexPathFirstResponder:indexPath];
+ }
+ [self scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
+}
+
+- (void)deselectRowAtIndexPath:(TUIFastIndexPath *)indexPath animated:(BOOL)animated
+{
+
+ if([indexPath isEqual:_selectedIndexPath]) {
+ TUITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]; // may be nil
+
+ [cell setSelected:NO animated:animated];
+ _selectedIndexPath = nil;
+ [cell setNeedsDisplay];
+
+ // only notify when the selection actually changes
+ if([self.delegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)]){
+ [self.delegate tableView:self didDeselectRowAtIndexPath:indexPath];
+ }
+
+ }
+
+}
+
+- (TUIFastIndexPath *)indexPathForFirstVisibleRow
+{
+ TUIFastIndexPath *firstIndexPath = nil;
+ for(TUIFastIndexPath *indexPath in _visibleItems) {
+ if(firstIndexPath == nil || [indexPath compare:firstIndexPath] == NSOrderedAscending) {
+ firstIndexPath = indexPath;
+ }
+ }
+ return firstIndexPath;
+}
+
+- (TUIFastIndexPath *)indexPathForLastVisibleRow
+{
+ TUIFastIndexPath *lastIndexPath = nil;
+ for(TUIFastIndexPath *indexPath in _visibleItems) {
+ if(lastIndexPath == nil || [indexPath compare:lastIndexPath] == NSOrderedDescending) {
+ lastIndexPath = indexPath;
+ }
+ }
+ return lastIndexPath;
+}
+
+- (BOOL)performKeyAction:(NSEvent *)event
+{
+ // no selection or selected cell not visible and this is not repeative key press
+ BOOL noCurrentSelection = (_selectedIndexPath == nil || ([self cellForRowAtIndexPath:_selectedIndexPath] == nil && ![event isARepeat]));;
+
+ typedef TUIFastIndexPath * (^TUITableViewCalculateNextIndexPathBlock)(TUIFastIndexPath *lastIndexPath);
+ void (^selectValidIndexPath)(TUIFastIndexPath *startForNoSelection, TUITableViewCalculateNextIndexPathBlock calculateNextIndexPath) = ^(TUIFastIndexPath *startForNoSelection, TUITableViewCalculateNextIndexPathBlock calculateNextIndexPath) {
+ NSParameterAssert(calculateNextIndexPath != nil);
+
+ BOOL foundValidNextRow = NO;
+ TUIFastIndexPath *lastIndexPath = _selectedIndexPath;
+ while(!foundValidNextRow) {
+ TUIFastIndexPath *newIndexPath;
+ if(noCurrentSelection && lastIndexPath == nil) {
+ newIndexPath = startForNoSelection;
+ } else {
+ newIndexPath = calculateNextIndexPath(lastIndexPath);
+ }
+
+ if(![_delegate respondsToSelector:@selector(tableView:shouldSelectRowAtIndexPath:forEvent:)] || [_delegate tableView:self shouldSelectRowAtIndexPath:newIndexPath forEvent:event]){
+ [self selectRowAtIndexPath:newIndexPath animated:self.animateSelectionChanges scrollPosition:TUITableViewScrollPositionToVisible];
+ foundValidNextRow = YES;
+ }
+
+ if([lastIndexPath isEqual:newIndexPath]) foundValidNextRow = YES;
+
+ lastIndexPath = newIndexPath;
+ }
+ };
+
+ switch([[event charactersIgnoringModifiers] characterAtIndex:0]) {
+ case NSUpArrowFunctionKey: {
+ selectValidIndexPath([self indexPathForLastVisibleRow], ^(TUIFastIndexPath *lastIndexPath) {
+ NSUInteger section = lastIndexPath.section;
+ NSUInteger row = lastIndexPath.row;
+ if(row > 0) {
+ row--;
+ } else {
+ while(section > 0) {
+ section--;
+ NSUInteger rowsInSection = [self numberOfRowsInSection:section];
+ if(rowsInSection > 0) {
+ row = rowsInSection - 1;
+ break;
+ }
+ }
+ }
+
+ return [TUIFastIndexPath indexPathForRow:row inSection:section];
+ });
+
+ return YES;
+ }
+
+ case NSDownArrowFunctionKey: {
+ selectValidIndexPath([self indexPathForFirstVisibleRow], ^(TUIFastIndexPath *lastIndexPath) {
+ NSUInteger section = lastIndexPath.section;
+ NSUInteger row = lastIndexPath.row;
+ NSUInteger rowsInSection = [self numberOfRowsInSection:section];
+ if(row + 1 < rowsInSection) {
+ row++;
+ } else {
+ NSUInteger sections = [self numberOfSections];
+ while(section + 1 < sections) {
+ section++;
+ NSUInteger rowsInSection = [self numberOfRowsInSection:section];
+ if(rowsInSection > 0) {
+ row = 0;
+ break;
+ }
+ }
+ }
+
+ return [TUIFastIndexPath indexPathForRow:row inSection:section];
+ });
+
+ return YES;
+ }
+ }
+
+ return [super performKeyAction:event];
+}
+
+- (BOOL)maintainContentOffsetAfterReload
+{
+ return _tableFlags.maintainContentOffsetAfterReload;
+}
+
+- (void)setMaintainContentOffsetAfterReload:(BOOL)newValue
+{
+ _tableFlags.maintainContentOffsetAfterReload = newValue;
+}
+
+@end
+
+
+@implementation NSIndexPath (TUITableView)
+
++ (NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section
+{
+ NSUInteger i[] = {section, row};
+ return [NSIndexPath indexPathWithIndexes:i length:2];
+}
+
+- (NSUInteger)section
+{
+ return [self indexAtPosition:0];
+}
+
+- (NSUInteger)row
+{
+ return [self indexAtPosition:1];
+}
+
+@end
diff --git a/samples/objective-c/empty.m b/samples/objective-c/empty.m
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/objective-c/empty.m
diff --git a/samples/objective-c/hello.m b/samples/objective-c/hello.m
new file mode 100644
index 0000000..1968d76
--- /dev/null
+++ b/samples/objective-c/hello.m
@@ -0,0 +1,7 @@
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+ NSLog(@"Hello, World!\n");
+ return 0;
+}
diff --git a/samples/ocaml/Foo.ml b/samples/ocaml/Foo.ml
new file mode 100644
index 0000000..fb9c7e9
--- /dev/null
+++ b/samples/ocaml/Foo.ml
@@ -0,0 +1,82 @@
+(*
+ Copyright © 2011 MLstate
+
+ This file is part of OPA.
+
+ OPA is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License, version 3, as published by
+ the Free Software Foundation.
+
+ OPA is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with OPA. If not, see <http://www.gnu.org/licenses/>.
+*)
+(*
+ @author Louis Gesbert
+**)
+
+type 'a t = ('a -> unit) -> unit
+
+module Ops = struct
+ let (@>) f k = f k
+ let (|>) x k = k x
+end
+open Ops
+
+module List = struct
+ let rec map f l k = match l with
+ | [] -> [] |> k
+ | hd::tl -> f hd @> fun hd -> map f tl @> fun tl -> hd::tl |> k
+
+ let rec fold f acc l k = match l with
+ | [] -> acc |> k
+ | hd::tl -> f acc hd @> fun acc -> fold f acc tl @> k
+end
+
+module Option = struct
+ let rec map f opt k = match opt with
+ | None -> None |> k
+ | Some x -> f x @> fun x -> Some x |> k
+end
+
+module Lazy = struct
+ type 'a t = {
+ push : (unit -> unit) -> unit;
+ mutable value : 'a option;
+ mutable waiters : ('a -> unit) list;
+ cps : ('a -> unit) -> unit;
+ }
+
+ let make push cps = {
+ push = push;
+ value = None;
+ waiters = [];
+ cps = cps;
+ }
+
+ let force l k =
+ match l.value with
+ | Some x -> x |> k
+ | None when l.waiters != [] ->
+ l.waiters <- k::l.waiters
+ | None ->
+ l.waiters <- k::l.waiters;
+ l.cps
+ @> function x ->
+ Base.List.iter (fun k -> l.push (fun () -> k x)) l.waiters;
+ l.value <- Some x;
+ l.waiters <- []
+
+ let get_state cps = cps.value
+
+ let lazy_from_val x = {
+ push = (fun _ -> ());
+ value = Some x;
+ waiters = [];
+ cps = fun k -> k x;
+ }
+end
diff --git a/samples/opa/hello_syntax1.opa b/samples/opa/hello_syntax1.opa
new file mode 100644
index 0000000..88a9cc8
--- /dev/null
+++ b/samples/opa/hello_syntax1.opa
@@ -0,0 +1,9 @@
+/**
+ * To compile & run on port 8080:
+ * opa hello_syntax1.opa --
+ */
+server = Server.one_page_server(
+ "Hello, world",
+ -> (<h1>Hello, world</h1>)
+)
+
diff --git a/samples/opa/hello_syntax2.opa b/samples/opa/hello_syntax2.opa
new file mode 100644
index 0000000..14c0c93
--- /dev/null
+++ b/samples/opa/hello_syntax2.opa
@@ -0,0 +1,11 @@
+/**
+ * To compile & run on port 8080:
+ * opa --parser js-like hello_syntax2.opa --
+ */
+Server.start(
+ Server.http,
+ {
+ page: function() { <h1>Hello, world</h1> },
+ title: "Hello, world"
+ }
+)
diff --git a/samples/opencl/fft.cl b/samples/opencl/fft.cl
new file mode 100644
index 0000000..e18c6e0
--- /dev/null
+++ b/samples/opencl/fft.cl
@@ -0,0 +1,13 @@
+double run_fftw(int n,const float * x,float * y)
+{
+ fftwf_plan p1 = fftwf_plan_dft_1d(n,(fftwf_complex *)x,(fftwf_complex *)y,
+ FFTW_FORWARD,FFTW_ESTIMATE);
+ const int nops = 10;
+ double t = cl::realTime();
+ for (int op = 0;op < nops;op++) {
+ fftwf_execute(p1);
+ }
+ t = (cl::realTime() - t)/(double)nops;
+ fftwf_destroy_plan(p1);
+ return t;
+}
diff --git a/samples/openedge-abl/Email.cls b/samples/openedge-abl/Email.cls
new file mode 100644
index 0000000..bd30c43
--- /dev/null
+++ b/samples/openedge-abl/Email.cls
@@ -0,0 +1,574 @@
+
+ /*------------------------------------------------------------------------
+ File : Email
+ Purpose : Abstraction of an Email message
+ Description : Holds information needed for an email message - senders,
+ recipients, subject, a message body, attachment files, and
+ other extra information such as importance, priority,
+ sensitivity, custom reply-to addresses, delivery receipts,
+ read receipts, custom sent date, reply-by date, and expire date.
+ Author(s) : Abe Voelker
+ Created : Sat Jul 17 16:27:05 CDT 2010
+ ----------------------------------------------------------------------*/
+
+USING Progress.Lang.*.
+
+
+CLASS email.Email USE-WIDGET-POOL:
+
+ &SCOPED-DEFINE QUOTES """"
+ &SCOPED-DEFINE CR CHR(13)
+ &SCOPED-DEFINE LF CHR(10)
+ &SCOPED-DEFINE DEFAULT_MIME_BOUNDARY "!@#$%^&*+-._MIME_BOUNDARY_.-+*&^%$#@!"
+
+ /*------------------------------------------------------------------------------
+ Purpose:
+ Notes:
+ ------------------------------------------------------------------------------*/
+
+ DEFINE PRIVATE VARIABLE objSendEmailAlgorithm AS email.SendEmailAlgorithm NO-UNDO.
+
+ DEFINE PRIVATE TEMP-TABLE ttSenders NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttSenders cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttToRecipients NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttToRecipients cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttCCRecipients NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttCCRecipients cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttBCCRecipients NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttBCCRecipients cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttReplyToRecipients NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttReplyToRecipients cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttReadReceiptRecipients NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttReadReceiptRecipients cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttDeliveryReceiptRecipients NO-UNDO
+ FIELD cEmailAddress AS CHARACTER
+ FIELD cRealName AS CHARACTER INITIAL ?
+ INDEX IXPK_ttDeliveryReceiptRecipients cEmailAddress.
+
+ DEFINE PRIVATE TEMP-TABLE ttAttachments NO-UNDO
+ FIELD cFileName AS CHARACTER
+ FIELD lcData AS Object /* Longchar object */
+ FIELD lBase64Encode AS LOGICAL.
+
+ DEFINE PRIVATE VARIABLE cMimeBoundary AS CHARACTER NO-UNDO.
+ DEFINE PRIVATE VARIABLE lcBody AS LONGCHAR NO-UNDO.
+ DEFINE PRIVATE VARIABLE lBodyIsBase64 AS LOGICAL NO-UNDO.
+ DEFINE PRIVATE VARIABLE cSubject AS CHARACTER NO-UNDO.
+ DEFINE PRIVATE VARIABLE mptrAttachments AS MEMPTR NO-UNDO.
+ DEFINE PRIVATE VARIABLE cImportance AS CHARACTER NO-UNDO.
+ DEFINE PRIVATE VARIABLE cSensitivity AS CHARACTER NO-UNDO.
+ DEFINE PRIVATE VARIABLE cPriority AS CHARACTER NO-UNDO.
+ DEFINE PRIVATE VARIABLE dttmtzSentDate AS DATETIME-TZ INITIAL ? NO-UNDO.
+ DEFINE PRIVATE VARIABLE dttmtzReplyByDate AS DATETIME-TZ INITIAL ? NO-UNDO.
+ DEFINE PRIVATE VARIABLE dttmtzExpireDate AS DATETIME-TZ INITIAL ? NO-UNDO.
+
+ DEFINE PRIVATE VARIABLE cNewLine AS CHARACTER NO-UNDO.
+
+ /* Other email headers: */
+
+ CONSTRUCTOR PUBLIC Email (INPUT ipobjSendEmailAlgorithm AS email.SendEmailAlgorithm):
+ SUPER ().
+ ASSIGN objSendEmailAlgorithm = ipobjSendEmailAlgorithm
+ cMimeBoundary = {&DEFAULT_MIME_BOUNDARY}
+ lBodyIsBase64 = TRUE.
+ IF (OPSYS BEGINS "WIN") THEN
+ ASSIGN cNewLine = {&CR} + {&LF}.
+ ELSE
+ ASSIGN cNewLine = {&LF}.
+ END CONSTRUCTOR.
+
+ DESTRUCTOR PUBLIC Email ():
+ FOR EACH ttAttachments:
+ IF VALID-OBJECT(ttAttachments.lcData) THEN
+ DELETE OBJECT ttAttachments.lcData NO-ERROR.
+ END. /* FOR EACH ttAttachments */
+ END DESTRUCTOR.
+
+ /* Add a sender ("From:" address) to the email */
+ METHOD PUBLIC VOID addSender(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttSenders
+ WHERE ttSenders.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttSenders.
+ ASSIGN ttSenders.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a sender ("From:" address) (with Real Name) to the email */
+ METHOD PUBLIC VOID addSender(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttSenders
+ WHERE ttSenders.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttSenders.
+ ASSIGN ttSenders.cEmailAddress = ipcEmailAddress
+ ttSenders.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Add a "To:" recipient to the email */
+ METHOD PUBLIC VOID addToRecipient(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttToRecipients
+ WHERE ttToRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttToRecipients.
+ ASSIGN ttToRecipients.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a "To:" recipient (with Real Name) to the email */
+ METHOD PUBLIC VOID addToRecipient(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttToRecipients
+ WHERE ttToRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttToRecipients.
+ ASSIGN ttToRecipients.cEmailAddress = ipcEmailAddress
+ ttToRecipients.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Add a "CC:" recipient to the email */
+ METHOD PUBLIC VOID addCCRecipient(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttCCRecipients
+ WHERE ttCCRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttCCRecipients.
+ ASSIGN ttCCRecipients.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a "CC:" recipient (with Real Name) to the email */
+ METHOD PUBLIC VOID addCCRecipient(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttCCRecipients
+ WHERE ttCCRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttCCRecipients.
+ ASSIGN ttCCRecipients.cEmailAddress = ipcEmailAddress
+ ttToRecipients.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Add a "BCC:" recipient to the email */
+ METHOD PUBLIC VOID addBCCRecipient(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttBCCRecipients
+ WHERE ttBCCRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttBCCRecipients.
+ ASSIGN ttBCCRecipients.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a "BCC:" recipient (with Real Name) to the email */
+ METHOD PUBLIC VOID addBCCRecipient(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttBCCRecipients
+ WHERE ttBCCRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttBCCRecipients.
+ ASSIGN ttBCCRecipients.cEmailAddress = ipcEmailAddress
+ ttToRecipients.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Add a reply-to recipient to the email */
+ METHOD PUBLIC VOID addReplyToRecipient(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttReplyToRecipients
+ WHERE ttReplyToRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttReplyToRecipients.
+ ASSIGN ttReplyToRecipients.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a reply-to recipient (with Real Name) to the email */
+ METHOD PUBLIC VOID addReplyToRecipient(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttReplyToRecipients
+ WHERE ttReplyToRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttReplyToRecipients.
+ ASSIGN ttReplyToRecipients.cEmailAddress = ipcEmailAddress
+ ttReplyToRecipients.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Add a delivery receipt recipient to the email */
+ METHOD PUBLIC VOID addDeliveryReceiptRecipient(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttDeliveryReceiptRecipients
+ WHERE ttDeliveryReceiptRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttDeliveryReceiptRecipients.
+ ASSIGN ttDeliveryReceiptRecipients.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a delivery receipt recipient (with Real Name) to the email */
+ METHOD PUBLIC VOID addDeliveryReceiptRecipient(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttDeliveryReceiptRecipients
+ WHERE ttDeliveryReceiptRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttDeliveryReceiptRecipients.
+ ASSIGN ttDeliveryReceiptRecipients.cEmailAddress = ipcEmailAddress
+ ttDeliveryReceiptRecipients.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Add a read receipt recipient to the email */
+ METHOD PUBLIC VOID addReadReceiptRecipient(INPUT ipcEmailAddress AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttReadReceiptRecipients
+ WHERE ttReadReceiptRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttReadReceiptRecipients.
+ ASSIGN ttReadReceiptRecipients.cEmailAddress = ipcEmailAddress.
+ END.
+ END METHOD.
+
+ /* Add a read receipt recipient (with Real Name) to the email */
+ METHOD PUBLIC VOID addReadReceiptRecipient(INPUT ipcEmailAddress AS CHARACTER,
+ INPUT ipcRealName AS CHARACTER):
+ IF NOT CAN-FIND(FIRST ttReadReceiptRecipients
+ WHERE ttReadReceiptRecipients.cEmailAddress EQ ipcEmailAddress) THEN DO:
+ CREATE ttReadReceiptRecipients.
+ ASSIGN ttReadReceiptRecipients.cEmailAddress = ipcEmailAddress
+ ttReadReceiptRecipients.cRealName = ipcRealName.
+ END.
+ END METHOD.
+
+ /* Set the subject of the email */
+ METHOD PUBLIC VOID setSubject(INPUT ipcSubject AS CHARACTER):
+ ASSIGN cSubject = ipcSubject.
+ END METHOD.
+
+ /* Set the importance of the email. H = High, L = Low, anything else = Medium/None */
+ METHOD PUBLIC VOID setImportance(INPUT ipcImportance AS CHARACTER):
+ ASSIGN cImportance = ipcImportance.
+ END METHOD.
+
+ /* Set the sensitivity of the email. */
+ /* Possible values (from RFC 2156): "Personal", "Private", or "Company confidential" ("Company-confidential") */
+ METHOD PUBLIC VOID setSensitivity(INPUT ipcSensitivity AS CHARACTER):
+ ASSIGN cSensitivity = ipcSensitivity.
+ END METHOD.
+
+ /* Set the priority of the email (to affect transmission speed and delivery) */
+ /* Possible values (from RFC 2156): "normal", "urgent", or "non-urgent" */
+ METHOD PUBLIC VOID setPriority(INPUT ipcPriority AS CHARACTER):
+ ASSIGN cPriority = ipcPriority.
+ END METHOD.
+
+ /* Set the date/time the email was sent */
+ METHOD PUBLIC VOID setSentDate(INPUT ipdttmtzSentDate AS DATETIME-TZ):
+ ASSIGN dttmtzSentDate = ipdttmtzSentDate.
+ END METHOD.
+
+ /* Set the date/time recipient(s) should reply by */
+ METHOD PUBLIC VOID setReplyByDate(INPUT ipdttmtzReplyByDate AS DATETIME-TZ):
+ ASSIGN dttmtzReplyByDate = ipdttmtzReplyByDate.
+ END METHOD.
+
+ /* Set the date/time the message expires */
+ METHOD PUBLIC VOID setExpireDate(INPUT ipdttmtzExpireDate AS DATETIME-TZ):
+ ASSIGN dttmtzExpireDate = ipdttmtzExpireDate.
+ END METHOD.
+
+ /* If send email algorithm not set in constructor, you must set it using this method before the email can be sent */
+ METHOD PUBLIC VOID setSendEmailAlgorithm(INPUT ipobjSendEmailAlgorithm AS email.SendEmailAlgorithm):
+ ASSIGN objSendEmailAlgorithm = ipobjSendEmailAlgorithm.
+ END METHOD.
+
+ METHOD PUBLIC VOID setBodyText(INPUT ipcBodyText AS CHARACTER):
+ ASSIGN lcBody = ipcBodyText.
+ END METHOD.
+
+ METHOD PUBLIC VOID setBodyText(INPUT iplcBodyText AS LONGCHAR):
+ ASSIGN lcBody = iplcBodyText.
+ END METHOD.
+
+ /* Set the body by reading in an external file */
+ METHOD PUBLIC CHARACTER setBodyFile(INPUT ipcBodyFile AS CHARACTER):
+ FILE-INFO:FILE-NAME = ipcBodyFile.
+ IF FILE-INFO:FULL-PATHNAME EQ ? THEN
+ RETURN "Cannot locate file '" + ipcBodyFile + "' in the filesystem!".
+ IF INDEX(FILE-INFO:FILE-TYPE, "R") EQ 0 THEN
+ RETURN "File '" + FILE-INFO:FULL-PATHNAME + "' exists but is not readable!".
+ COPY-LOB FROM FILE FILE-INFO:FULL-PATHNAME TO OBJECT lcBody NO-ERROR.
+ IF ERROR-STATUS:ERROR THEN
+ RETURN "Error copying from file: " + ERROR-STATUS:GET-MESSAGE(1).
+ RETURN "". /* Success */
+ END METHOD.
+
+ /* Body defaults to base64 encoding, but can be manually disabled */
+ METHOD PUBLIC VOID setBodyEncoding(INPUT iplBase64Encode AS LOGICAL):
+ ASSIGN lBodyIsBase64 = iplBase64Encode.
+ END METHOD.
+
+ /* Add a non-encoded file attachment to the email */
+ METHOD PUBLIC CHARACTER addTextAttachment(INPUT ipcFileName AS CHARACTER):
+ DEFINE VARIABLE lcTemp AS LONGCHAR NO-UNDO.
+ FILE-INFO:FILE-NAME = ipcFileName.
+ IF FILE-INFO:FULL-PATHNAME EQ ? THEN
+ RETURN "Cannot locate file '" + ipcFileName + "' in the filesystem!".
+ IF INDEX(FILE-INFO:FILE-TYPE, "R") EQ 0 THEN
+ RETURN "File '" + FILE-INFO:FULL-PATHNAME + "' exists but is not readable!".
+ /* Load file into memory */
+ COPY-LOB FROM FILE FILE-INFO:FULL-PATHNAME TO OBJECT lcTemp NO-ERROR.
+ IF ERROR-STATUS:ERROR THEN
+ RETURN "Error copying from file: " + ERROR-STATUS:GET-MESSAGE(1).
+ CREATE ttAttachments.
+ ASSIGN ttAttachments.cFileName = ipcFileName
+ ttAttachments.lcData = NEW email.LongcharWrapper(lcTemp)
+ ttAttachments.lBase64Encode = FALSE.
+ RETURN "". /* Success */
+ END.
+
+ /* Add a file attachment to the email; it defaults to base-64 encoding */
+ METHOD PUBLIC CHARACTER addAttachment(INPUT ipcFileName AS CHARACTER):
+ DEFINE VARIABLE lcTemp AS LONGCHAR NO-UNDO.
+ FILE-INFO:FILE-NAME = ipcFileName.
+ IF FILE-INFO:FULL-PATHNAME EQ ? THEN
+ RETURN "Cannot locate file '" + ipcFileName + "' in the filesystem!".
+ IF INDEX(FILE-INFO:FILE-TYPE, "R") EQ 0 THEN
+ RETURN "File '" + FILE-INFO:FULL-PATHNAME + "' exists but is not readable!".
+ /* Load file into memory */
+ COPY-LOB FROM FILE FILE-INFO:FULL-PATHNAME TO OBJECT lcTemp NO-ERROR.
+ IF ERROR-STATUS:ERROR THEN
+ RETURN "Error copying from file: " + ERROR-STATUS:GET-MESSAGE(1).
+ CREATE ttAttachments.
+ ASSIGN ttAttachments.cFileName = ipcFileName
+ ttAttachments.lcData = NEW email.LongcharWrapper(EmailClient.Util:ConvertDataToBase64(lcTemp))
+ ttAttachments.lBase64Encode = TRUE.
+ RETURN "". /* Success */
+ END.
+
+ /* Override default MIME boundary */
+ METHOD PUBLIC VOID setMimeBoundary(INPUT ipcMimeBoundary AS CHARACTER):
+ ASSIGN cMimeBoundary = ipcMimeBoundary.
+ END METHOD.
+
+ /* Return a concatenated list of To:, CC:, and BCC: recipients */
+ METHOD PUBLIC CHARACTER getRecipients():
+ DEFINE VARIABLE cRecipients AS CHARACTER NO-UNDO.
+
+ FOR EACH ttToRecipients
+ BREAK BY ttToRecipients.cEmailAddress:
+ ASSIGN cRecipients = cRecipients + ttToRecipients.cEmailAddress.
+ IF NOT LAST(ttToRecipients.cEmailAddress) THEN DO:
+ ASSIGN cRecipients = cRecipients + ", ".
+ END.
+ END.
+ FOR EACH ttCCRecipients
+ BREAK BY ttCCRecipients.cEmailAddress:
+ IF FIRST(ttCCRecipients.cEmailAddress) AND
+ cRecipients NE "" THEN
+ ASSIGN cRecipients = cRecipients + ", ".
+ ASSIGN cRecipients = cRecipients + ttCCRecipients.cEmailAddress.
+ IF NOT LAST(ttCCRecipients.cEmailAddress) THEN
+ ASSIGN cRecipients = cRecipients + ttCCRecipients.cEmailAddress.
+ END.
+ FOR EACH ttBCCRecipients
+ BREAK BY ttBCCRecipients.cEmailAddress:
+ IF FIRST(ttBCCRecipients.cEmailAddress) AND
+ cRecipients NE "" THEN
+ ASSIGN cRecipients = cRecipients + ", ".
+ ASSIGN cRecipients = cRecipients + ttBCCRecipients.cEmailAddress.
+ IF NOT LAST(ttBCCRecipients.cEmailAddress) THEN
+ ASSIGN cRecipients = cRecipients + ttBCCRecipients.cEmailAddress.
+ END.
+ RETURN cRecipients.
+ END METHOD.
+
+ /* Dumps all email message headers to CHAR */
+ METHOD PUBLIC CHARACTER getHeaders():
+ DEFINE VARIABLE cReturnData AS CHARACTER NO-UNDO.
+
+ /* Write the "From:" header */
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "From:".
+ FOR EACH ttSenders
+ BREAK BY ttSenders.cEmailAddress:
+ IF ttSenders.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttSenders.cRealName + " <" + ttSenders.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttSenders.cEmailAddress.
+ IF NOT LAST(ttSenders.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END.
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "\n".
+ /* Write the "To:" header */
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "To:".
+ FOR EACH ttToRecipients
+ BREAK BY ttToRecipients.cEmailAddress:
+ IF ttToRecipients.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttToRecipients.cRealName + " <" + ttToRecipients.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttToRecipients.cEmailAddress.
+ IF NOT LAST(ttToRecipients.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END.
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "\n".
+ /* Write the "Reply-To:" header */
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "Reply-To:".
+ IF TEMP-TABLE ttReplyToRecipients:HAS-RECORDS THEN DO:
+ /* Use manually-overridden reply-to addresses */
+ FOR EACH ttReplyToRecipients
+ BREAK BY ttReplyToRecipients.cEmailAddress:
+ IF ttReplyToRecipients.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttReplyToRecipients.cRealName + " <" + ttReplyToRecipients.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttReplyToRecipients.cEmailAddress.
+ IF NOT LAST(ttReplyToRecipients.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END. /* FOR EACH ttReplyToRecipients ... */
+ END. /* IF TEMP-TABLE ttReplyToRecipients:HAS-RECORDS */
+ ELSE DO:
+ /* Write reply-to using sender addresses if reply-to addresses not manually overriddden */
+ FOR EACH ttSenders
+ BREAK BY ttSenders.cEmailAddress:
+ IF ttSenders.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttSenders.cRealName + " <" + ttSenders.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttSenders.cEmailAddress.
+ IF NOT LAST(ttSenders.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END.
+ END. /* ELSE / IF TEMP-TABLE ttReplyToRecipients:HAS-RECORDS */
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "\n".
+ /* Write the "Cc:" header */
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "Cc:".
+ FOR EACH ttCCRecipients
+ BREAK BY ttCCRecipients.cEmailAddress:
+ IF ttCCRecipients.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttCCRecipients.cRealName + " <" + ttCCRecipients.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttCCRecipients.cEmailAddress.
+ IF NOT LAST(ttCCRecipients.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END.
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "\n".
+ /* Write the "Bcc:" header */
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "Bcc:".
+ FOR EACH ttBCCRecipients
+ BREAK BY ttBCCRecipients.cEmailAddress:
+ IF ttBCCRecipients.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttBCCRecipients.cRealName + " <" + ttBCCRecipients.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttBCCRecipients.cEmailAddress.
+ IF NOT LAST(ttBCCRecipients.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END.
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "\n".
+ /* If delivery recipients specified, write each recipient out */
+ IF TEMP-TABLE ttDeliveryReceiptRecipients:HAS-RECORDS THEN DO:
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "Return-Receipt-To:".
+ FOR EACH ttDeliveryReceiptRecipients
+ BREAK BY ttDeliveryReceiptRecipients.cEmailAddress:
+ IF ttDeliveryReceiptRecipients.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttDeliveryReceiptRecipients.cRealName + " <" + ttDeliveryReceiptRecipients.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttDeliveryReceiptRecipients.cEmailAddress.
+ IF NOT LAST(ttDeliveryReceiptRecipients.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END. /* FOR EACH ttDeliveryReceiptRecipients */
+ ASSIGN cReturnData = cReturnData + {&QUOTES}.
+ END. /* IF TEMP-TABLE ttDeliveryReceiptRecipients:HAS-RECORDS */
+ /* If read recipients specified, write each recipient out */
+ IF TEMP-TABLE ttReadReceiptRecipients:HAS-RECORDS THEN DO:
+ ASSIGN cReturnData = cReturnData + {&QUOTES} + "Disposition-Notification-To:".
+ FOR EACH ttReadReceiptRecipients
+ BREAK BY ttReadReceiptRecipients.cEmailAddress:
+ IF ttReadReceiptRecipients.cRealName NE ? THEN
+ ASSIGN cReturnData = cReturnData + ttReadReceiptRecipients.cRealName + " <" + ttReadReceiptRecipients.cEmailAddress + ">".
+ ELSE
+ ASSIGN cReturnData = cReturnData + ttReadReceiptRecipients.cEmailAddress.
+ IF NOT LAST(ttReadReceiptRecipients.cEmailAddress) THEN
+ ASSIGN cReturnData = cReturnData + ", ".
+ END. /* FOR EACH ttReadReceiptRecipients */
+ ASSIGN cReturnData = cReturnData + {&QUOTES}.
+ END. /* IF TEMP-TABLE ttReadReceiptRecipients:HAS-RECORDS */
+ /* Write the "Subject:" header */
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Subject:" + cSubject + {&QUOTES}.
+ /* Write the "Importance:" header */
+ IF cImportance BEGINS "H" THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Importance:High" + {&QUOTES}.
+ ELSE IF cImportance BEGINS "L" THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Importance:Low" + {&QUOTES}.
+ /* Write the "Sensitivity" header */
+ IF cSensitivity NE "" THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Sensitivity:" + cSensitivity + {&QUOTES}.
+ /* Write the "Priority" header */
+ IF cPriority NE "" THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Priority:" + cPriority + {&QUOTES}.
+ /* Write the "Date" (sent date) header */
+ IF dttmtzSentDate NE ? THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Date:" + email.Util:ABLDateTimeToEmail(dttmtzSentDate) + {&QUOTES}.
+ IF dttmtzReplyByDate NE ? THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Reply-By:" + email.Util:ABLDateTimeToEmail(dttmtzReplyByDate) + {&QUOTES}.
+ /* Write the "Expiry-Date" header */
+ IF dttmtzExpireDate NE ? THEN
+ ASSIGN cReturnData = cReturnData + "\n" + {&QUOTES} + "Expiry-Date:" + email.Util:ABLDateTimeToEmail(dttmtzExpireDate) + {&QUOTES}.
+ RETURN cReturnData.
+ END METHOD.
+
+ /* Dumps all email message payload data (body and attachments) to LONGCHAR */
+ METHOD PUBLIC LONGCHAR getPayload():
+ DEFINE VARIABLE lcReturnData AS LONGCHAR NO-UNDO.
+
+ /* If no body and no text, then return empty string ("") */
+ IF lcBody EQ "" AND NOT TEMP-TABLE ttAttachments:HAS-RECORDS THEN
+ RETURN lcReturnData.
+
+ /* Write payload header */
+ ASSIGN lcReturnData = "Mime-Version: 1.0" + cNewLine +
+ "Content-Type: multipart/mixed; boundary=" + cMimeBoundary + cNewLine + cNewLine.
+
+ /* Write out the email body, if it exists */
+ IF lcBody NE "" THEN DO:
+ ASSIGN lcReturnData = lcReturnData + "--" + cMimeBoundary + cNewLine +
+ "Content-Type: text/plain; charset=~"us-ascii~"" + cNewLine.
+ IF lBodyIsBase64 THEN DO:
+ ASSIGN lcReturnData = lcReturnData + "Content-Transfer-Encoding: base64" + cNewLine +
+ cNewLine +
+ email.Util:ConvertDataToBase64(lcBody) + cNewLine.
+ END.
+ ELSE DO:
+ ASSIGN lcReturnData = lcReturnData + "Content-Transfer-Encoding: 7bit" + cNewLine +
+ cNewLine +
+ lcBody + cNewLine.
+ END.
+ END.
+
+ /* Write out each email attachment */
+ FOR EACH ttAttachments:
+ ASSIGN lcReturnData = lcReturnData + "--" + cMimeBoundary + cNewLine.
+ IF ttAttachments.lBase64Encode THEN DO:
+ ASSIGN lcReturnData = lcReturnData + "Content-Type: application/octet-stream" + cNewLine +
+ "Content-Disposition: attachment; filename=~"" + ttAttachments.cFileName + "~"" + cNewLine +
+ "Content-Transfer-Encoding: base64" + cNewLine + cNewLine +
+ CAST(ttAttachments.lcData, email.LongcharWrapper):getLongchar() + cNewLine.
+ END.
+ ELSE DO:
+ ASSIGN lcReturnData = lcReturnData + "Content-Type: text/plain; charset=~"us-ascii~"" + cNewLine +
+ "Content-Disposition: attachment; filename=~"" + ttAttachments.cFileName + "~"" + cNewLine +
+ "Content-Transfer-Encoding: 7bit" + cNewLine + cNewLine +
+ CAST(ttAttachments.lcData, email.LongcharWrapper):getLongchar() + cNewLine.
+ END.
+ END.
+
+ /* Write payload footer */
+ ASSIGN lcReturnData = lcReturnData + "--" + cMimeBoundary + "--" + cNewLine.
+
+ RETURN lcReturnData.
+ END METHOD.
+
+ METHOD PUBLIC CHARACTER send():
+ RETURN objSendEmailAlgorithm:sendEmail(INPUT THIS-OBJECT).
+ END METHOD.
+
+END CLASS.
diff --git a/samples/openedge-abl/SendEmailAlgorithm.cls b/samples/openedge-abl/SendEmailAlgorithm.cls
new file mode 100644
index 0000000..8b8eecf
--- /dev/null
+++ b/samples/openedge-abl/SendEmailAlgorithm.cls
@@ -0,0 +1,23 @@
+
+/*------------------------------------------------------------------------
+ File : SendEmailAlgorithm
+ Purpose :
+ Syntax :
+ Description : Uses object-oriented Strategy Pattern to abstract away the
+ algorithm for sending an email by encapsulating it
+ into a data structure.
+ Author(s) : Abe Voelker
+ Created : Sat Jul 17 17:11:18 CDT 2010
+ Notes :
+ ----------------------------------------------------------------------*/
+
+USING Progress.Lang.*.
+
+INTERFACE email.SendEmailAlgorithm:
+
+ /* Returns: */
+ /* SUCCESS = empty return string */
+ /* FAILURE = error message in return string */
+ METHOD PUBLIC CHARACTER sendEmail(INPUT ipobjEmail AS email.Email).
+
+END INTERFACE.
diff --git a/samples/openedge-abl/SocketReader.p b/samples/openedge-abl/SocketReader.p
new file mode 100644
index 0000000..0f7add8
--- /dev/null
+++ b/samples/openedge-abl/SocketReader.p
@@ -0,0 +1,118 @@
+
+/*------------------------------------------------------------------------
+ File : SocketReader.p
+ Purpose :
+ Author(s) : Abe Voelker
+ Created : Sat Aug 21 08:31:38 CDT 2010
+ Notes : Based on code from smtpmail.p
+ ----------------------------------------------------------------------*/
+
+/* *************************** Definitions ************************** */
+DEFINE INPUT PARAMETER objSendEmailAlg AS email.SendEmailSocket NO-UNDO.
+DEFINE VARIABLE vbuffer AS MEMPTR NO-UNDO.
+DEFINE VARIABLE vstatus AS LOGICAL NO-UNDO.
+DEFINE VARIABLE vState AS INTEGER NO-UNDO.
+
+
+ASSIGN vstate = 1.
+
+/* ******************** Preprocessor Definitions ******************** */
+
+
+/* *************************** Main Block *************************** */
+
+FUNCTION getHostname RETURNS CHARACTER():
+ DEFINE VARIABLE cHostname AS CHARACTER NO-UNDO.
+ INPUT THROUGH hostname NO-ECHO.
+ IMPORT UNFORMATTED cHostname.
+ INPUT CLOSE.
+ RETURN cHostname.
+END FUNCTION.
+
+/*
+ Status:
+ 0 - No Connection to the server
+ 1 - Waiting for 220 connection to SMTP server
+ 2 - Waiting for 250 OK status to start sending email
+ 3 - Waiting for 250 OK status for sender
+ 4 - Waiting for 250 OK status for recipient
+ 5 - Waiting for 354 OK status to send data
+ 6 - Waiting for 250 OK status for message received
+ 7 - Quiting
+*/
+
+PROCEDURE newState:
+ DEFINE INPUT PARAMETER newState AS INTEGER.
+ DEFINE INPUT PARAMETER pstring AS CHARACTER.
+ vState = newState.
+ IF pstring = "" THEN
+ RETURN.
+ SET-SIZE(vbuffer) = LENGTH(pstring) + 1.
+ PUT-STRING(vbuffer,1) = pstring.
+ SELF:WRITE(vbuffer, 1, LENGTH(pstring)).
+ SET-SIZE(vbuffer) = 0.
+END PROCEDURE.
+
+PROCEDURE ReadSocketResponse:
+ DEFINE VARIABLE vlength AS INTEGER NO-UNDO.
+ DEFINE VARIABLE str AS CHARACTER NO-UNDO.
+ DEFINE VARIABLE v AS INTEGER NO-UNDO.
+
+ MESSAGE SELF:GET-BYTES-AVAILABLE() VIEW-AS ALERT-BOX.
+ vlength = SELF:GET-BYTES-AVAILABLE().
+ IF vlength > 0 THEN DO:
+ SET-SIZE(vbuffer) = vlength + 1.
+ SELF:READ(vbuffer, 1, vlength, 1).
+ str = GET-STRING(vbuffer,1).
+ SET-SIZE(vbuffer) = 0.
+ objSendEmailAlg:handleResponse(str).
+ /*
+ v = INTEGER(ENTRY(1, str," ")).
+ CASE vState:
+ WHEN 1 THEN
+ IF v = 220 THEN
+ RUN newState(2, "HELO " + getHostname() + "~r~n").
+ ELSE
+ vState = -1.
+ WHEN 2 THEN
+ IF v = 250 THEN
+ RUN newState(3, "MAIL From: " + "hardcoded@gmail.com" + "~r~n").
+ ELSE
+ vState = -1.
+ WHEN 3 THEN
+ IF v = 250 THEN
+ RUN newState(4, "RCPT TO: " + "hardcoded@gmail.com" + "~r~n").
+ ELSE
+ vState = -1.
+ WHEN 4 THEN
+ IF v = 250 THEN
+ RUN newState(5, "DATA ~r~n").
+ ELSE
+ vState = -1.
+ WHEN 5 THEN
+ IF v = 354 THEN
+ RUN newState(6, "From: " + "hardcoded@gmail.com" + "~r~n" +
+ "To: " + "hardcoded@gmail.com" + " ~r~n" +
+ "Subject: " + "Test Subject" +
+ " ~r~n~r~n" +
+ "Test Body" + "~r~n" +
+ ".~r~n").
+ ELSE
+ vState = -1.
+
+ WHEN 6 THEN
+ IF v = 250 THEN
+ RUN newState(7,"QUIT~r~n").
+ ELSE
+ vState = -1.
+ END CASE.
+ */
+ END.
+ /*
+ IF vState = 7 THEN
+ MESSAGE "Email has been accepted for delivery.".
+ IF vState < 0 THEN
+ MESSAGE "Email has been aborted".
+ */
+END PROCEDURE.
+
diff --git a/samples/openedge-abl/Util.cls b/samples/openedge-abl/Util.cls
new file mode 100644
index 0000000..72d1c13
--- /dev/null
+++ b/samples/openedge-abl/Util.cls
@@ -0,0 +1,57 @@
+
+ /*------------------------------------------------------------------------
+ File : Util.cls
+ Description : Utility class for various methods that do not fit neatly into
+ existing class structures.
+ Author(s) : Abe Voelker
+ Created : Sat Jun 26 16:05:14 CDT 2010
+ Notes :
+ ----------------------------------------------------------------------*/
+
+USING Progress.Lang.*.
+
+
+CLASS email.Util USE-WIDGET-POOL FINAL:
+
+ DEFINE PRIVATE STATIC VARIABLE cMonthMap AS CHARACTER EXTENT 12 INITIAL
+ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].
+
+ /* Converts ABL DateTime-TZ default string format (07/21/2010 21:16:47.141-05:00) */
+ /* to Email standard format (21 Jul 2010 21:16:47 -0500) */
+ METHOD PUBLIC STATIC CHARACTER ABLDateTimeToEmail(INPUT ipdttzDateTime AS DATETIME-TZ):
+ RETURN STRING(DAY(ipdttzDateTime)) + " " + cMonthMap[MONTH(ipdttzDateTime)] + " " +
+ STRING(YEAR(ipdttzDateTime)) + " " +
+ STRING( INTEGER( TRUNCATE( MTIME( ipdttzDateTime ) / 1000, 0 ) ), "HH:MM:SS" ) + " " +
+ ABLTimeZoneToString(TIMEZONE(ipdttzDateTime)).
+ END METHOD.
+
+ METHOD PUBLIC STATIC CHARACTER ABLDateTimeToEmail(INPUT ipdtDateTime AS DATETIME):
+ RETURN ABLDateTimeToEmail(DATETIME-TZ(ipdtDateTime)). /* Time zone will be session value */
+ END METHOD.
+
+ /* Note: ABL MODULO function returns incorrect values for negative numbers! */
+ METHOD PUBLIC STATIC CHARACTER ABLTimeZoneToString(INPUT ipiTimeZone AS INTEGER):
+ RETURN STRING(TRUNCATE(ipiTimeZone / 60, 0), "-99") + STRING(ABSOLUTE(ipiTimeZone) MODULO 60, "99").
+ END METHOD.
+
+ /* Converts input plain text into base64-encoded, email-standard width string data */
+ METHOD PUBLIC STATIC LONGCHAR ConvertDataToBase64(INPUT iplcNonEncodedData AS LONGCHAR):
+ DEFINE VARIABLE lcPreBase64Data AS LONGCHAR NO-UNDO.
+ DEFINE VARIABLE lcPostBase64Data AS LONGCHAR NO-UNDO.
+ DEFINE VARIABLE mptrPostBase64Data AS MEMPTR NO-UNDO.
+ DEFINE VARIABLE i AS INTEGER NO-UNDO.
+
+ /* Read file into MEMPTR and convert it to base-64 */
+ COPY-LOB FROM OBJECT iplcNonEncodedData TO mptrPostBase64Data.
+ lcPreBase64Data = BASE64-ENCODE(mptrPostBase64Data).
+ SET-SIZE(mptrPostBase64Data) = 0. /* Free memory */
+
+ /* Convert base-64 data into 77-char width lines (for email standard) */
+ DO i=1 TO LENGTH(lcPreBase64Data) BY 77:
+ ASSIGN lcPostBase64Data = lcPostBase64Data + SUBSTRING(lcPreBase64Data, i, 77) + CHR(13) + CHR(10).
+ END.
+
+ RETURN lcPostBase64Data.
+ END METHOD.
+
+END CLASS. \ No newline at end of file
diff --git a/samples/openedge-abl/openedge.p b/samples/openedge-abl/openedge.p
new file mode 100644
index 0000000..d6ec595
--- /dev/null
+++ b/samples/openedge-abl/openedge.p
@@ -0,0 +1 @@
+MESSAGE "Hello, world!".
diff --git a/samples/pasm/hello.pasm b/samples/pasm/hello.pasm
new file mode 100644
index 0000000..6f2efaa
--- /dev/null
+++ b/samples/pasm/hello.pasm
@@ -0,0 +1,5 @@
+#!/usr/bin/env parrot
+
+.pcc_sub :main main:
+ say "Hello!"
+ end
diff --git a/samples/perl/Ack.pm b/samples/perl/Ack.pm
new file mode 100644
index 0000000..84eea8c
--- /dev/null
+++ b/samples/perl/Ack.pm
@@ -0,0 +1,1684 @@
+package App::Ack;
+
+use warnings;
+use strict;
+
+use File::Next 0.40;
+
+use App::Ack::Plugin::Basic;
+
+=head1 NAME
+
+App::Ack - A container for functions for the ack program
+
+=head1 VERSION
+
+Version 1.94
+
+=cut
+
+our $VERSION;
+our $COPYRIGHT;
+BEGIN {
+ $VERSION = '1.96';
+ $COPYRIGHT = 'Copyright 2005-2011 Andy Lester.';
+}
+
+our $fh;
+
+BEGIN {
+ $fh = *STDOUT;
+}
+
+
+our %types;
+our %type_wanted;
+our %mappings;
+our %ignore_dirs;
+
+our $input_from_pipe;
+our $output_to_pipe;
+
+our $dir_sep_chars;
+our $is_cygwin;
+our $is_windows;
+
+use File::Spec ();
+use File::Glob ':glob';
+use Getopt::Long ();
+
+BEGIN {
+ %ignore_dirs = (
+ '.bzr' => 'Bazaar',
+ '.cdv' => 'Codeville',
+ '~.dep' => 'Interface Builder',
+ '~.dot' => 'Interface Builder',
+ '~.nib' => 'Interface Builder',
+ '~.plst' => 'Interface Builder',
+ '.git' => 'Git',
+ '.hg' => 'Mercurial',
+ '.pc' => 'quilt',
+ '.svn' => 'Subversion',
+ _MTN => 'Monotone',
+ blib => 'Perl module building',
+ CVS => 'CVS',
+ RCS => 'RCS',
+ SCCS => 'SCCS',
+ _darcs => 'darcs',
+ _sgbak => 'Vault/Fortress',
+ 'autom4te.cache' => 'autoconf',
+ 'cover_db' => 'Devel::Cover',
+ _build => 'Module::Build',
+ );
+
+ %mappings = (
+ actionscript => [qw( as mxml )],
+ ada => [qw( ada adb ads )],
+ asm => [qw( asm s )],
+ batch => [qw( bat cmd )],
+ binary => q{Binary files, as defined by Perl's -B op (default: off)},
+ cc => [qw( c h xs )],
+ cfmx => [qw( cfc cfm cfml )],
+ clojure => [qw( clj )],
+ cpp => [qw( cpp cc cxx m hpp hh h hxx )],
+ csharp => [qw( cs )],
+ css => [qw( css )],
+ delphi => [qw( pas int dfm nfm dof dpk dproj groupproj bdsgroup bdsproj )],
+ elisp => [qw( el )],
+ erlang => [qw( erl hrl )],
+ fortran => [qw( f f77 f90 f95 f03 for ftn fpp )],
+ go => [qw( go )],
+ groovy => [qw( groovy gtmpl gpp grunit )],
+ haskell => [qw( hs lhs )],
+ hh => [qw( h )],
+ html => [qw( htm html shtml xhtml )],
+ java => [qw( java properties )],
+ js => [qw( js )],
+ jsp => [qw( jsp jspx jhtm jhtml )],
+ lisp => [qw( lisp lsp )],
+ lua => [qw( lua )],
+ make => q{Makefiles (including *.mk and *.mak)},
+ mason => [qw( mas mhtml mpl mtxt )],
+ objc => [qw( m h )],
+ objcpp => [qw( mm h )],
+ ocaml => [qw( ml mli )],
+ parrot => [qw( pir pasm pmc ops pod pg tg )],
+ perl => [qw( pl pm pm6 pod t psgi )],
+ php => [qw( php phpt php3 php4 php5 phtml)],
+ plone => [qw( pt cpt metadata cpy py )],
+ python => [qw( py )],
+ rake => q{Rakefiles},
+ ruby => [qw( rb rhtml rjs rxml erb rake spec )],
+ scala => [qw( scala )],
+ scheme => [qw( scm ss )],
+ shell => [qw( sh bash csh tcsh ksh zsh )],
+ skipped => q{Files, but not directories, normally skipped by ack (default: off)},
+ smalltalk => [qw( st )],
+ sql => [qw( sql ctl )],
+ tcl => [qw( tcl itcl itk )],
+ tex => [qw( tex cls sty )],
+ text => q{Text files, as defined by Perl's -T op (default: off)},
+ tt => [qw( tt tt2 ttml )],
+ vb => [qw( bas cls frm ctl vb resx )],
+ verilog => [qw( v vh sv )],
+ vhdl => [qw( vhd vhdl )],
+ vim => [qw( vim )],
+ yaml => [qw( yaml yml )],
+ xml => [qw( xml dtd xsl xslt ent )],
+ );
+
+ while ( my ($type,$exts) = each %mappings ) {
+ if ( ref $exts ) {
+ for my $ext ( @{$exts} ) {
+ push( @{$types{$ext}}, $type );
+ }
+ }
+ }
+ # add manually Makefile extensions
+ push @{$types{$_}}, 'make' for qw{ mk mak };
+
+ # These have to be checked before any filehandle diddling.
+ $output_to_pipe = not -t *STDOUT;
+ $input_from_pipe = -p STDIN;
+
+ $is_cygwin = ($^O eq 'cygwin');
+ $is_windows = ($^O =~ /MSWin32/);
+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
+}
+
+=head1 SYNOPSIS
+
+If you want to know about the F<ack> program, see the F<ack> file itself.
+
+No user-serviceable parts inside. F<ack> is all that should use this.
+
+=head1 FUNCTIONS
+
+=head2 read_ackrc
+
+Reads the contents of the .ackrc file and returns the arguments.
+
+=cut
+
+sub read_ackrc {
+ my @files = ( $ENV{ACKRC} );
+ my @dirs =
+ $is_windows
+ ? ( $ENV{HOME}, $ENV{USERPROFILE} )
+ : ( '~', $ENV{HOME} );
+ for my $dir ( grep { defined } @dirs ) {
+ for my $file ( '.ackrc', '_ackrc' ) {
+ push( @files, bsd_glob( "$dir/$file", GLOB_TILDE ) );
+ }
+ }
+ for my $filename ( @files ) {
+ if ( defined $filename && -e $filename ) {
+ open( my $fh, '<', $filename ) or App::Ack::die( "$filename: $!\n" );
+ my @lines = grep { /./ && !/^\s*#/ } <$fh>;
+ chomp @lines;
+ close $fh or App::Ack::die( "$filename: $!\n" );
+
+ # get rid of leading and trailing whitespaces
+ for ( @lines ) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+
+ return @lines;
+ }
+ }
+
+ return;
+}
+
+=head2 get_command_line_options()
+
+Gets command-line arguments and does the Ack-specific tweaking.
+
+=cut
+
+sub get_command_line_options {
+ my %opt = (
+ pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
+ );
+
+ my $getopt_specs = {
+ 1 => sub { $opt{1} = $opt{m} = 1 },
+ 'A|after-context=i' => \$opt{after_context},
+ 'B|before-context=i' => \$opt{before_context},
+ 'C|context:i' => sub { shift; my $val = shift; $opt{before_context} = $opt{after_context} = ($val || 2) },
+ 'a|all-types' => \$opt{all},
+ 'break!' => \$opt{break},
+ c => \$opt{count},
+ 'color|colour!' => \$opt{color},
+ 'color-match=s' => \$ENV{ACK_COLOR_MATCH},
+ 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},
+ 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},
+ 'column!' => \$opt{column},
+ count => \$opt{count},
+ 'env!' => sub { }, # ignore this option, it is handled beforehand
+ f => \$opt{f},
+ flush => \$opt{flush},
+ 'follow!' => \$opt{follow},
+ 'g=s' => sub { shift; $opt{G} = shift; $opt{f} = 1 },
+ 'G=s' => \$opt{G},
+ 'group!' => sub { shift; $opt{heading} = $opt{break} = shift },
+ 'heading!' => \$opt{heading},
+ 'h|no-filename' => \$opt{h},
+ 'H|with-filename' => \$opt{H},
+ 'i|ignore-case' => \$opt{i},
+ 'invert-file-match' => \$opt{invert_file_match},
+ 'lines=s' => sub { shift; my $val = shift; push @{$opt{lines}}, $val },
+ 'l|files-with-matches' => \$opt{l},
+ 'L|files-without-matches' => sub { $opt{l} = $opt{v} = 1 },
+ 'm|max-count=i' => \$opt{m},
+ 'match=s' => \$opt{regex},
+ 'n|no-recurse' => \$opt{n},
+ o => sub { $opt{output} = '$&' },
+ 'output=s' => \$opt{output},
+ 'pager=s' => \$opt{pager},
+ 'nopager' => sub { $opt{pager} = undef },
+ 'passthru' => \$opt{passthru},
+ 'print0' => \$opt{print0},
+ 'Q|literal' => \$opt{Q},
+ 'r|R|recurse' => sub { $opt{n} = 0 },
+ 'show-types' => \$opt{show_types},
+ 'smart-case!' => \$opt{smart_case},
+ 'sort-files' => \$opt{sort_files},
+ 'u|unrestricted' => \$opt{u},
+ 'v|invert-match' => \$opt{v},
+ 'w|word-regexp' => \$opt{w},
+
+ 'ignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); $ignore_dirs{$dir} = '--ignore-dirs' },
+ 'noignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); delete $ignore_dirs{$dir} },
+
+ 'version' => sub { print_version_statement(); exit; },
+ 'help|?:s' => sub { shift; show_help(@_); exit; },
+ 'help-types'=> sub { show_help_types(); exit; },
+ 'man' => sub {
+ require Pod::Usage;
+ Pod::Usage::pod2usage({
+ -verbose => 2,
+ -exitval => 0,
+ });
+ },
+
+ 'type=s' => sub {
+ # Whatever --type=xxx they specify, set it manually in the hash
+ my $dummy = shift;
+ my $type = shift;
+ my $wanted = ($type =~ s/^no//) ? 0 : 1; # must not be undef later
+
+ if ( exists $type_wanted{ $type } ) {
+ $type_wanted{ $type } = $wanted;
+ }
+ else {
+ App::Ack::die( qq{Unknown --type "$type"} );
+ }
+ }, # type sub
+ };
+
+ # Stick any default switches at the beginning, so they can be overridden
+ # by the command line switches.
+ unshift @ARGV, split( ' ', $ENV{ACK_OPTIONS} ) if defined $ENV{ACK_OPTIONS};
+
+ # first pass through options, looking for type definitions
+ def_types_from_ARGV();
+
+ for my $i ( filetypes_supported() ) {
+ $getopt_specs->{ "$i!" } = \$type_wanted{ $i };
+ }
+
+
+ my $parser = Getopt::Long::Parser->new();
+ $parser->configure( 'bundling', 'no_ignore_case', );
+ $parser->getoptions( %{$getopt_specs} ) or
+ App::Ack::die( 'See ack --help, ack --help-types or ack --man for options.' );
+
+ my $to_screen = not output_to_pipe();
+ my %defaults = (
+ all => 0,
+ color => $to_screen,
+ follow => 0,
+ break => $to_screen,
+ heading => $to_screen,
+ before_context => 0,
+ after_context => 0,
+ );
+ if ( $is_windows && $defaults{color} && not $ENV{ACK_PAGER_COLOR} ) {
+ if ( $ENV{ACK_PAGER} || not eval { require Win32::Console::ANSI } ) {
+ $defaults{color} = 0;
+ }
+ }
+ if ( $to_screen && $ENV{ACK_PAGER_COLOR} ) {
+ $defaults{color} = 1;
+ }
+
+ while ( my ($key,$value) = each %defaults ) {
+ if ( not defined $opt{$key} ) {
+ $opt{$key} = $value;
+ }
+ }
+
+ if ( defined $opt{m} && $opt{m} <= 0 ) {
+ App::Ack::die( '-m must be greater than zero' );
+ }
+
+ for ( qw( before_context after_context ) ) {
+ if ( defined $opt{$_} && $opt{$_} < 0 ) {
+ App::Ack::die( "--$_ may not be negative" );
+ }
+ }
+
+ if ( defined( my $val = $opt{output} ) ) {
+ $opt{output} = eval qq[ sub { "$val" } ];
+ }
+ if ( defined( my $l = $opt{lines} ) ) {
+ # --line=1 --line=5 is equivalent to --line=1,5
+ my @lines = split( /,/, join( ',', @{$l} ) );
+
+ # --line=1-3 is equivalent to --line=1,2,3
+ @lines = map {
+ my @ret;
+ if ( /-/ ) {
+ my ($from, $to) = split /-/, $_;
+ if ( $from > $to ) {
+ App::Ack::warn( "ignoring --line=$from-$to" );
+ @ret = ();
+ }
+ else {
+ @ret = ( $from .. $to );
+ }
+ }
+ else {
+ @ret = ( $_ );
+ };
+ @ret
+ } @lines;
+
+ if ( @lines ) {
+ my %uniq;
+ @uniq{ @lines } = ();
+ $opt{lines} = [ sort { $a <=> $b } keys %uniq ]; # numerical sort and each line occurs only once!
+ }
+ else {
+ # happens if there are only ignored --line directives
+ App::Ack::die( 'All --line options are invalid.' );
+ }
+ }
+
+ return \%opt;
+}
+
+=head2 def_types_from_ARGV
+
+Go through the command line arguments and look for
+I<--type-set foo=.foo,.bar> and I<--type-add xml=.rdf>.
+Remove them from @ARGV and add them to the supported filetypes,
+i.e. into %mappings, etc.
+
+=cut
+
+sub def_types_from_ARGV {
+ my @typedef;
+
+ my $parser = Getopt::Long::Parser->new();
+ # pass_through => leave unrecognized command line arguments alone
+ # no_auto_abbrev => otherwise -c is expanded and not left alone
+ $parser->configure( 'no_ignore_case', 'pass_through', 'no_auto_abbrev' );
+ $parser->getoptions(
+ 'type-set=s' => sub { shift; push @typedef, ['c', shift] },
+ 'type-add=s' => sub { shift; push @typedef, ['a', shift] },
+ ) or App::Ack::die( 'See ack --help or ack --man for options.' );
+
+ for my $td (@typedef) {
+ my ($type, $ext) = split /=/, $td->[1];
+
+ if ( $td->[0] eq 'c' ) {
+ # type-set
+ if ( exists $mappings{$type} ) {
+ # can't redefine types 'make', 'skipped', 'text' and 'binary'
+ App::Ack::die( qq{--type-set: Builtin type "$type" cannot be changed.} )
+ if ref $mappings{$type} ne 'ARRAY';
+
+ delete_type($type);
+ }
+ }
+ else {
+ # type-add
+
+ # can't append to types 'make', 'skipped', 'text' and 'binary'
+ App::Ack::die( qq{--type-add: Builtin type "$type" cannot be changed.} )
+ if exists $mappings{$type} && ref $mappings{$type} ne 'ARRAY';
+
+ App::Ack::warn( qq{--type-add: Type "$type" does not exist, creating with "$ext" ...} )
+ unless exists $mappings{$type};
+ }
+
+ my @exts = split /,/, $ext;
+ s/^\.// for @exts;
+
+ if ( !exists $mappings{$type} || ref($mappings{$type}) eq 'ARRAY' ) {
+ push @{$mappings{$type}}, @exts;
+ for my $e ( @exts ) {
+ push @{$types{$e}}, $type;
+ }
+ }
+ else {
+ App::Ack::die( qq{Cannot append to type "$type".} );
+ }
+ }
+
+ return;
+}
+
+=head2 delete_type
+
+Removes a type from the internal structures containing type
+information: %mappings, %types and %type_wanted.
+
+=cut
+
+sub delete_type {
+ my $type = shift;
+
+ App::Ack::die( qq{Internal error: Cannot delete builtin type "$type".} )
+ unless ref $mappings{$type} eq 'ARRAY';
+
+ delete $mappings{$type};
+ delete $type_wanted{$type};
+ for my $ext ( keys %types ) {
+ $types{$ext} = [ grep { $_ ne $type } @{$types{$ext}} ];
+ }
+}
+
+=head2 ignoredir_filter
+
+Standard filter to pass as a L<File::Next> descend_filter. It
+returns true if the directory is any of the ones we know we want
+to ignore.
+
+=cut
+
+sub ignoredir_filter {
+ return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};
+}
+
+=head2 remove_dir_sep( $path )
+
+This functions removes a trailing path separator, if there is one, from its argument
+
+=cut
+
+sub remove_dir_sep {
+ my $path = shift;
+ $path =~ s/[$dir_sep_chars]$//;
+
+ return $path;
+}
+
+=head2 filetypes( $filename )
+
+Returns a list of types that I<$filename> could be. For example, a file
+F<foo.pod> could be "perl" or "parrot".
+
+The filetype will be C<undef> if we can't determine it. This could
+be if the file doesn't exist, or it can't be read.
+
+It will be 'skipped' if it's something that ack should avoid searching,
+even under -a.
+
+=cut
+
+use constant TEXT => 'text';
+
+sub filetypes {
+ my $filename = shift;
+
+ my $basename = $filename;
+ $basename =~ s{.*[$dir_sep_chars]}{};
+
+ return 'skipped' unless is_searchable( $basename );
+
+ my $lc_basename = lc $basename;
+ return ('make',TEXT) if $lc_basename eq 'makefile' || $lc_basename eq 'gnumakefile';
+ return ('rake','ruby',TEXT) if $lc_basename eq 'rakefile';
+
+ # If there's an extension, look it up
+ if ( $filename =~ m{\.([^\.$dir_sep_chars]+)$}o ) {
+ my $ref = $types{lc $1};
+ return (@{$ref},TEXT) if $ref;
+ }
+
+ # At this point, we can't tell from just the name. Now we have to
+ # open it and look inside.
+
+ return unless -e $filename;
+ # From Elliot Shank:
+ # I can't see any reason that -r would fail on these-- the ACLs look
+ # fine, and no program has any of them open, so the busted Windows
+ # file locking model isn't getting in there. If I comment the if
+ # statement out, everything works fine
+ # So, for cygwin, don't bother trying to check for readability.
+ if ( !$is_cygwin ) {
+ if ( !-r $filename ) {
+ App::Ack::warn( "$filename: Permission denied" );
+ return;
+ }
+ }
+
+ return 'binary' if -B $filename;
+
+ # If there's no extension, or we don't recognize it, check the shebang line
+ my $fh;
+ if ( !open( $fh, '<', $filename ) ) {
+ App::Ack::warn( "$filename: $!" );
+ return;
+ }
+ my $header = <$fh>;
+ close $fh;
+
+ if ( $header =~ /^#!/ ) {
+ return ($1,TEXT) if $header =~ /\b(ruby|lua|p(?:erl|hp|ython))-?(\d[\d.]*)?\b/;
+ return ('shell',TEXT) if $header =~ /\b(?:ba|t?c|k|z)?sh\b/;
+ }
+ else {
+ return ('xml',TEXT) if $header =~ /\Q<?xml /i;
+ }
+
+ return (TEXT);
+}
+
+=head2 is_searchable( $filename )
+
+Returns true if the filename is one that we can search, and false
+if it's one that we should skip like a coredump or a backup file.
+
+Recognized files:
+ /~$/ - Unix backup files
+ /#.+#$/ - Emacs swap files
+ /[._].*\.swp$/ - Vi(m) swap files
+ /core\.\d+$/ - core dumps
+ /[.-]min\.js$/ - Minified javascript files
+
+Note that I<$filename> must be just a file, not a full path.
+
+=cut
+
+sub is_searchable {
+ my $filename = shift;
+
+ # If these are updated, update the --help message
+ return if $filename =~ /[.]bak$/;
+ return if $filename =~ /~$/;
+ return if $filename =~ m{^#.*#$}o;
+ return if $filename =~ m{^core\.\d+$}o;
+ return if $filename =~ m{[._].*\.swp$}o;
+ return if $filename =~ /[.-]min\.js$/;
+
+ return 1;
+}
+
+=head2 build_regex( $str, \%opts )
+
+Returns a regex object based on a string and command-line options.
+
+Dies when the regex $str is undefinied (i.e. not given on command line).
+
+=cut
+
+sub build_regex {
+ my $str = shift;
+ my $opt = shift;
+
+ defined $str or App::Ack::die( 'No regular expression found.' );
+
+ $str = quotemeta( $str ) if $opt->{Q};
+ if ( $opt->{w} ) {
+ $str = "\\b$str" if $str =~ /^\w/;
+ $str = "$str\\b" if $str =~ /\w$/;
+ }
+
+ my $regex_is_lc = $str eq lc $str;
+ if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {
+ $str = "(?i)$str";
+ }
+
+ return $str;
+}
+
+=head2 check_regex( $regex_str )
+
+Checks that the $regex_str can be compiled into a perl regular expression.
+Dies with the error message if this is not the case.
+
+No return value.
+
+=cut
+
+sub check_regex {
+ my $regex = shift;
+
+ return unless defined $regex;
+
+ eval { qr/$regex/ };
+ if ($@) {
+ (my $error = $@) =~ s/ at \S+ line \d+.*//;
+ chomp($error);
+ App::Ack::die( "Invalid regex '$regex':\n $error" );
+ }
+
+ return;
+}
+
+
+
+=head2 warn( @_ )
+
+Put out an ack-specific warning.
+
+=cut
+
+sub warn { ## no critic (ProhibitBuiltinHomonyms)
+ return CORE::warn( _my_program(), ': ', @_, "\n" );
+}
+
+=head2 die( @_ )
+
+Die in an ack-specific way.
+
+=cut
+
+sub die { ## no critic (ProhibitBuiltinHomonyms)
+ return CORE::die( _my_program(), ': ', @_, "\n" );
+}
+
+sub _my_program {
+ require File::Basename;
+ return File::Basename::basename( $0 );
+}
+
+
+=head2 filetypes_supported()
+
+Returns a list of all the types that we can detect.
+
+=cut
+
+sub filetypes_supported {
+ return keys %mappings;
+}
+
+sub _get_thpppt {
+ my $y = q{_ /|,\\'!.x',=(www)=, U };
+ $y =~ tr/,x!w/\nOo_/;
+ return $y;
+}
+
+sub _thpppt {
+ my $y = _get_thpppt();
+ App::Ack::print( "$y ack $_[0]!\n" );
+ exit 0;
+}
+
+sub _bar {
+ my $x;
+ $x = <<'_BAR';
+ 6?!I'7!I"?%+!
+ 3~!I#7#I"7#I!?!+!="+"="+!:!
+ 2?#I!7!I!?#I!7!I"+"=%+"=#
+ 1?"+!?*+!=#~"=!+#?"="+!
+ 0?"+!?"I"?&+!="~!=!~"=!+%="+"
+ /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!
+ .?%I"?%+%='?!=#~$="
+ ,,!?%I"?(+$=$~!=#:"~$:!~!
+ ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!
+ +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"
+ *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!
+ *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"
+ )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!
+ (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!
+ (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!
+ %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"
+ $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/
+ $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!
+ $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/
+ %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!
+ %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!
+ %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!
+ %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!
+ &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!
+ &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!
+ &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!
+ &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!
+ &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"
+ &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!
+ %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&
+ %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)
+ $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!
+ $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"
+ "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!
+ !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!
+ !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*
+7+:!,!~#,"=!7'I!?#I"7/+!7+
+77I!+!7!?!7!I"71+!7,
+_BAR
+
+ $x =~ s/(.)(.)/$1x(ord($2)-32)/eg;
+ App::Ack::print( $x );
+ exit 0;
+}
+
+sub _key {
+ my $str = lc shift;
+ $str =~ s/[^a-z]//g;
+
+ return $str;
+}
+
+=head2 show_help()
+
+Dumps the help page to the user.
+
+=cut
+
+sub show_help {
+ my $help_arg = shift || 0;
+
+ return show_help_types() if $help_arg =~ /^types?/;
+
+ my $ignore_dirs = _listify( sort { _key($a) cmp _key($b) } keys %ignore_dirs );
+
+ App::Ack::print( <<"END_OF_HELP" );
+Usage: ack [OPTION]... PATTERN [FILE]
+
+Search for PATTERN in each source file in the tree from cwd on down.
+If [FILES] is specified, then only those files/directories are checked.
+ack may also search STDIN, but only if no FILE are specified, or if
+one of FILES is "-".
+
+Default switches may be specified in ACK_OPTIONS environment variable or
+an .ackrc file. If you want no dependency on the environment, turn it
+off with --noenv.
+
+Example: ack -i select
+
+Searching:
+ -i, --ignore-case Ignore case distinctions in PATTERN
+ --[no]smart-case Ignore case distinctions in PATTERN,
+ only if PATTERN contains no upper case
+ Ignored if -i is specified
+ -v, --invert-match Invert match: select non-matching lines
+ -w, --word-regexp Force PATTERN to match only whole words
+ -Q, --literal Quote all metacharacters; PATTERN is literal
+
+Search output:
+ --line=NUM Only print line(s) NUM of each file
+ -l, --files-with-matches
+ Only print filenames containing matches
+ -L, --files-without-matches
+ Only print filenames with no matches
+ -o Show only the part of a line matching PATTERN
+ (turns off text highlighting)
+ --passthru Print all lines, whether matching or not
+ --output=expr Output the evaluation of expr for each line
+ (turns off text highlighting)
+ --match PATTERN Specify PATTERN explicitly.
+ -m, --max-count=NUM Stop searching in each file after NUM matches
+ -1 Stop searching after one match of any kind
+ -H, --with-filename Print the filename for each match
+ -h, --no-filename Suppress the prefixing filename on output
+ -c, --count Show number of lines matching per file
+ --column Show the column number of the first match
+
+ -A NUM, --after-context=NUM
+ Print NUM lines of trailing context after matching
+ lines.
+ -B NUM, --before-context=NUM
+ Print NUM lines of leading context before matching
+ lines.
+ -C [NUM], --context[=NUM]
+ Print NUM lines (default 2) of output context.
+
+ --print0 Print null byte as separator between filenames,
+ only works with -f, -g, -l, -L or -c.
+
+File presentation:
+ --pager=COMMAND Pipes all ack output through COMMAND. For example,
+ --pager="less -R". Ignored if output is redirected.
+ --nopager Do not send output through a pager. Cancels any
+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.
+ --[no]heading Print a filename heading above each file's results.
+ (default: on when used interactively)
+ --[no]break Print a break between results from different files.
+ (default: on when used interactively)
+ --group Same as --heading --break
+ --nogroup Same as --noheading --nobreak
+ --[no]color Highlight the matching text (default: on unless
+ output is redirected, or on Windows)
+ --[no]colour Same as --[no]color
+ --color-filename=COLOR
+ --color-match=COLOR
+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.
+ --flush Flush output immediately, even when ack is used
+ non-interactively (when output goes to a pipe or
+ file).
+
+File finding:
+ -f Only print the files found, without searching.
+ The PATTERN must not be specified.
+ -g REGEX Same as -f, but only print files matching REGEX.
+ --sort-files Sort the found files lexically.
+ --invert-file-match Print/search handle files that do not match -g/-G.
+ --show-types Show which types each file has.
+
+File inclusion/exclusion:
+ -a, --all-types All file types searched;
+ Ignores CVS, .svn and other ignored directories
+ -u, --unrestricted All files and directories searched
+ --[no]ignore-dir=name Add/Remove directory from the list of ignored dirs
+ -r, -R, --recurse Recurse into subdirectories (ack's default behavior)
+ -n, --no-recurse No descending into subdirectories
+ -G REGEX Only search files that match REGEX
+
+ --perl Include only Perl files.
+ --type=perl Include only Perl files.
+ --noperl Exclude Perl files.
+ --type=noperl Exclude Perl files.
+ See "ack --help type" for supported filetypes.
+
+ --type-set TYPE=.EXTENSION[,.EXT2[,...]]
+ Files with the given EXTENSION(s) are recognized as
+ being of type TYPE. This replaces an existing
+ definition for type TYPE.
+ --type-add TYPE=.EXTENSION[,.EXT2[,...]]
+ Files with the given EXTENSION(s) are recognized as
+ being of (the existing) type TYPE
+
+ --[no]follow Follow symlinks. Default is off.
+
+ Directories ignored by default:
+ $ignore_dirs
+
+ Files not checked for type:
+ /~\$/ - Unix backup files
+ /#.+#\$/ - Emacs swap files
+ /[._].*\\.swp\$/ - Vi(m) swap files
+ /core\\.\\d+\$/ - core dumps
+ /[.-]min\\.js\$/ - Minified javascript files
+
+Miscellaneous:
+ --noenv Ignore environment variables and ~/.ackrc
+ --help This help
+ --man Man page
+ --version Display version & copyright
+ --thpppt Bill the Cat
+ --bar The warning admiral
+
+Exit status is 0 if match, 1 if no match.
+
+This is version $VERSION of ack.
+END_OF_HELP
+
+ return;
+ }
+
+
+=head2 show_help_types()
+
+Display the filetypes help subpage.
+
+=cut
+
+sub show_help_types {
+ App::Ack::print( <<'END_OF_HELP' );
+Usage: ack [OPTION]... PATTERN [FILES]
+
+The following is the list of filetypes supported by ack. You can
+specify a file type with the --type=TYPE format, or the --TYPE
+format. For example, both --type=perl and --perl work.
+
+Note that some extensions may appear in multiple types. For example,
+.pod files are both Perl and Parrot.
+
+END_OF_HELP
+
+ my @types = filetypes_supported();
+ my $maxlen = 0;
+ for ( @types ) {
+ $maxlen = length if $maxlen < length;
+ }
+ for my $type ( sort @types ) {
+ next if $type =~ /^-/; # Stuff to not show
+ my $ext_list = $mappings{$type};
+
+ if ( ref $ext_list ) {
+ $ext_list = join( ' ', map { ".$_" } @{$ext_list} );
+ }
+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
+ }
+
+ return;
+}
+
+sub _listify {
+ my @whats = @_;
+
+ return '' if !@whats;
+
+ my $end = pop @whats;
+ my $str = @whats ? join( ', ', @whats ) . " and $end" : $end;
+
+ no warnings 'once';
+ require Text::Wrap;
+ $Text::Wrap::columns = 75;
+ return Text::Wrap::wrap( '', ' ', $str );
+}
+
+=head2 get_version_statement
+
+Returns the version information for ack.
+
+=cut
+
+sub get_version_statement {
+ require Config;
+
+ my $copyright = get_copyright();
+ my $this_perl = $Config::Config{perlpath};
+ if ($^O ne 'VMS') {
+ my $ext = $Config::Config{_exe};
+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;
+ }
+ my $ver = sprintf( '%vd', $^V );
+
+ return <<"END_OF_VERSION";
+ack $VERSION
+Running under Perl $ver at $this_perl
+
+$copyright
+
+This program is free software. You may modify or distribute it
+under the terms of the Artistic License v2.0.
+END_OF_VERSION
+}
+
+=head2 print_version_statement
+
+Prints the version information for ack.
+
+=cut
+
+sub print_version_statement {
+ App::Ack::print( get_version_statement() );
+
+ return;
+}
+
+=head2 get_copyright
+
+Return the copyright for ack.
+
+=cut
+
+sub get_copyright {
+ return $COPYRIGHT;
+}
+
+=head2 load_colors
+
+Set default colors, load Term::ANSIColor
+
+=cut
+
+sub load_colors {
+ eval 'use Term::ANSIColor ()';
+
+ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';
+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';
+
+ return;
+}
+
+=head2 is_interesting
+
+File type filter, filtering based on the wanted file types
+
+=cut
+
+sub is_interesting {
+ return if /^\./;
+
+ my $include;
+
+ for my $type ( filetypes( $File::Next::name ) ) {
+ if ( defined $type_wanted{$type} ) {
+ if ( $type_wanted{$type} ) {
+ $include = 1;
+ }
+ else {
+ return;
+ }
+ }
+ }
+
+ return $include;
+}
+
+
+
+# print subs added in order to make it easy for a third party
+# module (such as App::Wack) to redefine the display methods
+# and show the results in a different way.
+sub print { print {$fh} @_ }
+sub print_first_filename { App::Ack::print( $_[0], "\n" ) }
+sub print_blank_line { App::Ack::print( "\n" ) }
+sub print_separator { App::Ack::print( "--\n" ) }
+sub print_filename { App::Ack::print( $_[0], $_[1] ) }
+sub print_line_no { App::Ack::print( $_[0], $_[1] ) }
+sub print_column_no { App::Ack::print( $_[0], $_[1] ) }
+sub print_count {
+ my $filename = shift;
+ my $nmatches = shift;
+ my $ors = shift;
+ my $count = shift;
+ my $show_filename = shift;
+
+ if ($show_filename) {
+ App::Ack::print( $filename );
+ App::Ack::print( ':', $nmatches ) if $count;
+ }
+ else {
+ App::Ack::print( $nmatches ) if $count;
+ }
+ App::Ack::print( $ors );
+}
+
+sub print_count0 {
+ my $filename = shift;
+ my $ors = shift;
+ my $show_filename = shift;
+
+ if ($show_filename) {
+ App::Ack::print( $filename, ':0', $ors );
+ }
+ else {
+ App::Ack::print( '0', $ors );
+ }
+}
+
+
+=head2 search_resource( $resource, \%opt )
+
+Main search method.
+
+Assumes an open resource, and that the caller will close the resource.
+
+=cut
+
+{
+ my $filename;
+ my $regex;
+ my $display_filename;
+
+ my $keep_context;
+
+ my $last_output_line; # number of the last line that has been output
+ my $any_output; # has there been any output for the current file yet
+ my $context_overall_output_count; # has there been any output at all
+
+sub search_resource {
+ my $res = shift;
+ my $opt = shift;
+
+ $filename = $res->name();
+
+ my $v = $opt->{v};
+ my $passthru = $opt->{passthru};
+ my $max = $opt->{m};
+ my $nmatches = 0;
+
+ $display_filename = undef;
+
+ # for --line processing
+ my $has_lines = 0;
+ my @lines;
+ if ( defined $opt->{lines} ) {
+ $has_lines = 1;
+ @lines = ( @{$opt->{lines}}, -1 );
+ undef $regex; # Don't match when printing matching line
+ }
+ else {
+ $regex = qr/$opt->{regex}/;
+ }
+
+ # for context processing
+ $last_output_line = -1;
+ $any_output = 0;
+ my $before_context = $opt->{before_context};
+ my $after_context = $opt->{after_context};
+
+ $keep_context = ($before_context || $after_context) && !$passthru;
+
+ my @before;
+ my $before_starts_at_line;
+ my $after = 0; # number of lines still to print after a match
+
+ while ( $res->next_text ) {
+ # XXX Optimize away the case when there are no more @lines to find.
+ # XXX $has_lines, $passthru and $v never change. Optimize.
+ if ( $has_lines
+ ? $. != $lines[0] # $lines[0] should be a scalar
+ : $v ? m/$regex/ : !m/$regex/ ) {
+ if ( $passthru ) {
+ App::Ack::print( $_ );
+ next;
+ }
+
+ if ( $keep_context ) {
+ if ( $after ) {
+ print_match_or_context( $opt, 0, $., $-[0], $+[0], $_ );
+ $after--;
+ }
+ elsif ( $before_context ) {
+ if ( @before ) {
+ if ( @before >= $before_context ) {
+ shift @before;
+ ++$before_starts_at_line;
+ }
+ }
+ else {
+ $before_starts_at_line = $.;
+ }
+ push @before, $_;
+ }
+ last if $max && ( $nmatches >= $max ) && !$after;
+ }
+ next;
+ } # not a match
+
+ ++$nmatches;
+
+ # print an empty line as a divider before first line in each file (not before the first file)
+ if ( !$any_output && $opt->{show_filename} && $opt->{break} && defined( $context_overall_output_count ) ) {
+ App::Ack::print_blank_line();
+ }
+
+ shift @lines if $has_lines;
+
+ if ( $res->is_binary ) {
+ App::Ack::print( "Binary file $filename matches\n" );
+ last;
+ }
+ if ( $keep_context ) {
+ if ( @before ) {
+ print_match_or_context( $opt, 0, $before_starts_at_line, $-[0], $+[0], @before );
+ @before = ();
+ $before_starts_at_line = 0;
+ }
+ if ( $max && $nmatches > $max ) {
+ --$after;
+ }
+ else {
+ $after = $after_context;
+ }
+ }
+ print_match_or_context( $opt, 1, $., $-[0], $+[0], $_ );
+
+ last if $max && ( $nmatches >= $max ) && !$after;
+ } # while
+
+ return $nmatches;
+} # search_resource()
+
+
+=head2 print_match_or_context( $opt, $is_match, $starting_line_no, $match_start, $match_end, @lines )
+
+Prints out a matching line or a line of context around a match.
+
+=cut
+
+sub print_match_or_context {
+ my $opt = shift; # opts array
+ my $is_match = shift; # is there a match on the line?
+ my $line_no = shift;
+ my $match_start = shift;
+ my $match_end = shift;
+
+ my $color = $opt->{color};
+ my $heading = $opt->{heading};
+ my $show_filename = $opt->{show_filename};
+ my $show_column = $opt->{column};
+
+ if ( $show_filename ) {
+ if ( not defined $display_filename ) {
+ $display_filename =
+ $color
+ ? Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} )
+ : $filename;
+ if ( $heading && !$any_output ) {
+ App::Ack::print_first_filename($display_filename);
+ }
+ }
+ }
+
+ my $sep = $is_match ? ':' : '-';
+ my $output_func = $opt->{output};
+ for ( @_ ) {
+ if ( $keep_context && !$output_func ) {
+ if ( ( $last_output_line != $line_no - 1 ) &&
+ ( $any_output || ( !$heading && defined( $context_overall_output_count ) ) ) ) {
+ App::Ack::print_separator();
+ }
+ # to ensure separators between different files when --noheading
+
+ $last_output_line = $line_no;
+ }
+
+ if ( $show_filename ) {
+ App::Ack::print_filename($display_filename, $sep) if not $heading;
+ my $display_line_no =
+ $color
+ ? Term::ANSIColor::colored( $line_no, $ENV{ACK_COLOR_LINENO} )
+ : $line_no;
+ App::Ack::print_line_no($display_line_no, $sep);
+ }
+
+ if ( $output_func ) {
+ while ( /$regex/go ) {
+ App::Ack::print( $output_func->() . "\n" );
+ }
+ }
+ else {
+ if ( $color && $is_match && $regex &&
+ s/$regex/Term::ANSIColor::colored( substr($_, $-[0], $+[0] - $-[0]), $ENV{ACK_COLOR_MATCH} )/eg ) {
+ # At the end of the line reset the color and remove newline
+ s/[\r\n]*\z/\e[0m\e[K/;
+ }
+ else {
+ # remove any kind of newline at the end of the line
+ s/[\r\n]*\z//;
+ }
+ if ( $show_column ) {
+ App::Ack::print_column_no( $match_start+1, $sep );
+ }
+ App::Ack::print($_ . "\n");
+ }
+ $any_output = 1;
+ ++$context_overall_output_count;
+ ++$line_no;
+ }
+
+ return;
+} # print_match_or_context()
+
+} # scope around search_resource() and print_match_or_context()
+
+
+TOTAL_COUNT_SCOPE: {
+my $total_count;
+
+sub get_total_count {
+ return $total_count;
+}
+
+sub reset_total_count {
+ $total_count = 0;
+}
+
+=head2 search_and_list( $res, \%opt )
+
+Optimized version of searching for -l and --count, which do not
+show lines.
+
+=cut
+
+sub search_and_list {
+ my $res = shift;
+ my $opt = shift;
+
+ my $nmatches = 0;
+ my $count = $opt->{count};
+ my $ors = $opt->{print0} ? "\0" : "\n"; # output record separator
+ my $show_filename = $opt->{show_filename};
+
+ my $regex = qr/$opt->{regex}/;
+
+ if ( $opt->{v} ) {
+ while ( $res->next_text ) {
+ if ( /$regex/ ) {
+ return 0 unless $count;
+ }
+ else {
+ ++$nmatches;
+ }
+ }
+ }
+ else {
+ while ( $res->next_text ) {
+ if ( /$regex/ ) {
+ ++$nmatches;
+ last unless $count;
+ }
+ }
+ }
+
+ if ( $opt->{show_total} ) {
+ $total_count += $nmatches;
+ }
+ else {
+ if ( $nmatches ) {
+ App::Ack::print_count( $res->name, $nmatches, $ors, $count, $show_filename );
+ }
+ elsif ( $count && !$opt->{l} ) {
+ App::Ack::print_count0( $res->name, $ors, $show_filename );
+ }
+ }
+
+ return $nmatches ? 1 : 0;
+} # search_and_list()
+
+} # scope around $total_count
+
+
+=head2 filetypes_supported_set
+
+True/False - are the filetypes set?
+
+=cut
+
+sub filetypes_supported_set {
+ return grep { defined $type_wanted{$_} && ($type_wanted{$_} == 1) } filetypes_supported();
+}
+
+
+=head2 print_files( $iter, $one [, $regex, [, $ors ]] )
+
+Prints all the files returned by the iterator matching I<$regex>.
+
+If I<$one> is set, stop after the first.
+The output record separator I<$ors> defaults to C<"\n"> and defines, what to
+print after each filename.
+
+=cut
+
+sub print_files {
+ my $iter = shift;
+ my $opt = shift;
+
+ my $ors = $opt->{print0} ? "\0" : "\n";
+
+ my $nmatches = 0;
+ while ( defined ( my $file = $iter->() ) ) {
+ App::Ack::print $file, $opt->{show_types} ? " => " . join( ',', filetypes( $file ) ) : (), $ors;
+ $nmatches++;
+ last if $opt->{1};
+ }
+
+ return $nmatches;
+}
+
+=head2 print_files_with_matches( $iter, $opt )
+
+Prints the name of the files where a match was found.
+
+=cut
+
+sub print_files_with_matches {
+ my $iter = shift;
+ my $opt = shift;
+
+ # if we have -l and only 1 file given on command line (this means
+ # show_filename is set to 0), we want to see the filename nevertheless
+ $opt->{show_filename} = 1 if $opt->{l};
+
+ $opt->{show_filename} = 0 if $opt->{h};
+ $opt->{show_filename} = 1 if $opt->{H};
+
+ # abuse options to hand in the show_total parameter to search_and_list
+ $opt->{show_total} = $opt->{count} && !$opt->{show_filename};
+ reset_total_count();
+
+ my $nmatches = 0;
+ while ( defined ( my $filename = $iter->() ) ) {
+ my $repo = App::Ack::Repository::Basic->new( $filename );
+ my $res;
+ while ( $res = $repo->next_resource() ) {
+ $nmatches += search_and_list( $res, $opt );
+ $res->close();
+ last if $nmatches && $opt->{1};
+ }
+ $repo->close();
+ }
+
+ if ( $nmatches && $opt->{show_total} ) {
+ App::Ack::print_count('', get_total_count(), "\n", 1, 0 )
+ }
+
+ return $nmatches;
+}
+
+=head2 print_matches( $iter, $opt )
+
+Print matching lines.
+
+=cut
+
+sub print_matches {
+ my $iter = shift;
+ my $opt = shift;
+
+ $opt->{show_filename} = 0 if $opt->{h};
+ $opt->{show_filename} = 1 if $opt->{H};
+
+ my $nmatches = 0;
+ while ( defined ( my $filename = $iter->() ) ) {
+ my $repo;
+ my $tarballs_work = 0;
+ if ( $tarballs_work && $filename =~ /\.tar\.gz$/ ) {
+ App::Ack::die( 'Not working here yet' );
+ require App::Ack::Repository::Tar; # XXX Error checking
+ $repo = App::Ack::Repository::Tar->new( $filename );
+ }
+ else {
+ $repo = App::Ack::Repository::Basic->new( $filename );
+ }
+ $repo or next;
+
+ while ( my $res = $repo->next_resource() ) {
+ my $needs_line_scan;
+ if ( $opt->{regex} && !$opt->{passthru} ) {
+ $needs_line_scan = $res->needs_line_scan( $opt );
+ if ( $needs_line_scan ) {
+ $res->reset();
+ }
+ }
+ else {
+ $needs_line_scan = 1;
+ }
+ if ( $needs_line_scan ) {
+ $nmatches += search_resource( $res, $opt );
+ }
+ $res->close();
+ }
+ last if $nmatches && $opt->{1};
+ $repo->close();
+ }
+ return $nmatches;
+}
+
+=head2 filetype_setup()
+
+Minor housekeeping before we go matching files.
+
+=cut
+
+sub filetype_setup {
+ my $filetypes_supported_set = filetypes_supported_set();
+ # If anyone says --no-whatever, we assume all other types must be on.
+ if ( !$filetypes_supported_set ) {
+ for my $i ( keys %type_wanted ) {
+ $type_wanted{$i} = 1 unless ( defined( $type_wanted{$i} ) || $i eq 'binary' || $i eq 'text' || $i eq 'skipped' );
+ }
+ }
+ return;
+}
+
+=head2 expand_filenames( \@ARGV )
+
+Returns reference to list of expanded filename globs (Win32 only).
+
+=cut
+
+EXPAND_FILENAMES_SCOPE: {
+ my $filter;
+
+ sub expand_filenames {
+ my $argv = shift;
+
+ my $attr;
+ my @files;
+
+ foreach my $pattern ( @{$argv} ) {
+ my @results = bsd_glob( $pattern );
+
+ if (@results == 0) {
+ @results = $pattern; # Glob didn't match, pass it thru unchanged
+ }
+ elsif ( (@results > 1) or ($results[0] ne $pattern) ) {
+ if (not defined $filter) {
+ eval 'require Win32::File;';
+ if ($@) {
+ $filter = 0;
+ }
+ else {
+ $filter = Win32::File::HIDDEN()|Win32::File::SYSTEM();
+ }
+ } # end unless we've tried to load Win32::File
+ if ( $filter ) {
+ # Filter out hidden and system files:
+ @results = grep { not(Win32::File::GetAttributes($_, $attr) and $attr & $filter) } @results;
+ App::Ack::warn( "$pattern: Matched only hidden files" ) unless @results;
+ } # end if we can filter by file attributes
+ } # end elsif this pattern got expanded
+
+ push @files, @results;
+ } # end foreach pattern
+
+ return \@files;
+ } # end expand_filenames
+} # EXPAND_FILENAMES_SCOPE
+
+
+=head2 get_starting_points( \@ARGV, \%opt )
+
+Returns reference to list of starting directories and files.
+
+=cut
+
+sub get_starting_points {
+ my $argv = shift;
+ my $opt = shift;
+
+ my @what;
+
+ if ( @{$argv} ) {
+ @what = @{ $is_windows ? expand_filenames($argv) : $argv };
+ $_ = File::Next::reslash( $_ ) for @what;
+
+ # Show filenames unless we've specified one single file
+ $opt->{show_filename} = (@what > 1) || (!-f $what[0]);
+ }
+ else {
+ @what = '.'; # Assume current directory
+ $opt->{show_filename} = 1;
+ }
+
+ for my $start_point (@what) {
+ App::Ack::warn( "$start_point: No such file or directory" ) unless -e $start_point;
+ }
+ return \@what;
+}
+
+sub _match {
+ my ( $target, $expression, $invert_flag ) = @_;
+
+ if ( $invert_flag ) {
+ return $target !~ $expression;
+ }
+ else {
+ return $target =~ $expression;
+ }
+}
+
+=head2 get_iterator
+
+Return the File::Next file iterator
+
+=cut
+
+sub get_iterator {
+ my $what = shift;
+ my $opt = shift;
+
+ # Starting points are always searched, no matter what
+ my %starting_point = map { ($_ => 1) } @{$what};
+
+ my $g_regex = defined $opt->{G} ? qr/$opt->{G}/ : undef;
+ my $file_filter;
+
+ if ( $g_regex ) {
+ $file_filter
+ = $opt->{u} ? sub { _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) } # XXX Maybe this should be a 1, no?
+ : $opt->{all} ? sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_searchable( $_ ) ) }
+ : sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_interesting( @ _) ) }
+ ;
+ }
+ else {
+ $file_filter
+ = $opt->{u} ? sub {1}
+ : $opt->{all} ? sub { $starting_point{ $File::Next::name } || is_searchable( $_ ) }
+ : sub { $starting_point{ $File::Next::name } || is_interesting( @_ ) }
+ ;
+ }
+
+ my $descend_filter
+ = $opt->{n} ? sub {0}
+ : $opt->{u} ? sub {1}
+ : \&ignoredir_filter;
+
+ my $iter =
+ File::Next::files( {
+ file_filter => $file_filter,
+ descend_filter => $descend_filter,
+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },
+ sort_files => $opt->{sort_files},
+ follow_symlinks => $opt->{follow},
+ }, @{$what} );
+ return $iter;
+}
+
+
+sub set_up_pager {
+ my $command = shift;
+
+ return if App::Ack::output_to_pipe();
+
+ my $pager;
+ if ( not open( $pager, '|-', $command ) ) {
+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
+ }
+ $fh = $pager;
+
+ return;
+}
+
+=head2 input_from_pipe()
+
+Returns true if ack's input is coming from a pipe.
+
+=cut
+
+sub input_from_pipe {
+ return $input_from_pipe;
+}
+
+
+=head2 output_to_pipe()
+
+Returns true if ack's output is going to a pipe.
+
+=cut
+
+sub output_to_pipe {
+ return $output_to_pipe;
+}
+
+=head2 exit_from_ack
+
+Exit from the application with the correct exit code.
+
+Returns with 0 if a match was found, otherwise with 1. The number of matches is
+handed in as the only argument.
+
+=cut
+
+sub exit_from_ack {
+ my $nmatches = shift;
+
+ my $rc = $nmatches ? 0 : 1;
+ exit $rc;
+}
+
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2005-2011 Andy Lester.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the Artistic License v2.0.
+
+=cut
+
+1; # End of App::Ack
diff --git a/samples/perl/Request.pm b/samples/perl/Request.pm
new file mode 100644
index 0000000..23d2d3b
--- /dev/null
+++ b/samples/perl/Request.pm
@@ -0,0 +1,695 @@
+package Plack::Request;
+use strict;
+use warnings;
+use 5.008_001;
+our $VERSION = '0.9988';
+$VERSION = eval $VERSION;
+
+use HTTP::Headers;
+use Carp ();
+use Hash::MultiValue;
+use HTTP::Body;
+
+use Plack::Request::Upload;
+use Plack::TempBuffer;
+use URI;
+use URI::Escape ();
+
+sub _deprecated {
+ my $alt = shift;
+ my $method = (caller(1))[3];
+ Carp::carp("$method is deprecated. Use '$alt' instead.");
+}
+
+sub new {
+ my($class, $env) = @_;
+ Carp::croak(q{$env is required})
+ unless defined $env && ref($env) eq 'HASH';
+
+ bless { env => $env }, $class;
+}
+
+sub env { $_[0]->{env} }
+
+sub address { $_[0]->env->{REMOTE_ADDR} }
+sub remote_host { $_[0]->env->{REMOTE_HOST} }
+sub protocol { $_[0]->env->{SERVER_PROTOCOL} }
+sub method { $_[0]->env->{REQUEST_METHOD} }
+sub port { $_[0]->env->{SERVER_PORT} }
+sub user { $_[0]->env->{REMOTE_USER} }
+sub request_uri { $_[0]->env->{REQUEST_URI} }
+sub path_info { $_[0]->env->{PATH_INFO} }
+sub path { $_[0]->env->{PATH_INFO} || '/' }
+sub script_name { $_[0]->env->{SCRIPT_NAME} }
+sub scheme { $_[0]->env->{'psgi.url_scheme'} }
+sub secure { $_[0]->scheme eq 'https' }
+sub body { $_[0]->env->{'psgi.input'} }
+sub input { $_[0]->env->{'psgi.input'} }
+
+sub content_length { $_[0]->env->{CONTENT_LENGTH} }
+sub content_type { $_[0]->env->{CONTENT_TYPE} }
+
+sub session { $_[0]->env->{'psgix.session'} }
+sub session_options { $_[0]->env->{'psgix.session.options'} }
+sub logger { $_[0]->env->{'psgix.logger'} }
+
+sub cookies {
+ my $self = shift;
+
+ return {} unless $self->env->{HTTP_COOKIE};
+
+ # HTTP_COOKIE hasn't changed: reuse the parsed cookie
+ if ( $self->env->{'plack.cookie.parsed'}
+ && $self->env->{'plack.cookie.string'} eq $self->env->{HTTP_COOKIE}) {
+ return $self->env->{'plack.cookie.parsed'};
+ }
+
+ $self->env->{'plack.cookie.string'} = $self->env->{HTTP_COOKIE};
+
+ my %results;
+ my @pairs = grep /=/, split "[;,] ?", $self->env->{'plack.cookie.string'};
+ for my $pair ( @pairs ) {
+ # trim leading trailing whitespace
+ $pair =~ s/^\s+//; $pair =~ s/\s+$//;
+
+ my ($key, $value) = map URI::Escape::uri_unescape($_), split( "=", $pair, 2 );
+
+ # Take the first one like CGI.pm or rack do
+ $results{$key} = $value unless exists $results{$key};
+ }
+
+ $self->env->{'plack.cookie.parsed'} = \%results;
+}
+
+sub query_parameters {
+ my $self = shift;
+ $self->env->{'plack.request.query'} ||= Hash::MultiValue->new($self->uri->query_form);
+}
+
+sub content {
+ my $self = shift;
+
+ unless ($self->env->{'psgix.input.buffered'}) {
+ $self->_parse_request_body;
+ }
+
+ my $fh = $self->input or return '';
+ my $cl = $self->env->{CONTENT_LENGTH} or return'';
+ $fh->read(my($content), $cl, 0);
+ $fh->seek(0, 0);
+
+ return $content;
+}
+
+sub raw_body { $_[0]->content }
+
+# XXX you can mutate headers with ->headers but it's not written through to the env
+
+sub headers {
+ my $self = shift;
+ if (!defined $self->{headers}) {
+ my $env = $self->env;
+ $self->{headers} = HTTP::Headers->new(
+ map {
+ (my $field = $_) =~ s/^HTTPS?_//;
+ ( $field => $env->{$_} );
+ }
+ grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env
+ );
+ }
+ $self->{headers};
+}
+
+sub content_encoding { shift->headers->content_encoding(@_) }
+sub header { shift->headers->header(@_) }
+sub referer { shift->headers->referer(@_) }
+sub user_agent { shift->headers->user_agent(@_) }
+
+sub body_parameters {
+ my $self = shift;
+
+ unless ($self->env->{'plack.request.body'}) {
+ $self->_parse_request_body;
+ }
+
+ return $self->env->{'plack.request.body'};
+}
+
+# contains body + query
+sub parameters {
+ my $self = shift;
+
+ $self->env->{'plack.request.merged'} ||= do {
+ my $query = $self->query_parameters;
+ my $body = $self->body_parameters;
+ Hash::MultiValue->new($query->flatten, $body->flatten);
+ };
+}
+
+sub uploads {
+ my $self = shift;
+
+ if ($self->env->{'plack.request.upload'}) {
+ return $self->env->{'plack.request.upload'};
+ }
+
+ $self->_parse_request_body;
+ return $self->env->{'plack.request.upload'};
+}
+
+sub hostname { _deprecated 'remote_host'; $_[0]->remote_host || $_[0]->address }
+sub url_scheme { _deprecated 'scheme'; $_[0]->scheme }
+sub params { _deprecated 'parameters'; shift->parameters(@_) }
+sub query_params { _deprecated 'query_parameters'; shift->query_parameters(@_) }
+sub body_params { _deprecated 'body_parameters'; shift->body_parameters(@_) }
+
+sub cookie {
+ my $self = shift;
+ _deprecated 'cookies';
+
+ return keys %{ $self->cookies } if @_ == 0;
+
+ my $name = shift;
+ return $self->cookies->{$name};
+}
+
+sub param {
+ my $self = shift;
+
+ return keys %{ $self->parameters } if @_ == 0;
+
+ my $key = shift;
+ return $self->parameters->{$key} unless wantarray;
+ return $self->parameters->get_all($key);
+}
+
+sub upload {
+ my $self = shift;
+
+ return keys %{ $self->uploads } if @_ == 0;
+
+ my $key = shift;
+ return $self->uploads->{$key} unless wantarray;
+ return $self->uploads->get_all($key);
+}
+
+sub raw_uri {
+ my $self = shift;
+ _deprecated 'base';
+
+ my $base = $self->base;
+ $base->path_query($self->env->{REQUEST_URI});
+
+ $base;
+}
+
+sub uri {
+ my $self = shift;
+
+ my $base = $self->_uri_base;
+
+ # We have to escape back PATH_INFO in case they include stuff like
+ # ? or # so that the URI parser won't be tricked. However we should
+ # preserve '/' since encoding them into %2f doesn't make sense.
+ # This means when a request like /foo%2fbar comes in, we recognize
+ # it as /foo/bar which is not ideal, but that's how the PSGI PATH_INFO
+ # spec goes and we can't do anything about it. See PSGI::FAQ for details.
+ # http://github.com/miyagawa/Plack/issues#issue/118
+ my $path_escape_class = '^A-Za-z0-9\-\._~/';
+
+ my $path = URI::Escape::uri_escape($self->env->{PATH_INFO} || '', $path_escape_class);
+ $path .= '?' . $self->env->{QUERY_STRING}
+ if defined $self->env->{QUERY_STRING} && $self->env->{QUERY_STRING} ne '';
+
+ $base =~ s!/$!! if $path =~ m!^/!;
+
+ return URI->new($base . $path)->canonical;
+}
+
+sub base {
+ my $self = shift;
+ URI->new($self->_uri_base)->canonical;
+}
+
+sub _uri_base {
+ my $self = shift;
+
+ my $env = $self->env;
+
+ my $uri = ($env->{'psgi.url_scheme'} || "http") .
+ "://" .
+ ($env->{HTTP_HOST} || (($env->{SERVER_NAME} || "") . ":" . ($env->{SERVER_PORT} || 80))) .
+ ($env->{SCRIPT_NAME} || '/');
+
+ return $uri;
+}
+
+sub new_response {
+ my $self = shift;
+ require Plack::Response;
+ Plack::Response->new(@_);
+}
+
+sub _parse_request_body {
+ my $self = shift;
+
+ my $ct = $self->env->{CONTENT_TYPE};
+ my $cl = $self->env->{CONTENT_LENGTH};
+ if (!$ct && !$cl) {
+ # No Content-Type nor Content-Length -> GET/HEAD
+ $self->env->{'plack.request.body'} = Hash::MultiValue->new;
+ $self->env->{'plack.request.upload'} = Hash::MultiValue->new;
+ return;
+ }
+
+ my $body = HTTP::Body->new($ct, $cl);
+
+ # HTTP::Body will create temporary files in case there was an
+ # upload. Those temporary files can be cleaned up by telling
+ # HTTP::Body to do so. It will run the cleanup when the request
+ # env is destroyed. That the object will not go out of scope by
+ # the end of this sub we will store a reference here.
+ $self->env->{'plack.request.http.body'} = $body;
+ $body->cleanup(1);
+
+ my $input = $self->input;
+
+ my $buffer;
+ if ($self->env->{'psgix.input.buffered'}) {
+ # Just in case if input is read by middleware/apps beforehand
+ $input->seek(0, 0);
+ } else {
+ $buffer = Plack::TempBuffer->new($cl);
+ }
+
+ my $spin = 0;
+ while ($cl) {
+ $input->read(my $chunk, $cl < 8192 ? $cl : 8192);
+ my $read = length $chunk;
+ $cl -= $read;
+ $body->add($chunk);
+ $buffer->print($chunk) if $buffer;
+
+ if ($read == 0 && $spin++ > 2000) {
+ Carp::croak "Bad Content-Length: maybe client disconnect? ($cl bytes remaining)";
+ }
+ }
+
+ if ($buffer) {
+ $self->env->{'psgix.input.buffered'} = 1;
+ $self->env->{'psgi.input'} = $buffer->rewind;
+ } else {
+ $input->seek(0, 0);
+ }
+
+ $self->env->{'plack.request.body'} = Hash::MultiValue->from_mixed($body->param);
+
+ my @uploads = Hash::MultiValue->from_mixed($body->upload)->flatten;
+ my @obj;
+ while (my($k, $v) = splice @uploads, 0, 2) {
+ push @obj, $k, $self->_make_upload($v);
+ }
+
+ $self->env->{'plack.request.upload'} = Hash::MultiValue->new(@obj);
+
+ 1;
+}
+
+sub _make_upload {
+ my($self, $upload) = @_;
+ my %copy = %$upload;
+ $copy{headers} = HTTP::Headers->new(%{$upload->{headers}});
+ Plack::Request::Upload->new(%copy);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Plack::Request - Portable HTTP request object from PSGI env hash
+
+=head1 SYNOPSIS
+
+ use Plack::Request;
+
+ my $app_or_middleware = sub {
+ my $env = shift; # PSGI env
+
+ my $req = Plack::Request->new($env);
+
+ my $path_info = $req->path_info;
+ my $query = $req->param('query');
+
+ my $res = $req->new_response(200); # new Plack::Response
+ $res->finalize;
+ };
+
+=head1 DESCRIPTION
+
+L<Plack::Request> provides a consistent API for request objects across
+web server environments.
+
+=head1 CAVEAT
+
+Note that this module is intended to be used by Plack middleware
+developers and web application framework developers rather than
+application developers (end users).
+
+Writing your web application directly using Plack::Request is
+certainly possible but not recommended: it's like doing so with
+mod_perl's Apache::Request: yet too low level.
+
+If you're writing a web application, not a framework, then you're
+encouraged to use one of the web application frameworks that support PSGI (L<http://plackperl.org/#frameworks>),
+or see modules like L<HTTP::Engine> to provide higher level
+Request and Response API on top of PSGI.
+
+=head1 METHODS
+
+Some of the methods defined in the earlier versions are deprecated in
+version 0.99. Take a look at L</"INCOMPATIBILITIES">.
+
+Unless otherwise noted, all methods and attributes are B<read-only>,
+and passing values to the method like an accessor doesn't work like
+you expect it to.
+
+=head2 new
+
+ Plack::Request->new( $env );
+
+Creates a new request object.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item env
+
+Returns the shared PSGI environment hash reference. This is a
+reference, so writing to this environment passes through during the
+whole PSGI request/response cycle.
+
+=item address
+
+Returns the IP address of the client (C<REMOTE_ADDR>).
+
+=item remote_host
+
+Returns the remote host (C<REMOTE_HOST>) of the client. It may be
+empty, in which case you have to get the IP address using C<address>
+method and resolve on your own.
+
+=item method
+
+Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
+
+=item protocol
+
+Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
+
+=item request_uri
+
+Returns the raw, undecoded request URI path. You probably do B<NOT>
+want to use this to dispatch requests.
+
+=item path_info
+
+Returns B<PATH_INFO> in the environment. Use this to get the local
+path for the requests.
+
+=item path
+
+Similar to C<path_info> but returns C</> in case it is empty. In other
+words, it returns the virtual path of the request URI after C<<
+$req->base >>. See L</"DISPATCHING"> for details.
+
+=item script_name
+
+Returns B<SCRIPT_NAME> in the environment. This is the absolute path
+where your application is hosted.
+
+=item scheme
+
+Returns the scheme (C<http> or C<https>) of the request.
+
+=item secure
+
+Returns true or false, indicating whether the connection is secure (https).
+
+=item body, input
+
+Returns C<psgi.input> handle.
+
+=item session
+
+Returns (optional) C<psgix.session> hash. When it exists, you can
+retrieve and store per-session data from and to this hash.
+
+=item session_options
+
+Returns (optional) C<psgix.session.options> hash.
+
+=item logger
+
+Returns (optional) C<psgix.logger> code reference. When it exists,
+your application is supposed to send the log message to this logger,
+using:
+
+ $req->logger->({ level => 'debug', message => "This is a debug message" });
+
+=item cookies
+
+Returns a reference to a hash containing the cookies. Values are
+strings that are sent by clients and are URI decoded.
+
+=item query_parameters
+
+Returns a reference to a hash containing query string (GET)
+parameters. This hash reference is L<Hash::MultiValue> object.
+
+=item body_parameters
+
+Returns a reference to a hash containing posted parameters in the
+request body (POST). As with C<query_parameters>, the hash
+reference is a L<Hash::MultiValue> object.
+
+=item parameters
+
+Returns a L<Hash::MultiValue> hash reference containing (merged) GET
+and POST parameters.
+
+=item content, raw_body
+
+Returns the request content in an undecoded byte string for POST requests.
+
+=item uri
+
+Returns an URI object for the current request. The URI is constructed
+using various environment values such as C<SCRIPT_NAME>, C<PATH_INFO>,
+C<QUERY_STRING>, C<HTTP_HOST>, C<SERVER_NAME> and C<SERVER_PORT>.
+
+Every time this method is called it returns a new, cloned URI object.
+
+=item base
+
+Returns an URI object for the base path of current request. This is
+like C<uri> but only contains up to C<SCRIPT_NAME> where your
+application is hosted at.
+
+Every time this method is called it returns a new, cloned URI object.
+
+=item user
+
+Returns C<REMOTE_USER> if it's set.
+
+=item headers
+
+Returns an L<HTTP::Headers> object containing the headers for the current request.
+
+=item uploads
+
+Returns a reference to a hash containing uploads. The hash reference
+is a L<Hash::MultiValue> object and values are L<Plack::Request::Upload>
+objects.
+
+=item content_encoding
+
+Shortcut to $req->headers->content_encoding.
+
+=item content_length
+
+Shortcut to $req->headers->content_length.
+
+=item content_type
+
+Shortcut to $req->headers->content_type.
+
+=item header
+
+Shortcut to $req->headers->header.
+
+=item referer
+
+Shortcut to $req->headers->referer.
+
+=item user_agent
+
+Shortcut to $req->headers->user_agent.
+
+=item param
+
+Returns GET and POST parameters with a CGI.pm-compatible param
+method. This is an alternative method for accessing parameters in
+$req->parameters. Unlike CGI.pm, it does I<not> allow
+setting or modifying query parameters.
+
+ $value = $req->param( 'foo' );
+ @values = $req->param( 'foo' );
+ @params = $req->param;
+
+=item upload
+
+A convenient method to access $req->uploads.
+
+ $upload = $req->upload('field');
+ @uploads = $req->upload('field');
+ @fields = $req->upload;
+
+ for my $upload ( $req->upload('field') ) {
+ print $upload->filename;
+ }
+
+=item new_response
+
+ my $res = $req->new_response;
+
+Creates a new L<Plack::Response> object. Handy to remove dependency on
+L<Plack::Response> in your code for easy subclassing and duck typing
+in web application frameworks, as well as overriding Response
+generation in middlewares.
+
+=back
+
+=head2 Hash::MultiValue parameters
+
+Parameters that can take one or multiple values (i.e. C<parameters>,
+C<query_parameters>, C<body_parameters> and C<uploads>) store the
+hash reference as a L<Hash::MultiValue> object. This means you can use
+the hash reference as a plain hash where values are B<always> scalars
+(B<NOT> array references), so you don't need to code ugly and unsafe
+C<< ref ... eq 'ARRAY' >> anymore.
+
+And if you explicitly want to get multiple values of the same key, you
+can call the C<get_all> method on it, such as:
+
+ my @foo = $req->query_parameters->get_all('foo');
+
+You can also call C<get_one> to always get one parameter independent
+of the context (unlike C<param>), and even call C<mixed> (with
+Hash::MultiValue 0.05 or later) to get the I<traditional> hash
+reference,
+
+ my $params = $req->parameters->mixed;
+
+where values are either a scalar or an array reference depending on
+input, so it might be useful if you already have the code to deal with
+that ugliness.
+
+=head2 PARSING POST BODY and MULTIPLE OBJECTS
+
+The methods to parse request body (C<content>, C<body_parameters> and
+C<uploads>) are carefully coded to save the parsed body in the
+environment hash as well as in the temporary buffer, so you can call
+them multiple times and create Plack::Request objects multiple times
+in a request and they should work safely, and won't parse request body
+more than twice for the efficiency.
+
+=head1 DISPATCHING
+
+If your application or framework wants to dispatch (or route) actions
+based on request paths, be sure to use C<< $req->path_info >> not C<<
+$req->uri->path >>.
+
+This is because C<path_info> gives you the virtual path of the request,
+regardless of how your application is mounted. If your application is
+hosted with mod_perl or CGI scripts, or even multiplexed with tools
+like L<Plack::App::URLMap>, request's C<path_info> always gives you
+the action path.
+
+Note that C<path_info> might give you an empty string, in which case
+you should assume that the path is C</>.
+
+You will also want to use C<< $req->base >> as a base prefix when
+building URLs in your templates or in redirections. It's a good idea
+for you to subclass Plack::Request and define methods such as:
+
+ sub uri_for {
+ my($self, $path, $args) = @_;
+ my $uri = $self->base;
+ $uri->path($uri->path . $path);
+ $uri->query_form(@$args) if $args;
+ $uri;
+ }
+
+So you can say:
+
+ my $link = $req->uri_for('/logout', [ signoff => 1 ]);
+
+and if C<< $req->base >> is C</app> you'll get the full URI for
+C</app/logout?signoff=1>.
+
+=head1 INCOMPATIBILITIES
+
+In version 0.99, many utility methods are removed or deprecated, and
+most methods are made read-only.
+
+The following methods are deprecated: C<hostname>, C<url_scheme>,
+C<params>, C<query_params>, C<body_params>, C<cookie> and
+C<raw_uri>. They will be removed in the next major release.
+
+All parameter-related methods such as C<parameters>,
+C<body_parameters>, C<query_parameters> and C<uploads> now contains
+L<Hash::MultiValue> objects, rather than I<scalar or an array
+reference depending on the user input> which is insecure. See
+L<Hash::MultiValue> for more about this change.
+
+C<< $req->path >> method had a bug, where the code and the document
+was mismatching. The document was suggesting it returns the sub
+request path after C<< $req->base >> but the code was always returning
+the absolute URI path. The code is now updated to be an alias of C<<
+$req->path_info >> but returns C</> in case it's empty. If you need
+the older behavior, just call C<< $req->uri->path >> instead.
+
+Cookie handling is simplified, and doesn't use L<CGI::Simple::Cookie>
+anymore, which means you B<CAN NOT> set array reference or hash
+reference as a cookie value and expect it be serialized. You're always
+required to set string value, and encoding or decoding them is totally
+up to your application or framework. Also, C<cookies> hash reference
+now returns I<strings> for the cookies rather than CGI::Simple::Cookie
+objects, which means you no longer have to write a wacky code such as:
+
+ $v = $req->cookie->{foo} ? $req->cookie->{foo}->value : undef;
+
+and instead, simply do:
+
+ $v = $req->cookie->{foo};
+
+=head1 AUTHORS
+
+Tatsuhiko Miyagawa
+
+Kazuhiro Osawa
+
+Tokuhiro Matsuno
+
+=head1 SEE ALSO
+
+L<Plack::Response> L<HTTP::Request>, L<Catalyst::Request>
+
+=head1 LICENSE
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/samples/perl/Response.pm b/samples/perl/Response.pm
new file mode 100644
index 0000000..c1b8a7d
--- /dev/null
+++ b/samples/perl/Response.pm
@@ -0,0 +1,309 @@
+package Plack::Response;
+use strict;
+use warnings;
+our $VERSION = '0.9988';
+$VERSION = eval $VERSION;
+
+use Plack::Util::Accessor qw(body status);
+use Carp ();
+use Scalar::Util ();
+use HTTP::Headers;
+use URI::Escape ();
+
+sub code { shift->status(@_) }
+sub content { shift->body(@_) }
+
+sub new {
+ my($class, $rc, $headers, $content) = @_;
+
+ my $self = bless {}, $class;
+ $self->status($rc) if defined $rc;
+ $self->headers($headers) if defined $headers;
+ $self->body($content) if defined $content;
+
+ $self;
+}
+
+sub headers {
+ my $self = shift;
+
+ if (@_) {
+ my $headers = shift;
+ if (ref $headers eq 'ARRAY') {
+ Carp::carp("Odd number of headers") if @$headers % 2 != 0;
+ $headers = HTTP::Headers->new(@$headers);
+ } elsif (ref $headers eq 'HASH') {
+ $headers = HTTP::Headers->new(%$headers);
+ }
+ return $self->{headers} = $headers;
+ } else {
+ return $self->{headers} ||= HTTP::Headers->new();
+ }
+}
+
+sub cookies {
+ my $self = shift;
+ if (@_) {
+ $self->{cookies} = shift;
+ } else {
+ return $self->{cookies} ||= +{ };
+ }
+}
+
+sub header { shift->headers->header(@_) } # shortcut
+
+sub content_length {
+ shift->headers->content_length(@_);
+}
+
+sub content_type {
+ shift->headers->content_type(@_);
+}
+
+sub content_encoding {
+ shift->headers->content_encoding(@_);
+}
+
+sub location {
+ my $self = shift;
+ return $self->headers->header('Location' => @_);
+}
+
+sub redirect {
+ my $self = shift;
+
+ if (@_) {
+ my $url = shift;
+ my $status = shift || 302;
+ $self->location($url);
+ $self->status($status);
+ }
+
+ return $self->location;
+}
+
+sub finalize {
+ my $self = shift;
+ Carp::croak "missing status" unless $self->status();
+
+ my $headers = $self->headers->clone;
+ $self->_finalize_cookies($headers);
+
+ return [
+ $self->status,
+ +[
+ map {
+ my $k = $_;
+ map {
+ my $v = $_;
+ $v =~ s/\015\012[\040|\011]+/chr(32)/ge; # replace LWS with a single SP
+ $v =~ s/\015|\012//g; # remove CR and LF since the char is invalid here
+
+ ( $k => $v )
+ } $headers->header($_);
+
+ } $headers->header_field_names
+ ],
+ $self->_body,
+ ];
+}
+
+sub _body {
+ my $self = shift;
+ my $body = $self->body;
+ $body = [] unless defined $body;
+ if (!ref $body or Scalar::Util::blessed($body) && overload::Method($body, q("")) && !$body->can('getline')) {
+ return [ $body ];
+ } else {
+ return $body;
+ }
+}
+
+sub _finalize_cookies {
+ my($self, $headers) = @_;
+
+ while (my($name, $val) = each %{$self->cookies}) {
+ my $cookie = $self->_bake_cookie($name, $val);
+ $headers->push_header('Set-Cookie' => $cookie);
+ }
+}
+
+sub _bake_cookie {
+ my($self, $name, $val) = @_;
+
+ return '' unless defined $val;
+ $val = { value => $val } unless ref $val eq 'HASH';
+
+ my @cookie = ( URI::Escape::uri_escape($name) . "=" . URI::Escape::uri_escape($val->{value}) );
+ push @cookie, "domain=" . $val->{domain} if $val->{domain};
+ push @cookie, "path=" . $val->{path} if $val->{path};
+ push @cookie, "expires=" . $self->_date($val->{expires}) if $val->{expires};
+ push @cookie, "secure" if $val->{secure};
+ push @cookie, "HttpOnly" if $val->{httponly};
+
+ return join "; ", @cookie;
+}
+
+my @MON = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
+my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat );
+
+sub _date {
+ my($self, $expires) = @_;
+
+ if ($expires =~ /^\d+$/) {
+ # all numbers -> epoch date
+ # (cookies use '-' as date separator, HTTP uses ' ')
+ my($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($expires);
+ $year += 1900;
+
+ return sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
+ $WDAY[$wday], $mday, $MON[$mon], $year, $hour, $min, $sec);
+
+ }
+
+ return $expires;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Plack::Response - Portable HTTP Response object for PSGI response
+
+=head1 SYNOPSIS
+
+ use Plack::Response;
+
+ sub psgi_handler {
+ my $env = shift;
+
+ my $res = Plack::Response->new(200);
+ $res->content_type('text/html');
+ $res->body("Hello World");
+
+ return $res->finalize;
+ }
+
+=head1 DESCRIPTION
+
+Plack::Response allows you a way to create PSGI response array ref through a simple API.
+
+=head1 METHODS
+
+=over 4
+
+=item new
+
+ $res = Plack::Response->new;
+ $res = Plack::Response->new($status);
+ $res = Plack::Response->new($status, $headers);
+ $res = Plack::Response->new($status, $headers, $body);
+
+Creates a new Plack::Response object.
+
+=item status
+
+ $res->status(200);
+ $status = $res->status;
+
+Sets and gets HTTP status code. C<code> is an alias.
+
+=item headers
+
+ $headers = $res->headers;
+ $res->headers([ 'Content-Type' => 'text/html' ]);
+ $res->headers({ 'Content-Type' => 'text/html' });
+ $res->headers( HTTP::Headers->new );
+
+Sets and gets HTTP headers of the response. Setter can take either an
+array ref, a hash ref or L<HTTP::Headers> object containing a list of
+headers.
+
+=item body
+
+ $res->body($body_str);
+ $res->body([ "Hello", "World" ]);
+ $res->body($io);
+
+Gets and sets HTTP response body. Setter can take either a string, an
+array ref, or an IO::Handle-like object. C<content> is an alias.
+
+Note that this method doesn't automatically set I<Content-Length> for
+the response. You have to set it manually if you want, with the
+C<content_length> method (see below).
+
+=item header
+
+ $res->header('X-Foo' => 'bar');
+ my $val = $res->header('X-Foo');
+
+Shortcut for C<< $res->headers->header >>.
+
+=item content_type, content_length, content_encoding
+
+ $res->content_type('text/plain');
+ $res->content_length(123);
+ $res->content_encoding('gzip');
+
+Shortcut for the equivalent get/set methods in C<< $res->headers >>.
+
+=item redirect
+
+ $res->redirect($url);
+ $res->redirect($url, 301);
+
+Sets redirect URL with an optional status code, which defaults to 302.
+
+Note that this method doesn't normalize the given URI string. Users of
+this module have to be responsible about properly encoding URI paths
+and parameters.
+
+=item location
+
+Gets and sets C<Location> header.
+
+Note that this method doesn't normalize the given URI string in the
+setter. See above in C<redirect> for details.
+
+=item cookies
+
+ $res->cookies->{foo} = 123;
+ $res->cookies->{foo} = { value => '123' };
+
+Returns a hash reference containing cookies to be set in the
+response. The keys of the hash are the cookies' names, and their
+corresponding values are a plain string (for C<value> with everything
+else defaults) or a hash reference that can contain keys such as
+C<value>, C<domain>, C<expires>, C<path>, C<httponly>, C<secure>.
+
+C<expires> can take a string or an integer (as an epoch time) and
+B<does not> convert string formats such as C<+3M>.
+
+ $res->cookies->{foo} = {
+ value => 'test',
+ path => "/",
+ domain => '.example.com',
+ expires => time + 24 * 60 * 60,
+ };
+
+=item finalize
+
+ $res->finalize;
+
+Returns the status code, headers, and body of this response as a PSGI
+response array reference.
+
+=back
+
+=head1 AUTHOR
+
+Tokuhiro Matsuno
+
+Tatsuhiko Miyagawa
+
+=head1 SEE ALSO
+
+L<Plack::Request>
+
+=cut
diff --git a/samples/perl/ack b/samples/perl/ack
new file mode 100755
index 0000000..6a9c66f
--- /dev/null
+++ b/samples/perl/ack
@@ -0,0 +1,2844 @@
+#!/usr/bin/env perl
+#
+# This file, ack, is generated code.
+# Please DO NOT EDIT or send patches for it.
+#
+# Please take a look at the source from
+# https://github.com/petdance/ack
+# and submit patches against the individual files
+# that build ack.
+#
+
+use warnings;
+use strict;
+
+our $VERSION = '1.96';
+# Check http://betterthangrep.com/ for updates
+
+# These are all our globals.
+
+
+MAIN: {
+ if ( $App::Ack::VERSION ne $main::VERSION ) {
+ App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" );
+ }
+
+ # Do preliminary arg checking;
+ my $env_is_usable = 1;
+ for ( @ARGV ) {
+ last if ( $_ eq '--' );
+
+ # Priorities! Get the --thpppt and --bar checking out of the way.
+ /^--th[pt]+t+$/ && App::Ack::_thpppt($_);
+ /^--bar$/ && App::Ack::_bar();
+
+ # See if we want to ignore the environment. (Don't tell Al Gore.)
+ if ( /^--(no)?env$/ ) {
+ $env_is_usable = defined $1 ? 0 : 1;
+ }
+ }
+ if ( $env_is_usable ) {
+ unshift( @ARGV, App::Ack::read_ackrc() );
+ }
+ else {
+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );
+ delete @ENV{@keys};
+ }
+ App::Ack::load_colors();
+
+ if ( exists $ENV{ACK_SWITCHES} ) {
+ App::Ack::warn( 'ACK_SWITCHES is no longer supported. Use ACK_OPTIONS.' );
+ }
+
+ if ( !@ARGV ) {
+ App::Ack::show_help();
+ exit 1;
+ }
+
+ main();
+}
+
+sub main {
+ my $opt = App::Ack::get_command_line_options();
+
+ $| = 1 if $opt->{flush}; # Unbuffer the output if flush mode
+
+ if ( App::Ack::input_from_pipe() ) {
+ # We're going into filter mode
+ for ( qw( f g l ) ) {
+ $opt->{$_} and App::Ack::die( "Can't use -$_ when acting as a filter." );
+ }
+ $opt->{show_filename} = 0;
+ $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt );
+ if ( my $nargs = @ARGV ) {
+ my $s = $nargs == 1 ? '' : 's';
+ App::Ack::warn( "Ignoring $nargs argument$s on the command-line while acting as a filter." );
+ }
+
+ my $res = App::Ack::Resource::Basic->new( '-' );
+ my $nmatches;
+ if ( $opt->{count} ) {
+ $nmatches = App::Ack::search_and_list( $res, $opt );
+ }
+ else {
+ # normal searching
+ $nmatches = App::Ack::search_resource( $res, $opt );
+ }
+ $res->close();
+ App::Ack::exit_from_ack( $nmatches );
+ }
+
+ my $file_matching = $opt->{f} || $opt->{lines};
+ if ( $file_matching ) {
+ App::Ack::die( "Can't specify both a regex ($opt->{regex}) and use one of --line, -f or -g." ) if $opt->{regex};
+ }
+ else {
+ $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt );
+ }
+
+ # check that all regexes do compile fine
+ App::Ack::check_regex( $_ ) for ( $opt->{regex}, $opt->{G} );
+
+ my $what = App::Ack::get_starting_points( \@ARGV, $opt );
+ my $iter = App::Ack::get_iterator( $what, $opt );
+ App::Ack::filetype_setup();
+
+ my $nmatches = 0;
+
+ App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};
+ if ( $opt->{f} ) {
+ $nmatches = App::Ack::print_files( $iter, $opt );
+ }
+ elsif ( $opt->{l} || $opt->{count} ) {
+ $nmatches = App::Ack::print_files_with_matches( $iter, $opt );
+ }
+ else {
+ $nmatches = App::Ack::print_matches( $iter, $opt );
+ }
+ close $App::Ack::fh;
+ App::Ack::exit_from_ack( $nmatches );
+}
+
+=head1 NAME
+
+ack - grep-like text finder
+
+=head1 SYNOPSIS
+
+ ack [options] PATTERN [FILE...]
+ ack -f [options] [DIRECTORY...]
+
+=head1 DESCRIPTION
+
+Ack is designed as a replacement for 99% of the uses of F<grep>.
+
+Ack searches the named input FILEs (or standard input if no files are
+named, or the file name - is given) for lines containing a match to the
+given PATTERN. By default, ack prints the matching lines.
+
+Ack can also list files that would be searched, without actually searching
+them, to let you take advantage of ack's file-type filtering capabilities.
+
+=head1 FILE SELECTION
+
+I<ack> is intelligent about the files it searches. It knows about
+certain file types, based on both the extension on the file and,
+in some cases, the contents of the file. These selections can be
+made with the B<--type> option.
+
+With no file selections, I<ack> only searches files of types that
+it recognizes. If you have a file called F<foo.wango>, and I<ack>
+doesn't know what a .wango file is, I<ack> won't search it.
+
+The B<-a> option tells I<ack> to select all files, regardless of
+type.
+
+Some files will never be selected by I<ack>, even with B<-a>,
+including:
+
+=over 4
+
+=item * Backup files: Files matching F<#*#> or ending with F<~>.
+
+=item * Coredumps: Files matching F<core.\d+>
+
+=back
+
+However, I<ack> always searches the files given on the command line,
+no matter what type. Furthermore, by specifying the B<-u> option all
+files will be searched.
+
+=head1 DIRECTORY SELECTION
+
+I<ack> descends through the directory tree of the starting directories
+specified. However, it will ignore the shadow directories used by
+many version control systems, and the build directories used by the
+Perl MakeMaker system. You may add or remove a directory from this
+list with the B<--[no]ignore-dir> option. The option may be repeated
+to add/remove multiple directories from the ignore list.
+
+For a complete list of directories that do not get searched, run
+F<ack --help>.
+
+=head1 WHEN TO USE GREP
+
+I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't
+throw I<grep> away, because there are times you'll still need it.
+
+E.g., searching through huge files looking for regexes that can be
+expressed with I<grep> syntax should be quicker with I<grep>.
+
+If your script or parent program uses I<grep> C<--quiet> or
+C<--silent> or needs exit 2 on IO error, use I<grep>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-a>, B<--all>
+
+Operate on all files, regardless of type (but still skip directories
+like F<blib>, F<CVS>, etc.)
+
+=item B<-A I<NUM>>, B<--after-context=I<NUM>>
+
+Print I<NUM> lines of trailing context after matching lines.
+
+=item B<-B I<NUM>>, B<--before-context=I<NUM>>
+
+Print I<NUM> lines of leading context before matching lines.
+
+=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
+
+Print I<NUM> lines (default 2) of context around matching lines.
+
+=item B<-c>, B<--count>
+
+Suppress normal output; instead print a count of matching lines for
+each input file. If B<-l> is in effect, it will only show the
+number of lines for each file that has lines matching. Without
+B<-l>, some line counts may be zeroes.
+
+If combined with B<-h> (B<--no-filename>) ack outputs only one total count.
+
+=item B<--color>, B<--nocolor>
+
+B<--color> highlights the matching text. B<--nocolor> supresses
+the color. This is on by default unless the output is redirected.
+
+On Windows, this option is off by default unless the
+L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>
+environment variable is used.
+
+=item B<--color-filename=I<color>>
+
+Sets the color to be used for filenames.
+
+=item B<--color-match=I<color>>
+
+Sets the color to be used for matches.
+
+=item B<--color-lineno=I<color>>
+
+Sets the color to be used for line numbers.
+
+=item B<--column>
+
+Show the column number of the first match. This is helpful for editors
+that can place your cursor at a given position.
+
+=item B<--env>, B<--noenv>
+
+B<--noenv> disables all environment processing. No F<.ackrc> is read
+and all environment variables are ignored. By default, F<ack> considers
+F<.ackrc> and settings in the environment.
+
+=item B<--flush>
+
+B<--flush> flushes output immediately. This is off by default
+unless ack is running interactively (when output goes to a pipe
+or file).
+
+=item B<-f>
+
+Only print the files that would be searched, without actually doing
+any searching. PATTERN must not be specified, or it will be taken as
+a path to search.
+
+=item B<--follow>, B<--nofollow>
+
+Follow or don't follow symlinks, other than whatever starting files
+or directories were specified on the command line.
+
+This is off by default.
+
+=item B<-G I<REGEX>>
+
+Only paths matching I<REGEX> are included in the search. The entire
+path and filename are matched against I<REGEX>, and I<REGEX> is a
+Perl regular expression, not a shell glob.
+
+The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.
+
+=item B<-g I<REGEX>>
+
+Print files where the relative path + filename matches I<REGEX>. This option is
+a convenience shortcut for B<-f> B<-G I<REGEX>>.
+
+The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.
+
+=item B<--group>, B<--nogroup>
+
+B<--group> groups matches by file name with. This is the default when
+used interactively.
+
+B<--nogroup> prints one result per line, like grep. This is the default
+when output is redirected.
+
+=item B<-H>, B<--with-filename>
+
+Print the filename for each match.
+
+=item B<-h>, B<--no-filename>
+
+Suppress the prefixing of filenames on output when multiple files are
+searched.
+
+=item B<--help>
+
+Print a short help statement.
+
+=item B<-i>, B<--ignore-case>
+
+Ignore case in the search strings.
+
+This applies only to the PATTERN, not to the regexes given for the B<-g>
+and B<-G> options.
+
+=item B<--[no]ignore-dir=I<DIRNAME>>
+
+Ignore directory (as CVS, .svn, etc are ignored). May be used multiple times
+to ignore multiple directories. For example, mason users may wish to include
+B<--ignore-dir=data>. The B<--noignore-dir> option allows users to search
+directories which would normally be ignored (perhaps to research the contents
+of F<.svn/props> directories).
+
+The I<DIRNAME> must always be a simple directory name. Nested directories like
+F<foo/bar> are NOT supported. You would need to specify B<--ignore-dir=foo> and
+then no files from any foo directory are taken into account by ack unless given
+explicitly on the command line.
+
+=item B<--line=I<NUM>>
+
+Only print line I<NUM> of each file. Multiple lines can be given with multiple
+B<--line> options or as a comma separated list (B<--line=3,5,7>). B<--line=4-7>
+also works. The lines are always output in ascending order, no matter the
+order given on the command line.
+
+=item B<-l>, B<--files-with-matches>
+
+Only print the filenames of matching files, instead of the matching text.
+
+=item B<-L>, B<--files-without-matches>
+
+Only print the filenames of files that do I<NOT> match. This is equivalent
+to specifying B<-l> and B<-v>.
+
+=item B<--match I<REGEX>>
+
+Specify the I<REGEX> explicitly. This is helpful if you don't want to put the
+regex as your first argument, e.g. when executing multiple searches over the
+same set of files.
+
+ # search for foo and bar in given files
+ ack file1 t/file* --match foo
+ ack file1 t/file* --match bar
+
+=item B<-m=I<NUM>>, B<--max-count=I<NUM>>
+
+Stop reading a file after I<NUM> matches.
+
+=item B<--man>
+
+Print this manual page.
+
+=item B<-n>, B<--no-recurse>
+
+No descending into subdirectories.
+
+=item B<-o>
+
+Show only the part of each line matching PATTERN (turns off text
+highlighting)
+
+=item B<--output=I<expr>>
+
+Output the evaluation of I<expr> for each line (turns off text
+highlighting)
+
+=item B<--pager=I<program>>
+
+Direct ack's output through I<program>. This can also be specified
+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.
+
+Using --pager does not suppress grouping and coloring like piping
+output on the command-line does.
+
+=item B<--passthru>
+
+Prints all lines, whether or not they match the expression. Highlighting
+will still work, though, so it can be used to highlight matches while
+still seeing the entire file, as in:
+
+ # Watch a log file, and highlight a certain IP address
+ $ tail -f ~/access.log | ack --passthru 123.45.67.89
+
+=item B<--print0>
+
+Only works in conjunction with -f, -g, -l or -c (filename output). The filenames
+are output separated with a null byte instead of the usual newline. This is
+helpful when dealing with filenames that contain whitespace, e.g.
+
+ # remove all files of type html
+ ack -f --html --print0 | xargs -0 rm -f
+
+=item B<-Q>, B<--literal>
+
+Quote all metacharacters in PATTERN, it is treated as a literal.
+
+This applies only to the PATTERN, not to the regexes given for the B<-g>
+and B<-G> options.
+
+=item B<-r>, B<-R>, B<--recurse>
+
+Recurse into sub-directories. This is the default and just here for
+compatibility with grep. You can also use it for turning B<--no-recurse> off.
+
+=item B<--smart-case>, B<--no-smart-case>
+
+Ignore case in the search strings if PATTERN contains no uppercase
+characters. This is similar to C<smartcase> in vim. This option is
+off by default.
+
+B<-i> always overrides this option.
+
+This applies only to the PATTERN, not to the regexes given for the
+B<-g> and B<-G> options.
+
+=item B<--sort-files>
+
+Sorts the found files lexically. Use this if you want your file
+listings to be deterministic between runs of I<ack>.
+
+=item B<--show-types>
+
+Outputs the filetypes that ack associates with each file.
+
+Works with B<-f> and B<-g> options.
+
+=item B<--thpppt>
+
+Display the all-important Bill The Cat logo. Note that the exact
+spelling of B<--thpppppt> is not important. It's checked against
+a regular expression.
+
+=item B<--bar>
+
+Check with the admiral for traps.
+
+=item B<--type=TYPE>, B<--type=noTYPE>
+
+Specify the types of files to include or exclude from a search.
+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can
+also be specified as B<--perl>, and B<--type=noperl> can be done
+as B<--noperl>.
+
+If a file is of both type "foo" and "bar", specifying --foo and
+--nobar will exclude the file, because an exclusion takes precedence
+over an inclusion.
+
+Type specifications can be repeated and are ORed together.
+
+See I<ack --help=types> for a list of valid types.
+
+=item B<--type-add I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>
+
+Files with the given EXTENSION(s) are recognized as being of (the
+existing) type TYPE. See also L</"Defining your own types">.
+
+
+=item B<--type-set I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>
+
+Files with the given EXTENSION(s) are recognized as being of type
+TYPE. This replaces an existing definition for type TYPE. See also
+L</"Defining your own types">.
+
+=item B<-u>, B<--unrestricted>
+
+All files and directories (including blib/, core.*, ...) are searched,
+nothing is skipped. When both B<-u> and B<--ignore-dir> are used, the
+B<--ignore-dir> option has no effect.
+
+=item B<-v>, B<--invert-match>
+
+Invert match: select non-matching lines
+
+This applies only to the PATTERN, not to the regexes given for the B<-g>
+and B<-G> options.
+
+=item B<--version>
+
+Display version and copyright information.
+
+=item B<-w>, B<--word-regexp>
+
+Force PATTERN to match only whole words. The PATTERN is wrapped with
+C<\b> metacharacters.
+
+This applies only to the PATTERN, not to the regexes given for the B<-g>
+and B<-G> options.
+
+=item B<-1>
+
+Stops after reporting first match of any kind. This is different
+from B<--max-count=1> or B<-m1>, where only one match per file is
+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does
+not.
+
+=back
+
+=head1 THE .ackrc FILE
+
+The F<.ackrc> file contains command-line options that are prepended
+to the command line before processing. Multiple options may live
+on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>
+might look like this:
+
+ # Always sort the files
+ --sort-files
+
+ # Always color, even if piping to a another program
+ --color
+
+ # Use "less -r" as my pager
+ --pager=less -r
+
+Note that arguments with spaces in them do not need to be quoted,
+as they are not interpreted by the shell. Basically, each I<line>
+in the F<.ackrc> file is interpreted as one element of C<@ARGV>.
+
+F<ack> looks in your home directory for the F<.ackrc>. You can
+specify another location with the F<ACKRC> variable, below.
+
+If B<--noenv> is specified on the command line, the F<.ackrc> file
+is ignored.
+
+=head1 Defining your own types
+
+ack allows you to define your own types in addition to the predefined
+types. This is done with command line options that are best put into
+an F<.ackrc> file - then you do not have to define your types over and
+over again. In the following examples the options will always be shown
+on one command line so that they can be easily copy & pasted.
+
+I<ack --perl foo> searches for foo in all perl files. I<ack --help=types>
+tells you, that perl files are files ending
+in .pl, .pm, .pod or .t. So what if you would like to include .xs
+files as well when searching for --perl files? I<ack --type-add perl=.xs --perl foo>
+does this for you. B<--type-add> appends
+additional extensions to an existing type.
+
+If you want to define a new type, or completely redefine an existing
+type, then use B<--type-set>. I<ack --type-set
+eiffel=.e,.eiffel> defines the type I<eiffel> to include files with
+the extensions .e or .eiffel. So to search for all eiffel files
+containing the word Bertrand use I<ack --type-set eiffel=.e,.eiffel --eiffel Bertrand>.
+As usual, you can also write B<--type=eiffel>
+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes
+all eiffel files from a search. Redefining also works: I<ack --type-set cc=.c,.h>
+and I<.xs> files no longer belong to the type I<cc>.
+
+When defining your own types in the F<.ackrc> file you have to use
+the following:
+
+ --type-set=eiffel=.e,.eiffel
+
+or writing on separate lines
+
+ --type-set
+ eiffel=.e,.eiffel
+
+The following does B<NOT> work in the F<.ackrc> file:
+
+ --type-set eiffel=.e,.eiffel
+
+
+In order to see all currently defined types, use I<--help types>, e.g.
+I<ack --type-set backup=.bak --type-add perl=.perl --help types>
+
+Restrictions:
+
+=over 4
+
+=item
+
+The types 'skipped', 'make', 'binary' and 'text' are considered "builtin" and
+cannot be altered.
+
+=item
+
+The shebang line recognition of the types 'perl', 'ruby', 'php', 'python',
+'shell' and 'xml' cannot be redefined by I<--type-set>, it is always
+active. However, the shebang line is only examined for files where the
+extension is not recognised. Therefore it is possible to say
+I<ack --type-set perl=.perl --type-set foo=.pl,.pm,.pod,.t --perl --nofoo> and
+only find your shiny new I<.perl> files (and all files with unrecognized extension
+and perl on the shebang line).
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+For commonly-used ack options, environment variables can make life much easier.
+These variables are ignored if B<--noenv> is specified on the command line.
+
+=over 4
+
+=item ACKRC
+
+Specifies the location of the F<.ackrc> file. If this file doesn't
+exist, F<ack> looks in the default location.
+
+=item ACK_OPTIONS
+
+This variable specifies default options to be placed in front of
+any explicit options on the command line.
+
+=item ACK_COLOR_FILENAME
+
+Specifies the color of the filename when it's printed in B<--group>
+mode. By default, it's "bold green".
+
+The recognized attributes are clear, reset, dark, bold, underline,
+underscore, blink, reverse, concealed black, red, green, yellow,
+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,
+on_magenta, on_cyan, and on_white. Case is not significant.
+Underline and underscore are equivalent, as are clear and reset.
+The color alone sets the foreground color, and on_color sets the
+background color.
+
+This option can also be set with B<--color-filename>.
+
+=item ACK_COLOR_MATCH
+
+Specifies the color of the matching text when printed in B<--color>
+mode. By default, it's "black on_yellow".
+
+This option can also be set with B<--color-match>.
+
+See B<ACK_COLOR_FILENAME> for the color specifications.
+
+=item ACK_COLOR_LINENO
+
+Specifies the color of the line number when printed in B<--color>
+mode. By default, it's "bold yellow".
+
+This option can also be set with B<--color-lineno>.
+
+See B<ACK_COLOR_FILENAME> for the color specifications.
+
+=item ACK_PAGER
+
+Specifies a pager program, such as C<more>, C<less> or C<most>, to which
+ack will send its output.
+
+Using C<ACK_PAGER> does not suppress grouping and coloring like
+piping output on the command-line does, except that on Windows
+ack will assume that C<ACK_PAGER> does not support color.
+
+C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.
+
+=item ACK_PAGER_COLOR
+
+Specifies a pager program that understands ANSI color sequences.
+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring
+like piping output on the command-line does.
+
+If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.
+
+=back
+
+=head1 ACK & OTHER TOOLS
+
+=head2 Vim integration
+
+F<ack> integrates easily with the Vim text editor. Set this in your
+F<.vimrc> to use F<ack> instead of F<grep>:
+
+ set grepprg=ack\ -a
+
+That examples uses C<-a> to search through all files, but you may
+use other default flags. Now you can search with F<ack> and easily
+step through the results in Vim:
+
+ :grep Dumper perllib
+
+=head2 Emacs integration
+
+Phil Jackson put together an F<ack.el> extension that "provides a
+simple compilation mode ... has the ability to guess what files you
+want to search for based on the major-mode."
+
+L<http://www.shellarchive.co.uk/content/emacs.html>
+
+=head2 TextMate integration
+
+Pedro Melo is a TextMate user who writes "I spend my day mostly
+inside TextMate, and the built-in find-in-project sucks with large
+projects. So I hacked a TextMate command that was using find +
+grep to use ack. The result is the Search in Project with ack, and
+you can find it here:
+L<http://www.simplicidade.org/notes/archives/2008/03/search_in_proje.html>"
+
+=head2 Shell and Return Code
+
+For greater compatibility with I<grep>, I<ack> in normal use returns
+shell return or exit code of 0 only if something is found and 1 if
+no match is found.
+
+(Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)
+
+The I<grep> code 2 for errors is not used.
+
+If C<-f> or C<-g> are specified, then 0 is returned if at least one
+file is found. If no files are found, then 1 is returned.
+
+=cut
+
+=head1 DEBUGGING ACK PROBLEMS
+
+If ack gives you output you're not expecting, start with a few simple steps.
+
+=head2 Use B<--noenv>
+
+Your environment variables and F<.ackrc> may be doing things you're
+not expecting, or forgotten you specified. Use B<--noenv> to ignore
+your environment and F<.ackrc>.
+
+=head2 Use B<-f> to see what files you're scanning
+
+The reason I created B<-f> in the first place was as a debugging
+tool. If ack is not finding matches you think it should find, run
+F<ack -f> to see what files are being checked.
+
+=head1 TIPS
+
+=head2 Use the F<.ackrc> file.
+
+The F<.ackrc> is the place to put all your options you use most of
+the time but don't want to remember. Put all your --type-add and
+--type-set definitions in it. If you like --smart-case, set it
+there, too. I also set --sort-files there.
+
+=head2 Use F<-f> for working with big codesets
+
+Ack does more than search files. C<ack -f --perl> will create a
+list of all the Perl files in a tree, ideal for sending into F<xargs>.
+For example:
+
+ # Change all "this" to "that" in all Perl files in a tree.
+ ack -f --perl | xargs perl -p -i -e's/this/that/g'
+
+or if you prefer:
+
+ perl -p -i -e's/this/that/g' $(ack -f --perl)
+
+=head2 Use F<-Q> when in doubt about metacharacters
+
+If you're searching for something with a regular expression
+metacharacter, most often a period in a filename or IP address, add
+the -Q to avoid false positives without all the backslashing. See
+the following example for more...
+
+=head2 Use ack to watch log files
+
+Here's one I used the other day to find trouble spots for a website
+visitor. The user had a problem loading F<troublesome.gif>, so I
+took the access log and scanned it with ack twice.
+
+ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif
+
+The first ack finds only the lines in the Apache log for the given
+IP. The second finds the match on my troublesome GIF, and shows
+the previous five lines from the log in each case.
+
+=head2 Share your knowledge
+
+Join the ack-users mailing list. Send me your tips and I may add
+them here.
+
+=head1 FAQ
+
+=head2 Why isn't ack finding a match in (some file)?
+
+Probably because it's of a type that ack doesn't recognize. ack's
+searching behavior is driven by filetype. B<If ack doesn't know
+what kind of file it is, ack ignores the file.>
+
+Use the C<-f> switch to see a list of files that ack will search
+for you.
+
+If you want ack to search files that it doesn't recognize, use the
+C<-a> switch.
+
+If you want ack to search every file, even ones that it always
+ignores like coredumps and backup files, use the C<-u> switch.
+
+=head2 Why does ack ignore unknown files by default?
+
+ack is designed by a programmer, for programmers, for searching
+large trees of code. Most codebases have a lot files in them which
+aren't source files (like compiled object files, source control
+metadata, etc), and grep wastes a lot of time searching through all
+of those as well and returning matches from those files.
+
+That's why ack's behavior of not searching things it doesn't recognize
+is one of its greatest strengths: the speed you get from only
+searching the things that you want to be looking at.
+
+=head2 Wouldn't it be great if F<ack> did search & replace?
+
+No, ack will always be read-only. Perl has a perfectly good way
+to do search & replace in files, using the C<-i>, C<-p> and C<-n>
+switches.
+
+You can certainly use ack to select your files to update. For
+example, to change all "foo" to "bar" in all PHP files, you can do
+this from the Unix shell:
+
+ $ perl -i -p -e's/foo/bar/g' $(ack -f --php)
+
+=head2 Can you make ack recognize F<.xyz> files?
+
+That's an enhancement. Please see the section in the manual about
+enhancements.
+
+=head2 There's already a program/package called ack.
+
+Yes, I know.
+
+=head2 Why is it called ack if it's called ack-grep?
+
+The name of the program is "ack". Some packagers have called it
+"ack-grep" when creating packages because there's already a package
+out there called "ack" that has nothing to do with this ack.
+
+I suggest you make a symlink named F<ack> that points to F<ack-grep>
+because one of the crucial benefits of ack is having a name that's
+so short and simple to type.
+
+To do that, run this with F<sudo> or as root:
+
+ ln -s /usr/bin/ack-grep /usr/bin/ack
+
+=head2 What does F<ack> mean?
+
+Nothing. I wanted a name that was easy to type and that you could
+pronounce as a single syllable.
+
+=head2 Can I do multi-line regexes?
+
+No, ack does not support regexes that match multiple lines. Doing
+so would require reading in the entire file at a time.
+
+If you want to see lines near your match, use the C<--A>, C<--B>
+and C<--C> switches for displaying context.
+
+=head1 AUTHOR
+
+Andy Lester, C<< <andy at petdance.com> >>
+
+=head1 BUGS
+
+Please report any bugs or feature requests to the issues list at
+Github: L<https://github.com/petdance/ack/issues>
+
+=head1 ENHANCEMENTS
+
+All enhancement requests MUST first be posted to the ack-users
+mailing list at L<http://groups.google.com/group/ack-users>. I
+will not consider a request without it first getting seen by other
+ack users. This includes requests for new filetypes.
+
+There is a list of enhancements I want to make to F<ack> in the ack
+issues list at Github: L<https://github.com/petdance/ack/issues>
+
+Patches are always welcome, but patches with tests get the most
+attention.
+
+=head1 SUPPORT
+
+Support for and information about F<ack> can be found at:
+
+=over 4
+
+=item * The ack homepage
+
+L<http://betterthangrep.com/>
+
+=item * The ack-users mailing list
+
+L<http://groups.google.com/group/ack-users>
+
+=item * The ack issues list at Github
+
+L<https://github.com/petdance/ack/issues>
+
+=item * AnnoCPAN: Annotated CPAN documentation
+
+L<http://annocpan.org/dist/ack>
+
+=item * CPAN Ratings
+
+L<http://cpanratings.perl.org/d/ack>
+
+=item * Search CPAN
+
+L<http://search.cpan.org/dist/ack>
+
+=item * Git source repository
+
+L<https://github.com/petdance/ack>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+How appropriate to have I<ack>nowledgements!
+
+Thanks to everyone who has contributed to ack in any way, including
+Shlomi Fish,
+Karen Etheridge,
+Olivier Mengue,
+Matthew Wild,
+Scott Kyle,
+Nick Hooey,
+Bo Borgerson,
+Mark Szymanski,
+Marq Schneider,
+Packy Anderson,
+JR Boyens,
+Dan Sully,
+Ryan Niebur,
+Kent Fredric,
+Mike Morearty,
+Ingmar Vanhassel,
+Eric Van Dewoestine,
+Sitaram Chamarty,
+Adam James,
+Richard Carlsson,
+Pedro Melo,
+AJ Schuster,
+Phil Jackson,
+Michael Schwern,
+Jan Dubois,
+Christopher J. Madsen,
+Matthew Wickline,
+David Dyck,
+Jason Porritt,
+Jjgod Jiang,
+Thomas Klausner,
+Uri Guttman,
+Peter Lewis,
+Kevin Riggle,
+Ori Avtalion,
+Torsten Blix,
+Nigel Metheringham,
+GE<aacute>bor SzabE<oacute>,
+Tod Hagan,
+Michael Hendricks,
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,
+Piers Cawley,
+Stephen Steneker,
+Elias Lutfallah,
+Mark Leighton Fisher,
+Matt Diephouse,
+Christian Jaeger,
+Bill Sully,
+Bill Ricker,
+David Golden,
+Nilson Santos F. Jr,
+Elliot Shank,
+Merijn Broeren,
+Uwe Voelker,
+Rick Scott,
+Ask BjE<oslash>rn Hansen,
+Jerry Gay,
+Will Coleda,
+Mike O'Regan,
+Slaven ReziE<0x107>,
+Mark Stosberg,
+David Alan Pisoni,
+Adriano Ferreira,
+James Keenan,
+Leland Johnson,
+Ricardo Signes
+and Pete Krawczyk.
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2005-2012 Andy Lester.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the Artistic License v2.0.
+
+=cut
+package File::Next;
+
+use strict;
+use warnings;
+
+
+our $VERSION = '1.06';
+
+
+
+use File::Spec ();
+
+
+our $name; # name of the current file
+our $dir; # dir of the current file
+
+our %files_defaults;
+our %skip_dirs;
+
+BEGIN {
+ %files_defaults = (
+ file_filter => undef,
+ descend_filter => undef,
+ error_handler => sub { CORE::die @_ },
+ sort_files => undef,
+ follow_symlinks => 1,
+ );
+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
+}
+
+
+sub files {
+ ($_[0] eq __PACKAGE__) && die 'File::Next::files must not be invoked as File::Next->files';
+
+ my ($parms,@queue) = _setup( \%files_defaults, @_ );
+ my $filter = $parms->{file_filter};
+
+ return sub {
+ while (@queue) {
+ my ($dir,$file,$fullpath) = splice( @queue, 0, 3 );
+ if ( -f $fullpath ) {
+ if ( $filter ) {
+ local $_ = $file;
+ local $File::Next::dir = $dir;
+ local $File::Next::name = $fullpath;
+ next if not $filter->();
+ }
+ return wantarray ? ($dir,$file,$fullpath) : $fullpath;
+ }
+ elsif ( -d _ ) {
+ unshift( @queue, _candidate_files( $parms, $fullpath ) );
+ }
+ } # while
+
+ return;
+ }; # iterator
+}
+
+
+
+
+
+
+
+sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }
+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }
+
+sub reslash {
+ my $path = shift;
+
+ my @parts = split( /\//, $path );
+
+ return $path if @parts < 2;
+
+ return File::Spec->catfile( @parts );
+}
+
+
+
+sub _setup {
+ my $defaults = shift;
+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
+
+ my %passed_parms = %{$passed_parms};
+
+ my $parms = {};
+ for my $key ( keys %{$defaults} ) {
+ $parms->{$key} =
+ exists $passed_parms{$key}
+ ? delete $passed_parms{$key}
+ : $defaults->{$key};
+ }
+
+ # Any leftover keys are bogus
+ for my $badkey ( keys %passed_parms ) {
+ my $sub = (caller(1))[3];
+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );
+ }
+
+ # If it's not a code ref, assume standard sort
+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {
+ $parms->{sort_files} = \&sort_standard;
+ }
+ my @queue;
+
+ for ( @_ ) {
+ my $start = reslash( $_ );
+ if (-d $start) {
+ push @queue, ($start,undef,$start);
+ }
+ else {
+ push @queue, (undef,$start,$start);
+ }
+ }
+
+ return ($parms,@queue);
+}
+
+
+sub _candidate_files {
+ my $parms = shift;
+ my $dir = shift;
+
+ my $dh;
+ if ( !opendir $dh, $dir ) {
+ $parms->{error_handler}->( "$dir: $!" );
+ return;
+ }
+
+ my @newfiles;
+ my $descend_filter = $parms->{descend_filter};
+ my $follow_symlinks = $parms->{follow_symlinks};
+ my $sort_sub = $parms->{sort_files};
+
+ for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
+ my $has_stat;
+
+ # Only do directory checking if we have a descend_filter
+ my $fullpath = File::Spec->catdir( $dir, $file );
+ if ( !$follow_symlinks ) {
+ next if -l $fullpath;
+ $has_stat = 1;
+ }
+
+ if ( $descend_filter ) {
+ if ( $has_stat ? (-d _) : (-d $fullpath) ) {
+ local $File::Next::dir = $fullpath;
+ local $_ = $file;
+ next if not $descend_filter->();
+ }
+ }
+ if ( $sort_sub ) {
+ push( @newfiles, [ $dir, $file, $fullpath ] );
+ }
+ else {
+ push( @newfiles, $dir, $file, $fullpath );
+ }
+ }
+ closedir $dh;
+
+ if ( $sort_sub ) {
+ return map { @{$_} } sort $sort_sub @newfiles;
+ }
+
+ return @newfiles;
+}
+
+
+1; # End of File::Next
+package App::Ack;
+
+use warnings;
+use strict;
+
+
+
+
+our $VERSION;
+our $COPYRIGHT;
+BEGIN {
+ $VERSION = '1.96';
+ $COPYRIGHT = 'Copyright 2005-2011 Andy Lester.';
+}
+
+our $fh;
+
+BEGIN {
+ $fh = *STDOUT;
+}
+
+
+our %types;
+our %type_wanted;
+our %mappings;
+our %ignore_dirs;
+
+our $input_from_pipe;
+our $output_to_pipe;
+
+our $dir_sep_chars;
+our $is_cygwin;
+our $is_windows;
+
+use File::Spec ();
+use File::Glob ':glob';
+use Getopt::Long ();
+
+BEGIN {
+ %ignore_dirs = (
+ '.bzr' => 'Bazaar',
+ '.cdv' => 'Codeville',
+ '~.dep' => 'Interface Builder',
+ '~.dot' => 'Interface Builder',
+ '~.nib' => 'Interface Builder',
+ '~.plst' => 'Interface Builder',
+ '.git' => 'Git',
+ '.hg' => 'Mercurial',
+ '.pc' => 'quilt',
+ '.svn' => 'Subversion',
+ _MTN => 'Monotone',
+ blib => 'Perl module building',
+ CVS => 'CVS',
+ RCS => 'RCS',
+ SCCS => 'SCCS',
+ _darcs => 'darcs',
+ _sgbak => 'Vault/Fortress',
+ 'autom4te.cache' => 'autoconf',
+ 'cover_db' => 'Devel::Cover',
+ _build => 'Module::Build',
+ );
+
+ %mappings = (
+ actionscript => [qw( as mxml )],
+ ada => [qw( ada adb ads )],
+ asm => [qw( asm s )],
+ batch => [qw( bat cmd )],
+ binary => q{Binary files, as defined by Perl's -B op (default: off)},
+ cc => [qw( c h xs )],
+ cfmx => [qw( cfc cfm cfml )],
+ clojure => [qw( clj )],
+ cpp => [qw( cpp cc cxx m hpp hh h hxx )],
+ csharp => [qw( cs )],
+ css => [qw( css )],
+ delphi => [qw( pas int dfm nfm dof dpk dproj groupproj bdsgroup bdsproj )],
+ elisp => [qw( el )],
+ erlang => [qw( erl hrl )],
+ fortran => [qw( f f77 f90 f95 f03 for ftn fpp )],
+ go => [qw( go )],
+ groovy => [qw( groovy gtmpl gpp grunit )],
+ haskell => [qw( hs lhs )],
+ hh => [qw( h )],
+ html => [qw( htm html shtml xhtml )],
+ java => [qw( java properties )],
+ js => [qw( js )],
+ jsp => [qw( jsp jspx jhtm jhtml )],
+ lisp => [qw( lisp lsp )],
+ lua => [qw( lua )],
+ make => q{Makefiles (including *.mk and *.mak)},
+ mason => [qw( mas mhtml mpl mtxt )],
+ objc => [qw( m h )],
+ objcpp => [qw( mm h )],
+ ocaml => [qw( ml mli )],
+ parrot => [qw( pir pasm pmc ops pod pg tg )],
+ perl => [qw( pl pm pm6 pod t psgi )],
+ php => [qw( php phpt php3 php4 php5 phtml)],
+ plone => [qw( pt cpt metadata cpy py )],
+ python => [qw( py )],
+ rake => q{Rakefiles},
+ ruby => [qw( rb rhtml rjs rxml erb rake spec )],
+ scala => [qw( scala )],
+ scheme => [qw( scm ss )],
+ shell => [qw( sh bash csh tcsh ksh zsh )],
+ skipped => q{Files, but not directories, normally skipped by ack (default: off)},
+ smalltalk => [qw( st )],
+ sql => [qw( sql ctl )],
+ tcl => [qw( tcl itcl itk )],
+ tex => [qw( tex cls sty )],
+ text => q{Text files, as defined by Perl's -T op (default: off)},
+ tt => [qw( tt tt2 ttml )],
+ vb => [qw( bas cls frm ctl vb resx )],
+ verilog => [qw( v vh sv )],
+ vhdl => [qw( vhd vhdl )],
+ vim => [qw( vim )],
+ yaml => [qw( yaml yml )],
+ xml => [qw( xml dtd xsl xslt ent )],
+ );
+
+ while ( my ($type,$exts) = each %mappings ) {
+ if ( ref $exts ) {
+ for my $ext ( @{$exts} ) {
+ push( @{$types{$ext}}, $type );
+ }
+ }
+ }
+ # add manually Makefile extensions
+ push @{$types{$_}}, 'make' for qw{ mk mak };
+
+ # These have to be checked before any filehandle diddling.
+ $output_to_pipe = not -t *STDOUT;
+ $input_from_pipe = -p STDIN;
+
+ $is_cygwin = ($^O eq 'cygwin');
+ $is_windows = ($^O =~ /MSWin32/);
+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
+}
+
+
+sub read_ackrc {
+ my @files = ( $ENV{ACKRC} );
+ my @dirs =
+ $is_windows
+ ? ( $ENV{HOME}, $ENV{USERPROFILE} )
+ : ( '~', $ENV{HOME} );
+ for my $dir ( grep { defined } @dirs ) {
+ for my $file ( '.ackrc', '_ackrc' ) {
+ push( @files, bsd_glob( "$dir/$file", GLOB_TILDE ) );
+ }
+ }
+ for my $filename ( @files ) {
+ if ( defined $filename && -e $filename ) {
+ open( my $fh, '<', $filename ) or App::Ack::die( "$filename: $!\n" );
+ my @lines = grep { /./ && !/^\s*#/ } <$fh>;
+ chomp @lines;
+ close $fh or App::Ack::die( "$filename: $!\n" );
+
+ # get rid of leading and trailing whitespaces
+ for ( @lines ) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+
+ return @lines;
+ }
+ }
+
+ return;
+}
+
+
+sub get_command_line_options {
+ my %opt = (
+ pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
+ );
+
+ my $getopt_specs = {
+ 1 => sub { $opt{1} = $opt{m} = 1 },
+ 'A|after-context=i' => \$opt{after_context},
+ 'B|before-context=i' => \$opt{before_context},
+ 'C|context:i' => sub { shift; my $val = shift; $opt{before_context} = $opt{after_context} = ($val || 2) },
+ 'a|all-types' => \$opt{all},
+ 'break!' => \$opt{break},
+ c => \$opt{count},
+ 'color|colour!' => \$opt{color},
+ 'color-match=s' => \$ENV{ACK_COLOR_MATCH},
+ 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},
+ 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},
+ 'column!' => \$opt{column},
+ count => \$opt{count},
+ 'env!' => sub { }, # ignore this option, it is handled beforehand
+ f => \$opt{f},
+ flush => \$opt{flush},
+ 'follow!' => \$opt{follow},
+ 'g=s' => sub { shift; $opt{G} = shift; $opt{f} = 1 },
+ 'G=s' => \$opt{G},
+ 'group!' => sub { shift; $opt{heading} = $opt{break} = shift },
+ 'heading!' => \$opt{heading},
+ 'h|no-filename' => \$opt{h},
+ 'H|with-filename' => \$opt{H},
+ 'i|ignore-case' => \$opt{i},
+ 'invert-file-match' => \$opt{invert_file_match},
+ 'lines=s' => sub { shift; my $val = shift; push @{$opt{lines}}, $val },
+ 'l|files-with-matches' => \$opt{l},
+ 'L|files-without-matches' => sub { $opt{l} = $opt{v} = 1 },
+ 'm|max-count=i' => \$opt{m},
+ 'match=s' => \$opt{regex},
+ 'n|no-recurse' => \$opt{n},
+ o => sub { $opt{output} = '$&' },
+ 'output=s' => \$opt{output},
+ 'pager=s' => \$opt{pager},
+ 'nopager' => sub { $opt{pager} = undef },
+ 'passthru' => \$opt{passthru},
+ 'print0' => \$opt{print0},
+ 'Q|literal' => \$opt{Q},
+ 'r|R|recurse' => sub { $opt{n} = 0 },
+ 'show-types' => \$opt{show_types},
+ 'smart-case!' => \$opt{smart_case},
+ 'sort-files' => \$opt{sort_files},
+ 'u|unrestricted' => \$opt{u},
+ 'v|invert-match' => \$opt{v},
+ 'w|word-regexp' => \$opt{w},
+
+ 'ignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); $ignore_dirs{$dir} = '--ignore-dirs' },
+ 'noignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); delete $ignore_dirs{$dir} },
+
+ 'version' => sub { print_version_statement(); exit; },
+ 'help|?:s' => sub { shift; show_help(@_); exit; },
+ 'help-types'=> sub { show_help_types(); exit; },
+ 'man' => sub {
+ require Pod::Usage;
+ Pod::Usage::pod2usage({
+ -verbose => 2,
+ -exitval => 0,
+ });
+ },
+
+ 'type=s' => sub {
+ # Whatever --type=xxx they specify, set it manually in the hash
+ my $dummy = shift;
+ my $type = shift;
+ my $wanted = ($type =~ s/^no//) ? 0 : 1; # must not be undef later
+
+ if ( exists $type_wanted{ $type } ) {
+ $type_wanted{ $type } = $wanted;
+ }
+ else {
+ App::Ack::die( qq{Unknown --type "$type"} );
+ }
+ }, # type sub
+ };
+
+ # Stick any default switches at the beginning, so they can be overridden
+ # by the command line switches.
+ unshift @ARGV, split( ' ', $ENV{ACK_OPTIONS} ) if defined $ENV{ACK_OPTIONS};
+
+ # first pass through options, looking for type definitions
+ def_types_from_ARGV();
+
+ for my $i ( filetypes_supported() ) {
+ $getopt_specs->{ "$i!" } = \$type_wanted{ $i };
+ }
+
+
+ my $parser = Getopt::Long::Parser->new();
+ $parser->configure( 'bundling', 'no_ignore_case', );
+ $parser->getoptions( %{$getopt_specs} ) or
+ App::Ack::die( 'See ack --help, ack --help-types or ack --man for options.' );
+
+ my $to_screen = not output_to_pipe();
+ my %defaults = (
+ all => 0,
+ color => $to_screen,
+ follow => 0,
+ break => $to_screen,
+ heading => $to_screen,
+ before_context => 0,
+ after_context => 0,
+ );
+ if ( $is_windows && $defaults{color} && not $ENV{ACK_PAGER_COLOR} ) {
+ if ( $ENV{ACK_PAGER} || not eval { require Win32::Console::ANSI } ) {
+ $defaults{color} = 0;
+ }
+ }
+ if ( $to_screen && $ENV{ACK_PAGER_COLOR} ) {
+ $defaults{color} = 1;
+ }
+
+ while ( my ($key,$value) = each %defaults ) {
+ if ( not defined $opt{$key} ) {
+ $opt{$key} = $value;
+ }
+ }
+
+ if ( defined $opt{m} && $opt{m} <= 0 ) {
+ App::Ack::die( '-m must be greater than zero' );
+ }
+
+ for ( qw( before_context after_context ) ) {
+ if ( defined $opt{$_} && $opt{$_} < 0 ) {
+ App::Ack::die( "--$_ may not be negative" );
+ }
+ }
+
+ if ( defined( my $val = $opt{output} ) ) {
+ $opt{output} = eval qq[ sub { "$val" } ];
+ }
+ if ( defined( my $l = $opt{lines} ) ) {
+ # --line=1 --line=5 is equivalent to --line=1,5
+ my @lines = split( /,/, join( ',', @{$l} ) );
+
+ # --line=1-3 is equivalent to --line=1,2,3
+ @lines = map {
+ my @ret;
+ if ( /-/ ) {
+ my ($from, $to) = split /-/, $_;
+ if ( $from > $to ) {
+ App::Ack::warn( "ignoring --line=$from-$to" );
+ @ret = ();
+ }
+ else {
+ @ret = ( $from .. $to );
+ }
+ }
+ else {
+ @ret = ( $_ );
+ };
+ @ret
+ } @lines;
+
+ if ( @lines ) {
+ my %uniq;
+ @uniq{ @lines } = ();
+ $opt{lines} = [ sort { $a <=> $b } keys %uniq ]; # numerical sort and each line occurs only once!
+ }
+ else {
+ # happens if there are only ignored --line directives
+ App::Ack::die( 'All --line options are invalid.' );
+ }
+ }
+
+ return \%opt;
+}
+
+
+sub def_types_from_ARGV {
+ my @typedef;
+
+ my $parser = Getopt::Long::Parser->new();
+ # pass_through => leave unrecognized command line arguments alone
+ # no_auto_abbrev => otherwise -c is expanded and not left alone
+ $parser->configure( 'no_ignore_case', 'pass_through', 'no_auto_abbrev' );
+ $parser->getoptions(
+ 'type-set=s' => sub { shift; push @typedef, ['c', shift] },
+ 'type-add=s' => sub { shift; push @typedef, ['a', shift] },
+ ) or App::Ack::die( 'See ack --help or ack --man for options.' );
+
+ for my $td (@typedef) {
+ my ($type, $ext) = split /=/, $td->[1];
+
+ if ( $td->[0] eq 'c' ) {
+ # type-set
+ if ( exists $mappings{$type} ) {
+ # can't redefine types 'make', 'skipped', 'text' and 'binary'
+ App::Ack::die( qq{--type-set: Builtin type "$type" cannot be changed.} )
+ if ref $mappings{$type} ne 'ARRAY';
+
+ delete_type($type);
+ }
+ }
+ else {
+ # type-add
+
+ # can't append to types 'make', 'skipped', 'text' and 'binary'
+ App::Ack::die( qq{--type-add: Builtin type "$type" cannot be changed.} )
+ if exists $mappings{$type} && ref $mappings{$type} ne 'ARRAY';
+
+ App::Ack::warn( qq{--type-add: Type "$type" does not exist, creating with "$ext" ...} )
+ unless exists $mappings{$type};
+ }
+
+ my @exts = split /,/, $ext;
+ s/^\.// for @exts;
+
+ if ( !exists $mappings{$type} || ref($mappings{$type}) eq 'ARRAY' ) {
+ push @{$mappings{$type}}, @exts;
+ for my $e ( @exts ) {
+ push @{$types{$e}}, $type;
+ }
+ }
+ else {
+ App::Ack::die( qq{Cannot append to type "$type".} );
+ }
+ }
+
+ return;
+}
+
+
+sub delete_type {
+ my $type = shift;
+
+ App::Ack::die( qq{Internal error: Cannot delete builtin type "$type".} )
+ unless ref $mappings{$type} eq 'ARRAY';
+
+ delete $mappings{$type};
+ delete $type_wanted{$type};
+ for my $ext ( keys %types ) {
+ $types{$ext} = [ grep { $_ ne $type } @{$types{$ext}} ];
+ }
+}
+
+
+sub ignoredir_filter {
+ return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};
+}
+
+
+sub remove_dir_sep {
+ my $path = shift;
+ $path =~ s/[$dir_sep_chars]$//;
+
+ return $path;
+}
+
+
+use constant TEXT => 'text';
+
+sub filetypes {
+ my $filename = shift;
+
+ my $basename = $filename;
+ $basename =~ s{.*[$dir_sep_chars]}{};
+
+ return 'skipped' unless is_searchable( $basename );
+
+ my $lc_basename = lc $basename;
+ return ('make',TEXT) if $lc_basename eq 'makefile' || $lc_basename eq 'gnumakefile';
+ return ('rake','ruby',TEXT) if $lc_basename eq 'rakefile';
+
+ # If there's an extension, look it up
+ if ( $filename =~ m{\.([^\.$dir_sep_chars]+)$}o ) {
+ my $ref = $types{lc $1};
+ return (@{$ref},TEXT) if $ref;
+ }
+
+ # At this point, we can't tell from just the name. Now we have to
+ # open it and look inside.
+
+ return unless -e $filename;
+ # From Elliot Shank:
+ # I can't see any reason that -r would fail on these-- the ACLs look
+ # fine, and no program has any of them open, so the busted Windows
+ # file locking model isn't getting in there. If I comment the if
+ # statement out, everything works fine
+ # So, for cygwin, don't bother trying to check for readability.
+ if ( !$is_cygwin ) {
+ if ( !-r $filename ) {
+ App::Ack::warn( "$filename: Permission denied" );
+ return;
+ }
+ }
+
+ return 'binary' if -B $filename;
+
+ # If there's no extension, or we don't recognize it, check the shebang line
+ my $fh;
+ if ( !open( $fh, '<', $filename ) ) {
+ App::Ack::warn( "$filename: $!" );
+ return;
+ }
+ my $header = <$fh>;
+ close $fh;
+
+ if ( $header =~ /^#!/ ) {
+ return ($1,TEXT) if $header =~ /\b(ruby|lua|p(?:erl|hp|ython))-?(\d[\d.]*)?\b/;
+ return ('shell',TEXT) if $header =~ /\b(?:ba|t?c|k|z)?sh\b/;
+ }
+ else {
+ return ('xml',TEXT) if $header =~ /\Q<?xml /i;
+ }
+
+ return (TEXT);
+}
+
+
+sub is_searchable {
+ my $filename = shift;
+
+ # If these are updated, update the --help message
+ return if $filename =~ /[.]bak$/;
+ return if $filename =~ /~$/;
+ return if $filename =~ m{^#.*#$}o;
+ return if $filename =~ m{^core\.\d+$}o;
+ return if $filename =~ m{[._].*\.swp$}o;
+ return if $filename =~ /[.-]min\.js$/;
+
+ return 1;
+}
+
+
+sub build_regex {
+ my $str = shift;
+ my $opt = shift;
+
+ defined $str or App::Ack::die( 'No regular expression found.' );
+
+ $str = quotemeta( $str ) if $opt->{Q};
+ if ( $opt->{w} ) {
+ $str = "\\b$str" if $str =~ /^\w/;
+ $str = "$str\\b" if $str =~ /\w$/;
+ }
+
+ my $regex_is_lc = $str eq lc $str;
+ if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {
+ $str = "(?i)$str";
+ }
+
+ return $str;
+}
+
+
+sub check_regex {
+ my $regex = shift;
+
+ return unless defined $regex;
+
+ eval { qr/$regex/ };
+ if ($@) {
+ (my $error = $@) =~ s/ at \S+ line \d+.*//;
+ chomp($error);
+ App::Ack::die( "Invalid regex '$regex':\n $error" );
+ }
+
+ return;
+}
+
+
+
+
+sub warn {
+ return CORE::warn( _my_program(), ': ', @_, "\n" );
+}
+
+
+sub die {
+ return CORE::die( _my_program(), ': ', @_, "\n" );
+}
+
+sub _my_program {
+ require File::Basename;
+ return File::Basename::basename( $0 );
+}
+
+
+
+sub filetypes_supported {
+ return keys %mappings;
+}
+
+sub _get_thpppt {
+ my $y = q{_ /|,\\'!.x',=(www)=, U };
+ $y =~ tr/,x!w/\nOo_/;
+ return $y;
+}
+
+sub _thpppt {
+ my $y = _get_thpppt();
+ App::Ack::print( "$y ack $_[0]!\n" );
+ exit 0;
+}
+
+sub _bar {
+ my $x;
+ $x = <<'_BAR';
+ 6?!I'7!I"?%+!
+ 3~!I#7#I"7#I!?!+!="+"="+!:!
+ 2?#I!7!I!?#I!7!I"+"=%+"=#
+ 1?"+!?*+!=#~"=!+#?"="+!
+ 0?"+!?"I"?&+!="~!=!~"=!+%="+"
+ /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!
+ .?%I"?%+%='?!=#~$="
+ ,,!?%I"?(+$=$~!=#:"~$:!~!
+ ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!
+ +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"
+ *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!
+ *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"
+ )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!
+ (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!
+ (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!
+ %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"
+ $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/
+ $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!
+ $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/
+ %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!
+ %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!
+ %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!
+ %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!
+ &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!
+ &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!
+ &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!
+ &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!
+ &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"
+ &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!
+ %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&
+ %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)
+ $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!
+ $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"
+ "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!
+ !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!
+ !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*
+7+:!,!~#,"=!7'I!?#I"7/+!7+
+77I!+!7!?!7!I"71+!7,
+_BAR
+
+ $x =~ s/(.)(.)/$1x(ord($2)-32)/eg;
+ App::Ack::print( $x );
+ exit 0;
+}
+
+sub _key {
+ my $str = lc shift;
+ $str =~ s/[^a-z]//g;
+
+ return $str;
+}
+
+
+sub show_help {
+ my $help_arg = shift || 0;
+
+ return show_help_types() if $help_arg =~ /^types?/;
+
+ my $ignore_dirs = _listify( sort { _key($a) cmp _key($b) } keys %ignore_dirs );
+
+ App::Ack::print( <<"END_OF_HELP" );
+Usage: ack [OPTION]... PATTERN [FILE]
+
+Search for PATTERN in each source file in the tree from cwd on down.
+If [FILES] is specified, then only those files/directories are checked.
+ack may also search STDIN, but only if no FILE are specified, or if
+one of FILES is "-".
+
+Default switches may be specified in ACK_OPTIONS environment variable or
+an .ackrc file. If you want no dependency on the environment, turn it
+off with --noenv.
+
+Example: ack -i select
+
+Searching:
+ -i, --ignore-case Ignore case distinctions in PATTERN
+ --[no]smart-case Ignore case distinctions in PATTERN,
+ only if PATTERN contains no upper case
+ Ignored if -i is specified
+ -v, --invert-match Invert match: select non-matching lines
+ -w, --word-regexp Force PATTERN to match only whole words
+ -Q, --literal Quote all metacharacters; PATTERN is literal
+
+Search output:
+ --line=NUM Only print line(s) NUM of each file
+ -l, --files-with-matches
+ Only print filenames containing matches
+ -L, --files-without-matches
+ Only print filenames with no matches
+ -o Show only the part of a line matching PATTERN
+ (turns off text highlighting)
+ --passthru Print all lines, whether matching or not
+ --output=expr Output the evaluation of expr for each line
+ (turns off text highlighting)
+ --match PATTERN Specify PATTERN explicitly.
+ -m, --max-count=NUM Stop searching in each file after NUM matches
+ -1 Stop searching after one match of any kind
+ -H, --with-filename Print the filename for each match
+ -h, --no-filename Suppress the prefixing filename on output
+ -c, --count Show number of lines matching per file
+ --column Show the column number of the first match
+
+ -A NUM, --after-context=NUM
+ Print NUM lines of trailing context after matching
+ lines.
+ -B NUM, --before-context=NUM
+ Print NUM lines of leading context before matching
+ lines.
+ -C [NUM], --context[=NUM]
+ Print NUM lines (default 2) of output context.
+
+ --print0 Print null byte as separator between filenames,
+ only works with -f, -g, -l, -L or -c.
+
+File presentation:
+ --pager=COMMAND Pipes all ack output through COMMAND. For example,
+ --pager="less -R". Ignored if output is redirected.
+ --nopager Do not send output through a pager. Cancels any
+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.
+ --[no]heading Print a filename heading above each file's results.
+ (default: on when used interactively)
+ --[no]break Print a break between results from different files.
+ (default: on when used interactively)
+ --group Same as --heading --break
+ --nogroup Same as --noheading --nobreak
+ --[no]color Highlight the matching text (default: on unless
+ output is redirected, or on Windows)
+ --[no]colour Same as --[no]color
+ --color-filename=COLOR
+ --color-match=COLOR
+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.
+ --flush Flush output immediately, even when ack is used
+ non-interactively (when output goes to a pipe or
+ file).
+
+File finding:
+ -f Only print the files found, without searching.
+ The PATTERN must not be specified.
+ -g REGEX Same as -f, but only print files matching REGEX.
+ --sort-files Sort the found files lexically.
+ --invert-file-match Print/search handle files that do not match -g/-G.
+ --show-types Show which types each file has.
+
+File inclusion/exclusion:
+ -a, --all-types All file types searched;
+ Ignores CVS, .svn and other ignored directories
+ -u, --unrestricted All files and directories searched
+ --[no]ignore-dir=name Add/Remove directory from the list of ignored dirs
+ -r, -R, --recurse Recurse into subdirectories (ack's default behavior)
+ -n, --no-recurse No descending into subdirectories
+ -G REGEX Only search files that match REGEX
+
+ --perl Include only Perl files.
+ --type=perl Include only Perl files.
+ --noperl Exclude Perl files.
+ --type=noperl Exclude Perl files.
+ See "ack --help type" for supported filetypes.
+
+ --type-set TYPE=.EXTENSION[,.EXT2[,...]]
+ Files with the given EXTENSION(s) are recognized as
+ being of type TYPE. This replaces an existing
+ definition for type TYPE.
+ --type-add TYPE=.EXTENSION[,.EXT2[,...]]
+ Files with the given EXTENSION(s) are recognized as
+ being of (the existing) type TYPE
+
+ --[no]follow Follow symlinks. Default is off.
+
+ Directories ignored by default:
+ $ignore_dirs
+
+ Files not checked for type:
+ /~\$/ - Unix backup files
+ /#.+#\$/ - Emacs swap files
+ /[._].*\\.swp\$/ - Vi(m) swap files
+ /core\\.\\d+\$/ - core dumps
+ /[.-]min\\.js\$/ - Minified javascript files
+
+Miscellaneous:
+ --noenv Ignore environment variables and ~/.ackrc
+ --help This help
+ --man Man page
+ --version Display version & copyright
+ --thpppt Bill the Cat
+ --bar The warning admiral
+
+Exit status is 0 if match, 1 if no match.
+
+This is version $VERSION of ack.
+END_OF_HELP
+
+ return;
+ }
+
+
+
+sub show_help_types {
+ App::Ack::print( <<'END_OF_HELP' );
+Usage: ack [OPTION]... PATTERN [FILES]
+
+The following is the list of filetypes supported by ack. You can
+specify a file type with the --type=TYPE format, or the --TYPE
+format. For example, both --type=perl and --perl work.
+
+Note that some extensions may appear in multiple types. For example,
+.pod files are both Perl and Parrot.
+
+END_OF_HELP
+
+ my @types = filetypes_supported();
+ my $maxlen = 0;
+ for ( @types ) {
+ $maxlen = length if $maxlen < length;
+ }
+ for my $type ( sort @types ) {
+ next if $type =~ /^-/; # Stuff to not show
+ my $ext_list = $mappings{$type};
+
+ if ( ref $ext_list ) {
+ $ext_list = join( ' ', map { ".$_" } @{$ext_list} );
+ }
+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
+ }
+
+ return;
+}
+
+sub _listify {
+ my @whats = @_;
+
+ return '' if !@whats;
+
+ my $end = pop @whats;
+ my $str = @whats ? join( ', ', @whats ) . " and $end" : $end;
+
+ no warnings 'once';
+ require Text::Wrap;
+ $Text::Wrap::columns = 75;
+ return Text::Wrap::wrap( '', ' ', $str );
+}
+
+
+sub get_version_statement {
+ require Config;
+
+ my $copyright = get_copyright();
+ my $this_perl = $Config::Config{perlpath};
+ if ($^O ne 'VMS') {
+ my $ext = $Config::Config{_exe};
+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;
+ }
+ my $ver = sprintf( '%vd', $^V );
+
+ return <<"END_OF_VERSION";
+ack $VERSION
+Running under Perl $ver at $this_perl
+
+$copyright
+
+This program is free software. You may modify or distribute it
+under the terms of the Artistic License v2.0.
+END_OF_VERSION
+}
+
+
+sub print_version_statement {
+ App::Ack::print( get_version_statement() );
+
+ return;
+}
+
+
+sub get_copyright {
+ return $COPYRIGHT;
+}
+
+
+sub load_colors {
+ eval 'use Term::ANSIColor ()';
+
+ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';
+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';
+
+ return;
+}
+
+
+sub is_interesting {
+ return if /^\./;
+
+ my $include;
+
+ for my $type ( filetypes( $File::Next::name ) ) {
+ if ( defined $type_wanted{$type} ) {
+ if ( $type_wanted{$type} ) {
+ $include = 1;
+ }
+ else {
+ return;
+ }
+ }
+ }
+
+ return $include;
+}
+
+
+
+# print subs added in order to make it easy for a third party
+# module (such as App::Wack) to redefine the display methods
+# and show the results in a different way.
+sub print { print {$fh} @_ }
+sub print_first_filename { App::Ack::print( $_[0], "\n" ) }
+sub print_blank_line { App::Ack::print( "\n" ) }
+sub print_separator { App::Ack::print( "--\n" ) }
+sub print_filename { App::Ack::print( $_[0], $_[1] ) }
+sub print_line_no { App::Ack::print( $_[0], $_[1] ) }
+sub print_column_no { App::Ack::print( $_[0], $_[1] ) }
+sub print_count {
+ my $filename = shift;
+ my $nmatches = shift;
+ my $ors = shift;
+ my $count = shift;
+ my $show_filename = shift;
+
+ if ($show_filename) {
+ App::Ack::print( $filename );
+ App::Ack::print( ':', $nmatches ) if $count;
+ }
+ else {
+ App::Ack::print( $nmatches ) if $count;
+ }
+ App::Ack::print( $ors );
+}
+
+sub print_count0 {
+ my $filename = shift;
+ my $ors = shift;
+ my $show_filename = shift;
+
+ if ($show_filename) {
+ App::Ack::print( $filename, ':0', $ors );
+ }
+ else {
+ App::Ack::print( '0', $ors );
+ }
+}
+
+
+
+{
+ my $filename;
+ my $regex;
+ my $display_filename;
+
+ my $keep_context;
+
+ my $last_output_line; # number of the last line that has been output
+ my $any_output; # has there been any output for the current file yet
+ my $context_overall_output_count; # has there been any output at all
+
+sub search_resource {
+ my $res = shift;
+ my $opt = shift;
+
+ $filename = $res->name();
+
+ my $v = $opt->{v};
+ my $passthru = $opt->{passthru};
+ my $max = $opt->{m};
+ my $nmatches = 0;
+
+ $display_filename = undef;
+
+ # for --line processing
+ my $has_lines = 0;
+ my @lines;
+ if ( defined $opt->{lines} ) {
+ $has_lines = 1;
+ @lines = ( @{$opt->{lines}}, -1 );
+ undef $regex; # Don't match when printing matching line
+ }
+ else {
+ $regex = qr/$opt->{regex}/;
+ }
+
+ # for context processing
+ $last_output_line = -1;
+ $any_output = 0;
+ my $before_context = $opt->{before_context};
+ my $after_context = $opt->{after_context};
+
+ $keep_context = ($before_context || $after_context) && !$passthru;
+
+ my @before;
+ my $before_starts_at_line;
+ my $after = 0; # number of lines still to print after a match
+
+ while ( $res->next_text ) {
+ # XXX Optimize away the case when there are no more @lines to find.
+ # XXX $has_lines, $passthru and $v never change. Optimize.
+ if ( $has_lines
+ ? $. != $lines[0] # $lines[0] should be a scalar
+ : $v ? m/$regex/ : !m/$regex/ ) {
+ if ( $passthru ) {
+ App::Ack::print( $_ );
+ next;
+ }
+
+ if ( $keep_context ) {
+ if ( $after ) {
+ print_match_or_context( $opt, 0, $., $-[0], $+[0], $_ );
+ $after--;
+ }
+ elsif ( $before_context ) {
+ if ( @before ) {
+ if ( @before >= $before_context ) {
+ shift @before;
+ ++$before_starts_at_line;
+ }
+ }
+ else {
+ $before_starts_at_line = $.;
+ }
+ push @before, $_;
+ }
+ last if $max && ( $nmatches >= $max ) && !$after;
+ }
+ next;
+ } # not a match
+
+ ++$nmatches;
+
+ # print an empty line as a divider before first line in each file (not before the first file)
+ if ( !$any_output && $opt->{show_filename} && $opt->{break} && defined( $context_overall_output_count ) ) {
+ App::Ack::print_blank_line();
+ }
+
+ shift @lines if $has_lines;
+
+ if ( $res->is_binary ) {
+ App::Ack::print( "Binary file $filename matches\n" );
+ last;
+ }
+ if ( $keep_context ) {
+ if ( @before ) {
+ print_match_or_context( $opt, 0, $before_starts_at_line, $-[0], $+[0], @before );
+ @before = ();
+ $before_starts_at_line = 0;
+ }
+ if ( $max && $nmatches > $max ) {
+ --$after;
+ }
+ else {
+ $after = $after_context;
+ }
+ }
+ print_match_or_context( $opt, 1, $., $-[0], $+[0], $_ );
+
+ last if $max && ( $nmatches >= $max ) && !$after;
+ } # while
+
+ return $nmatches;
+} # search_resource()
+
+
+
+sub print_match_or_context {
+ my $opt = shift; # opts array
+ my $is_match = shift; # is there a match on the line?
+ my $line_no = shift;
+ my $match_start = shift;
+ my $match_end = shift;
+
+ my $color = $opt->{color};
+ my $heading = $opt->{heading};
+ my $show_filename = $opt->{show_filename};
+ my $show_column = $opt->{column};
+
+ if ( $show_filename ) {
+ if ( not defined $display_filename ) {
+ $display_filename =
+ $color
+ ? Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} )
+ : $filename;
+ if ( $heading && !$any_output ) {
+ App::Ack::print_first_filename($display_filename);
+ }
+ }
+ }
+
+ my $sep = $is_match ? ':' : '-';
+ my $output_func = $opt->{output};
+ for ( @_ ) {
+ if ( $keep_context && !$output_func ) {
+ if ( ( $last_output_line != $line_no - 1 ) &&
+ ( $any_output || ( !$heading && defined( $context_overall_output_count ) ) ) ) {
+ App::Ack::print_separator();
+ }
+ # to ensure separators between different files when --noheading
+
+ $last_output_line = $line_no;
+ }
+
+ if ( $show_filename ) {
+ App::Ack::print_filename($display_filename, $sep) if not $heading;
+ my $display_line_no =
+ $color
+ ? Term::ANSIColor::colored( $line_no, $ENV{ACK_COLOR_LINENO} )
+ : $line_no;
+ App::Ack::print_line_no($display_line_no, $sep);
+ }
+
+ if ( $output_func ) {
+ while ( /$regex/go ) {
+ App::Ack::print( $output_func->() . "\n" );
+ }
+ }
+ else {
+ if ( $color && $is_match && $regex &&
+ s/$regex/Term::ANSIColor::colored( substr($_, $-[0], $+[0] - $-[0]), $ENV{ACK_COLOR_MATCH} )/eg ) {
+ # At the end of the line reset the color and remove newline
+ s/[\r\n]*\z/\e[0m\e[K/;
+ }
+ else {
+ # remove any kind of newline at the end of the line
+ s/[\r\n]*\z//;
+ }
+ if ( $show_column ) {
+ App::Ack::print_column_no( $match_start+1, $sep );
+ }
+ App::Ack::print($_ . "\n");
+ }
+ $any_output = 1;
+ ++$context_overall_output_count;
+ ++$line_no;
+ }
+
+ return;
+} # print_match_or_context()
+
+} # scope around search_resource() and print_match_or_context()
+
+
+TOTAL_COUNT_SCOPE: {
+my $total_count;
+
+sub get_total_count {
+ return $total_count;
+}
+
+sub reset_total_count {
+ $total_count = 0;
+}
+
+
+sub search_and_list {
+ my $res = shift;
+ my $opt = shift;
+
+ my $nmatches = 0;
+ my $count = $opt->{count};
+ my $ors = $opt->{print0} ? "\0" : "\n"; # output record separator
+ my $show_filename = $opt->{show_filename};
+
+ my $regex = qr/$opt->{regex}/;
+
+ if ( $opt->{v} ) {
+ while ( $res->next_text ) {
+ if ( /$regex/ ) {
+ return 0 unless $count;
+ }
+ else {
+ ++$nmatches;
+ }
+ }
+ }
+ else {
+ while ( $res->next_text ) {
+ if ( /$regex/ ) {
+ ++$nmatches;
+ last unless $count;
+ }
+ }
+ }
+
+ if ( $opt->{show_total} ) {
+ $total_count += $nmatches;
+ }
+ else {
+ if ( $nmatches ) {
+ App::Ack::print_count( $res->name, $nmatches, $ors, $count, $show_filename );
+ }
+ elsif ( $count && !$opt->{l} ) {
+ App::Ack::print_count0( $res->name, $ors, $show_filename );
+ }
+ }
+
+ return $nmatches ? 1 : 0;
+} # search_and_list()
+
+} # scope around $total_count
+
+
+
+sub filetypes_supported_set {
+ return grep { defined $type_wanted{$_} && ($type_wanted{$_} == 1) } filetypes_supported();
+}
+
+
+
+sub print_files {
+ my $iter = shift;
+ my $opt = shift;
+
+ my $ors = $opt->{print0} ? "\0" : "\n";
+
+ my $nmatches = 0;
+ while ( defined ( my $file = $iter->() ) ) {
+ App::Ack::print $file, $opt->{show_types} ? " => " . join( ',', filetypes( $file ) ) : (), $ors;
+ $nmatches++;
+ last if $opt->{1};
+ }
+
+ return $nmatches;
+}
+
+
+sub print_files_with_matches {
+ my $iter = shift;
+ my $opt = shift;
+
+ # if we have -l and only 1 file given on command line (this means
+ # show_filename is set to 0), we want to see the filename nevertheless
+ $opt->{show_filename} = 1 if $opt->{l};
+
+ $opt->{show_filename} = 0 if $opt->{h};
+ $opt->{show_filename} = 1 if $opt->{H};
+
+ # abuse options to hand in the show_total parameter to search_and_list
+ $opt->{show_total} = $opt->{count} && !$opt->{show_filename};
+ reset_total_count();
+
+ my $nmatches = 0;
+ while ( defined ( my $filename = $iter->() ) ) {
+ my $repo = App::Ack::Repository::Basic->new( $filename );
+ my $res;
+ while ( $res = $repo->next_resource() ) {
+ $nmatches += search_and_list( $res, $opt );
+ $res->close();
+ last if $nmatches && $opt->{1};
+ }
+ $repo->close();
+ }
+
+ if ( $nmatches && $opt->{show_total} ) {
+ App::Ack::print_count('', get_total_count(), "\n", 1, 0 )
+ }
+
+ return $nmatches;
+}
+
+
+sub print_matches {
+ my $iter = shift;
+ my $opt = shift;
+
+ $opt->{show_filename} = 0 if $opt->{h};
+ $opt->{show_filename} = 1 if $opt->{H};
+
+ my $nmatches = 0;
+ while ( defined ( my $filename = $iter->() ) ) {
+ my $repo;
+ my $tarballs_work = 0;
+ if ( $tarballs_work && $filename =~ /\.tar\.gz$/ ) {
+ App::Ack::die( 'Not working here yet' );
+ require App::Ack::Repository::Tar; # XXX Error checking
+ $repo = App::Ack::Repository::Tar->new( $filename );
+ }
+ else {
+ $repo = App::Ack::Repository::Basic->new( $filename );
+ }
+ $repo or next;
+
+ while ( my $res = $repo->next_resource() ) {
+ my $needs_line_scan;
+ if ( $opt->{regex} && !$opt->{passthru} ) {
+ $needs_line_scan = $res->needs_line_scan( $opt );
+ if ( $needs_line_scan ) {
+ $res->reset();
+ }
+ }
+ else {
+ $needs_line_scan = 1;
+ }
+ if ( $needs_line_scan ) {
+ $nmatches += search_resource( $res, $opt );
+ }
+ $res->close();
+ }
+ last if $nmatches && $opt->{1};
+ $repo->close();
+ }
+ return $nmatches;
+}
+
+
+sub filetype_setup {
+ my $filetypes_supported_set = filetypes_supported_set();
+ # If anyone says --no-whatever, we assume all other types must be on.
+ if ( !$filetypes_supported_set ) {
+ for my $i ( keys %type_wanted ) {
+ $type_wanted{$i} = 1 unless ( defined( $type_wanted{$i} ) || $i eq 'binary' || $i eq 'text' || $i eq 'skipped' );
+ }
+ }
+ return;
+}
+
+
+EXPAND_FILENAMES_SCOPE: {
+ my $filter;
+
+ sub expand_filenames {
+ my $argv = shift;
+
+ my $attr;
+ my @files;
+
+ foreach my $pattern ( @{$argv} ) {
+ my @results = bsd_glob( $pattern );
+
+ if (@results == 0) {
+ @results = $pattern; # Glob didn't match, pass it thru unchanged
+ }
+ elsif ( (@results > 1) or ($results[0] ne $pattern) ) {
+ if (not defined $filter) {
+ eval 'require Win32::File;';
+ if ($@) {
+ $filter = 0;
+ }
+ else {
+ $filter = Win32::File::HIDDEN()|Win32::File::SYSTEM();
+ }
+ } # end unless we've tried to load Win32::File
+ if ( $filter ) {
+ # Filter out hidden and system files:
+ @results = grep { not(Win32::File::GetAttributes($_, $attr) and $attr & $filter) } @results;
+ App::Ack::warn( "$pattern: Matched only hidden files" ) unless @results;
+ } # end if we can filter by file attributes
+ } # end elsif this pattern got expanded
+
+ push @files, @results;
+ } # end foreach pattern
+
+ return \@files;
+ } # end expand_filenames
+} # EXPAND_FILENAMES_SCOPE
+
+
+
+sub get_starting_points {
+ my $argv = shift;
+ my $opt = shift;
+
+ my @what;
+
+ if ( @{$argv} ) {
+ @what = @{ $is_windows ? expand_filenames($argv) : $argv };
+ $_ = File::Next::reslash( $_ ) for @what;
+
+ # Show filenames unless we've specified one single file
+ $opt->{show_filename} = (@what > 1) || (!-f $what[0]);
+ }
+ else {
+ @what = '.'; # Assume current directory
+ $opt->{show_filename} = 1;
+ }
+
+ for my $start_point (@what) {
+ App::Ack::warn( "$start_point: No such file or directory" ) unless -e $start_point;
+ }
+ return \@what;
+}
+
+sub _match {
+ my ( $target, $expression, $invert_flag ) = @_;
+
+ if ( $invert_flag ) {
+ return $target !~ $expression;
+ }
+ else {
+ return $target =~ $expression;
+ }
+}
+
+
+sub get_iterator {
+ my $what = shift;
+ my $opt = shift;
+
+ # Starting points are always searched, no matter what
+ my %starting_point = map { ($_ => 1) } @{$what};
+
+ my $g_regex = defined $opt->{G} ? qr/$opt->{G}/ : undef;
+ my $file_filter;
+
+ if ( $g_regex ) {
+ $file_filter
+ = $opt->{u} ? sub { _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) } # XXX Maybe this should be a 1, no?
+ : $opt->{all} ? sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_searchable( $_ ) ) }
+ : sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_interesting( @ _) ) }
+ ;
+ }
+ else {
+ $file_filter
+ = $opt->{u} ? sub {1}
+ : $opt->{all} ? sub { $starting_point{ $File::Next::name } || is_searchable( $_ ) }
+ : sub { $starting_point{ $File::Next::name } || is_interesting( @_ ) }
+ ;
+ }
+
+ my $descend_filter
+ = $opt->{n} ? sub {0}
+ : $opt->{u} ? sub {1}
+ : \&ignoredir_filter;
+
+ my $iter =
+ File::Next::files( {
+ file_filter => $file_filter,
+ descend_filter => $descend_filter,
+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },
+ sort_files => $opt->{sort_files},
+ follow_symlinks => $opt->{follow},
+ }, @{$what} );
+ return $iter;
+}
+
+
+sub set_up_pager {
+ my $command = shift;
+
+ return if App::Ack::output_to_pipe();
+
+ my $pager;
+ if ( not open( $pager, '|-', $command ) ) {
+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
+ }
+ $fh = $pager;
+
+ return;
+}
+
+
+sub input_from_pipe {
+ return $input_from_pipe;
+}
+
+
+
+sub output_to_pipe {
+ return $output_to_pipe;
+}
+
+
+sub exit_from_ack {
+ my $nmatches = shift;
+
+ my $rc = $nmatches ? 0 : 1;
+ exit $rc;
+}
+
+
+
+1; # End of App::Ack
+package App::Ack::Repository;
+
+
+use warnings;
+use strict;
+
+sub FAIL {
+ require Carp;
+ Carp::confess( 'Must be overloaded' );
+}
+
+
+sub new {
+ FAIL();
+}
+
+
+sub next_resource {
+ FAIL();
+}
+
+
+sub close {
+ FAIL();
+}
+
+1;
+package App::Ack::Resource;
+
+
+use warnings;
+use strict;
+
+sub FAIL {
+ require Carp;
+ Carp::confess( 'Must be overloaded' );
+}
+
+
+sub new {
+ FAIL();
+}
+
+
+sub name {
+ FAIL();
+}
+
+
+sub is_binary {
+ FAIL();
+}
+
+
+
+sub needs_line_scan {
+ FAIL();
+}
+
+
+sub reset {
+ FAIL();
+}
+
+
+sub next_text {
+ FAIL();
+}
+
+
+sub close {
+ FAIL();
+}
+
+1;
+package App::Ack::Plugin::Basic;
+
+
+
+package App::Ack::Resource::Basic;
+
+
+use warnings;
+use strict;
+
+
+our @ISA = qw( App::Ack::Resource );
+
+
+sub new {
+ my $class = shift;
+ my $filename = shift;
+
+ my $self = bless {
+ filename => $filename,
+ fh => undef,
+ could_be_binary => undef,
+ opened => undef,
+ id => undef,
+ }, $class;
+
+ if ( $self->{filename} eq '-' ) {
+ $self->{fh} = *STDIN;
+ $self->{could_be_binary} = 0;
+ }
+ else {
+ if ( !open( $self->{fh}, '<', $self->{filename} ) ) {
+ App::Ack::warn( "$self->{filename}: $!" );
+ return;
+ }
+ $self->{could_be_binary} = 1;
+ }
+
+ return $self;
+}
+
+
+sub name {
+ my $self = shift;
+
+ return $self->{filename};
+}
+
+
+sub is_binary {
+ my $self = shift;
+
+ if ( $self->{could_be_binary} ) {
+ return -B $self->{filename};
+ }
+
+ return 0;
+}
+
+
+
+sub needs_line_scan {
+ my $self = shift;
+ my $opt = shift;
+
+ return 1 if $opt->{v};
+
+ my $size = -s $self->{fh};
+ if ( $size == 0 ) {
+ return 0;
+ }
+ elsif ( $size > 100_000 ) {
+ return 1;
+ }
+
+ my $buffer;
+ my $rc = sysread( $self->{fh}, $buffer, $size );
+ if ( not defined $rc ) {
+ App::Ack::warn( "$self->{filename}: $!" );
+ return 1;
+ }
+ return 0 unless $rc && ( $rc == $size );
+
+ my $regex = $opt->{regex};
+ return $buffer =~ /$regex/m;
+}
+
+
+sub reset {
+ my $self = shift;
+
+ seek( $self->{fh}, 0, 0 )
+ or App::Ack::warn( "$self->{filename}: $!" );
+
+ return;
+}
+
+
+sub next_text {
+ if ( defined ($_ = readline $_[0]->{fh}) ) {
+ $. = ++$_[0]->{line};
+ return 1;
+ }
+
+ return;
+}
+
+
+sub close {
+ my $self = shift;
+
+ if ( not close $self->{fh} ) {
+ App::Ack::warn( $self->name() . ": $!" );
+ }
+
+ return;
+}
+
+package App::Ack::Repository::Basic;
+
+
+our @ISA = qw( App::Ack::Repository );
+
+
+use warnings;
+use strict;
+
+sub new {
+ my $class = shift;
+ my $filename = shift;
+
+ my $self = bless {
+ filename => $filename,
+ nexted => 0,
+ }, $class;
+
+ return $self;
+}
+
+
+sub next_resource {
+ my $self = shift;
+
+ return if $self->{nexted};
+ $self->{nexted} = 1;
+
+ return App::Ack::Resource::Basic->new( $self->{filename} );
+}
+
+
+sub close {
+}
+
+
+
+1;
diff --git a/samples/perl/fib.pl b/samples/perl/fib.pl
new file mode 100644
index 0000000..287ebf1
--- /dev/null
+++ b/samples/perl/fib.pl
@@ -0,0 +1,42 @@
+#! perl
+# Copyright (C) 2001-2003, Parrot Foundation.
+
+=head1 NAME
+
+examples/benchmarks/fib.pl - Fibonacci Benchmark
+
+=head1 SYNOPSIS
+
+ % time perl examples/benchmarks/fib.pl n
+
+=head1 DESCRIPTION
+
+Calculates the Fibonacci Number for C<n> (defaults to 28 if
+unspecified).
+
+=cut
+
+use strict;
+use warnings;
+
+sub fib {
+ my $n = shift;
+ return $n if ( $n < 2 );
+ return fib( $n - 1 ) + fib( $n - 2 );
+}
+my $N = shift || 28;
+
+print "fib($N) = ", fib($N), "\n";
+
+=head1 SEE ALSO
+
+F<examples/benchmarks/fib.pir>.
+
+=cut
+
+# Local Variables:
+# mode: cperl
+# cperl-indent-level: 4
+# fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/samples/perl/oo1.pl b/samples/perl/oo1.pl
new file mode 100644
index 0000000..3789d38
--- /dev/null
+++ b/samples/perl/oo1.pl
@@ -0,0 +1,27 @@
+#! perl
+
+# Copyright (C) 2004-2006, Parrot Foundation.
+
+use strict;
+use warnings;
+
+for my $i ( 1 .. 100000 ) {
+ my $o = new Foo();
+}
+my $o = new Foo();
+print $o->[0], "\n";
+
+package Foo;
+
+sub new {
+ my $self = ref $_[0] ? ref shift : shift;
+ return bless [ 10, 20 ], $self;
+}
+1;
+
+# Local Variables:
+# mode: cperl
+# cperl-indent-level: 4
+# fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/samples/perl/oo2.pl b/samples/perl/oo2.pl
new file mode 100644
index 0000000..82f2bdf
--- /dev/null
+++ b/samples/perl/oo2.pl
@@ -0,0 +1,27 @@
+#! perl
+
+# Copyright (C) 2004-2006, Parrot Foundation.
+
+use strict;
+use warnings;
+
+for my $i ( 1 .. 500000 ) {
+ my $o = new Foo();
+}
+my $o = new Foo();
+print $o->[0], "\n";
+
+package Foo;
+
+sub new {
+ my $self = ref $_[0] ? ref shift : shift;
+ return bless [ 10, 20 ], $self;
+}
+1;
+
+# Local Variables:
+# mode: cperl
+# cperl-indent-level: 4
+# fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/samples/perl/oo3.pl b/samples/perl/oo3.pl
new file mode 100644
index 0000000..e86a190
--- /dev/null
+++ b/samples/perl/oo3.pl
@@ -0,0 +1,29 @@
+#! perl
+
+# Copyright (C) 2004-2006, Parrot Foundation.
+
+use strict;
+use warnings;
+
+my $o = new Foo();
+for my $i ( 1 .. 500000 ) {
+ my $x = $o->[0];
+ my $y = $o->[1];
+}
+print $o->[0], "\n";
+
+package Foo;
+
+sub new {
+ my $self = ref $_[0] ? ref shift : shift;
+ return bless [ 10, 20 ], $self;
+}
+
+1;
+
+# Local Variables:
+# mode: cperl
+# cperl-indent-level: 4
+# fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
diff --git a/samples/perl/perl-test.t b/samples/perl/perl-test.t
new file mode 100644
index 0000000..a234f9a
--- /dev/null
+++ b/samples/perl/perl-test.t
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+
+use Foo::Bar
+
+$n = 42;
+$name = "world";
+@array = ("1","2","3");
+%hash = ("foo":"bar");
+my $name = "josh";
diff --git a/samples/perl/script.pl b/samples/perl/script.pl
new file mode 100755
index 0000000..bb20fbc
--- /dev/null
+++ b/samples/perl/script.pl
@@ -0,0 +1,2 @@
+#!/usr/local/bin/perl
+print "Perl\n"
diff --git a/samples/perl/test-perl.pl b/samples/perl/test-perl.pl
new file mode 100644
index 0000000..8371027
--- /dev/null
+++ b/samples/perl/test-perl.pl
@@ -0,0 +1,2 @@
+#!/usr/bin/perl
+print "Hello, world!\n";
diff --git a/samples/perl/test-perl2.pl b/samples/perl/test-perl2.pl
new file mode 100644
index 0000000..55a2762
--- /dev/null
+++ b/samples/perl/test-perl2.pl
@@ -0,0 +1,3 @@
+
+# Perl file without shebang
+print "Hello, world!\n";
diff --git a/samples/php/Application.php b/samples/php/Application.php
new file mode 100644
index 0000000..d1c2e39
--- /dev/null
+++ b/samples/php/Application.php
@@ -0,0 +1,1066 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\ArgvInput;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputDefinition;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Output\Output;
+use Symfony\Component\Console\Output\ConsoleOutput;
+use Symfony\Component\Console\Output\ConsoleOutputInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Command\HelpCommand;
+use Symfony\Component\Console\Command\ListCommand;
+use Symfony\Component\Console\Helper\HelperSet;
+use Symfony\Component\Console\Helper\FormatterHelper;
+use Symfony\Component\Console\Helper\DialogHelper;
+
+/**
+ * An Application is the container for a collection of commands.
+ *
+ * It is the main entry point of a Console application.
+ *
+ * This class is optimized for a standard CLI environment.
+ *
+ * Usage:
+ *
+ * $app = new Application('myapp', '1.0 (stable)');
+ * $app->add(new SimpleCommand());
+ * $app->run();
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Application
+{
+ private $commands;
+ private $wantHelps = false;
+ private $runningCommand;
+ private $name;
+ private $version;
+ private $catchExceptions;
+ private $autoExit;
+ private $definition;
+ private $helperSet;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name The name of the application
+ * @param string $version The version of the application
+ *
+ * @api
+ */
+ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
+ {
+ $this->name = $name;
+ $this->version = $version;
+ $this->catchExceptions = true;
+ $this->autoExit = true;
+ $this->commands = array();
+ $this->helperSet = $this->getDefaultHelperSet();
+ $this->definition = $this->getDefaultInputDefinition();
+
+ foreach ($this->getDefaultCommands() as $command) {
+ $this->add($command);
+ }
+ }
+
+ /**
+ * Runs the current application.
+ *
+ * @param InputInterface $input An Input instance
+ * @param OutputInterface $output An Output instance
+ *
+ * @return integer 0 if everything went fine, or an error code
+ *
+ * @throws \Exception When doRun returns Exception
+ *
+ * @api
+ */
+ public function run(InputInterface $input = null, OutputInterface $output = null)
+ {
+ if (null === $input) {
+ $input = new ArgvInput();
+ }
+
+ if (null === $output) {
+ $output = new ConsoleOutput();
+ }
+
+ try {
+ $statusCode = $this->doRun($input, $output);
+ } catch (\Exception $e) {
+ if (!$this->catchExceptions) {
+ throw $e;
+ }
+
+ if ($output instanceof ConsoleOutputInterface) {
+ $this->renderException($e, $output->getErrorOutput());
+ } else {
+ $this->renderException($e, $output);
+ }
+ $statusCode = $e->getCode();
+
+ $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1;
+ }
+
+ if ($this->autoExit) {
+ if ($statusCode > 255) {
+ $statusCode = 255;
+ }
+ // @codeCoverageIgnoreStart
+ exit($statusCode);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $statusCode;
+ }
+
+ /**
+ * Runs the current application.
+ *
+ * @param InputInterface $input An Input instance
+ * @param OutputInterface $output An Output instance
+ *
+ * @return integer 0 if everything went fine, or an error code
+ */
+ public function doRun(InputInterface $input, OutputInterface $output)
+ {
+ $name = $this->getCommandName($input);
+
+ if (true === $input->hasParameterOption(array('--ansi'))) {
+ $output->setDecorated(true);
+ } elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
+ $output->setDecorated(false);
+ }
+
+ if (true === $input->hasParameterOption(array('--help', '-h'))) {
+ if (!$name) {
+ $name = 'help';
+ $input = new ArrayInput(array('command' => 'help'));
+ } else {
+ $this->wantHelps = true;
+ }
+ }
+
+ if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
+ $input->setInteractive(false);
+ }
+
+ if (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) {
+ $inputStream = $this->getHelperSet()->get('dialog')->getInputStream();
+ if (!posix_isatty($inputStream)) {
+ $input->setInteractive(false);
+ }
+ }
+
+ if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
+ $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
+ } elseif (true === $input->hasParameterOption(array('--verbose', '-v'))) {
+ $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
+ }
+
+ if (true === $input->hasParameterOption(array('--version', '-V'))) {
+ $output->writeln($this->getLongVersion());
+
+ return 0;
+ }
+
+ if (!$name) {
+ $name = 'list';
+ $input = new ArrayInput(array('command' => 'list'));
+ }
+
+ // the command name MUST be the first element of the input
+ $command = $this->find($name);
+
+ $this->runningCommand = $command;
+ $statusCode = $command->run($input, $output);
+ $this->runningCommand = null;
+
+ return is_numeric($statusCode) ? $statusCode : 0;
+ }
+
+ /**
+ * Set a helper set to be used with the command.
+ *
+ * @param HelperSet $helperSet The helper set
+ *
+ * @api
+ */
+ public function setHelperSet(HelperSet $helperSet)
+ {
+ $this->helperSet = $helperSet;
+ }
+
+ /**
+ * Get the helper set associated with the command.
+ *
+ * @return HelperSet The HelperSet instance associated with this command
+ *
+ * @api
+ */
+ public function getHelperSet()
+ {
+ return $this->helperSet;
+ }
+
+ /**
+ * Gets the InputDefinition related to this Application.
+ *
+ * @return InputDefinition The InputDefinition instance
+ */
+ public function getDefinition()
+ {
+ return $this->definition;
+ }
+
+ /**
+ * Gets the help message.
+ *
+ * @return string A help message.
+ */
+ public function getHelp()
+ {
+ $messages = array(
+ $this->getLongVersion(),
+ '',
+ '<comment>Usage:</comment>',
+ sprintf(" [options] command [arguments]\n"),
+ '<comment>Options:</comment>',
+ );
+
+ foreach ($this->getDefinition()->getOptions() as $option) {
+ $messages[] = sprintf(' %-29s %s %s',
+ '<info>--'.$option->getName().'</info>',
+ $option->getShortcut() ? '<info>-'.$option->getShortcut().'</info>' : ' ',
+ $option->getDescription()
+ );
+ }
+
+ return implode(PHP_EOL, $messages);
+ }
+
+ /**
+ * Sets whether to catch exceptions or not during commands execution.
+ *
+ * @param Boolean $boolean Whether to catch exceptions or not during commands execution
+ *
+ * @api
+ */
+ public function setCatchExceptions($boolean)
+ {
+ $this->catchExceptions = (Boolean) $boolean;
+ }
+
+ /**
+ * Sets whether to automatically exit after a command execution or not.
+ *
+ * @param Boolean $boolean Whether to automatically exit after a command execution or not
+ *
+ * @api
+ */
+ public function setAutoExit($boolean)
+ {
+ $this->autoExit = (Boolean) $boolean;
+ }
+
+ /**
+ * Gets the name of the application.
+ *
+ * @return string The application name
+ *
+ * @api
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Sets the application name.
+ *
+ * @param string $name The application name
+ *
+ * @api
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Gets the application version.
+ *
+ * @return string The application version
+ *
+ * @api
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Sets the application version.
+ *
+ * @param string $version The application version
+ *
+ * @api
+ */
+ public function setVersion($version)
+ {
+ $this->version = $version;
+ }
+
+ /**
+ * Returns the long version of the application.
+ *
+ * @return string The long application version
+ *
+ * @api
+ */
+ public function getLongVersion()
+ {
+ if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
+ return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
+ }
+
+ return '<info>Console Tool</info>';
+ }
+
+ /**
+ * Registers a new command.
+ *
+ * @param string $name The command name
+ *
+ * @return Command The newly created command
+ *
+ * @api
+ */
+ public function register($name)
+ {
+ return $this->add(new Command($name));
+ }
+
+ /**
+ * Adds an array of command objects.
+ *
+ * @param Command[] $commands An array of commands
+ *
+ * @api
+ */
+ public function addCommands(array $commands)
+ {
+ foreach ($commands as $command) {
+ $this->add($command);
+ }
+ }
+
+ /**
+ * Adds a command object.
+ *
+ * If a command with the same name already exists, it will be overridden.
+ *
+ * @param Command $command A Command object
+ *
+ * @return Command The registered command
+ *
+ * @api
+ */
+ public function add(Command $command)
+ {
+ $command->setApplication($this);
+
+ if (!$command->isEnabled()) {
+ $command->setApplication(null);
+
+ return;
+ }
+
+ $this->commands[$command->getName()] = $command;
+
+ foreach ($command->getAliases() as $alias) {
+ $this->commands[$alias] = $command;
+ }
+
+ return $command;
+ }
+
+ /**
+ * Returns a registered command by name or alias.
+ *
+ * @param string $name The command name or alias
+ *
+ * @return Command A Command object
+ *
+ * @throws \InvalidArgumentException When command name given does not exist
+ *
+ * @api
+ */
+ public function get($name)
+ {
+ if (!isset($this->commands[$name])) {
+ throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
+ }
+
+ $command = $this->commands[$name];
+
+ if ($this->wantHelps) {
+ $this->wantHelps = false;
+
+ $helpCommand = $this->get('help');
+ $helpCommand->setCommand($command);
+
+ return $helpCommand;
+ }
+
+ return $command;
+ }
+
+ /**
+ * Returns true if the command exists, false otherwise.
+ *
+ * @param string $name The command name or alias
+ *
+ * @return Boolean true if the command exists, false otherwise
+ *
+ * @api
+ */
+ public function has($name)
+ {
+ return isset($this->commands[$name]);
+ }
+
+ /**
+ * Returns an array of all unique namespaces used by currently registered commands.
+ *
+ * It does not returns the global namespace which always exists.
+ *
+ * @return array An array of namespaces
+ */
+ public function getNamespaces()
+ {
+ $namespaces = array();
+ foreach ($this->commands as $command) {
+ $namespaces[] = $this->extractNamespace($command->getName());
+
+ foreach ($command->getAliases() as $alias) {
+ $namespaces[] = $this->extractNamespace($alias);
+ }
+ }
+
+ return array_values(array_unique(array_filter($namespaces)));
+ }
+
+ /**
+ * Finds a registered namespace by a name or an abbreviation.
+ *
+ * @param string $namespace A namespace or abbreviation to search for
+ *
+ * @return string A registered namespace
+ *
+ * @throws \InvalidArgumentException When namespace is incorrect or ambiguous
+ */
+ public function findNamespace($namespace)
+ {
+ $allNamespaces = array();
+ foreach ($this->getNamespaces() as $n) {
+ $allNamespaces[$n] = explode(':', $n);
+ }
+
+ $found = array();
+ foreach (explode(':', $namespace) as $i => $part) {
+ $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $allNamespaces)))));
+
+ if (!isset($abbrevs[$part])) {
+ $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
+
+ if (1 <= $i) {
+ $part = implode(':', $found).':'.$part;
+ }
+
+ if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) {
+ $message .= "\n\nDid you mean one of these?\n ";
+ $message .= implode("\n ", $alternatives);
+ }
+
+ throw new \InvalidArgumentException($message);
+ }
+
+ if (count($abbrevs[$part]) > 1) {
+ throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part])));
+ }
+
+ $found[] = $abbrevs[$part][0];
+ }
+
+ return implode(':', $found);
+ }
+
+ /**
+ * Finds a command by name or alias.
+ *
+ * Contrary to get, this command tries to find the best
+ * match if you give it an abbreviation of a name or alias.
+ *
+ * @param string $name A command name or a command alias
+ *
+ * @return Command A Command instance
+ *
+ * @throws \InvalidArgumentException When command name is incorrect or ambiguous
+ *
+ * @api
+ */
+ public function find($name)
+ {
+ // namespace
+ $namespace = '';
+ $searchName = $name;
+ if (false !== $pos = strrpos($name, ':')) {
+ $namespace = $this->findNamespace(substr($name, 0, $pos));
+ $searchName = $namespace.substr($name, $pos);
+ }
+
+ // name
+ $commands = array();
+ foreach ($this->commands as $command) {
+ if ($this->extractNamespace($command->getName()) == $namespace) {
+ $commands[] = $command->getName();
+ }
+ }
+
+ $abbrevs = static::getAbbreviations(array_unique($commands));
+ if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) {
+ return $this->get($abbrevs[$searchName][0]);
+ }
+
+ if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) {
+ $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]);
+
+ throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
+ }
+
+ // aliases
+ $aliases = array();
+ foreach ($this->commands as $command) {
+ foreach ($command->getAliases() as $alias) {
+ if ($this->extractNamespace($alias) == $namespace) {
+ $aliases[] = $alias;
+ }
+ }
+ }
+
+ $aliases = static::getAbbreviations(array_unique($aliases));
+ if (!isset($aliases[$searchName])) {
+ $message = sprintf('Command "%s" is not defined.', $name);
+
+ if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) {
+ $message .= "\n\nDid you mean one of these?\n ";
+ $message .= implode("\n ", $alternatives);
+ }
+
+ throw new \InvalidArgumentException($message);
+ }
+
+ if (count($aliases[$searchName]) > 1) {
+ throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName])));
+ }
+
+ return $this->get($aliases[$searchName][0]);
+ }
+
+ /**
+ * Gets the commands (registered in the given namespace if provided).
+ *
+ * The array keys are the full names and the values the command instances.
+ *
+ * @param string $namespace A namespace name
+ *
+ * @return array An array of Command instances
+ *
+ * @api
+ */
+ public function all($namespace = null)
+ {
+ if (null === $namespace) {
+ return $this->commands;
+ }
+
+ $commands = array();
+ foreach ($this->commands as $name => $command) {
+ if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
+ $commands[$name] = $command;
+ }
+ }
+
+ return $commands;
+ }
+
+ /**
+ * Returns an array of possible abbreviations given a set of names.
+ *
+ * @param array $names An array of names
+ *
+ * @return array An array of abbreviations
+ */
+ static public function getAbbreviations($names)
+ {
+ $abbrevs = array();
+ foreach ($names as $name) {
+ for ($len = strlen($name) - 1; $len > 0; --$len) {
+ $abbrev = substr($name, 0, $len);
+ if (!isset($abbrevs[$abbrev])) {
+ $abbrevs[$abbrev] = array($name);
+ } else {
+ $abbrevs[$abbrev][] = $name;
+ }
+ }
+ }
+
+ // Non-abbreviations always get entered, even if they aren't unique
+ foreach ($names as $name) {
+ $abbrevs[$name] = array($name);
+ }
+
+ return $abbrevs;
+ }
+
+ /**
+ * Returns a text representation of the Application.
+ *
+ * @param string $namespace An optional namespace name
+ * @param boolean $raw Whether to return raw command list
+ *
+ * @return string A string representing the Application
+ */
+ public function asText($namespace = null, $raw = false)
+ {
+ $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands;
+
+ $width = 0;
+ foreach ($commands as $command) {
+ $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
+ }
+ $width += 2;
+
+ if ($raw) {
+ $messages = array();
+ foreach ($this->sortCommands($commands) as $space => $commands) {
+ foreach ($commands as $name => $command) {
+ $messages[] = sprintf("%-${width}s %s", $name, $command->getDescription());
+ }
+ }
+
+ return implode(PHP_EOL, $messages);
+ }
+
+ $messages = array($this->getHelp(), '');
+ if ($namespace) {
+ $messages[] = sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $namespace);
+ } else {
+ $messages[] = '<comment>Available commands:</comment>';
+ }
+
+ // add commands by namespace
+ foreach ($this->sortCommands($commands) as $space => $commands) {
+ if (!$namespace && '_global' !== $space) {
+ $messages[] = '<comment>'.$space.'</comment>';
+ }
+
+ foreach ($commands as $name => $command) {
+ $messages[] = sprintf(" <info>%-${width}s</info> %s", $name, $command->getDescription());
+ }
+ }
+
+ return implode(PHP_EOL, $messages);
+ }
+
+ /**
+ * Returns an XML representation of the Application.
+ *
+ * @param string $namespace An optional namespace name
+ * @param Boolean $asDom Whether to return a DOM or an XML string
+ *
+ * @return string|DOMDocument An XML string representing the Application
+ */
+ public function asXml($namespace = null, $asDom = false)
+ {
+ $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands;
+
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->formatOutput = true;
+ $dom->appendChild($xml = $dom->createElement('symfony'));
+
+ $xml->appendChild($commandsXML = $dom->createElement('commands'));
+
+ if ($namespace) {
+ $commandsXML->setAttribute('namespace', $namespace);
+ } else {
+ $namespacesXML = $dom->createElement('namespaces');
+ $xml->appendChild($namespacesXML);
+ }
+
+ // add commands by namespace
+ foreach ($this->sortCommands($commands) as $space => $commands) {
+ if (!$namespace) {
+ $namespaceArrayXML = $dom->createElement('namespace');
+ $namespacesXML->appendChild($namespaceArrayXML);
+ $namespaceArrayXML->setAttribute('id', $space);
+ }
+
+ foreach ($commands as $name => $command) {
+ if ($name !== $command->getName()) {
+ continue;
+ }
+
+ if (!$namespace) {
+ $commandXML = $dom->createElement('command');
+ $namespaceArrayXML->appendChild($commandXML);
+ $commandXML->appendChild($dom->createTextNode($name));
+ }
+
+ $node = $command->asXml(true)->getElementsByTagName('command')->item(0);
+ $node = $dom->importNode($node, true);
+
+ $commandsXML->appendChild($node);
+ }
+ }
+
+ return $asDom ? $dom : $dom->saveXml();
+ }
+
+ /**
+ * Renders a caught exception.
+ *
+ * @param Exception $e An exception instance
+ * @param OutputInterface $output An OutputInterface instance
+ */
+ public function renderException($e, $output)
+ {
+ $strlen = function ($string) {
+ if (!function_exists('mb_strlen')) {
+ return strlen($string);
+ }
+
+ if (false === $encoding = mb_detect_encoding($string)) {
+ return strlen($string);
+ }
+
+ return mb_strlen($string, $encoding);
+ };
+
+ do {
+ $title = sprintf(' [%s] ', get_class($e));
+ $len = $strlen($title);
+ $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
+ $lines = array();
+ foreach (preg_split("{\r?\n}", $e->getMessage()) as $line) {
+ foreach (str_split($line, $width - 4) as $line) {
+ $lines[] = sprintf(' %s ', $line);
+ $len = max($strlen($line) + 4, $len);
+ }
+ }
+
+ $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', max(0, $len - $strlen($title))));
+
+ foreach ($lines as $line) {
+ $messages[] = $line.str_repeat(' ', $len - $strlen($line));
+ }
+
+ $messages[] = str_repeat(' ', $len);
+
+ $output->writeln("");
+ $output->writeln("");
+ foreach ($messages as $message) {
+ $output->writeln('<error>'.$message.'</error>');
+ }
+ $output->writeln("");
+ $output->writeln("");
+
+ if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) {
+ $output->writeln('<comment>Exception trace:</comment>');
+
+ // exception related properties
+ $trace = $e->getTrace();
+ array_unshift($trace, array(
+ 'function' => '',
+ 'file' => $e->getFile() != null ? $e->getFile() : 'n/a',
+ 'line' => $e->getLine() != null ? $e->getLine() : 'n/a',
+ 'args' => array(),
+ ));
+
+ for ($i = 0, $count = count($trace); $i < $count; $i++) {
+ $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
+ $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
+ $function = $trace[$i]['function'];
+ $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
+ $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
+
+ $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
+ }
+
+ $output->writeln("");
+ $output->writeln("");
+ }
+ } while ($e = $e->getPrevious());
+
+ if (null !== $this->runningCommand) {
+ $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
+ $output->writeln("");
+ $output->writeln("");
+ }
+ }
+
+ /**
+ * Tries to figure out the terminal width in which this application runs
+ *
+ * @return int|null
+ */
+ protected function getTerminalWidth()
+ {
+ if (defined('PHP_WINDOWS_VERSION_BUILD') && $ansicon = getenv('ANSICON')) {
+ return preg_replace('{^(\d+)x.*$}', '$1', $ansicon);
+ }
+
+ if (preg_match("{rows.(\d+);.columns.(\d+);}i", $this->getSttyColumns(), $match)) {
+ return $match[2];
+ }
+ }
+
+ /**
+ * Tries to figure out the terminal height in which this application runs
+ *
+ * @return int|null
+ */
+ protected function getTerminalHeight()
+ {
+ if (defined('PHP_WINDOWS_VERSION_BUILD') && $ansicon = getenv('ANSICON')) {
+ return preg_replace('{^\d+x\d+ \(\d+x(\d+)\)$}', '$1', trim($ansicon));
+ }
+
+ if (preg_match("{rows.(\d+);.columns.(\d+);}i", $this->getSttyColumns(), $match)) {
+ return $match[1];
+ }
+ }
+
+ /**
+ * Gets the name of the command based on input.
+ *
+ * @param InputInterface $input The input interface
+ *
+ * @return string The command name
+ */
+ protected function getCommandName(InputInterface $input)
+ {
+ return $input->getFirstArgument('command');
+ }
+
+ /**
+ * Gets the default input definition.
+ *
+ * @return InputDefinition An InputDefinition instance
+ */
+ protected function getDefaultInputDefinition()
+ {
+ return new InputDefinition(array(
+ new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
+
+ new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
+ new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'),
+ new InputOption('--verbose', '-v', InputOption::VALUE_NONE, 'Increase verbosity of messages.'),
+ new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'),
+ new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'),
+ new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'),
+ new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
+ ));
+ }
+
+ /**
+ * Gets the default commands that should always be available.
+ *
+ * @return array An array of default Command instances
+ */
+ protected function getDefaultCommands()
+ {
+ return array(new HelpCommand(), new ListCommand());
+ }
+
+ /**
+ * Gets the default helper set with the helpers that should always be available.
+ *
+ * @return HelperSet A HelperSet instance
+ */
+ protected function getDefaultHelperSet()
+ {
+ return new HelperSet(array(
+ new FormatterHelper(),
+ new DialogHelper(),
+ ));
+ }
+
+ /**
+ * Runs and parses stty -a if it's available, suppressing any error output
+ *
+ * @return string
+ */
+ private function getSttyColumns()
+ {
+ if (!function_exists('proc_open')) {
+ return;
+ }
+
+ $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
+ $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
+ if (is_resource($process)) {
+ $info = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+ proc_close($process);
+
+ return $info;
+ }
+ }
+
+ /**
+ * Sorts commands in alphabetical order.
+ *
+ * @param array $commands An associative array of commands to sort
+ *
+ * @return array A sorted array of commands
+ */
+ private function sortCommands($commands)
+ {
+ $namespacedCommands = array();
+ foreach ($commands as $name => $command) {
+ $key = $this->extractNamespace($name, 1);
+ if (!$key) {
+ $key = '_global';
+ }
+
+ $namespacedCommands[$key][$name] = $command;
+ }
+ ksort($namespacedCommands);
+
+ foreach ($namespacedCommands as &$commands) {
+ ksort($commands);
+ }
+
+ return $namespacedCommands;
+ }
+
+ /**
+ * Returns abbreviated suggestions in string format.
+ *
+ * @param array $abbrevs Abbreviated suggestions to convert
+ *
+ * @return string A formatted string of abbreviated suggestions
+ */
+ private function getAbbreviationSuggestions($abbrevs)
+ {
+ return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
+ }
+
+ /**
+ * Returns the namespace part of the command name.
+ *
+ * @param string $name The full name of the command
+ * @param string $limit The maximum number of parts of the namespace
+ *
+ * @return string The namespace of the command
+ */
+ private function extractNamespace($name, $limit = null)
+ {
+ $parts = explode(':', $name);
+ array_pop($parts);
+
+ return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
+ }
+
+ /**
+ * Finds alternative commands of $name
+ *
+ * @param string $name The full name of the command
+ * @param array $abbrevs The abbreviations
+ *
+ * @return array A sorted array of similar commands
+ */
+ private function findAlternativeCommands($name, $abbrevs)
+ {
+ $callback = function($item) {
+ return $item->getName();
+ };
+
+ return $this->findAlternatives($name, $this->commands, $abbrevs, $callback);
+ }
+
+ /**
+ * Finds alternative namespace of $name
+ *
+ * @param string $name The full name of the namespace
+ * @param array $abbrevs The abbreviations
+ *
+ * @return array A sorted array of similar namespace
+ */
+ private function findAlternativeNamespace($name, $abbrevs)
+ {
+ return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs);
+ }
+
+ /**
+ * Finds alternative of $name among $collection,
+ * if nothing is found in $collection, try in $abbrevs
+ *
+ * @param string $name The string
+ * @param array|Traversable $collection The collecion
+ * @param array $abbrevs The abbreviations
+ * @param Closure|string|array $callback The callable to transform collection item before comparison
+ *
+ * @return array A sorted array of similar string
+ */
+ private function findAlternatives($name, $collection, $abbrevs, $callback = null)
+ {
+ $alternatives = array();
+
+ foreach ($collection as $item) {
+ if (null !== $callback) {
+ $item = call_user_func($callback, $item);
+ }
+
+ $lev = levenshtein($name, $item);
+ if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
+ $alternatives[$item] = $lev;
+ }
+ }
+
+ if (!$alternatives) {
+ foreach ($abbrevs as $key => $values) {
+ $lev = levenshtein($name, $key);
+ if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
+ foreach ($values as $value) {
+ $alternatives[$value] = $lev;
+ }
+ }
+ }
+ }
+
+ asort($alternatives);
+
+ return array_keys($alternatives);
+ }
+}
diff --git a/samples/php/Client.php b/samples/php/Client.php
new file mode 100644
index 0000000..c57b9e3
--- /dev/null
+++ b/samples/php/Client.php
@@ -0,0 +1,492 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+use Symfony\Component\DomCrawler\Crawler;
+use Symfony\Component\DomCrawler\Link;
+use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\Process\PhpProcess;
+
+/**
+ * Client simulates a browser.
+ *
+ * To make the actual request, you need to implement the doRequest() method.
+ *
+ * If you want to be able to run requests in their own process (insulated flag),
+ * you need to also implement the getScript() method.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+abstract class Client
+{
+ protected $history;
+ protected $cookieJar;
+ protected $server;
+ protected $request;
+ protected $response;
+ protected $crawler;
+ protected $insulated;
+ protected $redirect;
+ protected $followRedirects;
+
+ /**
+ * Constructor.
+ *
+ * @param array $server The server parameters (equivalent of $_SERVER)
+ * @param History $history A History instance to store the browser history
+ * @param CookieJar $cookieJar A CookieJar instance to store the cookies
+ *
+ * @api
+ */
+ public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null)
+ {
+ $this->setServerParameters($server);
+ $this->history = null === $history ? new History() : $history;
+ $this->cookieJar = null === $cookieJar ? new CookieJar() : $cookieJar;
+ $this->insulated = false;
+ $this->followRedirects = true;
+ }
+
+ /**
+ * Sets whether to automatically follow redirects or not.
+ *
+ * @param Boolean $followRedirect Whether to follow redirects
+ *
+ * @api
+ */
+ public function followRedirects($followRedirect = true)
+ {
+ $this->followRedirects = (Boolean) $followRedirect;
+ }
+
+ /**
+ * Sets the insulated flag.
+ *
+ * @param Boolean $insulated Whether to insulate the requests or not
+ *
+ * @throws \RuntimeException When Symfony Process Component is not installed
+ *
+ * @api
+ */
+ public function insulate($insulated = true)
+ {
+ if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
+ // @codeCoverageIgnoreStart
+ throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
+ // @codeCoverageIgnoreEnd
+ }
+
+ $this->insulated = (Boolean) $insulated;
+ }
+
+ /**
+ * Sets server parameters.
+ *
+ * @param array $server An array of server parameters
+ *
+ * @api
+ */
+ public function setServerParameters(array $server)
+ {
+ $this->server = array_merge(array(
+ 'HTTP_HOST' => 'localhost',
+ 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
+ ), $server);
+ }
+
+ /**
+ * Sets single server parameter.
+ *
+ * @param string $key A key of the parameter
+ * @param string $value A value of the parameter
+ */
+ public function setServerParameter($key, $value)
+ {
+ $this->server[$key] = $value;
+ }
+
+ /**
+ * Gets single server parameter for specified key.
+ *
+ * @param string $key A key of the parameter to get
+ * @param string $default A default value when key is undefined
+ *
+ * @return string A value of the parameter
+ */
+ public function getServerParameter($key, $default = '')
+ {
+ return (isset($this->server[$key])) ? $this->server[$key] : $default;
+ }
+
+ /**
+ * Returns the History instance.
+ *
+ * @return History A History instance
+ *
+ * @api
+ */
+ public function getHistory()
+ {
+ return $this->history;
+ }
+
+ /**
+ * Returns the CookieJar instance.
+ *
+ * @return CookieJar A CookieJar instance
+ *
+ * @api
+ */
+ public function getCookieJar()
+ {
+ return $this->cookieJar;
+ }
+
+ /**
+ * Returns the current Crawler instance.
+ *
+ * @return Crawler A Crawler instance
+ *
+ * @api
+ */
+ public function getCrawler()
+ {
+ return $this->crawler;
+ }
+
+ /**
+ * Returns the current Response instance.
+ *
+ * @return Response A Response instance
+ *
+ * @api
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Returns the current Request instance.
+ *
+ * @return Request A Request instance
+ *
+ * @api
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ /**
+ * Clicks on a given link.
+ *
+ * @param Link $link A Link instance
+ *
+ * @return Crawler
+ *
+ * @api
+ */
+ public function click(Link $link)
+ {
+ if ($link instanceof Form) {
+ return $this->submit($link);
+ }
+
+ return $this->request($link->getMethod(), $link->getUri());
+ }
+
+ /**
+ * Submits a form.
+ *
+ * @param Form $form A Form instance
+ * @param array $values An array of form field values
+ *
+ * @return Crawler
+ *
+ * @api
+ */
+ public function submit(Form $form, array $values = array())
+ {
+ $form->setValues($values);
+
+ return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
+ }
+
+ /**
+ * Calls a URI.
+ *
+ * @param string $method The request method
+ * @param string $uri The URI to fetch
+ * @param array $parameters The Request parameters
+ * @param array $files The files
+ * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does)
+ * @param string $content The raw body data
+ * @param Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
+ *
+ * @return Crawler
+ *
+ * @api
+ */
+ public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
+ {
+ $uri = $this->getAbsoluteUri($uri);
+
+ $server = array_merge($this->server, $server);
+ if (!$this->history->isEmpty()) {
+ $server['HTTP_REFERER'] = $this->history->current()->getUri();
+ }
+ $server['HTTP_HOST'] = parse_url($uri, PHP_URL_HOST);
+ $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
+
+ $request = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
+
+ $this->request = $this->filterRequest($request);
+
+ if (true === $changeHistory) {
+ $this->history->add($request);
+ }
+
+ if ($this->insulated) {
+ $this->response = $this->doRequestInProcess($this->request);
+ } else {
+ $this->response = $this->doRequest($this->request);
+ }
+
+ $response = $this->filterResponse($this->response);
+
+ $this->cookieJar->updateFromResponse($response);
+
+ $this->redirect = $response->getHeader('Location');
+
+ if ($this->followRedirects && $this->redirect) {
+ return $this->crawler = $this->followRedirect();
+ }
+
+ return $this->crawler = $this->createCrawlerFromContent($request->getUri(), $response->getContent(), $response->getHeader('Content-Type'));
+ }
+
+ /**
+ * Makes a request in another process.
+ *
+ * @param Request $request A Request instance
+ *
+ * @return Response A Response instance
+ *
+ * @throws \RuntimeException When processing returns exit code
+ */
+ protected function doRequestInProcess($request)
+ {
+ // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user.
+ $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir()));
+ $process->run();
+
+ if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
+ throw new \RuntimeException('OUTPUT: '.$process->getOutput().' ERROR OUTPUT: '.$process->getErrorOutput());
+ }
+
+ return unserialize($process->getOutput());
+ }
+
+ /**
+ * Makes a request.
+ *
+ * @param Request $request A Request instance
+ *
+ * @return Response A Response instance
+ */
+ abstract protected function doRequest($request);
+
+ /**
+ * Returns the script to execute when the request must be insulated.
+ *
+ * @param Request $request A Request instance
+ *
+ * @throws \LogicException When this abstract class is not implemented
+ */
+ protected function getScript($request)
+ {
+ // @codeCoverageIgnoreStart
+ throw new \LogicException('To insulate requests, you need to override the getScript() method.');
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * Filters the request.
+ *
+ * @param Request $request The request to filter
+ *
+ * @return Request
+ */
+ protected function filterRequest(Request $request)
+ {
+ return $request;
+ }
+
+ /**
+ * Filters the Response.
+ *
+ * @param Response $response The Response to filter
+ *
+ * @return Response
+ */
+ protected function filterResponse($response)
+ {
+ return $response;
+ }
+
+ /**
+ * Creates a crawler.
+ *
+ * This method returns null if the DomCrawler component is not available.
+ *
+ * @param string $uri A uri
+ * @param string $content Content for the crawler to use
+ * @param string $type Content type
+ *
+ * @return Crawler|null
+ */
+ protected function createCrawlerFromContent($uri, $content, $type)
+ {
+ if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+ return null;
+ }
+
+ $crawler = new Crawler(null, $uri);
+ $crawler->addContent($content, $type);
+
+ return $crawler;
+ }
+
+ /**
+ * Goes back in the browser history.
+ *
+ * @return Crawler
+ *
+ * @api
+ */
+ public function back()
+ {
+ return $this->requestFromRequest($this->history->back(), false);
+ }
+
+ /**
+ * Goes forward in the browser history.
+ *
+ * @return Crawler
+ *
+ * @api
+ */
+ public function forward()
+ {
+ return $this->requestFromRequest($this->history->forward(), false);
+ }
+
+ /**
+ * Reloads the current browser.
+ *
+ * @return Crawler
+ *
+ * @api
+ */
+ public function reload()
+ {
+ return $this->requestFromRequest($this->history->current(), false);
+ }
+
+ /**
+ * Follow redirects?
+ *
+ * @return Crawler
+ *
+ * @throws \LogicException If request was not a redirect
+ *
+ * @api
+ */
+ public function followRedirect()
+ {
+ if (empty($this->redirect)) {
+ throw new \LogicException('The request was not redirected.');
+ }
+
+ return $this->request('get', $this->redirect);
+ }
+
+ /**
+ * Restarts the client.
+ *
+ * It flushes history and all cookies.
+ *
+ * @api
+ */
+ public function restart()
+ {
+ $this->cookieJar->clear();
+ $this->history->clear();
+ }
+
+ /**
+ * Takes a URI and converts it to absolute if it is not already absolute.
+ *
+ * @param string $uri A uri
+ *
+ * @return string An absolute uri
+ */
+ protected function getAbsoluteUri($uri)
+ {
+ // already absolute?
+ if (0 === strpos($uri, 'http')) {
+ return $uri;
+ }
+
+ if (!$this->history->isEmpty()) {
+ $currentUri = $this->history->current()->getUri();
+ } else {
+ $currentUri = sprintf('http%s://%s/',
+ isset($this->server['HTTPS']) ? 's' : '',
+ isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
+ );
+ }
+
+ // anchor?
+ if (!$uri || '#' == $uri[0]) {
+ return preg_replace('/#.*?$/', '', $currentUri).$uri;
+ }
+
+ if ('/' !== $uri[0]) {
+ $path = parse_url($currentUri, PHP_URL_PATH);
+
+ if ('/' !== substr($path, -1)) {
+ $path = substr($path, 0, strrpos($path, '/') + 1);
+ }
+
+ $uri = $path.$uri;
+ }
+
+ return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
+ }
+
+ /**
+ * Makes a request from a Request object directly.
+ *
+ * @param Request $request A Request instance
+ * @param Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
+ *
+ * @return Crawler
+ */
+ protected function requestFromRequest(Request $request, $changeHistory = true)
+ {
+ return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
+ }
+}
diff --git a/samples/php/Controller.php b/samples/php/Controller.php
new file mode 100644
index 0000000..6b8f195
--- /dev/null
+++ b/samples/php/Controller.php
@@ -0,0 +1,1230 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Controller
+ * @since CakePHP(tm) v 0.2.9
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('CakeResponse', 'Network');
+App::uses('ClassRegistry', 'Utility');
+App::uses('ComponentCollection', 'Controller');
+App::uses('View', 'View');
+App::uses('CakeEvent', 'Event');
+App::uses('CakeEventListener', 'Event');
+App::uses('CakeEventManager', 'Event');
+
+/**
+ * Application controller class for organization of business logic.
+ * Provides basic functionality, such as rendering views inside layouts,
+ * automatic model availability, redirection, callbacks, and more.
+ *
+ * Controllers should provide a number of 'action' methods. These are public methods on the controller
+ * that are not prefixed with a '_' and not part of Controller. Each action serves as an endpoint for
+ * performing a specific action on a resource or collection of resources. For example adding or editing a new
+ * object, or listing a set of objects.
+ *
+ * You can access request parameters, using `$this->request`. The request object contains all the POST, GET and FILES
+ * that were part of the request.
+ *
+ * After performing the required actions, controllers are responsible for creating a response. This usually
+ * takes the form of a generated View, or possibly a redirection to another controller action. In either case
+ * `$this->response` allows you to manipulate all aspects of the response.
+ *
+ * Controllers are created by Dispatcher based on request parameters and routing. By default controllers and actions
+ * use conventional names. For example `/posts/index` maps to `PostsController::index()`. You can re-map urls
+ * using Router::connect().
+ *
+ * @package Cake.Controller
+ * @property AclComponent $Acl
+ * @property AuthComponent $Auth
+ * @property CookieComponent $Cookie
+ * @property EmailComponent $Email
+ * @property PaginatorComponent $Paginator
+ * @property RequestHandlerComponent $RequestHandler
+ * @property SecurityComponent $Security
+ * @property SessionComponent $Session
+ * @link http://book.cakephp.org/2.0/en/controllers.html
+ */
+class Controller extends Object implements CakeEventListener {
+
+/**
+ * The name of this controller. Controller names are plural, named after the model they manipulate.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/controllers.html#controller-attributes
+ */
+ public $name = null;
+
+/**
+ * An array containing the class names of models this controller uses.
+ *
+ * Example: `public $uses = array('Product', 'Post', 'Comment');`
+ *
+ * Can be set to several values to express different options:
+ *
+ * - `true` Use the default inflected model name.
+ * - `array()` Use only models defined in the parent class.
+ * - `false` Use no models at all, do not merge with parent class either.
+ * - `array('Post', 'Comment')` Use only the Post and Comment models. Models
+ * Will also be merged with the parent class.
+ *
+ * The default value is `true`.
+ *
+ * @var mixed A single name as a string or a list of names as an array.
+ * @link http://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
+ */
+ public $uses = true;
+
+/**
+ * An array containing the names of helpers this controller uses. The array elements should
+ * not contain the "Helper" part of the classname.
+ *
+ * Example: `public $helpers = array('Html', 'Javascript', 'Time', 'Ajax');`
+ *
+ * @var mixed A single name as a string or a list of names as an array.
+ * @link http://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
+ */
+ public $helpers = array('Session', 'Html', 'Form');
+
+/**
+ * An instance of a CakeRequest object that contains information about the current request.
+ * This object contains all the information about a request and several methods for reading
+ * additional information about the request.
+ *
+ * @var CakeRequest
+ * @link http://book.cakephp.org/2.0/en/controllers/request-response.html#cakerequest
+ */
+ public $request;
+
+/**
+ * An instance of a CakeResponse object that contains information about the impending response
+ *
+ * @var CakeResponse
+ * @link http://book.cakephp.org/2.0/en/controllers/request-response.html#cakeresponse
+ */
+ public $response;
+
+/**
+ * The classname to use for creating the response object.
+ *
+ * @var string
+ */
+ protected $_responseClass = 'CakeResponse';
+
+/**
+ * The name of the views subfolder containing views for this controller.
+ *
+ * @var string
+ */
+ public $viewPath = null;
+
+/**
+ * The name of the layouts subfolder containing layouts for this controller.
+ *
+ * @var string
+ */
+ public $layoutPath = null;
+
+/**
+ * Contains variables to be handed to the view.
+ *
+ * @var array
+ */
+ public $viewVars = array();
+
+/**
+ * The name of the view file to render. The name specified
+ * is the filename in /app/View/<SubFolder> without the .ctp extension.
+ *
+ * @var string
+ */
+ public $view = null;
+
+/**
+ * The name of the layout file to render the view inside of. The name specified
+ * is the filename of the layout in /app/View/Layouts without the .ctp
+ * extension.
+ *
+ * @var string
+ */
+ public $layout = 'default';
+
+/**
+ * Set to true to automatically render the view
+ * after action logic.
+ *
+ * @var boolean
+ */
+ public $autoRender = true;
+
+/**
+ * Set to true to automatically render the layout around views.
+ *
+ * @var boolean
+ */
+ public $autoLayout = true;
+
+/**
+ * Instance of ComponentCollection used to handle callbacks.
+ *
+ * @var ComponentCollection
+ */
+ public $Components = null;
+
+/**
+ * Array containing the names of components this controller uses. Component names
+ * should not contain the "Component" portion of the classname.
+ *
+ * Example: `public $components = array('Session', 'RequestHandler', 'Acl');`
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/controllers/components.html
+ */
+ public $components = array('Session');
+
+/**
+ * The name of the View class this controller sends output to.
+ *
+ * @var string
+ */
+ public $viewClass = 'View';
+
+/**
+ * Instance of the View created during rendering. Won't be set until after
+ * Controller::render() is called.
+ *
+ * @var View
+ */
+ public $View;
+
+/**
+ * File extension for view templates. Defaults to Cake's conventional ".ctp".
+ *
+ * @var string
+ */
+ public $ext = '.ctp';
+
+/**
+ * Automatically set to the name of a plugin.
+ *
+ * @var string
+ */
+ public $plugin = null;
+
+/**
+ * Used to define methods a controller that will be cached. To cache a
+ * single action, the value is set to an array containing keys that match
+ * action names and values that denote cache expiration times (in seconds).
+ *
+ * Example:
+ *
+ * {{{
+ * public $cacheAction = array(
+ * 'view/23/' => 21600,
+ * 'recalled/' => 86400
+ * );
+ * }}}
+ *
+ * $cacheAction can also be set to a strtotime() compatible string. This
+ * marks all the actions in the controller for view caching.
+ *
+ * @var mixed
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/cache.html#additional-configuration-options
+ */
+ public $cacheAction = false;
+
+/**
+ * Holds all params passed and named.
+ *
+ * @var mixed
+ */
+ public $passedArgs = array();
+
+/**
+ * Triggers Scaffolding
+ *
+ * @var mixed
+ * @link http://book.cakephp.org/2.0/en/controllers/scaffolding.html
+ */
+ public $scaffold = false;
+
+/**
+ * Holds current methods of the controller. This is a list of all the methods reachable
+ * via url. Modifying this array, will allow you to change which methods can be reached.
+ *
+ * @var array
+ */
+ public $methods = array();
+
+/**
+ * This controller's primary model class name, the Inflector::singularize()'ed version of
+ * the controller's $name property.
+ *
+ * Example: For a controller named 'Comments', the modelClass would be 'Comment'
+ *
+ * @var string
+ */
+ public $modelClass = null;
+
+/**
+ * This controller's model key name, an underscored version of the controller's $modelClass property.
+ *
+ * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment'
+ *
+ * @var string
+ */
+ public $modelKey = null;
+
+/**
+ * Holds any validation errors produced by the last call of the validateErrors() method/
+ *
+ * @var array Validation errors, or false if none
+ */
+ public $validationErrors = null;
+
+/**
+ * The class name of the parent class you wish to merge with.
+ * Typically this is AppController, but you may wish to merge vars with a different
+ * parent class.
+ *
+ * @var string
+ */
+ protected $_mergeParent = 'AppController';
+
+/**
+ * Instance of the CakeEventManager this controller is using
+ * to dispatch inner events.
+ *
+ * @var CakeEventManager
+ */
+ protected $_eventManager = null;
+
+/**
+ * Constructor.
+ *
+ * @param CakeRequest $request Request object for this controller. Can be null for testing,
+ * but expect that features that use the request parameters will not work.
+ * @param CakeResponse $response Response object for this controller.
+ */
+ public function __construct($request = null, $response = null) {
+ if ($this->name === null) {
+ $this->name = substr(get_class($this), 0, -10);
+ }
+
+ if ($this->viewPath == null) {
+ $this->viewPath = $this->name;
+ }
+
+ $this->modelClass = Inflector::singularize($this->name);
+ $this->modelKey = Inflector::underscore($this->modelClass);
+ $this->Components = new ComponentCollection();
+
+ $childMethods = get_class_methods($this);
+ $parentMethods = get_class_methods('Controller');
+
+ $this->methods = array_diff($childMethods, $parentMethods);
+
+ if ($request instanceof CakeRequest) {
+ $this->setRequest($request);
+ }
+ if ($response instanceof CakeResponse) {
+ $this->response = $response;
+ }
+ parent::__construct();
+ }
+
+/**
+ * Provides backwards compatibility to avoid problems with empty and isset to alias properties.
+ * Lazy loads models using the loadModel() method if declared in $uses
+ *
+ * @param string $name
+ * @return void
+ */
+ public function __isset($name) {
+ switch ($name) {
+ case 'base':
+ case 'here':
+ case 'webroot':
+ case 'data':
+ case 'action':
+ case 'params':
+ return true;
+ }
+
+ if (is_array($this->uses)) {
+ foreach ($this->uses as $modelClass) {
+ list($plugin, $class) = pluginSplit($modelClass, true);
+ if ($name === $class) {
+ return $this->loadModel($modelClass);
+ }
+ }
+ }
+
+ if ($name === $this->modelClass) {
+ list($plugin, $class) = pluginSplit($name, true);
+ if (!$plugin) {
+ $plugin = $this->plugin ? $this->plugin . '.' : null;
+ }
+ return $this->loadModel($plugin . $this->modelClass);
+ }
+
+ return false;
+ }
+
+/**
+ * Provides backwards compatibility access to the request object properties.
+ * Also provides the params alias.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function __get($name) {
+ switch ($name) {
+ case 'base':
+ case 'here':
+ case 'webroot':
+ case 'data':
+ return $this->request->{$name};
+ case 'action':
+ return isset($this->request->params['action']) ? $this->request->params['action'] : '';
+ case 'params':
+ return $this->request;
+ case 'paginate':
+ return $this->Components->load('Paginator')->settings;
+ }
+
+ if (isset($this->{$name})) {
+ return $this->{$name};
+ }
+
+ return null;
+ }
+
+/**
+ * Provides backwards compatibility access for setting values to the request object.
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return void
+ */
+ public function __set($name, $value) {
+ switch ($name) {
+ case 'base':
+ case 'here':
+ case 'webroot':
+ case 'data':
+ return $this->request->{$name} = $value;
+ case 'action':
+ return $this->request->params['action'] = $value;
+ case 'params':
+ return $this->request->params = $value;
+ case 'paginate':
+ return $this->Components->load('Paginator')->settings = $value;
+ }
+ return $this->{$name} = $value;
+ }
+
+/**
+ * Sets the request objects and configures a number of controller properties
+ * based on the contents of the request. The properties that get set are
+ *
+ * - $this->request - To the $request parameter
+ * - $this->plugin - To the $request->params['plugin']
+ * - $this->view - To the $request->params['action']
+ * - $this->autoLayout - To the false if $request->params['bare']; is set.
+ * - $this->autoRender - To false if $request->params['return'] == 1
+ * - $this->passedArgs - The the combined results of params['named'] and params['pass]
+ *
+ * @param CakeRequest $request
+ * @return void
+ */
+ public function setRequest(CakeRequest $request) {
+ $this->request = $request;
+ $this->plugin = isset($request->params['plugin']) ? Inflector::camelize($request->params['plugin']) : null;
+ $this->view = isset($request->params['action']) ? $request->params['action'] : null;
+ if (isset($request->params['pass']) && isset($request->params['named'])) {
+ $this->passedArgs = array_merge($request->params['pass'], $request->params['named']);
+ }
+
+ if (array_key_exists('return', $request->params) && $request->params['return'] == 1) {
+ $this->autoRender = false;
+ }
+ if (!empty($request->params['bare'])) {
+ $this->autoLayout = false;
+ }
+ }
+
+/**
+ * Dispatches the controller action. Checks that the action
+ * exists and isn't private.
+ *
+ * @param CakeRequest $request
+ * @return mixed The resulting response.
+ * @throws PrivateActionException When actions are not public or prefixed by _
+ * @throws MissingActionException When actions are not defined and scaffolding is
+ * not enabled.
+ */
+ public function invokeAction(CakeRequest $request) {
+ try {
+ $method = new ReflectionMethod($this, $request->params['action']);
+
+ if ($this->_isPrivateAction($method, $request)) {
+ throw new PrivateActionException(array(
+ 'controller' => $this->name . "Controller",
+ 'action' => $request->params['action']
+ ));
+ }
+ return $method->invokeArgs($this, $request->params['pass']);
+
+ } catch (ReflectionException $e) {
+ if ($this->scaffold !== false) {
+ return $this->_getScaffold($request);
+ }
+ throw new MissingActionException(array(
+ 'controller' => $this->name . "Controller",
+ 'action' => $request->params['action']
+ ));
+ }
+ }
+
+/**
+ * Check if the request's action is marked as private, with an underscore,
+ * or if the request is attempting to directly accessing a prefixed action.
+ *
+ * @param ReflectionMethod $method The method to be invoked.
+ * @param CakeRequest $request The request to check.
+ * @return boolean
+ */
+ protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request) {
+ $privateAction = (
+ $method->name[0] === '_' ||
+ !$method->isPublic() ||
+ !in_array($method->name, $this->methods)
+ );
+ $prefixes = Router::prefixes();
+
+ if (!$privateAction && !empty($prefixes)) {
+ if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) {
+ list($prefix) = explode('_', $request->params['action']);
+ $privateAction = in_array($prefix, $prefixes);
+ }
+ }
+ return $privateAction;
+ }
+
+/**
+ * Returns a scaffold object to use for dynamically scaffolded controllers.
+ *
+ * @param CakeRequest $request
+ * @return Scaffold
+ */
+ protected function _getScaffold(CakeRequest $request) {
+ return new Scaffold($this, $request);
+ }
+
+/**
+ * Merge components, helpers, and uses vars from
+ * Controller::$_mergeParent and PluginAppController.
+ *
+ * @return void
+ */
+ protected function _mergeControllerVars() {
+ $pluginController = $pluginDot = null;
+ $mergeParent = is_subclass_of($this, $this->_mergeParent);
+ $pluginVars = array();
+ $appVars = array();
+
+ if (!empty($this->plugin)) {
+ $pluginController = $this->plugin . 'AppController';
+ if (!is_subclass_of($this, $pluginController)) {
+ $pluginController = null;
+ }
+ $pluginDot = $this->plugin . '.';
+ }
+
+ if ($pluginController) {
+ $merge = array('components', 'helpers');
+ $this->_mergeVars($merge, $pluginController);
+ }
+
+ if ($mergeParent || !empty($pluginController)) {
+ $appVars = get_class_vars($this->_mergeParent);
+ $uses = $appVars['uses'];
+ $merge = array('components', 'helpers');
+ $this->_mergeVars($merge, $this->_mergeParent, true);
+ }
+
+ if ($this->uses === null) {
+ $this->uses = false;
+ }
+ if ($this->uses === true) {
+ $this->uses = array($pluginDot . $this->modelClass);
+ }
+ if (isset($appVars['uses']) && $appVars['uses'] === $this->uses) {
+ array_unshift($this->uses, $pluginDot . $this->modelClass);
+ }
+ if ($pluginController) {
+ $pluginVars = get_class_vars($pluginController);
+ }
+ if ($this->uses !== false) {
+ $this->_mergeUses($pluginVars);
+ $this->_mergeUses($appVars);
+ } else {
+ $this->uses = array();
+ $this->modelClass = '';
+ }
+ }
+
+/**
+ * Helper method for merging the $uses property together.
+ *
+ * Merges the elements not already in $this->uses into
+ * $this->uses.
+ *
+ * @param mixed $merge The data to merge in.
+ * @return void
+ */
+ protected function _mergeUses($merge) {
+ if (!isset($merge['uses'])) {
+ return;
+ }
+ if ($merge['uses'] === true) {
+ return;
+ }
+ $this->uses = array_merge(
+ $this->uses,
+ array_diff($merge['uses'], $this->uses)
+ );
+ }
+
+/**
+ * Returns a list of all events that will fire in the controller during it's lifecycle.
+ * You can override this function to add you own listener callbacks
+ *
+ * @return array
+ */
+ public function implementedEvents() {
+ return array(
+ 'Controller.initialize' => 'beforeFilter',
+ 'Controller.beforeRender' => 'beforeRender',
+ 'Controller.beforeRedirect' => array('callable' => 'beforeRedirect', 'passParams' => true),
+ 'Controller.shutdown' => 'afterFilter'
+ );
+ }
+
+/**
+ * Loads Model classes based on the uses property
+ * see Controller::loadModel(); for more info.
+ * Loads Components and prepares them for initialization.
+ *
+ * @return mixed true if models found and instance created.
+ * @see Controller::loadModel()
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::constructClasses
+ * @throws MissingModelException
+ */
+ public function constructClasses() {
+ $this->_mergeControllerVars();
+ $this->Components->init($this);
+ if ($this->uses) {
+ $this->uses = (array)$this->uses;
+ list(, $this->modelClass) = pluginSplit(current($this->uses));
+ }
+ return true;
+ }
+
+/**
+ * Returns the CakeEventManager manager instance that is handling any callbacks.
+ * You can use this instance to register any new listeners or callbacks to the
+ * controller events, or create your own events and trigger them at will.
+ *
+ * @return CakeEventManager
+ */
+ public function getEventManager() {
+ if (empty($this->_eventManager)) {
+ $this->_eventManager = new CakeEventManager();
+ $this->_eventManager->attach($this->Components);
+ $this->_eventManager->attach($this);
+ }
+ return $this->_eventManager;
+ }
+
+/**
+ * Perform the startup process for this controller.
+ * Fire the Components and Controller callbacks in the correct order.
+ *
+ * - Initializes components, which fires their `initialize` callback
+ * - Calls the controller `beforeFilter`.
+ * - triggers Component `startup` methods.
+ *
+ * @return void
+ */
+ public function startupProcess() {
+ $this->getEventManager()->dispatch(new CakeEvent('Controller.initialize', $this));
+ $this->getEventManager()->dispatch(new CakeEvent('Controller.startup', $this));
+ }
+
+/**
+ * Perform the various shutdown processes for this controller.
+ * Fire the Components and Controller callbacks in the correct order.
+ *
+ * - triggers the component `shutdown` callback.
+ * - calls the Controller's `afterFilter` method.
+ *
+ * @return void
+ */
+ public function shutdownProcess() {
+ $this->getEventManager()->dispatch(new CakeEvent('Controller.shutdown', $this));
+ }
+
+/**
+ * Queries & sets valid HTTP response codes & messages.
+ *
+ * @param mixed $code If $code is an integer, then the corresponding code/message is
+ * returned if it exists, null if it does not exist. If $code is an array,
+ * then the 'code' and 'message' keys of each nested array are added to the default
+ * HTTP codes. Example:
+ *
+ * httpCodes(404); // returns array(404 => 'Not Found')
+ *
+ * httpCodes(array(
+ * 701 => 'Unicorn Moved',
+ * 800 => 'Unexpected Minotaur'
+ * )); // sets these new values, and returns true
+ *
+ * @return mixed Associative array of the HTTP codes as keys, and the message
+ * strings as values, or null of the given $code does not exist.
+ * @deprecated Use CakeResponse::httpCodes();
+ */
+ public function httpCodes($code = null) {
+ return $this->response->httpCodes($code);
+ }
+
+/**
+ * Loads and instantiates models required by this controller.
+ * If the model is non existent, it will throw a missing database table error, as Cake generates
+ * dynamic models for the time being.
+ *
+ * @param string $modelClass Name of model class to load
+ * @param mixed $id Initial ID the instanced model class should have
+ * @return mixed true when single model found and instance created, error returned if model not found.
+ * @throws MissingModelException if the model class cannot be found.
+ */
+ public function loadModel($modelClass = null, $id = null) {
+ if ($modelClass === null) {
+ $modelClass = $this->modelClass;
+ }
+
+ $this->uses = ($this->uses) ? (array)$this->uses : array();
+ if (!in_array($modelClass, $this->uses)) {
+ $this->uses[] = $modelClass;
+ }
+
+ list($plugin, $modelClass) = pluginSplit($modelClass, true);
+
+ $this->{$modelClass} = ClassRegistry::init(array(
+ 'class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id
+ ));
+ if (!$this->{$modelClass}) {
+ throw new MissingModelException($modelClass);
+ }
+ return true;
+ }
+
+/**
+ * Redirects to given $url, after turning off $this->autoRender.
+ * Script execution is halted after the redirect.
+ *
+ * @param mixed $url A string or array-based URL pointing to another location within the app,
+ * or an absolute URL
+ * @param integer $status Optional HTTP status code (eg: 404)
+ * @param boolean $exit If true, exit() will be called after the redirect
+ * @return mixed void if $exit = false. Terminates script if $exit = true
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::redirect
+ */
+ public function redirect($url, $status = null, $exit = true) {
+ $this->autoRender = false;
+
+ if (is_array($status)) {
+ extract($status, EXTR_OVERWRITE);
+ }
+ $event = new CakeEvent('Controller.beforeRedirect', $this, array($url, $status, $exit));
+ //TODO: Remove the following line when the events are fully migrated to the CakeEventManager
+ list($event->break, $event->breakOn, $event->collectReturn) = array(true, false, true);
+ $this->getEventManager()->dispatch($event);
+
+ if ($event->isStopped()) {
+ return;
+ }
+ $response = $event->result;
+ extract($this->_parseBeforeRedirect($response, $url, $status, $exit), EXTR_OVERWRITE);
+
+ if (function_exists('session_write_close')) {
+ session_write_close();
+ }
+
+ if ($url !== null) {
+ $this->response->header('Location', Router::url($url, true));
+ }
+
+ if (is_string($status)) {
+ $codes = array_flip($this->response->httpCodes());
+ if (isset($codes[$status])) {
+ $status = $codes[$status];
+ }
+ }
+
+ if ($status) {
+ $this->response->statusCode($status);
+ }
+
+ if ($exit) {
+ $this->response->send();
+ $this->_stop();
+ }
+ }
+
+/**
+ * Parse beforeRedirect Response
+ *
+ * @param mixed $response Response from beforeRedirect callback
+ * @param mixed $url The same value of beforeRedirect
+ * @param integer $status The same value of beforeRedirect
+ * @param boolean $exit The same value of beforeRedirect
+ * @return array Array with keys url, status and exit
+ */
+ protected function _parseBeforeRedirect($response, $url, $status, $exit) {
+ if (is_array($response)) {
+ foreach ($response as $resp) {
+ if (is_array($resp) && isset($resp['url'])) {
+ extract($resp, EXTR_OVERWRITE);
+ } elseif ($resp !== null) {
+ $url = $resp;
+ }
+ }
+ }
+ return compact('url', 'status', 'exit');
+ }
+
+/**
+ * Convenience and object wrapper method for CakeResponse::header().
+ *
+ * @param string $status The header message that is being set.
+ * @return void
+ * @deprecated Use CakeResponse::header()
+ */
+ public function header($status) {
+ $this->response->header($status);
+ }
+
+/**
+ * Saves a variable for use inside a view template.
+ *
+ * @param mixed $one A string or an array of data.
+ * @param mixed $two Value in case $one is a string (which then works as the key).
+ * Unused if $one is an associative array, otherwise serves as the values to $one's keys.
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/controllers.html#interacting-with-views
+ */
+ public function set($one, $two = null) {
+ if (is_array($one)) {
+ if (is_array($two)) {
+ $data = array_combine($one, $two);
+ } else {
+ $data = $one;
+ }
+ } else {
+ $data = array($one => $two);
+ }
+ $this->viewVars = $data + $this->viewVars;
+ }
+
+/**
+ * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
+ *
+ * Examples:
+ *
+ * {{{
+ * setAction('another_action');
+ * setAction('action_with_parameters', $parameter1);
+ * }}}
+ *
+ * @param string $action The new action to be 'redirected' to
+ * @param mixed Any other parameters passed to this method will be passed as
+ * parameters to the new action.
+ * @return mixed Returns the return value of the called action
+ */
+ public function setAction($action) {
+ $this->request->params['action'] = $action;
+ $this->view = $action;
+ $args = func_get_args();
+ unset($args[0]);
+ return call_user_func_array(array(&$this, $action), $args);
+ }
+
+/**
+ * Returns number of errors in a submitted FORM.
+ *
+ * @return integer Number of errors
+ */
+ public function validate() {
+ $args = func_get_args();
+ $errors = call_user_func_array(array(&$this, 'validateErrors'), $args);
+
+ if ($errors === false) {
+ return 0;
+ }
+ return count($errors);
+ }
+
+/**
+ * Validates models passed by parameters. Example:
+ *
+ * `$errors = $this->validateErrors($this->Article, $this->User);`
+ *
+ * @param mixed A list of models as a variable argument
+ * @return array Validation errors, or false if none
+ */
+ public function validateErrors() {
+ $objects = func_get_args();
+
+ if (empty($objects)) {
+ return false;
+ }
+
+ $errors = array();
+ foreach ($objects as $object) {
+ if (isset($this->{$object->alias})) {
+ $object = $this->{$object->alias};
+ }
+ $object->set($object->data);
+ $errors = array_merge($errors, $object->invalidFields());
+ }
+
+ return $this->validationErrors = (!empty($errors) ? $errors : false);
+ }
+
+/**
+ * Instantiates the correct view class, hands it its data, and uses it to render the view output.
+ *
+ * @param string $view View to use for rendering
+ * @param string $layout Layout to use
+ * @return CakeResponse A response object containing the rendered view.
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::render
+ */
+ public function render($view = null, $layout = null) {
+ $event = new CakeEvent('Controller.beforeRender', $this);
+ $this->getEventManager()->dispatch($event);
+ if ($event->isStopped()) {
+ $this->autoRender = false;
+ return $this->response;
+ }
+
+ if (!empty($this->uses) && is_array($this->uses)) {
+ foreach ($this->uses as $model) {
+ list($plugin, $className) = pluginSplit($model);
+ $this->request->params['models'][$className] = compact('plugin', 'className');
+ }
+ }
+
+ $viewClass = $this->viewClass;
+ if ($this->viewClass != 'View') {
+ list($plugin, $viewClass) = pluginSplit($viewClass, true);
+ $viewClass = $viewClass . 'View';
+ App::uses($viewClass, $plugin . 'View');
+ }
+
+ $View = new $viewClass($this);
+
+ $models = ClassRegistry::keys();
+ foreach ($models as $currentModel) {
+ $currentObject = ClassRegistry::getObject($currentModel);
+ if (is_a($currentObject, 'Model')) {
+ $className = get_class($currentObject);
+ list($plugin) = pluginSplit(App::location($className));
+ $this->request->params['models'][$currentObject->alias] = compact('plugin', 'className');
+ $View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors;
+ }
+ }
+
+ $this->autoRender = false;
+ $this->View = $View;
+ $this->response->body($View->render($view, $layout));
+ return $this->response;
+ }
+
+/**
+ * Returns the referring URL for this request.
+ *
+ * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers
+ * @param boolean $local If true, restrict referring URLs to local server
+ * @return string Referring URL
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::referer
+ */
+ public function referer($default = null, $local = false) {
+ if ($this->request) {
+ $referer = $this->request->referer($local);
+ if ($referer == '/' && $default != null) {
+ return Router::url($default, true);
+ }
+ return $referer;
+ }
+ return '/';
+ }
+
+/**
+ * Forces the user's browser not to cache the results of the current request.
+ *
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::disableCache
+ * @deprecated Use CakeResponse::disableCache()
+ */
+ public function disableCache() {
+ $this->response->disableCache();
+ }
+
+/**
+ * Shows a message to the user for $pause seconds, then redirects to $url.
+ * Uses flash.ctp as the default layout for the message.
+ * Does not work if the current debug level is higher than 0.
+ *
+ * @param string $message Message to display to the user
+ * @param mixed $url Relative string or array-based URL to redirect to after the time expires
+ * @param integer $pause Time to show the message
+ * @param string $layout Layout you want to use, defaults to 'flash'
+ * @return void Renders flash layout
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::flash
+ */
+ public function flash($message, $url, $pause = 1, $layout = 'flash') {
+ $this->autoRender = false;
+ $this->set('url', Router::url($url));
+ $this->set('message', $message);
+ $this->set('pause', $pause);
+ $this->set('page_title', $message);
+ $this->render(false, $layout);
+ }
+
+/**
+ * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call.
+ *
+ * @param array $data POST'ed data organized by model and field
+ * @param mixed $op A string containing an SQL comparison operator, or an array matching operators
+ * to fields
+ * @param string $bool SQL boolean operator: AND, OR, XOR, etc.
+ * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be
+ * included in the returned conditions
+ * @return array An array of model conditions
+ * @deprecated
+ */
+ public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
+ if (!is_array($data) || empty($data)) {
+ if (!empty($this->request->data)) {
+ $data = $this->request->data;
+ } else {
+ return null;
+ }
+ }
+ $cond = array();
+
+ if ($op === null) {
+ $op = '';
+ }
+
+ $arrayOp = is_array($op);
+ foreach ($data as $model => $fields) {
+ foreach ($fields as $field => $value) {
+ $key = $model . '.' . $field;
+ $fieldOp = $op;
+ if ($arrayOp) {
+ if (array_key_exists($key, $op)) {
+ $fieldOp = $op[$key];
+ } elseif (array_key_exists($field, $op)) {
+ $fieldOp = $op[$field];
+ } else {
+ $fieldOp = false;
+ }
+ }
+ if ($exclusive && $fieldOp === false) {
+ continue;
+ }
+ $fieldOp = strtoupper(trim($fieldOp));
+ if ($fieldOp === 'LIKE') {
+ $key = $key . ' LIKE';
+ $value = '%' . $value . '%';
+ } elseif ($fieldOp && $fieldOp != '=') {
+ $key = $key . ' ' . $fieldOp;
+ }
+ $cond[$key] = $value;
+ }
+ }
+ if ($bool != null && strtoupper($bool) != 'AND') {
+ $cond = array($bool => $cond);
+ }
+ return $cond;
+ }
+
+/**
+ * Handles automatic pagination of model records.
+ *
+ * @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
+ * @param mixed $scope Conditions to use while paginating
+ * @param array $whitelist List of allowed options for paging
+ * @return array Model query results
+ * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::paginate
+ * @deprecated Use PaginatorComponent instead
+ */
+ public function paginate($object = null, $scope = array(), $whitelist = array()) {
+ return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist);
+ }
+
+/**
+ * Called before the controller action. You can use this method to configure and customize components
+ * or perform logic that needs to happen before each controller action.
+ *
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
+ */
+ public function beforeFilter() {
+ }
+
+/**
+ * Called after the controller action is run, but before the view is rendered. You can use this method
+ * to perform logic or set view variables that are required on every request.
+ *
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
+ */
+ public function beforeRender() {
+ }
+
+/**
+ * The beforeRedirect method is invoked when the controller's redirect method is called but before any
+ * further action. If this method returns false the controller will not continue on to redirect the request.
+ * The $url, $status and $exit variables have same meaning as for the controller's method. You can also
+ * return a string which will be interpreted as the url to redirect to or return associative array with
+ * key 'url' and optionally 'status' and 'exit'.
+ *
+ * @param mixed $url A string or array-based URL pointing to another location within the app,
+ * or an absolute URL
+ * @param integer $status Optional HTTP status code (eg: 404)
+ * @param boolean $exit If true, exit() will be called after the redirect
+ * @return mixed
+ * false to stop redirection event,
+ * string controllers a new redirection url or
+ * array with the keys url, status and exit to be used by the redirect method.
+ * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
+ */
+ public function beforeRedirect($url, $status = null, $exit = true) {
+ }
+
+/**
+ * Called after the controller action is run and rendered.
+ *
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
+ */
+ public function afterFilter() {
+ }
+
+/**
+ * This method should be overridden in child classes.
+ *
+ * @param string $method name of method called example index, edit, etc.
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
+ */
+ public function beforeScaffold($method) {
+ return true;
+ }
+
+/**
+ * Alias to beforeScaffold()
+ *
+ * @param string $method
+ * @return boolean
+ * @see Controller::beforeScaffold()
+ * @deprecated
+ */
+ protected function _beforeScaffold($method) {
+ return $this->beforeScaffold($method);
+ }
+
+/**
+ * This method should be overridden in child classes.
+ *
+ * @param string $method name of method called either edit or update.
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
+ */
+ public function afterScaffoldSave($method) {
+ return true;
+ }
+
+/**
+ * Alias to afterScaffoldSave()
+ *
+ * @param string $method
+ * @return boolean
+ * @see Controller::afterScaffoldSave()
+ * @deprecated
+ */
+ protected function _afterScaffoldSave($method) {
+ return $this->afterScaffoldSave($method);
+ }
+
+/**
+ * This method should be overridden in child classes.
+ *
+ * @param string $method name of method called either edit or update.
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
+ */
+ public function afterScaffoldSaveError($method) {
+ return true;
+ }
+
+/**
+ * Alias to afterScaffoldSaveError()
+ *
+ * @param string $method
+ * @return boolean
+ * @see Controller::afterScaffoldSaveError()
+ * @deprecated
+ */
+ protected function _afterScaffoldSaveError($method) {
+ return $this->afterScaffoldSaveError($method);
+ }
+
+/**
+ * This method should be overridden in child classes.
+ * If not it will render a scaffold error.
+ * Method MUST return true in child classes
+ *
+ * @param string $method name of method called example index, edit, etc.
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
+ */
+ public function scaffoldError($method) {
+ return false;
+ }
+
+/**
+ * Alias to scaffoldError()
+ *
+ * @param string $method
+ * @return boolean
+ * @see Controller::scaffoldError()
+ * @deprecated
+ */
+ protected function _scaffoldError($method) {
+ return $this->scaffoldError($method);
+ }
+
+}
diff --git a/samples/php/Form.php b/samples/php/Form.php
new file mode 100644
index 0000000..9e60711
--- /dev/null
+++ b/samples/php/Form.php
@@ -0,0 +1,586 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\DomCrawler\Field\FormField;
+
+/**
+ * Form represents an HTML form.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Form extends Link implements \ArrayAccess
+{
+ /**
+ * @var \DOMNode
+ */
+ private $button;
+ /**
+ * @var Field\FormField[]
+ */
+ private $fields;
+
+ /**
+ * Constructor.
+ *
+ * @param \DOMNode $node A \DOMNode instance
+ * @param string $currentUri The URI of the page where the form is embedded
+ * @param string $method The method to use for the link (if null, it defaults to the method defined by the form)
+ *
+ * @throws \LogicException if the node is not a button inside a form tag
+ *
+ * @api
+ */
+ public function __construct(\DOMNode $node, $currentUri, $method = null)
+ {
+ parent::__construct($node, $currentUri, $method);
+
+ $this->initialize();
+ }
+
+ /**
+ * Gets the form node associated with this form.
+ *
+ * @return \DOMNode A \DOMNode instance
+ */
+ public function getFormNode()
+ {
+ return $this->node;
+ }
+
+ /**
+ * Sets the value of the fields.
+ *
+ * @param array $values An array of field values
+ *
+ * @return Form
+ *
+ * @api
+ */
+ public function setValues(array $values)
+ {
+ foreach ($values as $name => $value) {
+ $this->fields->set($name, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Gets the field values.
+ *
+ * The returned array does not include file fields (@see getFiles).
+ *
+ * @return array An array of field values.
+ *
+ * @api
+ */
+ public function getValues()
+ {
+ $values = array();
+ foreach ($this->fields->all() as $name => $field) {
+ if ($field->isDisabled()) {
+ continue;
+ }
+
+ if (!$field instanceof Field\FileFormField && $field->hasValue()) {
+ $values[$name] = $field->getValue();
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Gets the file field values.
+ *
+ * @return array An array of file field values.
+ *
+ * @api
+ */
+ public function getFiles()
+ {
+ if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) {
+ return array();
+ }
+
+ $files = array();
+
+ foreach ($this->fields->all() as $name => $field) {
+ if ($field->isDisabled()) {
+ continue;
+ }
+
+ if ($field instanceof Field\FileFormField) {
+ $files[$name] = $field->getValue();
+ }
+ }
+
+ return $files;
+ }
+
+ /**
+ * Gets the field values as PHP.
+ *
+ * This method converts fields with the array notation
+ * (like foo[bar] to arrays) like PHP does.
+ *
+ * @return array An array of field values.
+ *
+ * @api
+ */
+ public function getPhpValues()
+ {
+ $qs = http_build_query($this->getValues(), '', '&');
+ parse_str($qs, $values);
+
+ return $values;
+ }
+
+ /**
+ * Gets the file field values as PHP.
+ *
+ * This method converts fields with the array notation
+ * (like foo[bar] to arrays) like PHP does.
+ *
+ * @return array An array of field values.
+ *
+ * @api
+ */
+ public function getPhpFiles()
+ {
+ $qs = http_build_query($this->getFiles(), '', '&');
+ parse_str($qs, $values);
+
+ return $values;
+ }
+
+ /**
+ * Gets the URI of the form.
+ *
+ * The returned URI is not the same as the form "action" attribute.
+ * This method merges the value if the method is GET to mimics
+ * browser behavior.
+ *
+ * @return string The URI
+ *
+ * @api
+ */
+ public function getUri()
+ {
+ $uri = parent::getUri();
+
+ if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH')) && $queryString = http_build_query($this->getValues(), null, '&')) {
+ $sep = false === strpos($uri, '?') ? '?' : '&';
+ $uri .= $sep.$queryString;
+ }
+
+ return $uri;
+ }
+
+ protected function getRawUri()
+ {
+ return $this->node->getAttribute('action');
+ }
+
+ /**
+ * Gets the form method.
+ *
+ * If no method is defined in the form, GET is returned.
+ *
+ * @return string The method
+ *
+ * @api
+ */
+ public function getMethod()
+ {
+ if (null !== $this->method) {
+ return $this->method;
+ }
+
+ return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET';
+ }
+
+ /**
+ * Returns true if the named field exists.
+ *
+ * @param string $name The field name
+ *
+ * @return Boolean true if the field exists, false otherwise
+ *
+ * @api
+ */
+ public function has($name)
+ {
+ return $this->fields->has($name);
+ }
+
+ /**
+ * Removes a field from the form.
+ *
+ * @param string $name The field name
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ *
+ * @api
+ */
+ public function remove($name)
+ {
+ $this->fields->remove($name);
+ }
+
+ /**
+ * Gets a named field.
+ *
+ * @param string $name The field name
+ *
+ * @return FormField The field instance
+ *
+ * @throws \InvalidArgumentException When field is not present in this form
+ *
+ * @api
+ */
+ public function get($name)
+ {
+ return $this->fields->get($name);
+ }
+
+ /**
+ * Sets a named field.
+ *
+ * @param FormField $field The field
+ *
+ * @api
+ */
+ public function set(FormField $field)
+ {
+ $this->fields->add($field);
+ }
+
+ /**
+ * Gets all fields.
+ *
+ * @return array An array of fields
+ *
+ * @api
+ */
+ public function all()
+ {
+ return $this->fields->all();
+ }
+
+ /**
+ * Returns true if the named field exists.
+ *
+ * @param string $name The field name
+ *
+ * @return Boolean true if the field exists, false otherwise
+ */
+ public function offsetExists($name)
+ {
+ return $this->has($name);
+ }
+
+ /**
+ * Gets the value of a field.
+ *
+ * @param string $name The field name
+ *
+ * @return FormField The associated Field instance
+ *
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function offsetGet($name)
+ {
+ return $this->fields->get($name);
+ }
+
+ /**
+ * Sets the value of a field.
+ *
+ * @param string $name The field name
+ * @param string|array $value The value of the field
+ *
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function offsetSet($name, $value)
+ {
+ $this->fields->set($name, $value);
+ }
+
+ /**
+ * Removes a field from the form.
+ *
+ * @param string $name The field name
+ */
+ public function offsetUnset($name)
+ {
+ $this->fields->remove($name);
+ }
+
+ protected function setNode(\DOMNode $node)
+ {
+ $this->button = $node;
+ if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
+ do {
+ // use the ancestor form element
+ if (null === $node = $node->parentNode) {
+ throw new \LogicException('The selected node does not have a form ancestor.');
+ }
+ } while ('form' != $node->nodeName);
+ } elseif ('form' != $node->nodeName) {
+ throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
+ }
+
+ $this->node = $node;
+ }
+
+ private function initialize()
+ {
+ $this->fields = new FormFieldRegistry();
+
+ $document = new \DOMDocument('1.0', 'UTF-8');
+ $node = $document->importNode($this->node, true);
+ $button = $document->importNode($this->button, true);
+ $root = $document->appendChild($document->createElement('_root'));
+ $root->appendChild($node);
+ $root->appendChild($button);
+ $xpath = new \DOMXPath($document);
+
+ foreach ($xpath->query('descendant::input | descendant::textarea | descendant::select', $root) as $node) {
+ if (!$node->hasAttribute('name')) {
+ continue;
+ }
+
+ $nodeName = $node->nodeName;
+
+ if ($node === $button) {
+ $this->set(new Field\InputFormField($node));
+ } elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) {
+ $this->set(new Field\ChoiceFormField($node));
+ } elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) {
+ if ($this->has($node->getAttribute('name'))) {
+ $this->get($node->getAttribute('name'))->addChoice($node);
+ } else {
+ $this->set(new Field\ChoiceFormField($node));
+ }
+ } elseif ('input' == $nodeName && 'file' == $node->getAttribute('type')) {
+ $this->set(new Field\FileFormField($node));
+ } elseif ('input' == $nodeName && !in_array($node->getAttribute('type'), array('submit', 'button', 'image'))) {
+ $this->set(new Field\InputFormField($node));
+ } elseif ('textarea' == $nodeName) {
+ $this->set(new Field\TextareaFormField($node));
+ }
+ }
+ }
+}
+
+class FormFieldRegistry
+{
+ private $fields = array();
+
+ private $base;
+
+ /**
+ * Adds a field to the registry.
+ *
+ * @param FormField $field The field
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ */
+ public function add(FormField $field)
+ {
+ $segments = $this->getSegments($field->getName());
+
+ $target =& $this->fields;
+ while ($segments) {
+ if (!is_array($target)) {
+ $target = array();
+ }
+ $path = array_shift($segments);
+ if ('' === $path) {
+ $target =& $target[];
+ } else {
+ $target =& $target[$path];
+ }
+ }
+ $target = $field;
+ }
+
+ /**
+ * Removes a field and its children from the registry.
+ *
+ * @param string $name The fully qualified name of the base field
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ */
+ public function remove($name)
+ {
+ $segments = $this->getSegments($name);
+ $target =& $this->fields;
+ while (count($segments) > 1) {
+ $path = array_shift($segments);
+ if (!array_key_exists($path, $target)) {
+ return;
+ }
+ $target =& $target[$path];
+ }
+ unset($target[array_shift($segments)]);
+ }
+
+ /**
+ * Returns the value of the field and its children.
+ *
+ * @param string $name The fully qualified name of the field
+ *
+ * @return mixed The value of the field
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function &get($name)
+ {
+ $segments = $this->getSegments($name);
+ $target =& $this->fields;
+ while ($segments) {
+ $path = array_shift($segments);
+ if (!array_key_exists($path, $target)) {
+ throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
+ }
+ $target =& $target[$path];
+ }
+
+ return $target;
+ }
+
+ /**
+ * Tests whether the form has the given field.
+ *
+ * @param string $name The fully qualified name of the field
+ *
+ * @return Boolean Whether the form has the given field
+ */
+ public function has($name)
+ {
+ try {
+ $this->get($name);
+
+ return true;
+ } catch (\InvalidArgumentException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Set the value of a field and its children.
+ *
+ * @param string $name The fully qualified name of the field
+ * @param mixed $value The value
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ * @throws \InvalidArgumentException if the field does not exist
+ */
+ public function set($name, $value)
+ {
+ $target =& $this->get($name);
+ if (is_array($value)) {
+ $fields = self::create($name, $value);
+ foreach ($fields->all() as $k => $v) {
+ $this->set($k, $v);
+ }
+ } else {
+ $target->setValue($value);
+ }
+ }
+
+ /**
+ * Returns the list of field with their value.
+ *
+ * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+ */
+ public function all()
+ {
+ return $this->walk($this->fields, $this->base);
+ }
+
+ /**
+ * Creates an instance of the class.
+ *
+ * This function is made private because it allows overriding the $base and
+ * the $values properties without any type checking.
+ *
+ * @param string $base The fully qualified name of the base field
+ * @param array $values The values of the fields
+ *
+ * @return FormFieldRegistry
+ */
+ static private function create($base, array $values)
+ {
+ $registry = new static();
+ $registry->base = $base;
+ $registry->fields = $values;
+
+ return $registry;
+ }
+
+ /**
+ * Transforms a PHP array in a list of fully qualified name / value.
+ *
+ * @param array $array The PHP array
+ * @param string $base The name of the base field
+ * @param array $output The initial values
+ *
+ * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+ */
+ private function walk(array $array, $base = '', array &$output = array())
+ {
+ foreach ($array as $k => $v) {
+ $path = empty($base) ? $k : sprintf("%s[%s]", $base, $k);
+ if (is_array($v)) {
+ $this->walk($v, $path, $output);
+ } else {
+ $output[$path] = $v;
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Splits a field name into segments as a web browser would do.
+ *
+ * <code>
+ * getSegments('base[foo][3][]') = array('base', 'foo, '3', '');
+ * </code>
+ *
+ * @param string $name The name of the field
+ *
+ * @return array The list of segments
+ *
+ * @throws \InvalidArgumentException when the name is malformed
+ */
+ private function getSegments($name)
+ {
+ if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
+ $segments = array($m['base']);
+ while (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $m['extra'], $m)) {
+ $segments[] = $m['segment'];
+ }
+
+ return $segments;
+ }
+
+ throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
+ }
+}
diff --git a/samples/php/Model.php b/samples/php/Model.php
new file mode 100644
index 0000000..2eecea4
--- /dev/null
+++ b/samples/php/Model.php
@@ -0,0 +1,3640 @@
+<?php
+/**
+ * Object-relational mapper.
+ *
+ * DBO-backed object data model, for mapping database tables to Cake objects.
+ *
+ * PHP versions 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Model
+ * @since CakePHP(tm) v 0.10.0.0
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('ClassRegistry', 'Utility');
+App::uses('Validation', 'Utility');
+App::uses('String', 'Utility');
+App::uses('Set', 'Utility');
+App::uses('BehaviorCollection', 'Model');
+App::uses('ModelBehavior', 'Model');
+App::uses('ConnectionManager', 'Model');
+App::uses('Xml', 'Utility');
+App::uses('CakeEvent', 'Event');
+App::uses('CakeEventListener', 'Event');
+App::uses('CakeEventManager', 'Event');
+
+/**
+ * Object-relational mapper.
+ *
+ * DBO-backed object data model.
+ * Automatically selects a database table name based on a pluralized lowercase object class name
+ * (i.e. class 'User' => table 'users'; class 'Man' => table 'men')
+ * The table is required to have at least 'id auto_increment' primary key.
+ *
+ * @package Cake.Model
+ * @link http://book.cakephp.org/2.0/en/models.html
+ */
+class Model extends Object implements CakeEventListener {
+
+/**
+ * The name of the DataSource connection that this Model uses
+ *
+ * The value must be an attribute name that you defined in `app/Config/database.php`
+ * or created using `ConnectionManager::create()`.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#usedbconfig
+ */
+ public $useDbConfig = 'default';
+
+/**
+ * Custom database table name, or null/false if no table association is desired.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#useTable
+ */
+ public $useTable = null;
+
+/**
+ * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements.
+ *
+ * This field is also used in `find('list')` when called with no extra parameters in the fields list
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#displayField
+ */
+ public $displayField = null;
+
+/**
+ * Value of the primary key ID of the record that this model is currently pointing to.
+ * Automatically set after database insertions.
+ *
+ * @var mixed
+ */
+ public $id = false;
+
+/**
+ * Container for the data that this model gets from persistent storage (usually, a database).
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#data
+ */
+ public $data = array();
+
+/**
+ * Holds physical schema/database name for this model. Automatically set during Model creation.
+ *
+ * @var string
+ * @access public
+ */
+ public $schemaName = null;
+
+/**
+ * Table name for this Model.
+ *
+ * @var string
+ */
+ public $table = false;
+
+/**
+ * The name of the primary key field for this model.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#primaryKey
+ */
+ public $primaryKey = null;
+
+/**
+ * Field-by-field table metadata.
+ *
+ * @var array
+ */
+ protected $_schema = null;
+
+/**
+ * List of validation rules. It must be an array with the field name as key and using
+ * as value one of the following possibilities
+ *
+ * ### Validating using regular expressions
+ *
+ * {{{
+ * public $validate = array(
+ * 'name' => '/^[a-z].+$/i'
+ * );
+ * }}}
+ *
+ * ### Validating using methods (no parameters)
+ *
+ * {{{
+ * public $validate = array(
+ * 'name' => 'notEmpty'
+ * );
+ * }}}
+ *
+ * ### Validating using methods (with parameters)
+ *
+ * {{{
+ * public $validate = array(
+ * 'age' => array(
+ * 'rule' => array('between', 5, 25)
+ * )
+ * );
+ * }}}
+ *
+ * ### Validating using custom method
+ *
+ * {{{
+ * public $validate = array(
+ * 'password' => array(
+ * 'rule' => array('customValidation')
+ * )
+ * );
+ * public function customValidation($data) {
+ * // $data will contain array('password' => 'value')
+ * if (isset($this->data[$this->alias]['password2'])) {
+ * return $this->data[$this->alias]['password2'] === current($data);
+ * }
+ * return true;
+ * }
+ * }}}
+ *
+ * ### Validations with messages
+ *
+ * The messages will be used in Model::$validationErrors and can be used in the FormHelper
+ *
+ * {{{
+ * public $validate = array(
+ * 'age' => array(
+ * 'rule' => array('between', 5, 25),
+ * 'message' => array('The age must be between %d and %d.')
+ * )
+ * );
+ * }}}
+ *
+ * ### Multiple validations to the same field
+ *
+ * {{{
+ * public $validate = array(
+ * 'login' => array(
+ * array(
+ * 'rule' => 'alphaNumeric',
+ * 'message' => 'Only alphabets and numbers allowed',
+ * 'last' => true
+ * ),
+ * array(
+ * 'rule' => array('minLength', 8),
+ * 'message' => array('Minimum length of %d characters')
+ * )
+ * )
+ * );
+ * }}}
+ *
+ * ### Valid keys in validations
+ *
+ * - `rule`: String with method name, regular expression (started by slash) or array with method and parameters
+ * - `message`: String with the message or array if have multiple parameters. See http://php.net/sprintf
+ * - `last`: Boolean value to indicate if continue validating the others rules if the current fail [Default: true]
+ * - `required`: Boolean value to indicate if the field must be present on save
+ * - `allowEmpty`: Boolean value to indicate if the field can be empty
+ * - `on`: Possible values: `update`, `create`. Indicate to apply this rule only on update or create
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#validate
+ * @link http://book.cakephp.org/2.0/en/models/data-validation.html
+ */
+ public $validate = array();
+
+/**
+ * List of validation errors.
+ *
+ * @var array
+ */
+ public $validationErrors = array();
+
+/**
+ * Name of the validation string domain to use when translating validation errors.
+ *
+ * @var string
+ */
+ public $validationDomain = null;
+
+/**
+ * Database table prefix for tables in model.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#tableprefix
+ */
+ public $tablePrefix = null;
+
+/**
+ * Name of the model.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#name
+ */
+ public $name = null;
+
+/**
+ * Alias name for model.
+ *
+ * @var string
+ */
+ public $alias = null;
+
+/**
+ * List of table names included in the model description. Used for associations.
+ *
+ * @var array
+ */
+ public $tableToModel = array();
+
+/**
+ * Whether or not to cache queries for this model. This enables in-memory
+ * caching only, the results are not stored beyond the current request.
+ *
+ * @var boolean
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#cacheQueries
+ */
+ public $cacheQueries = false;
+
+/**
+ * Detailed list of belongsTo associations.
+ *
+ * ### Basic usage
+ *
+ * `public $belongsTo = array('Group', 'Department');`
+ *
+ * ### Detailed configuration
+ *
+ * {{{
+ * public $belongsTo = array(
+ * 'Group',
+ * 'Department' => array(
+ * 'className' => 'Department',
+ * 'foreignKey' => 'department_id'
+ * )
+ * );
+ * }}}
+ *
+ * ### Possible keys in association
+ *
+ * - `className`: the classname of the model being associated to the current model.
+ * If you're defining a 'Profile belongsTo User' relationship, the className key should equal 'User.'
+ * - `foreignKey`: the name of the foreign key found in the current model. This is
+ * especially handy if you need to define multiple belongsTo relationships. The default
+ * value for this key is the underscored, singular name of the other model, suffixed with '_id'.
+ * - `conditions`: An SQL fragment used to filter related model records. It's good
+ * practice to use model names in SQL fragments: 'User.active = 1' is always
+ * better than just 'active = 1.'
+ * - `type`: the type of the join to use in the SQL query, default is LEFT which
+ * may not fit your needs in all situations, INNER may be helpful when you want
+ * everything from your main and associated models or nothing at all!(effective
+ * when used with some conditions of course). (NB: type value is in lower case - i.e. left, inner)
+ * - `fields`: A list of fields to be retrieved when the associated model data is
+ * fetched. Returns all fields by default.
+ * - `order`: An SQL fragment that defines the sorting order for the returned associated rows.
+ * - `counterCache`: If set to true the associated Model will automatically increase or
+ * decrease the "[singular_model_name]_count" field in the foreign table whenever you do
+ * a save() or delete(). If its a string then its the field name to use. The value in the
+ * counter field represents the number of related rows.
+ * - `counterScope`: Optional conditions array to use for updating counter cache field.
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#belongsto
+ */
+ public $belongsTo = array();
+
+/**
+ * Detailed list of hasOne associations.
+ *
+ * ### Basic usage
+ *
+ * `public $hasOne = array('Profile', 'Address');`
+ *
+ * ### Detailed configuration
+ *
+ * {{{
+ * public $hasOne = array(
+ * 'Profile',
+ * 'Address' => array(
+ * 'className' => 'Address',
+ * 'foreignKey' => 'user_id'
+ * )
+ * );
+ * }}}
+ *
+ * ### Possible keys in association
+ *
+ * - `className`: the classname of the model being associated to the current model.
+ * If you're defining a 'User hasOne Profile' relationship, the className key should equal 'Profile.'
+ * - `foreignKey`: the name of the foreign key found in the other model. This is
+ * especially handy if you need to define multiple hasOne relationships.
+ * The default value for this key is the underscored, singular name of the
+ * current model, suffixed with '_id'. In the example above it would default to 'user_id'.
+ * - `conditions`: An SQL fragment used to filter related model records. It's good
+ * practice to use model names in SQL fragments: "Profile.approved = 1" is
+ * always better than just "approved = 1."
+ * - `fields`: A list of fields to be retrieved when the associated model data is
+ * fetched. Returns all fields by default.
+ * - `order`: An SQL fragment that defines the sorting order for the returned associated rows.
+ * - `dependent`: When the dependent key is set to true, and the model's delete()
+ * method is called with the cascade parameter set to true, associated model
+ * records are also deleted. In this case we set it true so that deleting a
+ * User will also delete her associated Profile.
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasone
+ */
+ public $hasOne = array();
+
+/**
+ * Detailed list of hasMany associations.
+ *
+ * ### Basic usage
+ *
+ * `public $hasMany = array('Comment', 'Task');`
+ *
+ * ### Detailed configuration
+ *
+ * {{{
+ * public $hasMany = array(
+ * 'Comment',
+ * 'Task' => array(
+ * 'className' => 'Task',
+ * 'foreignKey' => 'user_id'
+ * )
+ * );
+ * }}}
+ *
+ * ### Possible keys in association
+ *
+ * - `className`: the classname of the model being associated to the current model.
+ * If you're defining a 'User hasMany Comment' relationship, the className key should equal 'Comment.'
+ * - `foreignKey`: the name of the foreign key found in the other model. This is
+ * especially handy if you need to define multiple hasMany relationships. The default
+ * value for this key is the underscored, singular name of the actual model, suffixed with '_id'.
+ * - `conditions`: An SQL fragment used to filter related model records. It's good
+ * practice to use model names in SQL fragments: "Comment.status = 1" is always
+ * better than just "status = 1."
+ * - `fields`: A list of fields to be retrieved when the associated model data is
+ * fetched. Returns all fields by default.
+ * - `order`: An SQL fragment that defines the sorting order for the returned associated rows.
+ * - `limit`: The maximum number of associated rows you want returned.
+ * - `offset`: The number of associated rows to skip over (given the current
+ * conditions and order) before fetching and associating.
+ * - `dependent`: When dependent is set to true, recursive model deletion is
+ * possible. In this example, Comment records will be deleted when their
+ * associated User record has been deleted.
+ * - `exclusive`: When exclusive is set to true, recursive model deletion does
+ * the delete with a deleteAll() call, instead of deleting each entity separately.
+ * This greatly improves performance, but may not be ideal for all circumstances.
+ * - `finderQuery`: A complete SQL query CakePHP can use to fetch associated model
+ * records. This should be used in situations that require very custom results.
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany
+ */
+ public $hasMany = array();
+
+/**
+ * Detailed list of hasAndBelongsToMany associations.
+ *
+ * ### Basic usage
+ *
+ * `public $hasAndBelongsToMany = array('Role', 'Address');`
+ *
+ * ### Detailed configuration
+ *
+ * {{{
+ * public $hasAndBelongsToMany = array(
+ * 'Role',
+ * 'Address' => array(
+ * 'className' => 'Address',
+ * 'foreignKey' => 'user_id',
+ * 'associationForeignKey' => 'address_id',
+ * 'joinTable' => 'addresses_users'
+ * )
+ * );
+ * }}}
+ *
+ * ### Possible keys in association
+ *
+ * - `className`: the classname of the model being associated to the current model.
+ * If you're defining a 'Recipe HABTM Tag' relationship, the className key should equal 'Tag.'
+ * - `joinTable`: The name of the join table used in this association (if the
+ * current table doesn't adhere to the naming convention for HABTM join tables).
+ * - `with`: Defines the name of the model for the join table. By default CakePHP
+ * will auto-create a model for you. Using the example above it would be called
+ * RecipesTag. By using this key you can override this default name. The join
+ * table model can be used just like any "regular" model to access the join table directly.
+ * - `foreignKey`: the name of the foreign key found in the current model.
+ * This is especially handy if you need to define multiple HABTM relationships.
+ * The default value for this key is the underscored, singular name of the
+ * current model, suffixed with '_id'.
+ * - `associationForeignKey`: the name of the foreign key found in the other model.
+ * This is especially handy if you need to define multiple HABTM relationships.
+ * The default value for this key is the underscored, singular name of the other
+ * model, suffixed with '_id'.
+ * - `unique`: If true (default value) cake will first delete existing relationship
+ * records in the foreign keys table before inserting new ones, when updating a
+ * record. So existing associations need to be passed again when updating.
+ * To prevent deletion of existing relationship records, set this key to a string 'keepExisting'.
+ * - `conditions`: An SQL fragment used to filter related model records. It's good
+ * practice to use model names in SQL fragments: "Comment.status = 1" is always
+ * better than just "status = 1."
+ * - `fields`: A list of fields to be retrieved when the associated model data is
+ * fetched. Returns all fields by default.
+ * - `order`: An SQL fragment that defines the sorting order for the returned associated rows.
+ * - `limit`: The maximum number of associated rows you want returned.
+ * - `offset`: The number of associated rows to skip over (given the current
+ * conditions and order) before fetching and associating.
+ * - `finderQuery`, `deleteQuery`, `insertQuery`: A complete SQL query CakePHP
+ * can use to fetch, delete, or create new associated model records. This should
+ * be used in situations that require very custom results.
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
+ */
+ public $hasAndBelongsToMany = array();
+
+/**
+ * List of behaviors to load when the model object is initialized. Settings can be
+ * passed to behaviors by using the behavior name as index. Eg:
+ *
+ * public $actsAs = array('Translate', 'MyBehavior' => array('setting1' => 'value1'))
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/behaviors.html#using-behaviors
+ */
+ public $actsAs = null;
+
+/**
+ * Holds the Behavior objects currently bound to this model.
+ *
+ * @var BehaviorCollection
+ */
+ public $Behaviors = null;
+
+/**
+ * Whitelist of fields allowed to be saved.
+ *
+ * @var array
+ */
+ public $whitelist = array();
+
+/**
+ * Whether or not to cache sources for this model.
+ *
+ * @var boolean
+ */
+ public $cacheSources = true;
+
+/**
+ * Type of find query currently executing.
+ *
+ * @var string
+ */
+ public $findQueryType = null;
+
+/**
+ * Number of associations to recurse through during find calls. Fetches only
+ * the first level by default.
+ *
+ * @var integer
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive
+ */
+ public $recursive = 1;
+
+/**
+ * The column name(s) and direction(s) to order find results by default.
+ *
+ * public $order = "Post.created DESC";
+ * public $order = array("Post.view_count DESC", "Post.rating DESC");
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#order
+ */
+ public $order = null;
+
+/**
+ * Array of virtual fields this model has. Virtual fields are aliased
+ * SQL expressions. Fields added to this property will be read as other fields in a model
+ * but will not be saveable.
+ *
+ * `public $virtualFields = array('two' => '1 + 1');`
+ *
+ * Is a simplistic example of how to set virtualFields
+ *
+ * @var array
+ * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#virtualfields
+ */
+ public $virtualFields = array();
+
+/**
+ * Default list of association keys.
+ *
+ * @var array
+ */
+ protected $_associationKeys = array(
+ 'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'),
+ 'hasOne' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'dependent'),
+ 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'),
+ 'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery')
+ );
+
+/**
+ * Holds provided/generated association key names and other data for all associations.
+ *
+ * @var array
+ */
+ protected $_associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+
+/**
+ * Holds model associations temporarily to allow for dynamic (un)binding.
+ *
+ * @var array
+ */
+ public $__backAssociation = array();
+
+/**
+ * Back inner association
+ *
+ * @var array
+ */
+ public $__backInnerAssociation = array();
+
+/**
+ * Back original association
+ *
+ * @var array
+ */
+ public $__backOriginalAssociation = array();
+
+/**
+ * Back containable association
+ *
+ * @var array
+ */
+ public $__backContainableAssociation = array();
+
+/**
+ * The ID of the model record that was last inserted.
+ *
+ * @var integer
+ */
+ protected $_insertID = null;
+
+/**
+ * Has the datasource been configured.
+ *
+ * @var boolean
+ * @see Model::getDataSource
+ */
+ protected $_sourceConfigured = false;
+
+/**
+ * List of valid finder method options, supplied as the first parameter to find().
+ *
+ * @var array
+ */
+ public $findMethods = array(
+ 'all' => true, 'first' => true, 'count' => true,
+ 'neighbors' => true, 'list' => true, 'threaded' => true
+ );
+
+/**
+ * Instance of the CakeEventManager this model is using
+ * to dispatch inner events.
+ *
+ * @var CakeEventManager
+ */
+ protected $_eventManager = null;
+
+/**
+ * Constructor. Binds the model's database table to the object.
+ *
+ * If `$id` is an array it can be used to pass several options into the model.
+ *
+ * - id - The id to start the model on.
+ * - table - The table to use for this model.
+ * - ds - The connection name this model is connected to.
+ * - name - The name of the model eg. Post.
+ * - alias - The alias of the model, this is used for registering the instance in the `ClassRegistry`.
+ * eg. `ParentThread`
+ *
+ * ### Overriding Model's __construct method.
+ *
+ * When overriding Model::__construct() be careful to include and pass in all 3 of the
+ * arguments to `parent::__construct($id, $table, $ds);`
+ *
+ * ### Dynamically creating models
+ *
+ * You can dynamically create model instances using the $id array syntax.
+ *
+ * {{{
+ * $Post = new Model(array('table' => 'posts', 'name' => 'Post', 'ds' => 'connection2'));
+ * }}}
+ *
+ * Would create a model attached to the posts table on connection2. Dynamic model creation is useful
+ * when you want a model object that contains no associations or attached behaviors.
+ *
+ * @param mixed $id Set this ID for this model on startup, can also be an array of options, see above.
+ * @param string $table Name of database table to use.
+ * @param string $ds DataSource connection name.
+ */
+ public function __construct($id = false, $table = null, $ds = null) {
+ parent::__construct();
+
+ if (is_array($id)) {
+ extract(array_merge(
+ array(
+ 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig,
+ 'name' => $this->name, 'alias' => $this->alias
+ ),
+ $id
+ ));
+ }
+
+ if ($this->name === null) {
+ $this->name = (isset($name) ? $name : get_class($this));
+ }
+
+ if ($this->alias === null) {
+ $this->alias = (isset($alias) ? $alias : $this->name);
+ }
+
+ if ($this->primaryKey === null) {
+ $this->primaryKey = 'id';
+ }
+
+ ClassRegistry::addObject($this->alias, $this);
+
+ $this->id = $id;
+ unset($id);
+
+ if ($table === false) {
+ $this->useTable = false;
+ } elseif ($table) {
+ $this->useTable = $table;
+ }
+
+ if ($ds !== null) {
+ $this->useDbConfig = $ds;
+ }
+
+ if (is_subclass_of($this, 'AppModel')) {
+ $merge = array('actsAs', 'findMethods');
+ $parentClass = get_parent_class($this);
+ if ($parentClass !== 'AppModel') {
+ $this->_mergeVars($merge, $parentClass);
+ }
+ $this->_mergeVars($merge, 'AppModel');
+ }
+ $this->_mergeVars(array('findMethods'), 'Model');
+
+ $this->Behaviors = new BehaviorCollection();
+
+ if ($this->useTable !== false) {
+
+ if ($this->useTable === null) {
+ $this->useTable = Inflector::tableize($this->name);
+ }
+
+ if ($this->displayField == null) {
+ unset($this->displayField);
+ }
+ $this->table = $this->useTable;
+ $this->tableToModel[$this->table] = $this->alias;
+ } elseif ($this->table === false) {
+ $this->table = Inflector::tableize($this->name);
+ }
+
+ if ($this->tablePrefix === null) {
+ unset($this->tablePrefix);
+ }
+
+ $this->_createLinks();
+ $this->Behaviors->init($this->alias, $this->actsAs);
+ }
+
+/**
+ * Returns a list of all events that will fire in the model during it's lifecycle.
+ * You can override this function to add you own listener callbacks
+ *
+ * @return array
+ */
+ public function implementedEvents() {
+ return array(
+ 'Model.beforeFind' => array('callable' => 'beforeFind', 'passParams' => true),
+ 'Model.afterFind' => array('callable' => 'afterFind', 'passParams' => true),
+ 'Model.beforeValidate' => array('callable' => 'beforeValidate', 'passParams' => true),
+ 'Model.beforeSave' => array('callable' => 'beforeSave', 'passParams' => true),
+ 'Model.afterSave' => array('callable' => 'afterSave', 'passParams' => true),
+ 'Model.beforeDelete' => array('callable' => 'beforeDelete', 'passParams' => true),
+ 'Model.afterDelete' => array('callable' => 'afterDelete'),
+ );
+ }
+
+/**
+ * Returns the CakeEventManager manager instance that is handling any callbacks.
+ * You can use this instance to register any new listeners or callbacks to the
+ * model events, or create your own events and trigger them at will.
+ *
+ * @return CakeEventManager
+ */
+ public function getEventManager() {
+ if (empty($this->_eventManager)) {
+ $this->_eventManager = new CakeEventManager();
+ $this->_eventManager->attach($this->Behaviors);
+ $this->_eventManager->attach($this);
+ }
+ return $this->_eventManager;
+ }
+
+/**
+ * Handles custom method calls, like findBy<field> for DB models,
+ * and custom RPC calls for remote data sources.
+ *
+ * @param string $method Name of method to call.
+ * @param array $params Parameters for the method.
+ * @return mixed Whatever is returned by called method
+ */
+ public function __call($method, $params) {
+ $result = $this->Behaviors->dispatchMethod($this, $method, $params);
+ if ($result !== array('unhandled')) {
+ return $result;
+ }
+ $return = $this->getDataSource()->query($method, $params, $this);
+ return $return;
+ }
+
+/**
+ * Handles the lazy loading of model associations by looking in the association arrays for the requested variable
+ *
+ * @param string $name variable tested for existence in class
+ * @return boolean true if the variable exists (if is a not loaded model association it will be created), false otherwise
+ */
+ public function __isset($name) {
+ $className = false;
+
+ foreach ($this->_associations as $type) {
+ if (isset($name, $this->{$type}[$name])) {
+ $className = empty($this->{$type}[$name]['className']) ? $name : $this->{$type}[$name]['className'];
+ break;
+ } elseif (isset($name, $this->__backAssociation[$type][$name])) {
+ $className = empty($this->__backAssociation[$type][$name]['className']) ?
+ $name : $this->__backAssociation[$type][$name]['className'];
+ break;
+ } elseif ($type == 'hasAndBelongsToMany') {
+ foreach ($this->{$type} as $k => $relation) {
+ if (empty($relation['with'])) {
+ continue;
+ }
+ if (is_array($relation['with'])) {
+ if (key($relation['with']) === $name) {
+ $className = $name;
+ }
+ } else {
+ list($plugin, $class) = pluginSplit($relation['with']);
+ if ($class === $name) {
+ $className = $relation['with'];
+ }
+ }
+ if ($className) {
+ $assocKey = $k;
+ $dynamic = !empty($relation['dynamicWith']);
+ break(2);
+ }
+ }
+ }
+ }
+
+ if (!$className) {
+ return false;
+ }
+
+ list($plugin, $className) = pluginSplit($className);
+
+ if (!ClassRegistry::isKeySet($className) && !empty($dynamic)) {
+ $this->{$className} = new AppModel(array(
+ 'name' => $className,
+ 'table' => $this->hasAndBelongsToMany[$assocKey]['joinTable'],
+ 'ds' => $this->useDbConfig
+ ));
+ } else {
+ $this->_constructLinkedModel($name, $className, $plugin);
+ }
+
+ if (!empty($assocKey)) {
+ $this->hasAndBelongsToMany[$assocKey]['joinTable'] = $this->{$name}->table;
+ if (count($this->{$name}->schema()) <= 2 && $this->{$name}->primaryKey !== false) {
+ $this->{$name}->primaryKey = $this->hasAndBelongsToMany[$assocKey]['foreignKey'];
+ }
+ }
+
+ return true;
+ }
+
+/**
+ * Returns the value of the requested variable if it can be set by __isset()
+ *
+ * @param string $name variable requested for it's value or reference
+ * @return mixed value of requested variable if it is set
+ */
+ public function __get($name) {
+ if ($name === 'displayField') {
+ return $this->displayField = $this->hasField(array('title', 'name', $this->primaryKey));
+ }
+ if ($name === 'tablePrefix') {
+ $this->setDataSource();
+ if (property_exists($this, 'tablePrefix') && !empty($this->tablePrefix)) {
+ return $this->tablePrefix;
+ }
+ return $this->tablePrefix = null;
+ }
+ if (isset($this->{$name})) {
+ return $this->{$name};
+ }
+ }
+
+/**
+ * Bind model associations on the fly.
+ *
+ * If `$reset` is false, association will not be reset
+ * to the originals defined in the model
+ *
+ * Example: Add a new hasOne binding to the Profile model not
+ * defined in the model source code:
+ *
+ * `$this->User->bindModel( array('hasOne' => array('Profile')) );`
+ *
+ * Bindings that are not made permanent will be reset by the next Model::find() call on this
+ * model.
+ *
+ * @param array $params Set of bindings (indexed by binding type)
+ * @param boolean $reset Set to false to make the binding permanent
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly
+ */
+ public function bindModel($params, $reset = true) {
+ foreach ($params as $assoc => $model) {
+ if ($reset === true && !isset($this->__backAssociation[$assoc])) {
+ $this->__backAssociation[$assoc] = $this->{$assoc};
+ }
+ foreach ($model as $key => $value) {
+ $assocName = $key;
+
+ if (is_numeric($key)) {
+ $assocName = $value;
+ $value = array();
+ }
+ $this->{$assoc}[$assocName] = $value;
+ if (property_exists($this, $assocName)) {
+ unset($this->{$assocName});
+ }
+ if ($reset === false && isset($this->__backAssociation[$assoc])) {
+ $this->__backAssociation[$assoc][$assocName] = $value;
+ }
+ }
+ }
+ $this->_createLinks();
+ return true;
+ }
+
+/**
+ * Turn off associations on the fly.
+ *
+ * If $reset is false, association will not be reset
+ * to the originals defined in the model
+ *
+ * Example: Turn off the associated Model Support request,
+ * to temporarily lighten the User model:
+ *
+ * `$this->User->unbindModel( array('hasMany' => array('Supportrequest')) );`
+ *
+ * unbound models that are not made permanent will reset with the next call to Model::find()
+ *
+ * @param array $params Set of bindings to unbind (indexed by binding type)
+ * @param boolean $reset Set to false to make the unbinding permanent
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly
+ */
+ public function unbindModel($params, $reset = true) {
+ foreach ($params as $assoc => $models) {
+ if ($reset === true && !isset($this->__backAssociation[$assoc])) {
+ $this->__backAssociation[$assoc] = $this->{$assoc};
+ }
+ foreach ($models as $model) {
+ if ($reset === false && isset($this->__backAssociation[$assoc][$model])) {
+ unset($this->__backAssociation[$assoc][$model]);
+ }
+ unset($this->{$assoc}[$model]);
+ }
+ }
+ return true;
+ }
+
+/**
+ * Create a set of associations.
+ *
+ * @return void
+ */
+ protected function _createLinks() {
+ foreach ($this->_associations as $type) {
+ if (!is_array($this->{$type})) {
+ $this->{$type} = explode(',', $this->{$type});
+
+ foreach ($this->{$type} as $i => $className) {
+ $className = trim($className);
+ unset ($this->{$type}[$i]);
+ $this->{$type}[$className] = array();
+ }
+ }
+
+ if (!empty($this->{$type})) {
+ foreach ($this->{$type} as $assoc => $value) {
+ $plugin = null;
+
+ if (is_numeric($assoc)) {
+ unset ($this->{$type}[$assoc]);
+ $assoc = $value;
+ $value = array();
+
+ if (strpos($assoc, '.') !== false) {
+ list($plugin, $assoc) = pluginSplit($assoc);
+ $this->{$type}[$assoc] = array('className' => $plugin . '.' . $assoc);
+ } else {
+ $this->{$type}[$assoc] = $value;
+ }
+ }
+ $this->_generateAssociation($type, $assoc);
+ }
+ }
+ }
+ }
+
+/**
+ * Protected helper method to create associated models of a given class.
+ *
+ * @param string $assoc Association name
+ * @param string $className Class name
+ * @param string $plugin name of the plugin where $className is located
+ * examples: public $hasMany = array('Assoc' => array('className' => 'ModelName'));
+ * usage: $this->Assoc->modelMethods();
+ *
+ * public $hasMany = array('ModelName');
+ * usage: $this->ModelName->modelMethods();
+ * @return void
+ */
+ protected function _constructLinkedModel($assoc, $className = null, $plugin = null) {
+ if (empty($className)) {
+ $className = $assoc;
+ }
+
+ if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) {
+ if ($plugin) {
+ $plugin .= '.';
+ }
+ $model = array('class' => $plugin . $className, 'alias' => $assoc);
+ $this->{$assoc} = ClassRegistry::init($model);
+ if ($plugin) {
+ ClassRegistry::addObject($plugin . $className, $this->{$assoc});
+ }
+ if ($assoc) {
+ $this->tableToModel[$this->{$assoc}->table] = $assoc;
+ }
+ }
+ }
+
+/**
+ * Build an array-based association from string.
+ *
+ * @param string $type 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'
+ * @param string $assocKey
+ * @return void
+ */
+ protected function _generateAssociation($type, $assocKey) {
+ $class = $assocKey;
+ $dynamicWith = false;
+
+ foreach ($this->_associationKeys[$type] as $key) {
+
+ if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) {
+ $data = '';
+
+ switch ($key) {
+ case 'fields':
+ $data = '';
+ break;
+
+ case 'foreignKey':
+ $data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id';
+ break;
+
+ case 'associationForeignKey':
+ $data = Inflector::singularize($this->{$class}->table) . '_id';
+ break;
+
+ case 'with':
+ $data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable']));
+ $dynamicWith = true;
+ break;
+
+ case 'joinTable':
+ $tables = array($this->table, $this->{$class}->table);
+ sort ($tables);
+ $data = $tables[0] . '_' . $tables[1];
+ break;
+
+ case 'className':
+ $data = $class;
+ break;
+
+ case 'unique':
+ $data = true;
+ break;
+ }
+ $this->{$type}[$assocKey][$key] = $data;
+ }
+
+ if ($dynamicWith) {
+ $this->{$type}[$assocKey]['dynamicWith'] = true;
+ }
+
+ }
+ }
+
+/**
+ * Sets a custom table for your controller class. Used by your controller to select a database table.
+ *
+ * @param string $tableName Name of the custom table
+ * @throws MissingTableException when database table $tableName is not found on data source
+ * @return void
+ */
+ public function setSource($tableName) {
+ $this->setDataSource($this->useDbConfig);
+ $db = ConnectionManager::getDataSource($this->useDbConfig);
+ $db->cacheSources = ($this->cacheSources && $db->cacheSources);
+
+ if (method_exists($db, 'listSources')) {
+ $sources = $db->listSources();
+ if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) {
+ throw new MissingTableException(array(
+ 'table' => $this->tablePrefix . $tableName,
+ 'class' => $this->alias,
+ 'ds' => $this->useDbConfig,
+ ));
+ }
+ $this->_schema = null;
+ }
+ $this->table = $this->useTable = $tableName;
+ $this->tableToModel[$this->table] = $this->alias;
+ }
+
+/**
+ * This function does two things:
+ *
+ * 1. it scans the array $one for the primary key,
+ * and if that's found, it sets the current id to the value of $one[id].
+ * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object.
+ * 2. Returns an array with all of $one's keys and values.
+ * (Alternative indata: two strings, which are mangled to
+ * a one-item, two-dimensional array using $one for a key and $two as its value.)
+ *
+ * @param mixed $one Array or string of data
+ * @param string $two Value string for the alternative indata method
+ * @return array Data with all of $one's keys and values
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html
+ */
+ public function set($one, $two = null) {
+ if (!$one) {
+ return;
+ }
+ if (is_object($one)) {
+ if ($one instanceof SimpleXMLElement || $one instanceof DOMNode) {
+ $one = $this->_normalizeXmlData(Xml::toArray($one));
+ } else {
+ $one = Set::reverse($one);
+ }
+ }
+
+ if (is_array($one)) {
+ $data = $one;
+ if (empty($one[$this->alias])) {
+ $data = $this->_setAliasData($one);
+ }
+ } else {
+ $data = array($this->alias => array($one => $two));
+ }
+
+ foreach ($data as $modelName => $fieldSet) {
+ if (is_array($fieldSet)) {
+
+ foreach ($fieldSet as $fieldName => $fieldValue) {
+ if (isset($this->validationErrors[$fieldName])) {
+ unset ($this->validationErrors[$fieldName]);
+ }
+
+ if ($modelName === $this->alias) {
+ if ($fieldName === $this->primaryKey) {
+ $this->id = $fieldValue;
+ }
+ }
+ if (is_array($fieldValue) || is_object($fieldValue)) {
+ $fieldValue = $this->deconstruct($fieldName, $fieldValue);
+ }
+ $this->data[$modelName][$fieldName] = $fieldValue;
+ }
+ }
+ }
+ return $data;
+ }
+
+/**
+ * Move values to alias
+ *
+ * @param array $data
+ * @return array
+ */
+ protected function _setAliasData($data) {
+ $models = array_keys($this->getAssociated());
+ $schema = array_keys($this->schema());
+ foreach ($data as $field => $value) {
+ if (in_array($field, $schema) || !in_array($field, $models)) {
+ $data[$this->alias][$field] = $value;
+ unset($data[$field]);
+ }
+ }
+ return $data;
+ }
+
+/**
+ * Normalize Xml::toArray() to use in Model::save()
+ *
+ * @param array $xml XML as array
+ * @return array
+ */
+ protected function _normalizeXmlData(array $xml) {
+ $return = array();
+ foreach ($xml as $key => $value) {
+ if (is_array($value)) {
+ $return[Inflector::camelize($key)] = $this->_normalizeXmlData($value);
+ } elseif ($key[0] === '@') {
+ $return[substr($key, 1)] = $value;
+ } else {
+ $return[$key] = $value;
+ }
+ }
+ return $return;
+ }
+
+/**
+ * Deconstructs a complex data type (array or object) into a single field value.
+ *
+ * @param string $field The name of the field to be deconstructed
+ * @param mixed $data An array or object to be deconstructed into a field
+ * @return mixed The resulting data that should be assigned to a field
+ */
+ public function deconstruct($field, $data) {
+ if (!is_array($data)) {
+ return $data;
+ }
+
+ $type = $this->getColumnType($field);
+
+ if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) {
+ $useNewDate = (isset($data['year']) || isset($data['month']) ||
+ isset($data['day']) || isset($data['hour']) || isset($data['minute']));
+
+ $dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec');
+ $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec');
+ $date = array();
+
+ if (isset($data['meridian']) && empty($data['meridian'])) {
+ return null;
+ }
+
+ if (
+ isset($data['hour']) &&
+ isset($data['meridian']) &&
+ !empty($data['hour']) &&
+ $data['hour'] != 12 &&
+ 'pm' == $data['meridian']
+ ) {
+ $data['hour'] = $data['hour'] + 12;
+ }
+ if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) {
+ $data['hour'] = '00';
+ }
+ if ($type == 'time') {
+ foreach ($timeFields as $key => $val) {
+ if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
+ $data[$val] = '00';
+ } elseif ($data[$val] !== '') {
+ $data[$val] = sprintf('%02d', $data[$val]);
+ }
+ if (!empty($data[$val])) {
+ $date[$key] = $data[$val];
+ } else {
+ return null;
+ }
+ }
+ }
+
+ if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') {
+ foreach ($dateFields as $key => $val) {
+ if ($val == 'hour' || $val == 'min' || $val == 'sec') {
+ if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
+ $data[$val] = '00';
+ } else {
+ $data[$val] = sprintf('%02d', $data[$val]);
+ }
+ }
+ if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) {
+ return null;
+ }
+ if (isset($data[$val]) && !empty($data[$val])) {
+ $date[$key] = $data[$val];
+ }
+ }
+ }
+
+ if ($useNewDate && !empty($date)) {
+ $format = $this->getDataSource()->columns[$type]['format'];
+ foreach (array('m', 'd', 'H', 'i', 's') as $index) {
+ if (isset($date[$index])) {
+ $date[$index] = sprintf('%02d', $date[$index]);
+ }
+ }
+ return str_replace(array_keys($date), array_values($date), $format);
+ }
+ }
+ return $data;
+ }
+
+/**
+ * Returns an array of table metadata (column names and types) from the database.
+ * $field => keys(type, null, default, key, length, extra)
+ *
+ * @param mixed $field Set to true to reload schema, or a string to return a specific field
+ * @return array Array of table metadata
+ */
+ public function schema($field = false) {
+ if ($this->useTable !== false && (!is_array($this->_schema) || $field === true)) {
+ $db = $this->getDataSource();
+ $db->cacheSources = ($this->cacheSources && $db->cacheSources);
+ if (method_exists($db, 'describe') && $this->useTable !== false) {
+ $this->_schema = $db->describe($this);
+ } elseif ($this->useTable === false) {
+ $this->_schema = array();
+ }
+ }
+ if (is_string($field)) {
+ if (isset($this->_schema[$field])) {
+ return $this->_schema[$field];
+ } else {
+ return null;
+ }
+ }
+ return $this->_schema;
+ }
+
+/**
+ * Returns an associative array of field names and column types.
+ *
+ * @return array Field types indexed by field name
+ */
+ public function getColumnTypes() {
+ $columns = $this->schema();
+ if (empty($columns)) {
+ trigger_error(__d('cake_dev', '(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()'), E_USER_WARNING);
+ }
+ $cols = array();
+ foreach ($columns as $field => $values) {
+ $cols[$field] = $values['type'];
+ }
+ return $cols;
+ }
+
+/**
+ * Returns the column type of a column in the model.
+ *
+ * @param string $column The name of the model column
+ * @return string Column type
+ */
+ public function getColumnType($column) {
+ $db = $this->getDataSource();
+ $cols = $this->schema();
+ $model = null;
+
+ $startQuote = isset($db->startQuote) ? $db->startQuote : null;
+ $endQuote = isset($db->endQuote) ? $db->endQuote : null;
+ $column = str_replace(array($startQuote, $endQuote), '', $column);
+
+ if (strpos($column, '.')) {
+ list($model, $column) = explode('.', $column);
+ }
+ if ($model != $this->alias && isset($this->{$model})) {
+ return $this->{$model}->getColumnType($column);
+ }
+ if (isset($cols[$column]) && isset($cols[$column]['type'])) {
+ return $cols[$column]['type'];
+ }
+ return null;
+ }
+
+/**
+ * Returns true if the supplied field exists in the model's database table.
+ *
+ * @param mixed $name Name of field to look for, or an array of names
+ * @param boolean $checkVirtual checks if the field is declared as virtual
+ * @return mixed If $name is a string, returns a boolean indicating whether the field exists.
+ * If $name is an array of field names, returns the first field that exists,
+ * or false if none exist.
+ */
+ public function hasField($name, $checkVirtual = false) {
+ if (is_array($name)) {
+ foreach ($name as $n) {
+ if ($this->hasField($n, $checkVirtual)) {
+ return $n;
+ }
+ }
+ return false;
+ }
+
+ if ($checkVirtual && !empty($this->virtualFields)) {
+ if ($this->isVirtualField($name)) {
+ return true;
+ }
+ }
+
+ if (empty($this->_schema)) {
+ $this->schema();
+ }
+
+ if ($this->_schema != null) {
+ return isset($this->_schema[$name]);
+ }
+ return false;
+ }
+
+/**
+ * Check that a method is callable on a model. This will check both the model's own methods, its
+ * inherited methods and methods that could be callable through behaviors.
+ *
+ * @param string $method The method to be called.
+ * @return boolean True on method being callable.
+ */
+ public function hasMethod($method) {
+ if (method_exists($this, $method)) {
+ return true;
+ }
+ if ($this->Behaviors->hasMethod($method)) {
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Returns true if the supplied field is a model Virtual Field
+ *
+ * @param string $field Name of field to look for
+ * @return boolean indicating whether the field exists as a model virtual field.
+ */
+ public function isVirtualField($field) {
+ if (empty($this->virtualFields) || !is_string($field)) {
+ return false;
+ }
+ if (isset($this->virtualFields[$field])) {
+ return true;
+ }
+ if (strpos($field, '.') !== false) {
+ list($model, $field) = explode('.', $field);
+ if ($model == $this->alias && isset($this->virtualFields[$field])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * Returns the expression for a model virtual field
+ *
+ * @param string $field Name of field to look for
+ * @return mixed If $field is string expression bound to virtual field $field
+ * If $field is null, returns an array of all model virtual fields
+ * or false if none $field exist.
+ */
+ public function getVirtualField($field = null) {
+ if ($field == null) {
+ return empty($this->virtualFields) ? false : $this->virtualFields;
+ }
+ if ($this->isVirtualField($field)) {
+ if (strpos($field, '.') !== false) {
+ list($model, $field) = explode('.', $field);
+ }
+ return $this->virtualFields[$field];
+ }
+ return false;
+ }
+
+/**
+ * Initializes the model for writing a new record, loading the default values
+ * for those fields that are not defined in $data, and clearing previous validation errors.
+ * Especially helpful for saving data in loops.
+ *
+ * @param mixed $data Optional data array to assign to the model after it is created. If null or false,
+ * schema data defaults are not merged.
+ * @param boolean $filterKey If true, overwrites any primary key input with an empty value
+ * @return array The current Model::data; after merging $data and/or defaults from database
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-create-array-data-array
+ */
+ public function create($data = array(), $filterKey = false) {
+ $defaults = array();
+ $this->id = false;
+ $this->data = array();
+ $this->validationErrors = array();
+
+ if ($data !== null && $data !== false) {
+ foreach ($this->schema() as $field => $properties) {
+ if ($this->primaryKey !== $field && isset($properties['default']) && $properties['default'] !== '') {
+ $defaults[$field] = $properties['default'];
+ }
+ }
+ $this->set($defaults);
+ $this->set($data);
+ }
+ if ($filterKey) {
+ $this->set($this->primaryKey, false);
+ }
+ return $this->data;
+ }
+
+/**
+ * Returns a list of fields from the database, and sets the current model
+ * data (Model::$data) with the record found.
+ *
+ * @param mixed $fields String of single field name, or an array of field names.
+ * @param mixed $id The ID of the record to read
+ * @return array Array of database fields, or false if not found
+ * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-read
+ */
+ public function read($fields = null, $id = null) {
+ $this->validationErrors = array();
+
+ if ($id != null) {
+ $this->id = $id;
+ }
+
+ $id = $this->id;
+
+ if (is_array($this->id)) {
+ $id = $this->id[0];
+ }
+
+ if ($id !== null && $id !== false) {
+ $this->data = $this->find('first', array(
+ 'conditions' => array($this->alias . '.' . $this->primaryKey => $id),
+ 'fields' => $fields
+ ));
+ return $this->data;
+ } else {
+ return false;
+ }
+ }
+
+/**
+ * Returns the contents of a single field given the supplied conditions, in the
+ * supplied order.
+ *
+ * @param string $name Name of field to get
+ * @param array $conditions SQL conditions (defaults to NULL)
+ * @param string $order SQL ORDER BY fragment
+ * @return string field contents, or false if not found
+ * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-field
+ */
+ public function field($name, $conditions = null, $order = null) {
+ if ($conditions === null && $this->id !== false) {
+ $conditions = array($this->alias . '.' . $this->primaryKey => $this->id);
+ }
+ if ($this->recursive >= 1) {
+ $recursive = -1;
+ } else {
+ $recursive = $this->recursive;
+ }
+ $fields = $name;
+ if ($data = $this->find('first', compact('conditions', 'fields', 'order', 'recursive'))) {
+ if (strpos($name, '.') === false) {
+ if (isset($data[$this->alias][$name])) {
+ return $data[$this->alias][$name];
+ }
+ } else {
+ $name = explode('.', $name);
+ if (isset($data[$name[0]][$name[1]])) {
+ return $data[$name[0]][$name[1]];
+ }
+ }
+ if (isset($data[0]) && count($data[0]) > 0) {
+ return array_shift($data[0]);
+ }
+ } else {
+ return false;
+ }
+ }
+
+/**
+ * Saves the value of a single field to the database, based on the current
+ * model ID.
+ *
+ * @param string $name Name of the table field
+ * @param mixed $value Value of the field
+ * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed
+ * @return boolean See Model::save()
+ * @see Model::save()
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false
+ */
+ public function saveField($name, $value, $validate = false) {
+ $id = $this->id;
+ $this->create(false);
+
+ if (is_array($validate)) {
+ $options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate);
+ } else {
+ $options = array('validate' => $validate, 'fieldList' => array($name));
+ }
+ return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options);
+ }
+
+/**
+ * Saves model data (based on white-list, if supplied) to the database. By
+ * default, validation occurs before save.
+ *
+ * @param array $data Data to save.
+ * @param mixed $validate Either a boolean, or an array.
+ * If a boolean, indicates whether or not to validate before saving.
+ * If an array, allows control of validate, callbacks, and fieldList
+ * @param array $fieldList List of fields to allow to be written
+ * @return mixed On success Model::$data if its not empty or true, false on failure
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html
+ */
+ public function save($data = null, $validate = true, $fieldList = array()) {
+ $defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true);
+ $_whitelist = $this->whitelist;
+ $fields = array();
+
+ if (!is_array($validate)) {
+ $options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks'));
+ } else {
+ $options = array_merge($defaults, $validate);
+ }
+
+ if (!empty($options['fieldList'])) {
+ if (!empty($options['fieldList'][$this->alias]) && is_array($options['fieldList'][$this->alias])) {
+ $this->whitelist = $options['fieldList'][$this->alias];
+ } else {
+ $this->whitelist = $options['fieldList'];
+ }
+ } elseif ($options['fieldList'] === null) {
+ $this->whitelist = array();
+ }
+ $this->set($data);
+
+ if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) {
+ return false;
+ }
+
+ foreach (array('created', 'updated', 'modified') as $field) {
+ $keyPresentAndEmpty = (
+ isset($this->data[$this->alias]) &&
+ array_key_exists($field, $this->data[$this->alias]) &&
+ $this->data[$this->alias][$field] === null
+ );
+ if ($keyPresentAndEmpty) {
+ unset($this->data[$this->alias][$field]);
+ }
+ }
+
+ $exists = $this->exists();
+ $dateFields = array('modified', 'updated');
+
+ if (!$exists) {
+ $dateFields[] = 'created';
+ }
+ if (isset($this->data[$this->alias])) {
+ $fields = array_keys($this->data[$this->alias]);
+ }
+ if ($options['validate'] && !$this->validates($options)) {
+ $this->whitelist = $_whitelist;
+ return false;
+ }
+
+ $db = $this->getDataSource();
+
+ foreach ($dateFields as $updateCol) {
+ if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) {
+ $default = array('formatter' => 'date');
+ $colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]);
+ if (!array_key_exists('format', $colType)) {
+ $time = strtotime('now');
+ } else {
+ $time = call_user_func($colType['formatter'], $colType['format']);
+ }
+ if (!empty($this->whitelist)) {
+ $this->whitelist[] = $updateCol;
+ }
+ $this->set($updateCol, $time);
+ }
+ }
+
+ if ($options['callbacks'] === true || $options['callbacks'] === 'before') {
+ $event = new CakeEvent('Model.beforeSave', $this, array($options));
+ list($event->break, $event->breakOn) = array(true, array(false, null));
+ $this->getEventManager()->dispatch($event);
+ if (!$event->result) {
+ $this->whitelist = $_whitelist;
+ return false;
+ }
+ }
+
+ if (empty($this->data[$this->alias][$this->primaryKey])) {
+ unset($this->data[$this->alias][$this->primaryKey]);
+ }
+ $fields = $values = array();
+
+ foreach ($this->data as $n => $v) {
+ if (isset($this->hasAndBelongsToMany[$n])) {
+ if (isset($v[$n])) {
+ $v = $v[$n];
+ }
+ $joined[$n] = $v;
+ } else {
+ if ($n === $this->alias) {
+ foreach (array('created', 'updated', 'modified') as $field) {
+ if (array_key_exists($field, $v) && empty($v[$field])) {
+ unset($v[$field]);
+ }
+ }
+
+ foreach ($v as $x => $y) {
+ if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) {
+ list($fields[], $values[]) = array($x, $y);
+ }
+ }
+ }
+ }
+ }
+ $count = count($fields);
+
+ if (!$exists && $count > 0) {
+ $this->id = false;
+ }
+ $success = true;
+ $created = false;
+
+ if ($count > 0) {
+ $cache = $this->_prepareUpdateFields(array_combine($fields, $values));
+
+ if (!empty($this->id)) {
+ $success = (bool)$db->update($this, $fields, $values);
+ } else {
+ $fInfo = $this->schema($this->primaryKey);
+ $isUUID = ($fInfo['length'] == 36 &&
+ ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary')
+ );
+ if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) {
+ if (array_key_exists($this->primaryKey, $this->data[$this->alias])) {
+ $j = array_search($this->primaryKey, $fields);
+ $values[$j] = String::uuid();
+ } else {
+ list($fields[], $values[]) = array($this->primaryKey, String::uuid());
+ }
+ }
+
+ if (!$db->create($this, $fields, $values)) {
+ $success = $created = false;
+ } else {
+ $created = true;
+ }
+ }
+
+ if ($success && !empty($this->belongsTo)) {
+ $this->updateCounterCache($cache, $created);
+ }
+ }
+
+ if (!empty($joined) && $success === true) {
+ $this->_saveMulti($joined, $this->id, $db);
+ }
+
+ if ($success && $count > 0) {
+ if (!empty($this->data)) {
+ $success = $this->data;
+ if ($created) {
+ $this->data[$this->alias][$this->primaryKey] = $this->id;
+ }
+ }
+ if ($options['callbacks'] === true || $options['callbacks'] === 'after') {
+ $event = new CakeEvent('Model.afterSave', $this, array($created, $options));
+ $this->getEventManager()->dispatch($event);
+ }
+ if (!empty($this->data)) {
+ $success = Set::merge($success, $this->data);
+ }
+ $this->data = false;
+ $this->_clearCache();
+ $this->validationErrors = array();
+ }
+ $this->whitelist = $_whitelist;
+ return $success;
+ }
+
+/**
+ * Saves model hasAndBelongsToMany data to the database.
+ *
+ * @param array $joined Data to save
+ * @param mixed $id ID of record in this model
+ * @param DataSource $db
+ * @return void
+ */
+ protected function _saveMulti($joined, $id, $db) {
+ foreach ($joined as $assoc => $data) {
+
+ if (isset($this->hasAndBelongsToMany[$assoc])) {
+ list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
+
+ $keyInfo = $this->{$join}->schema($this->{$join}->primaryKey);
+ if ($with = $this->hasAndBelongsToMany[$assoc]['with']) {
+ $withModel = is_array($with) ? key($with) : $with;
+ list($pluginName, $withModel) = pluginSplit($withModel);
+ $dbMulti = $this->{$withModel}->getDataSource();
+ } else {
+ $dbMulti = $db;
+ }
+
+ $isUUID = !empty($this->{$join}->primaryKey) && (
+ $keyInfo['length'] == 36 && (
+ $keyInfo['type'] === 'string' ||
+ $keyInfo['type'] === 'binary'
+ )
+ );
+
+ $newData = $newValues = $newJoins = array();
+ $primaryAdded = false;
+
+ $fields = array(
+ $dbMulti->name($this->hasAndBelongsToMany[$assoc]['foreignKey']),
+ $dbMulti->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey'])
+ );
+
+ $idField = $db->name($this->{$join}->primaryKey);
+ if ($isUUID && !in_array($idField, $fields)) {
+ $fields[] = $idField;
+ $primaryAdded = true;
+ }
+
+ foreach ((array)$data as $row) {
+ if ((is_string($row) && (strlen($row) == 36 || strlen($row) == 16)) || is_numeric($row)) {
+ $newJoins[] = $row;
+ $values = array($id, $row);
+ if ($isUUID && $primaryAdded) {
+ $values[] = String::uuid();
+ }
+ $newValues[$row] = $values;
+ unset($values);
+ } elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+ if (!empty($row[$this->{$join}->primaryKey])) {
+ $newJoins[] = $row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']];
+ }
+ $newData[] = $row;
+ } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+ if (!empty($row[$join][$this->{$join}->primaryKey])) {
+ $newJoins[] = $row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']];
+ }
+ $newData[] = $row[$join];
+ }
+ }
+
+ $keepExisting = $this->hasAndBelongsToMany[$assoc]['unique'] === 'keepExisting';
+ if ($this->hasAndBelongsToMany[$assoc]['unique']) {
+ $conditions = array(
+ $join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id
+ );
+ if (!empty($this->hasAndBelongsToMany[$assoc]['conditions'])) {
+ $conditions = array_merge($conditions, (array)$this->hasAndBelongsToMany[$assoc]['conditions']);
+ }
+ $associationForeignKey = $this->{$join}->alias . '.' . $this->hasAndBelongsToMany[$assoc]['associationForeignKey'];
+ $links = $this->{$join}->find('all', array(
+ 'conditions' => $conditions,
+ 'recursive' => empty($this->hasAndBelongsToMany[$assoc]['conditions']) ? -1 : 0,
+ 'fields' => $associationForeignKey,
+ ));
+
+ $oldLinks = Set::extract($links, "{n}.{$associationForeignKey}");
+ if (!empty($oldLinks)) {
+ if ($keepExisting && !empty($newJoins)) {
+ $conditions[$associationForeignKey] = array_diff($oldLinks, $newJoins);
+ } else {
+ $conditions[$associationForeignKey] = $oldLinks;
+ }
+ $dbMulti->delete($this->{$join}, $conditions);
+ }
+ }
+
+ if (!empty($newData)) {
+ foreach ($newData as $data) {
+ $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id;
+ if (empty($data[$this->{$join}->primaryKey])) {
+ $this->{$join}->create();
+ }
+ $this->{$join}->save($data);
+ }
+ }
+
+ if (!empty($newValues)) {
+ if ($keepExisting && !empty($links)) {
+ foreach ($links as $link) {
+ $oldJoin = $link[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']];
+ if (! in_array($oldJoin, $newJoins) ) {
+ $conditions[$associationForeignKey] = $oldJoin;
+ $db->delete($this->{$join}, $conditions);
+ } else {
+ unset($newValues[$oldJoin]);
+ }
+ }
+ $newValues = array_values($newValues);
+ }
+ if (!empty($newValues)) {
+ $dbMulti->insertMulti($this->{$join}, $fields, $newValues);
+ }
+ }
+ }
+ }
+ }
+
+/**
+ * Updates the counter cache of belongsTo associations after a save or delete operation
+ *
+ * @param array $keys Optional foreign key data, defaults to the information $this->data
+ * @param boolean $created True if a new record was created, otherwise only associations with
+ * 'counterScope' defined get updated
+ * @return void
+ */
+ public function updateCounterCache($keys = array(), $created = false) {
+ $keys = empty($keys) ? $this->data[$this->alias] : $keys;
+ $keys['old'] = isset($keys['old']) ? $keys['old'] : array();
+
+ foreach ($this->belongsTo as $parent => $assoc) {
+ if (!empty($assoc['counterCache'])) {
+ if (!is_array($assoc['counterCache'])) {
+ if (isset($assoc['counterScope'])) {
+ $assoc['counterCache'] = array($assoc['counterCache'] => $assoc['counterScope']);
+ } else {
+ $assoc['counterCache'] = array($assoc['counterCache'] => array());
+ }
+ }
+
+ $foreignKey = $assoc['foreignKey'];
+ $fkQuoted = $this->escapeField($assoc['foreignKey']);
+
+ foreach ($assoc['counterCache'] as $field => $conditions) {
+ if (!is_string($field)) {
+ $field = Inflector::underscore($this->alias) . '_count';
+ }
+ if (!$this->{$parent}->hasField($field)) {
+ continue;
+ }
+ if ($conditions === true) {
+ $conditions = array();
+ } else {
+ $conditions = (array)$conditions;
+ }
+
+ if (!array_key_exists($foreignKey, $keys)) {
+ $keys[$foreignKey] = $this->field($foreignKey);
+ }
+ $recursive = (empty($conditions) ? -1 : 0);
+
+ if (isset($keys['old'][$foreignKey])) {
+ if ($keys['old'][$foreignKey] != $keys[$foreignKey]) {
+ $conditions[$fkQuoted] = $keys['old'][$foreignKey];
+ $count = intval($this->find('count', compact('conditions', 'recursive')));
+
+ $this->{$parent}->updateAll(
+ array($field => $count),
+ array($this->{$parent}->escapeField() => $keys['old'][$foreignKey])
+ );
+ }
+ }
+ $conditions[$fkQuoted] = $keys[$foreignKey];
+
+ if ($recursive === 0) {
+ $conditions = array_merge($conditions, (array)$conditions);
+ }
+ $count = intval($this->find('count', compact('conditions', 'recursive')));
+
+ $this->{$parent}->updateAll(
+ array($field => $count),
+ array($this->{$parent}->escapeField() => $keys[$foreignKey])
+ );
+ }
+ }
+ }
+ }
+
+/**
+ * Helper method for Model::updateCounterCache(). Checks the fields to be updated for
+ *
+ * @param array $data The fields of the record that will be updated
+ * @return array Returns updated foreign key values, along with an 'old' key containing the old
+ * values, or empty if no foreign keys are updated.
+ */
+ protected function _prepareUpdateFields($data) {
+ $foreignKeys = array();
+ foreach ($this->belongsTo as $assoc => $info) {
+ if ($info['counterCache']) {
+ $foreignKeys[$assoc] = $info['foreignKey'];
+ }
+ }
+ $included = array_intersect($foreignKeys, array_keys($data));
+
+ if (empty($included) || empty($this->id)) {
+ return array();
+ }
+ $old = $this->find('first', array(
+ 'conditions' => array($this->alias . '.' . $this->primaryKey => $this->id),
+ 'fields' => array_values($included),
+ 'recursive' => -1
+ ));
+ return array_merge($data, array('old' => $old[$this->alias]));
+ }
+
+/**
+ * Backwards compatible passthrough method for:
+ * saveMany(), validateMany(), saveAssociated() and validateAssociated()
+ *
+ * Saves multiple individual records for a single model; Also works with a single record, as well as
+ * all its associated records.
+ *
+ * #### Options
+ *
+ * - validate: Set to false to disable validation, true to validate each record before saving,
+ * 'first' to validate *all* records before any are saved (default),
+ * or 'only' to only validate the records, but not save them.
+ * - atomic: If true (default), will attempt to save all records in a single transaction.
+ * Should be set to false if database/table does not support transactions.
+ * - fieldList: Equivalent to the $fieldList parameter in Model::save().
+ * It should be an associate array with model name as key and array of fields as value. Eg.
+ * {{{
+ * array(
+ * 'SomeModel' => array('field'),
+ * 'AssociatedModel' => array('field', 'otherfield')
+ * )
+ * }}}
+ * - deep: see saveMany/saveAssociated
+ *
+ * @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple
+ * records of the same type), or an array indexed by association name.
+ * @param array $options Options to use when saving record data, See $options above.
+ * @return mixed If atomic: True on success, or false on failure.
+ * Otherwise: array similar to the $data array passed, but values are set to true/false
+ * depending on whether each record saved successfully.
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array
+ */
+ public function saveAll($data = null, $options = array()) {
+ $options = array_merge(array('validate' => 'first'), $options);
+ if (Set::numeric(array_keys($data))) {
+ if ($options['validate'] === 'only') {
+ return $this->validateMany($data, $options);
+ }
+ return $this->saveMany($data, $options);
+ }
+ if ($options['validate'] === 'only') {
+ return $this->validateAssociated($data, $options);
+ }
+ return $this->saveAssociated($data, $options);
+ }
+
+/**
+ * Saves multiple individual records for a single model
+ *
+ * #### Options
+ *
+ * - validate: Set to false to disable validation, true to validate each record before saving,
+ * 'first' to validate *all* records before any are saved (default),
+ * - atomic: If true (default), will attempt to save all records in a single transaction.
+ * Should be set to false if database/table does not support transactions.
+ * - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * - deep: If set to true, all associated data will be saved as well.
+ *
+ * @param array $data Record data to save. This should be a numerically-indexed array
+ * @param array $options Options to use when saving record data, See $options above.
+ * @return mixed If atomic: True on success, or false on failure.
+ * Otherwise: array similar to the $data array passed, but values are set to true/false
+ * depending on whether each record saved successfully.
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savemany-array-data-null-array-options-array
+ */
+ public function saveMany($data = null, $options = array()) {
+ if (empty($data)) {
+ $data = $this->data;
+ }
+
+ $options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options);
+ $this->validationErrors = $validationErrors = array();
+
+ if (empty($data) && $options['validate'] !== false) {
+ $result = $this->save($data, $options);
+ if (!$options['atomic']) {
+ return array(!empty($result));
+ }
+ return !empty($result);
+ }
+
+ if ($options['validate'] === 'first') {
+ $validates = $this->validateMany($data, $options);
+ if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) {
+ return $validates;
+ }
+ $options['validate'] = false;
+ }
+
+ if ($options['atomic']) {
+ $db = $this->getDataSource();
+ $transactionBegun = $db->begin();
+ }
+ $return = array();
+ foreach ($data as $key => $record) {
+ $validates = $this->create(null) !== null;
+ $saved = false;
+ if ($validates) {
+ if ($options['deep']) {
+ $saved = $this->saveAssociated($record, array_merge($options, array('atomic' => false)));
+ } else {
+ $saved = $this->save($record, $options);
+ }
+ }
+ $validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
+ if (!$validates) {
+ $validationErrors[$key] = $this->validationErrors;
+ }
+ if (!$options['atomic']) {
+ $return[$key] = $validates;
+ } elseif (!$validates) {
+ break;
+ }
+ }
+ $this->validationErrors = $validationErrors;
+
+ if (!$options['atomic']) {
+ return $return;
+ }
+ if ($validates) {
+ if ($transactionBegun) {
+ return $db->commit() !== false;
+ } else {
+ return true;
+ }
+ }
+ $db->rollback();
+ return false;
+ }
+
+/**
+ * Validates multiple individual records for a single model
+ *
+ * #### Options
+ *
+ * - atomic: If true (default), returns boolean. If false returns array.
+ * - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * - deep: If set to true, all associated data will be validated as well.
+ *
+ * Warning: This method could potentially change the passed argument `$data`,
+ * If you do not want this to happen, make a copy of `$data` before passing it
+ * to this method
+ *
+ * @param array $data Record data to validate. This should be a numerically-indexed array
+ * @param array $options Options to use when validating record data (see above), See also $options of validates().
+ * @return boolean True on success, or false on failure.
+ * @return mixed If atomic: True on success, or false on failure.
+ * Otherwise: array similar to the $data array passed, but values are set to true/false
+ * depending on whether each record validated successfully.
+ */
+ public function validateMany(&$data, $options = array()) {
+ $options = array_merge(array('atomic' => true, 'deep' => false), $options);
+ $this->validationErrors = $validationErrors = $return = array();
+ foreach ($data as $key => &$record) {
+ if ($options['deep']) {
+ $validates = $this->validateAssociated($record, $options);
+ } else {
+ $this->create(null);
+ $validates = $this->set($record) && $this->validates($options);
+ $data[$key] = $this->data;
+ }
+ if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) {
+ $validationErrors[$key] = $this->validationErrors;
+ $validates = false;
+ } else {
+ $validates = true;
+ }
+ $return[$key] = $validates;
+ }
+ $this->validationErrors = $validationErrors;
+ if (!$options['atomic']) {
+ return $return;
+ }
+ if (empty($this->validationErrors)) {
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Saves a single record, as well as all its directly associated records.
+ *
+ * #### Options
+ *
+ * - `validate` Set to `false` to disable validation, `true` to validate each record before saving,
+ * 'first' to validate *all* records before any are saved(default),
+ * - `atomic` If true (default), will attempt to save all records in a single transaction.
+ * Should be set to false if database/table does not support transactions.
+ * - fieldList: Equivalent to the $fieldList parameter in Model::save().
+ * It should be an associate array with model name as key and array of fields as value. Eg.
+ * {{{
+ * array(
+ * 'SomeModel' => array('field'),
+ * 'AssociatedModel' => array('field', 'otherfield')
+ * )
+ * }}}
+ * - deep: If set to true, not only directly associated data is saved, but deeper nested associated data as well.
+ *
+ * @param array $data Record data to save. This should be an array indexed by association name.
+ * @param array $options Options to use when saving record data, See $options above.
+ * @return mixed If atomic: True on success, or false on failure.
+ * Otherwise: array similar to the $data array passed, but values are set to true/false
+ * depending on whether each record saved successfully.
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
+ */
+ public function saveAssociated($data = null, $options = array()) {
+ if (empty($data)) {
+ $data = $this->data;
+ }
+
+ $options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options);
+ $this->validationErrors = $validationErrors = array();
+
+ if (empty($data) && $options['validate'] !== false) {
+ $result = $this->save($data, $options);
+ if (!$options['atomic']) {
+ return array(!empty($result));
+ }
+ return !empty($result);
+ }
+
+ if ($options['validate'] === 'first') {
+ $validates = $this->validateAssociated($data, $options);
+ if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) {
+ return $validates;
+ }
+ $options['validate'] = false;
+ }
+ if ($options['atomic']) {
+ $db = $this->getDataSource();
+ $transactionBegun = $db->begin();
+ }
+
+ $associations = $this->getAssociated();
+ $return = array();
+ $validates = true;
+ foreach ($data as $association => $values) {
+ $notEmpty = !empty($values[$association]) || (!isset($values[$association]) && !empty($values));
+ if (isset($associations[$association]) && $associations[$association] === 'belongsTo' && $notEmpty) {
+ $validates = $this->{$association}->create(null) !== null;
+ $saved = false;
+ if ($validates) {
+ if ($options['deep']) {
+ $saved = $this->{$association}->saveAssociated($values, array_merge($options, array('atomic' => false)));
+ } else {
+ $saved = $this->{$association}->save($values, array_merge($options, array('atomic' => false)));
+ }
+ $validates = ($saved === true || (is_array($saved) && !in_array(false, $saved, true)));
+ }
+ if ($validates) {
+ $key = $this->belongsTo[$association]['foreignKey'];
+ if (isset($data[$this->alias])) {
+ $data[$this->alias][$key] = $this->{$association}->id;
+ } else {
+ $data = array_merge(array($key => $this->{$association}->id), $data, array($key => $this->{$association}->id));
+ }
+ } else {
+ $validationErrors[$association] = $this->{$association}->validationErrors;
+ }
+ $return[$association] = $validates;
+ }
+ }
+ if ($validates && !($this->create(null) !== null && $this->save($data, $options))) {
+ $validationErrors[$this->alias] = $this->validationErrors;
+ $validates = false;
+ }
+ $return[$this->alias] = $validates;
+
+ foreach ($data as $association => $values) {
+ if (!$validates) {
+ break;
+ }
+ $notEmpty = !empty($values[$association]) || (!isset($values[$association]) && !empty($values));
+ if (isset($associations[$association]) && $notEmpty) {
+ $type = $associations[$association];
+ $key = $this->{$type}[$association]['foreignKey'];
+ switch ($type) {
+ case 'hasOne':
+ if (isset($values[$association])) {
+ $values[$association][$key] = $this->id;
+ } else {
+ $values = array_merge(array($key => $this->id), $values, array($key => $this->id));
+ }
+ $validates = $this->{$association}->create(null) !== null;
+ $saved = false;
+ if ($validates) {
+ if ($options['deep']) {
+ $saved = $this->{$association}->saveAssociated($values, array_merge($options, array('atomic' => false)));
+ } else {
+ $saved = $this->{$association}->save($values, $options);
+ }
+ }
+ $validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
+ if (!$validates) {
+ $validationErrors[$association] = $this->{$association}->validationErrors;
+ }
+ $return[$association] = $validates;
+ break;
+ case 'hasMany':
+ foreach ($values as $i => $value) {
+ if (isset($values[$i][$association])) {
+ $values[$i][$association][$key] = $this->id;
+ } else {
+ $values[$i] = array_merge(array($key => $this->id), $value, array($key => $this->id));
+ }
+ }
+ $_return = $this->{$association}->saveMany($values, array_merge($options, array('atomic' => false)));
+ if (in_array(false, $_return, true)) {
+ $validationErrors[$association] = $this->{$association}->validationErrors;
+ $validates = false;
+ }
+ $return[$association] = $_return;
+ break;
+ }
+ }
+ }
+ $this->validationErrors = $validationErrors;
+
+ if (isset($validationErrors[$this->alias])) {
+ $this->validationErrors = $validationErrors[$this->alias];
+ }
+
+ if (!$options['atomic']) {
+ return $return;
+ }
+ if ($validates) {
+ if ($transactionBegun) {
+ return $db->commit() !== false;
+ } else {
+ return true;
+ }
+ }
+ $db->rollback();
+ return false;
+ }
+
+/**
+ * Validates a single record, as well as all its directly associated records.
+ *
+ * #### Options
+ *
+ * - atomic: If true (default), returns boolean. If false returns array.
+ * - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * - deep: If set to true, not only directly associated data , but deeper nested associated data is validated as well.
+ *
+ * Warning: This method could potentially change the passed argument `$data`,
+ * If you do not want this to happen, make a copy of `$data` before passing it
+ * to this method
+ *
+ * @param array $data Record data to validate. This should be an array indexed by association name.
+ * @param array $options Options to use when validating record data (see above), See also $options of validates().
+ * @return array|boolean If atomic: True on success, or false on failure.
+ * Otherwise: array similar to the $data array passed, but values are set to true/false
+ * depending on whether each record validated successfully.
+ */
+ public function validateAssociated(&$data, $options = array()) {
+ $options = array_merge(array('atomic' => true, 'deep' => false), $options);
+ $this->validationErrors = $validationErrors = $return = array();
+ $this->create(null);
+ if (!($this->set($data) && $this->validates($options))) {
+ $validationErrors[$this->alias] = $this->validationErrors;
+ $return[$this->alias] = false;
+ } else {
+ $return[$this->alias] = true;
+ }
+ $data = $this->data;
+ if (!empty($options['deep']) && isset($data[$this->alias])) {
+ $recordData = $data[$this->alias];
+ unset($data[$this->alias]);
+ $data = array_merge($data, $recordData);
+ }
+
+ $associations = $this->getAssociated();
+ foreach ($data as $association => &$values) {
+ $validates = true;
+ if (isset($associations[$association])) {
+ if (in_array($associations[$association], array('belongsTo', 'hasOne'))) {
+ if ($options['deep']) {
+ $validates = $this->{$association}->validateAssociated($values, $options);
+ } else {
+ $validates = $this->{$association}->create($values) !== null && $this->{$association}->validates($options);
+ }
+ if (is_array($validates)) {
+ if (in_array(false, $validates, true)) {
+ $validates = false;
+ } else {
+ $validates = true;
+ }
+ }
+ $return[$association] = $validates;
+ } elseif ($associations[$association] === 'hasMany') {
+ $validates = $this->{$association}->validateMany($values, $options);
+ $return[$association] = $validates;
+ }
+ if (!$validates || (is_array($validates) && in_array(false, $validates, true))) {
+ $validationErrors[$association] = $this->{$association}->validationErrors;
+ }
+ }
+ }
+
+ $this->validationErrors = $validationErrors;
+ if (isset($validationErrors[$this->alias])) {
+ $this->validationErrors = $validationErrors[$this->alias];
+ }
+ if (!$options['atomic']) {
+ return $return;
+ }
+ if ($return[$this->alias] === false || !empty($this->validationErrors)) {
+ return false;
+ }
+ return true;
+ }
+
+/**
+ * Updates multiple model records based on a set of conditions.
+ *
+ * @param array $fields Set of fields and values, indexed by fields.
+ * Fields are treated as SQL snippets, to insert literal values manually escape your data.
+ * @param mixed $conditions Conditions to match, true for all records
+ * @return boolean True on success, false on failure
+ * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-array-conditions
+ */
+ public function updateAll($fields, $conditions = true) {
+ return $this->getDataSource()->update($this, $fields, null, $conditions);
+ }
+
+/**
+ * Removes record for given ID. If no ID is given, the current ID is used. Returns true on success.
+ *
+ * @param mixed $id ID of record to delete
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @return boolean True on success
+ * @link http://book.cakephp.org/2.0/en/models/deleting-data.html
+ */
+ public function delete($id = null, $cascade = true) {
+ if (!empty($id)) {
+ $this->id = $id;
+ }
+ $id = $this->id;
+
+ $event = new CakeEvent('Model.beforeDelete', $this, array($cascade));
+ list($event->break, $event->breakOn) = array(true, array(false, null));
+ $this->getEventManager()->dispatch($event);
+ if (!$event->isStopped()) {
+ if (!$this->exists()) {
+ return false;
+ }
+ $db = $this->getDataSource();
+
+ $this->_deleteDependent($id, $cascade);
+ $this->_deleteLinks($id);
+ $this->id = $id;
+
+ $updateCounterCache = false;
+ if (!empty($this->belongsTo)) {
+ foreach ($this->belongsTo as $parent => $assoc) {
+ if (!empty($assoc['counterCache'])) {
+ $updateCounterCache = true;
+ break;
+ }
+ }
+
+ $keys = $this->find('first', array(
+ 'fields' => $this->_collectForeignKeys(),
+ 'conditions' => array($this->alias . '.' . $this->primaryKey => $id),
+ 'recursive' => -1,
+ 'callbacks' => false
+ ));
+ }
+
+ if ($db->delete($this, array($this->alias . '.' . $this->primaryKey => $id))) {
+ if ($updateCounterCache) {
+ $this->updateCounterCache($keys[$this->alias]);
+ }
+ $this->getEventManager()->dispatch(new CakeEvent('Model.afterDelete', $this));
+ $this->_clearCache();
+ $this->id = false;
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * Cascades model deletes through associated hasMany and hasOne child records.
+ *
+ * @param string $id ID of record that was deleted
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @return void
+ */
+ protected function _deleteDependent($id, $cascade) {
+ if (!empty($this->__backAssociation)) {
+ $savedAssociatons = $this->__backAssociation;
+ $this->__backAssociation = array();
+ }
+ if ($cascade === true) {
+ foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) {
+ if ($data['dependent'] === true) {
+
+ $model = $this->{$assoc};
+
+ if ($data['foreignKey'] === false && $data['conditions'] && in_array($this->name, $model->getAssociated('belongsTo'))) {
+ $model->recursive = 0;
+ $conditions = array($this->escapeField(null, $this->name) => $id);
+ } else {
+ $model->recursive = -1;
+ $conditions = array($model->escapeField($data['foreignKey']) => $id);
+ if ($data['conditions']) {
+ $conditions = array_merge((array)$data['conditions'], $conditions);
+ }
+ }
+
+ if (isset($data['exclusive']) && $data['exclusive']) {
+ $model->deleteAll($conditions);
+ } else {
+ $records = $model->find('all', array(
+ 'conditions' => $conditions, 'fields' => $model->primaryKey
+ ));
+
+ if (!empty($records)) {
+ foreach ($records as $record) {
+ $model->delete($record[$model->alias][$model->primaryKey]);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (isset($savedAssociatons)) {
+ $this->__backAssociation = $savedAssociatons;
+ }
+ }
+
+/**
+ * Cascades model deletes through HABTM join keys.
+ *
+ * @param string $id ID of record that was deleted
+ * @return void
+ */
+ protected function _deleteLinks($id) {
+ foreach ($this->hasAndBelongsToMany as $assoc => $data) {
+ list($plugin, $joinModel) = pluginSplit($data['with']);
+ $records = $this->{$joinModel}->find('all', array(
+ 'conditions' => array($this->{$joinModel}->escapeField($data['foreignKey']) => $id),
+ 'fields' => $this->{$joinModel}->primaryKey,
+ 'recursive' => -1,
+ 'callbacks' => false
+ ));
+ if (!empty($records)) {
+ foreach ($records as $record) {
+ $this->{$joinModel}->delete($record[$this->{$joinModel}->alias][$this->{$joinModel}->primaryKey]);
+ }
+ }
+ }
+ }
+
+/**
+ * Deletes multiple model records based on a set of conditions.
+ *
+ * @param mixed $conditions Conditions to match
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @param boolean $callbacks Run callbacks
+ * @return boolean True on success, false on failure
+ * @link http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall
+ */
+ public function deleteAll($conditions, $cascade = true, $callbacks = false) {
+ if (empty($conditions)) {
+ return false;
+ }
+ $db = $this->getDataSource();
+
+ if (!$cascade && !$callbacks) {
+ return $db->delete($this, $conditions);
+ } else {
+ $ids = $this->find('all', array_merge(array(
+ 'fields' => "{$this->alias}.{$this->primaryKey}",
+ 'recursive' => 0), compact('conditions'))
+ );
+ if ($ids === false) {
+ return false;
+ }
+
+ $ids = Set::extract($ids, "{n}.{$this->alias}.{$this->primaryKey}");
+ if (empty($ids)) {
+ return true;
+ }
+
+ if ($callbacks) {
+ $_id = $this->id;
+ $result = true;
+ foreach ($ids as $id) {
+ $result = ($result && $this->delete($id, $cascade));
+ }
+ $this->id = $_id;
+ return $result;
+ } else {
+ foreach ($ids as $id) {
+ $this->_deleteLinks($id);
+ if ($cascade) {
+ $this->_deleteDependent($id, $cascade);
+ }
+ }
+ return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids));
+ }
+ }
+ }
+
+/**
+ * Collects foreign keys from associations.
+ *
+ * @param string $type
+ * @return array
+ */
+ protected function _collectForeignKeys($type = 'belongsTo') {
+ $result = array();
+
+ foreach ($this->{$type} as $assoc => $data) {
+ if (isset($data['foreignKey']) && is_string($data['foreignKey'])) {
+ $result[$assoc] = $data['foreignKey'];
+ }
+ }
+ return $result;
+ }
+
+/**
+ * Returns true if a record with particular ID exists.
+ *
+ * If $id is not passed it calls Model::getID() to obtain the current record ID,
+ * and then performs a Model::find('count') on the currently configured datasource
+ * to ascertain the existence of the record in persistent storage.
+ *
+ * @param mixed $id ID of record to check for existence
+ * @return boolean True if such a record exists
+ */
+ public function exists($id = null) {
+ if ($id === null) {
+ $id = $this->getID();
+ }
+ if ($id === false) {
+ return false;
+ }
+ $conditions = array($this->alias . '.' . $this->primaryKey => $id);
+ $query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false);
+ return ($this->find('count', $query) > 0);
+ }
+
+/**
+ * Returns true if a record that meets given conditions exists.
+ *
+ * @param array $conditions SQL conditions array
+ * @return boolean True if such a record exists
+ */
+ public function hasAny($conditions = null) {
+ return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false);
+ }
+
+/**
+ * Queries the datasource and returns a result set array.
+ *
+ * Also used to perform notation finds, where the first argument is type of find operation to perform
+ * (all / first / count / neighbors / list / threaded),
+ * second parameter options for finding ( indexed array, including: 'conditions', 'limit',
+ * 'recursive', 'page', 'fields', 'offset', 'order')
+ *
+ * Eg:
+ * {{{
+ * find('all', array(
+ * 'conditions' => array('name' => 'Thomas Anderson'),
+ * 'fields' => array('name', 'email'),
+ * 'order' => 'field3 DESC',
+ * 'recursive' => 2,
+ * 'group' => 'type'
+ * ));
+ * }}}
+ *
+ * In addition to the standard query keys above, you can provide Datasource, and behavior specific
+ * keys. For example, when using a SQL based datasource you can use the joins key to specify additional
+ * joins that should be part of the query.
+ *
+ * {{{
+ * find('all', array(
+ * 'conditions' => array('name' => 'Thomas Anderson'),
+ * 'joins' => array(
+ * array(
+ * 'alias' => 'Thought',
+ * 'table' => 'thoughts',
+ * 'type' => 'LEFT',
+ * 'conditions' => '`Thought`.`person_id` = `Person`.`id`'
+ * )
+ * )
+ * ));
+ * }}}
+ *
+ * Behaviors and find types can also define custom finder keys which are passed into find().
+ *
+ * Specifying 'fields' for notation 'list':
+ *
+ * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value.
+ * - If a single field is specified, 'id' is used for key and specified field is used for value.
+ * - If three fields are specified, they are used (in order) for key, value and group.
+ * - Otherwise, first and second fields are used for key and value.
+ *
+ * Note: find(list) + database views have issues with MySQL 5.0. Try upgrading to MySQL 5.1 if you
+ * have issues with database views.
+ * @param string $type Type of find operation (all / first / count / neighbors / list / threaded)
+ * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
+ * @return array Array of records
+ * @link http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall
+ */
+ public function find($type = 'first', $query = array()) {
+ $this->findQueryType = $type;
+ $this->id = $this->getID();
+
+ $query = $this->buildQuery($type, $query);
+ if (is_null($query)) {
+ return null;
+ }
+
+ $results = $this->getDataSource()->read($this, $query);
+ $this->resetAssociations();
+
+ if ($query['callbacks'] === true || $query['callbacks'] === 'after') {
+ $results = $this->_filterResults($results);
+ }
+
+ $this->findQueryType = null;
+
+ if ($type === 'all') {
+ return $results;
+ } else {
+ if ($this->findMethods[$type] === true) {
+ return $this->{'_find' . ucfirst($type)}('after', $query, $results);
+ }
+ }
+ }
+
+/**
+ * Builds the query array that is used by the data source to generate the query to fetch the data.
+ *
+ * @param string $type Type of find operation (all / first / count / neighbors / list / threaded)
+ * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
+ * @return array Query array or null if it could not be build for some reasons
+ * @see Model::find()
+ */
+ public function buildQuery($type = 'first', $query = array()) {
+ $query = array_merge(
+ array(
+ 'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null,
+ 'offset' => null, 'order' => null, 'page' => 1, 'group' => null, 'callbacks' => true,
+ ),
+ (array)$query
+ );
+
+ if ($type !== 'all') {
+ if ($this->findMethods[$type] === true) {
+ $query = $this->{'_find' . ucfirst($type)}('before', $query);
+ }
+ }
+
+ if (!is_numeric($query['page']) || intval($query['page']) < 1) {
+ $query['page'] = 1;
+ }
+ if ($query['page'] > 1 && !empty($query['limit'])) {
+ $query['offset'] = ($query['page'] - 1) * $query['limit'];
+ }
+ if ($query['order'] === null && $this->order !== null) {
+ $query['order'] = $this->order;
+ }
+ $query['order'] = array($query['order']);
+
+ if ($query['callbacks'] === true || $query['callbacks'] === 'before') {
+ $event = new CakeEvent('Model.beforeFind', $this, array($query));
+ list($event->break, $event->breakOn, $event->modParams) = array(true, array(false, null), 0);
+ $this->getEventManager()->dispatch($event);
+ if ($event->isStopped()) {
+ return null;
+ }
+ $query = $event->result === true ? $event->data[0] : $event->result;
+ }
+
+ return $query;
+ }
+
+/**
+ * Handles the before/after filter logic for find('first') operations. Only called by Model::find().
+ *
+ * @param string $state Either "before" or "after"
+ * @param array $query
+ * @param array $results
+ * @return array
+ * @see Model::find()
+ */
+ protected function _findFirst($state, $query, $results = array()) {
+ if ($state === 'before') {
+ $query['limit'] = 1;
+ return $query;
+ } elseif ($state === 'after') {
+ if (empty($results[0])) {
+ return false;
+ }
+ return $results[0];
+ }
+ }
+
+/**
+ * Handles the before/after filter logic for find('count') operations. Only called by Model::find().
+ *
+ * @param string $state Either "before" or "after"
+ * @param array $query
+ * @param array $results
+ * @return integer The number of records found, or false
+ * @see Model::find()
+ */
+ protected function _findCount($state, $query, $results = array()) {
+ if ($state === 'before') {
+ $db = $this->getDataSource();
+ $query['order'] = false;
+ if (!method_exists($db, 'calculate')) {
+ return $query;
+ }
+ if (empty($query['fields'])) {
+ $query['fields'] = $db->calculate($this, 'count');
+ } elseif (method_exists($db, 'expression') && is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) {
+ $query['fields'] = $db->calculate($this, 'count', array(
+ $db->expression($query['fields']), 'count'
+ ));
+ }
+ return $query;
+ } elseif ($state === 'after') {
+ foreach (array(0, $this->alias) as $key) {
+ if (isset($results[0][$key]['count'])) {
+ if (($count = count($results)) > 1) {
+ return $count;
+ } else {
+ return intval($results[0][$key]['count']);
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+/**
+ * Handles the before/after filter logic for find('list') operations. Only called by Model::find().
+ *
+ * @param string $state Either "before" or "after"
+ * @param array $query
+ * @param array $results
+ * @return array Key/value pairs of primary keys/display field values of all records found
+ * @see Model::find()
+ */
+ protected function _findList($state, $query, $results = array()) {
+ if ($state === 'before') {
+ if (empty($query['fields'])) {
+ $query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}");
+ $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null);
+ } else {
+ if (!is_array($query['fields'])) {
+ $query['fields'] = String::tokenize($query['fields']);
+ }
+
+ if (count($query['fields']) === 1) {
+ if (strpos($query['fields'][0], '.') === false) {
+ $query['fields'][0] = $this->alias . '.' . $query['fields'][0];
+ }
+
+ $list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null);
+ $query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]);
+ } elseif (count($query['fields']) === 3) {
+ for ($i = 0; $i < 3; $i++) {
+ if (strpos($query['fields'][$i], '.') === false) {
+ $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i];
+ }
+ }
+
+ $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]);
+ } else {
+ for ($i = 0; $i < 2; $i++) {
+ if (strpos($query['fields'][$i], '.') === false) {
+ $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i];
+ }
+ }
+
+ $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null);
+ }
+ }
+ if (!isset($query['recursive']) || $query['recursive'] === null) {
+ $query['recursive'] = -1;
+ }
+ list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list;
+ return $query;
+ } elseif ($state === 'after') {
+ if (empty($results)) {
+ return array();
+ }
+ $lst = $query['list'];
+ return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']);
+ }
+ }
+
+/**
+ * Detects the previous field's value, then uses logic to find the 'wrapping'
+ * rows and return them.
+ *
+ * @param string $state Either "before" or "after"
+ * @param mixed $query
+ * @param array $results
+ * @return array
+ */
+ protected function _findNeighbors($state, $query, $results = array()) {
+ if ($state === 'before') {
+ extract($query);
+ $conditions = (array)$conditions;
+ if (isset($field) && isset($value)) {
+ if (strpos($field, '.') === false) {
+ $field = $this->alias . '.' . $field;
+ }
+ } else {
+ $field = $this->alias . '.' . $this->primaryKey;
+ $value = $this->id;
+ }
+ $query['conditions'] = array_merge($conditions, array($field . ' <' => $value));
+ $query['order'] = $field . ' DESC';
+ $query['limit'] = 1;
+ $query['field'] = $field;
+ $query['value'] = $value;
+ return $query;
+ } elseif ($state === 'after') {
+ extract($query);
+ unset($query['conditions'][$field . ' <']);
+ $return = array();
+ if (isset($results[0])) {
+ $prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]);
+ $query['conditions'][$field . ' >='] = $prevVal[0];
+ $query['conditions'][$field . ' !='] = $value;
+ $query['limit'] = 2;
+ } else {
+ $return['prev'] = null;
+ $query['conditions'][$field . ' >'] = $value;
+ $query['limit'] = 1;
+ }
+ $query['order'] = $field . ' ASC';
+ $return2 = $this->find('all', $query);
+ if (!array_key_exists('prev', $return)) {
+ $return['prev'] = $return2[0];
+ }
+ if (count($return2) === 2) {
+ $return['next'] = $return2[1];
+ } elseif (count($return2) === 1 && !$return['prev']) {
+ $return['next'] = $return2[0];
+ } else {
+ $return['next'] = null;
+ }
+ return $return;
+ }
+ }
+
+/**
+ * In the event of ambiguous results returned (multiple top level results, with different parent_ids)
+ * top level results with different parent_ids to the first result will be dropped
+ *
+ * @param mixed $state
+ * @param mixed $query
+ * @param array $results
+ * @return array Threaded results
+ */
+ protected function _findThreaded($state, $query, $results = array()) {
+ if ($state === 'before') {
+ return $query;
+ } elseif ($state === 'after') {
+ $parent = 'parent_id';
+ if (isset($query['parent'])) {
+ $parent = $query['parent'];
+ }
+ return Set::nest($results, array(
+ 'idPath' => '/' . $this->alias . '/' . $this->primaryKey,
+ 'parentPath' => '/' . $this->alias . '/' . $parent
+ ));
+ }
+ }
+
+/**
+ * Passes query results through model and behavior afterFilter() methods.
+ *
+ * @param array $results Results to filter
+ * @param boolean $primary If this is the primary model results (results from model where the find operation was performed)
+ * @return array Set of filtered results
+ */
+ protected function _filterResults($results, $primary = true) {
+ $event = new CakeEvent('Model.afterFind', $this, array($results, $primary));
+ $event->modParams = 0;
+ $this->getEventManager()->dispatch($event);
+ return $event->result;
+ }
+
+/**
+ * This resets the association arrays for the model back
+ * to those originally defined in the model. Normally called at the end
+ * of each call to Model::find()
+ *
+ * @return boolean Success
+ */
+ public function resetAssociations() {
+ if (!empty($this->__backAssociation)) {
+ foreach ($this->_associations as $type) {
+ if (isset($this->__backAssociation[$type])) {
+ $this->{$type} = $this->__backAssociation[$type];
+ }
+ }
+ $this->__backAssociation = array();
+ }
+
+ foreach ($this->_associations as $type) {
+ foreach ($this->{$type} as $key => $name) {
+ if (property_exists($this, $key) && !empty($this->{$key}->__backAssociation)) {
+ $this->{$key}->resetAssociations();
+ }
+ }
+ }
+ $this->__backAssociation = array();
+ return true;
+ }
+
+/**
+ * Returns false if any fields passed match any (by default, all if $or = false) of their matching values.
+ *
+ * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data)
+ * @param boolean $or If false, all fields specified must match in order for a false return value
+ * @return boolean False if any records matching any fields are found
+ */
+ public function isUnique($fields, $or = true) {
+ if (!is_array($fields)) {
+ $fields = func_get_args();
+ if (is_bool($fields[count($fields) - 1])) {
+ $or = $fields[count($fields) - 1];
+ unset($fields[count($fields) - 1]);
+ }
+ }
+
+ foreach ($fields as $field => $value) {
+ if (is_numeric($field)) {
+ unset($fields[$field]);
+
+ $field = $value;
+ if (isset($this->data[$this->alias][$field])) {
+ $value = $this->data[$this->alias][$field];
+ } else {
+ $value = null;
+ }
+ }
+
+ if (strpos($field, '.') === false) {
+ unset($fields[$field]);
+ $fields[$this->alias . '.' . $field] = $value;
+ }
+ }
+ if ($or) {
+ $fields = array('or' => $fields);
+ }
+ if (!empty($this->id)) {
+ $fields[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id;
+ }
+ return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0);
+ }
+
+/**
+ * Returns a resultset for a given SQL statement. Custom SQL queries should be performed with this method.
+ *
+ * @param string $sql,... SQL statement
+ * @return mixed Resultset array or boolean indicating success / failure depending on the query executed
+ * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query
+ */
+ public function query($sql) {
+ $params = func_get_args();
+ $db = $this->getDataSource();
+ return call_user_func_array(array(&$db, 'query'), $params);
+ }
+
+/**
+ * Returns true if all fields pass validation. Will validate hasAndBelongsToMany associations
+ * that use the 'with' key as well. Since _saveMulti is incapable of exiting a save operation.
+ *
+ * Will validate the currently set data. Use Model::set() or Model::create() to set the active data.
+ *
+ * @param array $options An optional array of custom options to be made available in the beforeValidate callback
+ * @return boolean True if there are no errors
+ */
+ public function validates($options = array()) {
+ $errors = $this->invalidFields($options);
+ if (empty($errors) && $errors !== false) {
+ $errors = $this->_validateWithModels($options);
+ }
+ if (is_array($errors)) {
+ return count($errors) === 0;
+ }
+ return $errors;
+ }
+
+/**
+ * Returns an array of fields that have failed validation. On the current model.
+ *
+ * @param string $options An optional array of custom options to be made available in the beforeValidate callback
+ * @return array Array of invalid fields
+ * @see Model::validates()
+ */
+ public function invalidFields($options = array()) {
+ $event = new CakeEvent('Model.beforeValidate', $this, array($options));
+ list($event->break, $event->breakOn) = array(true, false);
+ $this->getEventManager()->dispatch($event);
+ if ($event->isStopped()) {
+ return false;
+ }
+
+ if (!isset($this->validate) || empty($this->validate)) {
+ return $this->validationErrors;
+ }
+
+ $data = $this->data;
+ $methods = array_map('strtolower', get_class_methods($this));
+ $behaviorMethods = array_keys($this->Behaviors->methods());
+
+ if (isset($data[$this->alias])) {
+ $data = $data[$this->alias];
+ } elseif (!is_array($data)) {
+ $data = array();
+ }
+
+ $exists = null;
+
+ $_validate = $this->validate;
+ $whitelist = $this->whitelist;
+
+ if (!empty($options['fieldList'])) {
+ if (!empty($options['fieldList'][$this->alias]) && is_array($options['fieldList'][$this->alias])) {
+ $whitelist = $options['fieldList'][$this->alias];
+ } else {
+ $whitelist = $options['fieldList'];
+ }
+ }
+
+ if (!empty($whitelist)) {
+ $validate = array();
+ foreach ((array)$whitelist as $f) {
+ if (!empty($this->validate[$f])) {
+ $validate[$f] = $this->validate[$f];
+ }
+ }
+ $this->validate = $validate;
+ }
+
+ $validationDomain = $this->validationDomain;
+ if (empty($validationDomain)) {
+ $validationDomain = 'default';
+ }
+
+ foreach ($this->validate as $fieldName => $ruleSet) {
+ if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) {
+ $ruleSet = array($ruleSet);
+ }
+ $default = array(
+ 'allowEmpty' => null,
+ 'required' => null,
+ 'rule' => 'blank',
+ 'last' => true,
+ 'on' => null
+ );
+
+ foreach ($ruleSet as $index => $validator) {
+ if (!is_array($validator)) {
+ $validator = array('rule' => $validator);
+ }
+ $validator = array_merge($default, $validator);
+
+ if (!empty($validator['on']) || in_array($validator['required'], array('create', 'update'), true)) {
+ if ($exists === null) {
+ $exists = $this->exists();
+ }
+ if ($validator['on'] == 'create' && $exists || $validator['on'] == 'update' && !$exists) {
+ continue;
+ }
+ if ($validator['required'] === 'create' && !$exists || $validator['required'] === 'update' && $exists) {
+ $validator['required'] = true;
+ }
+ }
+
+ $valid = true;
+ $requiredFail = (
+ (!isset($data[$fieldName]) && $validator['required'] === true) ||
+ (
+ isset($data[$fieldName]) && (empty($data[$fieldName]) &&
+ !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false
+ )
+ );
+
+ if (!$requiredFail && array_key_exists($fieldName, $data)) {
+ if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) {
+ break;
+ }
+ if (is_array($validator['rule'])) {
+ $rule = $validator['rule'][0];
+ unset($validator['rule'][0]);
+ $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule']));
+ } else {
+ $rule = $validator['rule'];
+ $ruleParams = array($data[$fieldName]);
+ }
+
+ if (in_array(strtolower($rule), $methods)) {
+ $ruleParams[] = $validator;
+ $ruleParams[0] = array($fieldName => $ruleParams[0]);
+ $valid = $this->dispatchMethod($rule, $ruleParams);
+ } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) {
+ $ruleParams[] = $validator;
+ $ruleParams[0] = array($fieldName => $ruleParams[0]);
+ $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams);
+ } elseif (method_exists('Validation', $rule)) {
+ $valid = call_user_func_array(array('Validation', $rule), $ruleParams);
+ } elseif (!is_array($validator['rule'])) {
+ $valid = preg_match($rule, $data[$fieldName]);
+ } elseif (Configure::read('debug') > 0) {
+ trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $rule, $fieldName), E_USER_WARNING);
+ }
+ }
+
+ if ($requiredFail || !$valid || (is_string($valid) && strlen($valid) > 0)) {
+ if (is_string($valid)) {
+ $message = $valid;
+ } elseif (isset($validator['message'])) {
+ $args = null;
+ if (is_array($validator['message'])) {
+ $message = $validator['message'][0];
+ $args = array_slice($validator['message'], 1);
+ } else {
+ $message = $validator['message'];
+ }
+ if (is_array($validator['rule']) && $args === null) {
+ $args = array_slice($ruleSet[$index]['rule'], 1);
+ }
+ $message = __d($validationDomain, $message, $args);
+ } elseif (is_string($index)) {
+ if (is_array($validator['rule'])) {
+ $args = array_slice($ruleSet[$index]['rule'], 1);
+ $message = __d($validationDomain, $index, $args);
+ } else {
+ $message = __d($validationDomain, $index);
+ }
+ } elseif (!$requiredFail && is_numeric($index) && count($ruleSet) > 1) {
+ $message = $index + 1;
+ } else {
+ $message = __d('cake_dev', 'This field cannot be left blank');
+ }
+
+ $this->invalidate($fieldName, $message);
+ if ($validator['last']) {
+ break;
+ }
+ }
+ }
+ }
+ $this->validate = $_validate;
+ return $this->validationErrors;
+ }
+
+/**
+ * Runs validation for hasAndBelongsToMany associations that have 'with' keys
+ * set. And data in the set() data set.
+ *
+ * @param array $options Array of options to use on Validation of with models
+ * @return boolean Failure of validation on with models.
+ * @see Model::validates()
+ */
+ protected function _validateWithModels($options) {
+ $valid = true;
+ foreach ($this->hasAndBelongsToMany as $assoc => $association) {
+ if (empty($association['with']) || !isset($this->data[$assoc])) {
+ continue;
+ }
+ list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
+ $data = $this->data[$assoc];
+
+ $newData = array();
+ foreach ((array)$data as $row) {
+ if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+ $newData[] = $row;
+ } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+ $newData[] = $row[$join];
+ }
+ }
+ if (empty($newData)) {
+ continue;
+ }
+ foreach ($newData as $data) {
+ $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id;
+ $this->{$join}->create($data);
+ $valid = ($valid && $this->{$join}->validates($options));
+ }
+ }
+ return $valid;
+ }
+
+/**
+ * Marks a field as invalid, optionally setting the name of validation
+ * rule (in case of multiple validation for field) that was broken.
+ *
+ * @param string $field The name of the field to invalidate
+ * @param mixed $value Name of validation rule that was not failed, or validation message to
+ * be returned. If no validation key is provided, defaults to true.
+ * @return void
+ */
+ public function invalidate($field, $value = true) {
+ if (!is_array($this->validationErrors)) {
+ $this->validationErrors = array();
+ }
+ $this->validationErrors[$field][] = $value;
+ }
+
+/**
+ * Returns true if given field name is a foreign key in this model.
+ *
+ * @param string $field Returns true if the input string ends in "_id"
+ * @return boolean True if the field is a foreign key listed in the belongsTo array.
+ */
+ public function isForeignKey($field) {
+ $foreignKeys = array();
+ if (!empty($this->belongsTo)) {
+ foreach ($this->belongsTo as $assoc => $data) {
+ $foreignKeys[] = $data['foreignKey'];
+ }
+ }
+ return in_array($field, $foreignKeys);
+ }
+
+/**
+ * Escapes the field name and prepends the model name. Escaping is done according to the
+ * current database driver's rules.
+ *
+ * @param string $field Field to escape (e.g: id)
+ * @param string $alias Alias for the model (e.g: Post)
+ * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`).
+ */
+ public function escapeField($field = null, $alias = null) {
+ if (empty($alias)) {
+ $alias = $this->alias;
+ }
+ if (empty($field)) {
+ $field = $this->primaryKey;
+ }
+ $db = $this->getDataSource();
+ if (strpos($field, $db->name($alias) . '.') === 0) {
+ return $field;
+ }
+ return $db->name($alias . '.' . $field);
+ }
+
+/**
+ * Returns the current record's ID
+ *
+ * @param integer $list Index on which the composed ID is located
+ * @return mixed The ID of the current record, false if no ID
+ */
+ public function getID($list = 0) {
+ if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) {
+ return false;
+ }
+
+ if (!is_array($this->id)) {
+ return $this->id;
+ }
+
+ if (isset($this->id[$list]) && !empty($this->id[$list])) {
+ return $this->id[$list];
+ } elseif (isset($this->id[$list])) {
+ return false;
+ }
+
+ return current($this->id);
+ }
+
+/**
+ * Returns the ID of the last record this model inserted.
+ *
+ * @return mixed Last inserted ID
+ */
+ public function getLastInsertID() {
+ return $this->getInsertID();
+ }
+
+/**
+ * Returns the ID of the last record this model inserted.
+ *
+ * @return mixed Last inserted ID
+ */
+ public function getInsertID() {
+ return $this->_insertID;
+ }
+
+/**
+ * Sets the ID of the last record this model inserted
+ *
+ * @param mixed $id Last inserted ID
+ * @return void
+ */
+ public function setInsertID($id) {
+ $this->_insertID = $id;
+ }
+
+/**
+ * Returns the number of rows returned from the last query.
+ *
+ * @return integer Number of rows
+ */
+ public function getNumRows() {
+ return $this->getDataSource()->lastNumRows();
+ }
+
+/**
+ * Returns the number of rows affected by the last query.
+ *
+ * @return integer Number of rows
+ */
+ public function getAffectedRows() {
+ return $this->getDataSource()->lastAffected();
+ }
+
+/**
+ * Sets the DataSource to which this model is bound.
+ *
+ * @param string $dataSource The name of the DataSource, as defined in app/Config/database.php
+ * @return void
+ * @throws MissingConnectionException
+ */
+ public function setDataSource($dataSource = null) {
+ $oldConfig = $this->useDbConfig;
+
+ if ($dataSource != null) {
+ $this->useDbConfig = $dataSource;
+ }
+ $db = ConnectionManager::getDataSource($this->useDbConfig);
+ if (!empty($oldConfig) && isset($db->config['prefix'])) {
+ $oldDb = ConnectionManager::getDataSource($oldConfig);
+
+ if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) {
+ $this->tablePrefix = $db->config['prefix'];
+ }
+ } elseif (isset($db->config['prefix'])) {
+ $this->tablePrefix = $db->config['prefix'];
+ }
+
+ $this->schemaName = $db->getSchemaName();
+
+ if (empty($db) || !is_object($db)) {
+ throw new MissingConnectionException(array('class' => $this->name));
+ }
+ }
+
+/**
+ * Gets the DataSource to which this model is bound.
+ *
+ * @return DataSource A DataSource object
+ */
+ public function getDataSource() {
+ if (!$this->_sourceConfigured && $this->useTable !== false) {
+ $this->_sourceConfigured = true;
+ $this->setSource($this->useTable);
+ }
+ return ConnectionManager::getDataSource($this->useDbConfig);
+ }
+
+/**
+ * Get associations
+ *
+ * @return array
+ */
+ public function associations() {
+ return $this->_associations;
+ }
+
+/**
+ * Gets all the models with which this model is associated.
+ *
+ * @param string $type Only result associations of this type
+ * @return array Associations
+ */
+ public function getAssociated($type = null) {
+ if ($type == null) {
+ $associated = array();
+ foreach ($this->_associations as $assoc) {
+ if (!empty($this->{$assoc})) {
+ $models = array_keys($this->{$assoc});
+ foreach ($models as $m) {
+ $associated[$m] = $assoc;
+ }
+ }
+ }
+ return $associated;
+ } elseif (in_array($type, $this->_associations)) {
+ if (empty($this->{$type})) {
+ return array();
+ }
+ return array_keys($this->{$type});
+ } else {
+ $assoc = array_merge(
+ $this->hasOne,
+ $this->hasMany,
+ $this->belongsTo,
+ $this->hasAndBelongsToMany
+ );
+ if (array_key_exists($type, $assoc)) {
+ foreach ($this->_associations as $a) {
+ if (isset($this->{$a}[$type])) {
+ $assoc[$type]['association'] = $a;
+ break;
+ }
+ }
+ return $assoc[$type];
+ }
+ return null;
+ }
+ }
+
+/**
+ * Gets the name and fields to be used by a join model. This allows specifying join fields
+ * in the association definition.
+ *
+ * @param string|array $assoc The model to be joined
+ * @param array $keys Any join keys which must be merged with the keys queried
+ * @return array
+ */
+ public function joinModel($assoc, $keys = array()) {
+ if (is_string($assoc)) {
+ list(, $assoc) = pluginSplit($assoc);
+ return array($assoc, array_keys($this->{$assoc}->schema()));
+ } elseif (is_array($assoc)) {
+ $with = key($assoc);
+ return array($with, array_unique(array_merge($assoc[$with], $keys)));
+ }
+ trigger_error(
+ __d('cake_dev', 'Invalid join model settings in %s', $model->alias),
+ E_USER_WARNING
+ );
+ }
+
+/**
+ * Called before each find operation. Return false if you want to halt the find
+ * call, otherwise return the (modified) query data.
+ *
+ * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
+ * @return mixed true if the operation should continue, false if it should abort; or, modified
+ * $queryData to continue with new $queryData
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#beforefind
+ */
+ public function beforeFind($queryData) {
+ return true;
+ }
+
+/**
+ * Called after each find operation. Can be used to modify any results returned by find().
+ * Return value should be the (modified) results.
+ *
+ * @param mixed $results The results of the find operation
+ * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
+ * @return mixed Result of the find operation
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#afterfind
+ */
+ public function afterFind($results, $primary = false) {
+ return $results;
+ }
+
+/**
+ * Called before each save operation, after validation. Return a non-true result
+ * to halt the save.
+ *
+ * @param array $options
+ * @return boolean True if the operation should continue, false if it should abort
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#beforesave
+ */
+ public function beforeSave($options = array()) {
+ return true;
+ }
+
+/**
+ * Called after each successful save operation.
+ *
+ * @param boolean $created True if this save created a new record
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#aftersave
+ */
+ public function afterSave($created) {
+ }
+
+/**
+ * Called before every deletion operation.
+ *
+ * @param boolean $cascade If true records that depend on this record will also be deleted
+ * @return boolean True if the operation should continue, false if it should abort
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#beforedelete
+ */
+ public function beforeDelete($cascade = true) {
+ return true;
+ }
+
+/**
+ * Called after every deletion operation.
+ *
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#afterdelete
+ */
+ public function afterDelete() {
+ }
+
+/**
+ * Called during validation operations, before validation. Please note that custom
+ * validation rules can be defined in $validate.
+ *
+ * @param array $options Options passed from model::save(), see $options of model::save().
+ * @return boolean True if validate operation should continue, false to abort
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#beforevalidate
+ */
+ public function beforeValidate($options = array()) {
+ return true;
+ }
+
+/**
+ * Called when a DataSource-level error occurs.
+ *
+ * @return void
+ * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#onerror
+ */
+ public function onError() {
+ }
+
+/**
+ * Clears cache for this model.
+ *
+ * @param string $type If null this deletes cached views if Cache.check is true
+ * Will be used to allow deleting query cache also
+ * @return boolean true on delete
+ */
+ protected function _clearCache($type = null) {
+ if ($type === null) {
+ if (Configure::read('Cache.check') === true) {
+ $assoc[] = strtolower(Inflector::pluralize($this->alias));
+ $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias)));
+ foreach ($this->_associations as $key => $association) {
+ foreach ($this->$association as $key => $className) {
+ $check = strtolower(Inflector::pluralize($className['className']));
+ if (!in_array($check, $assoc)) {
+ $assoc[] = strtolower(Inflector::pluralize($className['className']));
+ $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className'])));
+ }
+ }
+ }
+ clearCache($assoc);
+ return true;
+ }
+ } else {
+ //Will use for query cache deleting
+ }
+ }
+
+}
diff --git a/samples/php/drupal.module b/samples/php/drupal.module
new file mode 100644
index 0000000..37bf9a1
--- /dev/null
+++ b/samples/php/drupal.module
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * @file
+ * Additional filter for PHP input.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function php_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#php':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The PHP filter module adds a PHP filter to your site, for use with <a href="@filter">text formats</a>. This filter adds the ability to execute PHP code in any text field that uses a text format (such as the body of a content item or the text of a comment). <a href="@php-net">PHP</a> is a general-purpose scripting language widely-used for web development, and is the language with which Drupal has been developed. For more information, see the online handbook entry for the <a href="@php">PHP filter module</a>.', array('@filter' => url('admin/help/filter'), '@php-net' => 'http://www.php.net', '@php' => 'http://drupal.org/handbook/modules/php/')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Enabling execution of PHP in text fields') . '</dt>';
+ $output .= '<dd>' . t('The PHP filter module allows users with the proper permissions to include custom PHP code that will get executed when pages of your site are processed. While this is a powerful and flexible feature if used by a trusted user with PHP experience, it is a significant and dangerous security risk in the hands of a malicious or inexperienced user. Even a trusted user may accidentally compromise the site by entering malformed or incorrect PHP code. Only the most trusted users should be granted permission to use the PHP filter, and all PHP code added through the PHP filter should be carefully examined before use. <a href="@php-snippets">Example PHP snippets</a> can be found on Drupal.org.', array('@php-snippets' => url('http://drupal.org/handbook/customization/php-snippets'))) . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_permission().
+ */
+function php_permission() {
+ return array(
+ 'use PHP for settings' => array(
+ 'title' => t('Use PHP for settings'),
+ 'restrict access' => TRUE,
+ ),
+ );
+}
+
+/**
+ * Evaluate a string of PHP code.
+ *
+ * This is a wrapper around PHP's eval(). It uses output buffering to capture both
+ * returned and printed text. Unlike eval(), we require code to be surrounded by
+ * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone
+ * PHP file.
+ *
+ * Using this wrapper also ensures that the PHP code which is evaluated can not
+ * overwrite any variables in the calling code, unlike a regular eval() call.
+ *
+ * @param $code
+ * The code to evaluate.
+ * @return
+ * A string containing the printed output of the code, followed by the returned
+ * output of the code.
+ *
+ * @ingroup php_wrappers
+ */
+function php_eval($code) {
+ global $theme_path, $theme_info, $conf;
+
+ // Store current theme path.
+ $old_theme_path = $theme_path;
+
+ // Restore theme_path to the theme, as long as php_eval() executes,
+ // so code evaluated will not see the caller module as the current theme.
+ // If theme info is not initialized get the path from theme_default.
+ if (!isset($theme_info)) {
+ $theme_path = drupal_get_path('theme', $conf['theme_default']);
+ }
+ else {
+ $theme_path = dirname($theme_info->filename);
+ }
+
+ ob_start();
+ print eval('?>' . $code);
+ $output = ob_get_contents();
+ ob_end_clean();
+
+ // Recover original theme path.
+ $theme_path = $old_theme_path;
+
+ return $output;
+}
+
+/**
+ * Tips callback for php filter.
+ */
+function _php_filter_tips($filter, $format, $long = FALSE) {
+ global $base_url;
+ if ($long) {
+ $output = '<h4>' . t('Using custom PHP code') . '</h4>';
+ $output .= '<p>' . t('Custom PHP code may be embedded in some types of site content, including posts and blocks. While embedding PHP code inside a post or block is a powerful and flexible feature when used by a trusted user with PHP experience, it is a significant and dangerous security risk when used improperly. Even a small mistake when posting PHP code may accidentally compromise your site.') . '</p>';
+ $output .= '<p>' . t('If you are unfamiliar with PHP, SQL, or Drupal, avoid using custom PHP code within posts. Experimenting with PHP may corrupt your database, render your site inoperable, or significantly compromise security.') . '</p>';
+ $output .= '<p>' . t('Notes:') . '</p>';
+ $output .= '<ul><li>' . t('Remember to double-check each line for syntax and logic errors <strong>before</strong> saving.') . '</li>';
+ $output .= '<li>' . t('Statements must be correctly terminated with semicolons.') . '</li>';
+ $output .= '<li>' . t('Global variables used within your PHP code retain their values after your script executes.') . '</li>';
+ $output .= '<li>' . t('<code>register_globals</code> is <strong>turned off</strong>. If you need to use forms, understand and use the functions in <a href="@formapi">the Drupal Form API</a>.', array('@formapi' => url('http://api.drupal.org/api/group/form_api/7'))) . '</li>';
+ $output .= '<li>' . t('Use a <code>print</code> or <code>return</code> statement in your code to output content.') . '</li>';
+ $output .= '<li>' . t('Develop and test your PHP code using a separate test script and sample database before deploying on a production site.') . '</li>';
+ $output .= '<li>' . t('Consider including your custom PHP code within a site-specific module or <code>template.php</code> file rather than embedding it directly into a post or block.') . '</li>';
+ $output .= '<li>' . t('Be aware that the ability to embed PHP code within content is provided by the PHP Filter module. If this module is disabled or deleted, then blocks and posts with embedded PHP may display, rather than execute, the PHP code.') . '</li></ul>';
+ $output .= '<p>' . t('A basic example: <em>Creating a "Welcome" block that greets visitors with a simple message.</em>') . '</p>';
+ $output .= '<ul><li>' . t('<p>Add a custom block to your site, named "Welcome" . With its text format set to "PHP code" (or another format supporting PHP input), add the following in the Block body:</p>
+<pre>
+print t(\'Welcome visitor! Thank you for visiting.\');
+</pre>') . '</li>';
+ $output .= '<li>' . t('<p>To display the name of a registered user, use this instead:</p>
+<pre>
+global $user;
+if ($user->uid) {
+ print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => format_username($user)));
+}
+else {
+ print t(\'Welcome visitor! Thank you for visiting.\');
+}
+</pre>') . '</li></ul>';
+ $output .= '<p>' . t('<a href="@drupal">Drupal.org</a> offers <a href="@php-snippets">some example PHP snippets</a>, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://drupal.org/handbook/customization/php-snippets'))) . '</p>';
+ return $output;
+ }
+ else {
+ return t('You may post PHP code. You should include &lt;?php ?&gt; tags.');
+ }
+}
+
+/**
+ * Implements hook_filter_info().
+ *
+ * Provide PHP code filter. Use with care.
+ */
+function php_filter_info() {
+ $filters['php_code'] = array(
+ 'title' => t('PHP evaluator'),
+ 'description' => t('Executes a piece of PHP code. The usage of this filter should be restricted to administrators only!'),
+ 'process callback' => 'php_eval',
+ 'tips callback' => '_php_filter_tips',
+ 'cache' => FALSE,
+ );
+ return $filters;
+}
+
diff --git a/samples/pir/hello.pir b/samples/pir/hello.pir
new file mode 100644
index 0000000..6cb35c1
--- /dev/null
+++ b/samples/pir/hello.pir
@@ -0,0 +1,5 @@
+#!/usr/bin/env parrot
+
+.sub 'main' :main
+ say "Hello!"
+.end
diff --git a/samples/powershell/hello.ps1 b/samples/powershell/hello.ps1
new file mode 100644
index 0000000..eca1e76
--- /dev/null
+++ b/samples/powershell/hello.ps1
@@ -0,0 +1,2 @@
+# Hello world in powershell
+Write-Host 'Hello World' \ No newline at end of file
diff --git a/samples/powershell/hello.psm1 b/samples/powershell/hello.psm1
new file mode 100644
index 0000000..3db82f0
--- /dev/null
+++ b/samples/powershell/hello.psm1
@@ -0,0 +1,5 @@
+# Hello World powershell module
+
+function hello() {
+ Write-Host 'Hello World'
+} \ No newline at end of file
diff --git a/samples/prolog/test-prolog.pl b/samples/prolog/test-prolog.pl
new file mode 100644
index 0000000..aab83d5
--- /dev/null
+++ b/samples/prolog/test-prolog.pl
@@ -0,0 +1,12 @@
+/* Prolog test file */
+male(john).
+male(peter).
+
+female(vick).
+female(christie).
+
+parents(john, peter, christie).
+parents(vick, peter, christie).
+
+/* X is a brother of Y */
+brother(X, Y) :- male(X), parents(X, F, M), parents(Y, F, M).
diff --git a/samples/python/django-models-base.py b/samples/python/django-models-base.py
new file mode 100644
index 0000000..f52a626
--- /dev/null
+++ b/samples/python/django-models-base.py
@@ -0,0 +1,938 @@
+from __future__ import unicode_literals
+
+import copy
+import sys
+from functools import update_wrapper
+from future_builtins import zip
+
+import django.db.models.manager # Imported to register signal handler.
+from django.conf import settings
+from django.core.exceptions import (ObjectDoesNotExist,
+ MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS)
+from django.core import validators
+from django.db.models.fields import AutoField, FieldDoesNotExist
+from django.db.models.fields.related import (ManyToOneRel,
+ OneToOneField, add_lazy_relation)
+from django.db import (router, transaction, DatabaseError,
+ DEFAULT_DB_ALIAS)
+from django.db.models.query import Q
+from django.db.models.query_utils import DeferredAttribute
+from django.db.models.deletion import Collector
+from django.db.models.options import Options
+from django.db.models import signals
+from django.db.models.loading import register_models, get_model
+from django.utils.translation import ugettext_lazy as _
+from django.utils.functional import curry
+from django.utils.encoding import smart_str, force_unicode
+from django.utils.text import get_text_list, capfirst
+
+
+class ModelBase(type):
+ """
+ Metaclass for all models.
+ """
+ def __new__(cls, name, bases, attrs):
+ super_new = super(ModelBase, cls).__new__
+ parents = [b for b in bases if isinstance(b, ModelBase)]
+ if not parents:
+ # If this isn't a subclass of Model, don't do anything special.
+ return super_new(cls, name, bases, attrs)
+
+ # Create the class.
+ module = attrs.pop('__module__')
+ new_class = super_new(cls, name, bases, {'__module__': module})
+ attr_meta = attrs.pop('Meta', None)
+ abstract = getattr(attr_meta, 'abstract', False)
+ if not attr_meta:
+ meta = getattr(new_class, 'Meta', None)
+ else:
+ meta = attr_meta
+ base_meta = getattr(new_class, '_meta', None)
+
+ if getattr(meta, 'app_label', None) is None:
+ # Figure out the app_label by looking one level up.
+ # For 'django.contrib.sites.models', this would be 'sites'.
+ model_module = sys.modules[new_class.__module__]
+ kwargs = {"app_label": model_module.__name__.split('.')[-2]}
+ else:
+ kwargs = {}
+
+ new_class.add_to_class('_meta', Options(meta, **kwargs))
+ if not abstract:
+ new_class.add_to_class('DoesNotExist', subclass_exception(b'DoesNotExist',
+ tuple(x.DoesNotExist
+ for x in parents if hasattr(x, '_meta') and not x._meta.abstract)
+ or (ObjectDoesNotExist,), module))
+ new_class.add_to_class('MultipleObjectsReturned', subclass_exception(b'MultipleObjectsReturned',
+ tuple(x.MultipleObjectsReturned
+ for x in parents if hasattr(x, '_meta') and not x._meta.abstract)
+ or (MultipleObjectsReturned,), module))
+ if base_meta and not base_meta.abstract:
+ # Non-abstract child classes inherit some attributes from their
+ # non-abstract parent (unless an ABC comes before it in the
+ # method resolution order).
+ if not hasattr(meta, 'ordering'):
+ new_class._meta.ordering = base_meta.ordering
+ if not hasattr(meta, 'get_latest_by'):
+ new_class._meta.get_latest_by = base_meta.get_latest_by
+
+ is_proxy = new_class._meta.proxy
+
+ if getattr(new_class, '_default_manager', None):
+ if not is_proxy:
+ # Multi-table inheritance doesn't inherit default manager from
+ # parents.
+ new_class._default_manager = None
+ new_class._base_manager = None
+ else:
+ # Proxy classes do inherit parent's default manager, if none is
+ # set explicitly.
+ new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
+ new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
+
+ # Bail out early if we have already created this class.
+ m = get_model(new_class._meta.app_label, name,
+ seed_cache=False, only_installed=False)
+ if m is not None:
+ return m
+
+ # Add all attributes to the class.
+ for obj_name, obj in attrs.items():
+ new_class.add_to_class(obj_name, obj)
+
+ # All the fields of any type declared on this model
+ new_fields = new_class._meta.local_fields + \
+ new_class._meta.local_many_to_many + \
+ new_class._meta.virtual_fields
+ field_names = set([f.name for f in new_fields])
+
+ # Basic setup for proxy models.
+ if is_proxy:
+ base = None
+ for parent in [cls for cls in parents if hasattr(cls, '_meta')]:
+ if parent._meta.abstract:
+ if parent._meta.fields:
+ raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name)
+ else:
+ continue
+ if base is not None:
+ raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name)
+ else:
+ base = parent
+ if base is None:
+ raise TypeError("Proxy model '%s' has no non-abstract model base class." % name)
+ if (new_class._meta.local_fields or
+ new_class._meta.local_many_to_many):
+ raise FieldError("Proxy model '%s' contains model fields." % name)
+ new_class._meta.setup_proxy(base)
+ new_class._meta.concrete_model = base._meta.concrete_model
+ else:
+ new_class._meta.concrete_model = new_class
+
+ # Do the appropriate setup for any model parents.
+ o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
+ if isinstance(f, OneToOneField)])
+
+ for base in parents:
+ original_base = base
+ if not hasattr(base, '_meta'):
+ # Things without _meta aren't functional models, so they're
+ # uninteresting parents.
+ continue
+
+ parent_fields = base._meta.local_fields + base._meta.local_many_to_many
+ # Check for clashes between locally declared fields and those
+ # on the base classes (we cannot handle shadowed fields at the
+ # moment).
+ for field in parent_fields:
+ if field.name in field_names:
+ raise FieldError('Local field %r in class %r clashes '
+ 'with field of similar name from '
+ 'base class %r' %
+ (field.name, name, base.__name__))
+ if not base._meta.abstract:
+ # Concrete classes...
+ base = base._meta.concrete_model
+ if base in o2o_map:
+ field = o2o_map[base]
+ elif not is_proxy:
+ attr_name = '%s_ptr' % base._meta.module_name
+ field = OneToOneField(base, name=attr_name,
+ auto_created=True, parent_link=True)
+ new_class.add_to_class(attr_name, field)
+ else:
+ field = None
+ new_class._meta.parents[base] = field
+ else:
+ # .. and abstract ones.
+ for field in parent_fields:
+ new_class.add_to_class(field.name, copy.deepcopy(field))
+
+ # Pass any non-abstract parent classes onto child.
+ new_class._meta.parents.update(base._meta.parents)
+
+ # Inherit managers from the abstract base classes.
+ new_class.copy_managers(base._meta.abstract_managers)
+
+ # Proxy models inherit the non-abstract managers from their base,
+ # unless they have redefined any of them.
+ if is_proxy:
+ new_class.copy_managers(original_base._meta.concrete_managers)
+
+ # Inherit virtual fields (like GenericForeignKey) from the parent
+ # class
+ for field in base._meta.virtual_fields:
+ if base._meta.abstract and field.name in field_names:
+ raise FieldError('Local field %r in class %r clashes '\
+ 'with field of similar name from '\
+ 'abstract base class %r' % \
+ (field.name, name, base.__name__))
+ new_class.add_to_class(field.name, copy.deepcopy(field))
+
+ if abstract:
+ # Abstract base models can't be instantiated and don't appear in
+ # the list of models for an app. We do the final setup for them a
+ # little differently from normal models.
+ attr_meta.abstract = False
+ new_class.Meta = attr_meta
+ return new_class
+
+ new_class._prepare()
+ register_models(new_class._meta.app_label, new_class)
+
+ # Because of the way imports happen (recursively), we may or may not be
+ # the first time this model tries to register with the framework. There
+ # should only be one class for each model, so we always return the
+ # registered version.
+ return get_model(new_class._meta.app_label, name,
+ seed_cache=False, only_installed=False)
+
+ def copy_managers(cls, base_managers):
+ # This is in-place sorting of an Options attribute, but that's fine.
+ base_managers.sort()
+ for _, mgr_name, manager in base_managers:
+ val = getattr(cls, mgr_name, None)
+ if not val or val is manager:
+ new_manager = manager._copy_to_model(cls)
+ cls.add_to_class(mgr_name, new_manager)
+
+ def add_to_class(cls, name, value):
+ if hasattr(value, 'contribute_to_class'):
+ value.contribute_to_class(cls, name)
+ else:
+ setattr(cls, name, value)
+
+ def _prepare(cls):
+ """
+ Creates some methods once self._meta has been populated.
+ """
+ opts = cls._meta
+ opts._prepare(cls)
+
+ if opts.order_with_respect_to:
+ cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True)
+ cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)
+ # defer creating accessors on the foreign class until we are
+ # certain it has been created
+ def make_foreign_order_accessors(field, model, cls):
+ setattr(
+ field.rel.to,
+ 'get_%s_order' % cls.__name__.lower(),
+ curry(method_get_order, cls)
+ )
+ setattr(
+ field.rel.to,
+ 'set_%s_order' % cls.__name__.lower(),
+ curry(method_set_order, cls)
+ )
+ add_lazy_relation(
+ cls,
+ opts.order_with_respect_to,
+ opts.order_with_respect_to.rel.to,
+ make_foreign_order_accessors
+ )
+
+ # Give the class a docstring -- its definition.
+ if cls.__doc__ is None:
+ cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join([f.attname for f in opts.fields]))
+
+ if hasattr(cls, 'get_absolute_url'):
+ cls.get_absolute_url = update_wrapper(curry(get_absolute_url, opts, cls.get_absolute_url),
+ cls.get_absolute_url)
+
+ signals.class_prepared.send(sender=cls)
+
+class ModelState(object):
+ """
+ A class for storing instance state
+ """
+ def __init__(self, db=None):
+ self.db = db
+ # If true, uniqueness validation checks will consider this a new, as-yet-unsaved object.
+ # Necessary for correct validation of new instances of objects with explicit (non-auto) PKs.
+ # This impacts validation only; it has no effect on the actual save.
+ self.adding = True
+
+class Model(object):
+ __metaclass__ = ModelBase
+ _deferred = False
+
+ def __init__(self, *args, **kwargs):
+ signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
+
+ # Set up the storage for instance state
+ self._state = ModelState()
+
+ # There is a rather weird disparity here; if kwargs, it's set, then args
+ # overrides it. It should be one or the other; don't duplicate the work
+ # The reason for the kwargs check is that standard iterator passes in by
+ # args, and instantiation for iteration is 33% faster.
+ args_len = len(args)
+ if args_len > len(self._meta.fields):
+ # Daft, but matches old exception sans the err msg.
+ raise IndexError("Number of args exceeds number of fields")
+
+ fields_iter = iter(self._meta.fields)
+ if not kwargs:
+ # The ordering of the zip calls matter - zip throws StopIteration
+ # when an iter throws it. So if the first iter throws it, the second
+ # is *not* consumed. We rely on this, so don't change the order
+ # without changing the logic.
+ for val, field in zip(args, fields_iter):
+ setattr(self, field.attname, val)
+ else:
+ # Slower, kwargs-ready version.
+ for val, field in zip(args, fields_iter):
+ setattr(self, field.attname, val)
+ kwargs.pop(field.name, None)
+ # Maintain compatibility with existing calls.
+ if isinstance(field.rel, ManyToOneRel):
+ kwargs.pop(field.attname, None)
+
+ # Now we're left with the unprocessed fields that *must* come from
+ # keywords, or default.
+
+ for field in fields_iter:
+ is_related_object = False
+ # This slightly odd construct is so that we can access any
+ # data-descriptor object (DeferredAttribute) without triggering its
+ # __get__ method.
+ if (field.attname not in kwargs and
+ isinstance(self.__class__.__dict__.get(field.attname), DeferredAttribute)):
+ # This field will be populated on request.
+ continue
+ if kwargs:
+ if isinstance(field.rel, ManyToOneRel):
+ try:
+ # Assume object instance was passed in.
+ rel_obj = kwargs.pop(field.name)
+ is_related_object = True
+ except KeyError:
+ try:
+ # Object instance wasn't passed in -- must be an ID.
+ val = kwargs.pop(field.attname)
+ except KeyError:
+ val = field.get_default()
+ else:
+ # Object instance was passed in. Special case: You can
+ # pass in "None" for related objects if it's allowed.
+ if rel_obj is None and field.null:
+ val = None
+ else:
+ try:
+ val = kwargs.pop(field.attname)
+ except KeyError:
+ # This is done with an exception rather than the
+ # default argument on pop because we don't want
+ # get_default() to be evaluated, and then not used.
+ # Refs #12057.
+ val = field.get_default()
+ else:
+ val = field.get_default()
+ if is_related_object:
+ # If we are passed a related instance, set it using the
+ # field.name instead of field.attname (e.g. "user" instead of
+ # "user_id") so that the object gets properly cached (and type
+ # checked) by the RelatedObjectDescriptor.
+ setattr(self, field.name, rel_obj)
+ else:
+ setattr(self, field.attname, val)
+
+ if kwargs:
+ for prop in kwargs.keys():
+ try:
+ if isinstance(getattr(self.__class__, prop), property):
+ setattr(self, prop, kwargs.pop(prop))
+ except AttributeError:
+ pass
+ if kwargs:
+ raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
+ super(Model, self).__init__()
+ signals.post_init.send(sender=self.__class__, instance=self)
+
+ def __repr__(self):
+ try:
+ u = unicode(self)
+ except (UnicodeEncodeError, UnicodeDecodeError):
+ u = '[Bad Unicode data]'
+ return smart_str('<%s: %s>' % (self.__class__.__name__, u))
+
+ def __str__(self):
+ if hasattr(self, '__unicode__'):
+ return force_unicode(self).encode('utf-8')
+ return '%s object' % self.__class__.__name__
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self._get_pk_val())
+
+ def __reduce__(self):
+ """
+ Provides pickling support. Normally, this just dispatches to Python's
+ standard handling. However, for models with deferred field loading, we
+ need to do things manually, as they're dynamically created classes and
+ only module-level classes can be pickled by the default path.
+ """
+ data = self.__dict__
+ model = self.__class__
+ # The obvious thing to do here is to invoke super().__reduce__()
+ # for the non-deferred case. Don't do that.
+ # On Python 2.4, there is something weird with __reduce__,
+ # and as a result, the super call will cause an infinite recursion.
+ # See #10547 and #12121.
+ defers = []
+ if self._deferred:
+ from django.db.models.query_utils import deferred_class_factory
+ factory = deferred_class_factory
+ for field in self._meta.fields:
+ if isinstance(self.__class__.__dict__.get(field.attname),
+ DeferredAttribute):
+ defers.append(field.attname)
+ model = self._meta.proxy_for_model
+ else:
+ factory = simple_class_factory
+ return (model_unpickle, (model, defers, factory), data)
+
+ def _get_pk_val(self, meta=None):
+ if not meta:
+ meta = self._meta
+ return getattr(self, meta.pk.attname)
+
+ def _set_pk_val(self, value):
+ return setattr(self, self._meta.pk.attname, value)
+
+ pk = property(_get_pk_val, _set_pk_val)
+
+ def serializable_value(self, field_name):
+ """
+ Returns the value of the field name for this instance. If the field is
+ a foreign key, returns the id value, instead of the object. If there's
+ no Field object with this name on the model, the model attribute's
+ value is returned directly.
+
+ Used to serialize a field's value (in the serializer, or form output,
+ for example). Normally, you would just access the attribute directly
+ and not use this method.
+ """
+ try:
+ field = self._meta.get_field_by_name(field_name)[0]
+ except FieldDoesNotExist:
+ return getattr(self, field_name)
+ return getattr(self, field.attname)
+
+ def save(self, force_insert=False, force_update=False, using=None,
+ update_fields=None):
+ """
+ Saves the current instance. Override this in a subclass if you want to
+ control the saving process.
+
+ The 'force_insert' and 'force_update' parameters can be used to insist
+ that the "save" must be an SQL insert or update (or equivalent for
+ non-SQL backends), respectively. Normally, they should not be set.
+ """
+ if force_insert and (force_update or update_fields):
+ raise ValueError("Cannot force both insert and updating in model saving.")
+
+ if update_fields is not None:
+ # If update_fields is empty, skip the save. We do also check for
+ # no-op saves later on for inheritance cases. This bailout is
+ # still needed for skipping signal sending.
+ if len(update_fields) == 0:
+ return
+
+ update_fields = frozenset(update_fields)
+ field_names = set([field.name for field in self._meta.fields
+ if not field.primary_key])
+ non_model_fields = update_fields.difference(field_names)
+
+ if non_model_fields:
+ raise ValueError("The following fields do not exist in this "
+ "model or are m2m fields: %s"
+ % ', '.join(non_model_fields))
+
+ self.save_base(using=using, force_insert=force_insert,
+ force_update=force_update, update_fields=update_fields)
+ save.alters_data = True
+
+ def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
+ force_update=False, using=None, update_fields=None):
+ """
+ Does the heavy-lifting involved in saving. Subclasses shouldn't need to
+ override this method. It's separate from save() in order to hide the
+ need for overrides of save() to pass around internal-only parameters
+ ('raw', 'cls', and 'origin').
+ """
+ using = using or router.db_for_write(self.__class__, instance=self)
+ assert not (force_insert and (force_update or update_fields))
+ assert update_fields is None or len(update_fields) > 0
+ if cls is None:
+ cls = self.__class__
+ meta = cls._meta
+ if not meta.proxy:
+ origin = cls
+ else:
+ meta = cls._meta
+
+ if origin and not meta.auto_created:
+ signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
+ update_fields=update_fields)
+
+ # If we are in a raw save, save the object exactly as presented.
+ # That means that we don't try to be smart about saving attributes
+ # that might have come from the parent class - we just save the
+ # attributes we have been given to the class we have been given.
+ # We also go through this process to defer the save of proxy objects
+ # to their actual underlying model.
+ if not raw or meta.proxy:
+ if meta.proxy:
+ org = cls
+ else:
+ org = None
+ for parent, field in meta.parents.items():
+ # At this point, parent's primary key field may be unknown
+ # (for example, from administration form which doesn't fill
+ # this field). If so, fill it.
+ if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
+ setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
+
+ self.save_base(cls=parent, origin=org, using=using,
+ update_fields=update_fields)
+
+ if field:
+ setattr(self, field.attname, self._get_pk_val(parent._meta))
+ if meta.proxy:
+ return
+
+ if not meta.proxy:
+ non_pks = [f for f in meta.local_fields if not f.primary_key]
+
+ if update_fields:
+ non_pks = [f for f in non_pks if f.name in update_fields]
+
+ # First, try an UPDATE. If that doesn't update anything, do an INSERT.
+ pk_val = self._get_pk_val(meta)
+ pk_set = pk_val is not None
+ record_exists = True
+ manager = cls._base_manager
+ if pk_set:
+ # Determine if we should do an update (pk already exists, forced update,
+ # no force_insert)
+ if ((force_update or update_fields) or (not force_insert and
+ manager.using(using).filter(pk=pk_val).exists())):
+ if force_update or non_pks:
+ values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
+ if values:
+ rows = manager.using(using).filter(pk=pk_val)._update(values)
+ if force_update and not rows:
+ raise DatabaseError("Forced update did not affect any rows.")
+ if update_fields and not rows:
+ raise DatabaseError("Save with update_fields did not affect any rows.")
+ else:
+ record_exists = False
+ if not pk_set or not record_exists:
+ if meta.order_with_respect_to:
+ # If this is a model with an order_with_respect_to
+ # autopopulate the _order field
+ field = meta.order_with_respect_to
+ order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count()
+ self._order = order_value
+
+ fields = meta.local_fields
+ if not pk_set:
+ if force_update or update_fields:
+ raise ValueError("Cannot force an update in save() with no primary key.")
+ fields = [f for f in fields if not isinstance(f, AutoField)]
+
+ record_exists = False
+
+ update_pk = bool(meta.has_auto_field and not pk_set)
+ result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw)
+
+ if update_pk:
+ setattr(self, meta.pk.attname, result)
+ transaction.commit_unless_managed(using=using)
+
+ # Store the database on which the object was saved
+ self._state.db = using
+ # Once saved, this is no longer a to-be-added instance.
+ self._state.adding = False
+
+ # Signal that the save is complete
+ if origin and not meta.auto_created:
+ signals.post_save.send(sender=origin, instance=self, created=(not record_exists),
+ update_fields=update_fields, raw=raw, using=using)
+
+
+ save_base.alters_data = True
+
+ def delete(self, using=None):
+ using = using or router.db_for_write(self.__class__, instance=self)
+ assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
+
+ collector = Collector(using=using)
+ collector.collect([self])
+ collector.delete()
+
+ delete.alters_data = True
+
+ def _get_FIELD_display(self, field):
+ value = getattr(self, field.attname)
+ return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)
+
+ def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
+ if not self.pk:
+ raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
+ op = is_next and 'gt' or 'lt'
+ order = not is_next and '-' or ''
+ param = smart_str(getattr(self, field.attname))
+ q = Q(**{'%s__%s' % (field.name, op): param})
+ q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk})
+ qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
+ try:
+ return qs[0]
+ except IndexError:
+ raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name)
+
+ def _get_next_or_previous_in_order(self, is_next):
+ cachename = "__%s_order_cache" % is_next
+ if not hasattr(self, cachename):
+ op = is_next and 'gt' or 'lt'
+ order = not is_next and '-_order' or '_order'
+ order_field = self._meta.order_with_respect_to
+ obj = self._default_manager.filter(**{
+ order_field.name: getattr(self, order_field.attname)
+ }).filter(**{
+ '_order__%s' % op: self._default_manager.values('_order').filter(**{
+ self._meta.pk.name: self.pk
+ })
+ }).order_by(order)[:1].get()
+ setattr(self, cachename, obj)
+ return getattr(self, cachename)
+
+ def prepare_database_save(self, unused):
+ return self.pk
+
+ def clean(self):
+ """
+ Hook for doing any extra model-wide validation after clean() has been
+ called on every field by self.clean_fields. Any ValidationError raised
+ by this method will not be associated with a particular field; it will
+ have a special-case association with the field defined by NON_FIELD_ERRORS.
+ """
+ pass
+
+ def validate_unique(self, exclude=None):
+ """
+ Checks unique constraints on the model and raises ``ValidationError``
+ if any failed.
+ """
+ unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
+
+ errors = self._perform_unique_checks(unique_checks)
+ date_errors = self._perform_date_checks(date_checks)
+
+ for k, v in date_errors.items():
+ errors.setdefault(k, []).extend(v)
+
+ if errors:
+ raise ValidationError(errors)
+
+ def _get_unique_checks(self, exclude=None):
+ """
+ Gather a list of checks to perform. Since validate_unique could be
+ called from a ModelForm, some fields may have been excluded; we can't
+ perform a unique check on a model that is missing fields involved
+ in that check.
+ Fields that did not validate should also be excluded, but they need
+ to be passed in via the exclude argument.
+ """
+ if exclude is None:
+ exclude = []
+ unique_checks = []
+
+ unique_togethers = [(self.__class__, self._meta.unique_together)]
+ for parent_class in self._meta.parents.keys():
+ if parent_class._meta.unique_together:
+ unique_togethers.append((parent_class, parent_class._meta.unique_together))
+
+ for model_class, unique_together in unique_togethers:
+ for check in unique_together:
+ for name in check:
+ # If this is an excluded field, don't add this check.
+ if name in exclude:
+ break
+ else:
+ unique_checks.append((model_class, tuple(check)))
+
+ # These are checks for the unique_for_<date/year/month>.
+ date_checks = []
+
+ # Gather a list of checks for fields declared as unique and add them to
+ # the list of checks.
+
+ fields_with_class = [(self.__class__, self._meta.local_fields)]
+ for parent_class in self._meta.parents.keys():
+ fields_with_class.append((parent_class, parent_class._meta.local_fields))
+
+ for model_class, fields in fields_with_class:
+ for f in fields:
+ name = f.name
+ if name in exclude:
+ continue
+ if f.unique:
+ unique_checks.append((model_class, (name,)))
+ if f.unique_for_date and f.unique_for_date not in exclude:
+ date_checks.append((model_class, 'date', name, f.unique_for_date))
+ if f.unique_for_year and f.unique_for_year not in exclude:
+ date_checks.append((model_class, 'year', name, f.unique_for_year))
+ if f.unique_for_month and f.unique_for_month not in exclude:
+ date_checks.append((model_class, 'month', name, f.unique_for_month))
+ return unique_checks, date_checks
+
+ def _perform_unique_checks(self, unique_checks):
+ errors = {}
+
+ for model_class, unique_check in unique_checks:
+ # Try to look up an existing object with the same values as this
+ # object's values for all the unique field.
+
+ lookup_kwargs = {}
+ for field_name in unique_check:
+ f = self._meta.get_field(field_name)
+ lookup_value = getattr(self, f.attname)
+ if lookup_value is None:
+ # no value, skip the lookup
+ continue
+ if f.primary_key and not self._state.adding:
+ # no need to check for unique primary key when editing
+ continue
+ lookup_kwargs[str(field_name)] = lookup_value
+
+ # some fields were skipped, no reason to do the check
+ if len(unique_check) != len(lookup_kwargs.keys()):
+ continue
+
+ qs = model_class._default_manager.filter(**lookup_kwargs)
+
+ # Exclude the current object from the query if we are editing an
+ # instance (as opposed to creating a new one)
+ # Note that we need to use the pk as defined by model_class, not
+ # self.pk. These can be different fields because model inheritance
+ # allows single model to have effectively multiple primary keys.
+ # Refs #17615.
+ model_class_pk = self._get_pk_val(model_class._meta)
+ if not self._state.adding and model_class_pk is not None:
+ qs = qs.exclude(pk=model_class_pk)
+ if qs.exists():
+ if len(unique_check) == 1:
+ key = unique_check[0]
+ else:
+ key = NON_FIELD_ERRORS
+ errors.setdefault(key, []).append(self.unique_error_message(model_class, unique_check))
+
+ return errors
+
+ def _perform_date_checks(self, date_checks):
+ errors = {}
+ for model_class, lookup_type, field, unique_for in date_checks:
+ lookup_kwargs = {}
+ # there's a ticket to add a date lookup, we can remove this special
+ # case if that makes it's way in
+ date = getattr(self, unique_for)
+ if date is None:
+ continue
+ if lookup_type == 'date':
+ lookup_kwargs['%s__day' % unique_for] = date.day
+ lookup_kwargs['%s__month' % unique_for] = date.month
+ lookup_kwargs['%s__year' % unique_for] = date.year
+ else:
+ lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(date, lookup_type)
+ lookup_kwargs[field] = getattr(self, field)
+
+ qs = model_class._default_manager.filter(**lookup_kwargs)
+ # Exclude the current object from the query if we are editing an
+ # instance (as opposed to creating a new one)
+ if not self._state.adding and self.pk is not None:
+ qs = qs.exclude(pk=self.pk)
+
+ if qs.exists():
+ errors.setdefault(field, []).append(
+ self.date_error_message(lookup_type, field, unique_for)
+ )
+ return errors
+
+ def date_error_message(self, lookup_type, field, unique_for):
+ opts = self._meta
+ return _("%(field_name)s must be unique for %(date_field)s %(lookup)s.") % {
+ 'field_name': unicode(capfirst(opts.get_field(field).verbose_name)),
+ 'date_field': unicode(capfirst(opts.get_field(unique_for).verbose_name)),
+ 'lookup': lookup_type,
+ }
+
+ def unique_error_message(self, model_class, unique_check):
+ opts = model_class._meta
+ model_name = capfirst(opts.verbose_name)
+
+ # A unique field
+ if len(unique_check) == 1:
+ field_name = unique_check[0]
+ field = opts.get_field(field_name)
+ field_label = capfirst(field.verbose_name)
+ # Insert the error into the error dict, very sneaky
+ return field.error_messages['unique'] % {
+ 'model_name': unicode(model_name),
+ 'field_label': unicode(field_label)
+ }
+ # unique_together
+ else:
+ field_labels = map(lambda f: capfirst(opts.get_field(f).verbose_name), unique_check)
+ field_labels = get_text_list(field_labels, _('and'))
+ return _("%(model_name)s with this %(field_label)s already exists.") % {
+ 'model_name': unicode(model_name),
+ 'field_label': unicode(field_labels)
+ }
+
+ def full_clean(self, exclude=None):
+ """
+ Calls clean_fields, clean, and validate_unique, on the model,
+ and raises a ``ValidationError`` for any errors that occured.
+ """
+ errors = {}
+ if exclude is None:
+ exclude = []
+
+ try:
+ self.clean_fields(exclude=exclude)
+ except ValidationError as e:
+ errors = e.update_error_dict(errors)
+
+ # Form.clean() is run even if other validation fails, so do the
+ # same with Model.clean() for consistency.
+ try:
+ self.clean()
+ except ValidationError as e:
+ errors = e.update_error_dict(errors)
+
+ # Run unique checks, but only for fields that passed validation.
+ for name in errors.keys():
+ if name != NON_FIELD_ERRORS and name not in exclude:
+ exclude.append(name)
+ try:
+ self.validate_unique(exclude=exclude)
+ except ValidationError as e:
+ errors = e.update_error_dict(errors)
+
+ if errors:
+ raise ValidationError(errors)
+
+ def clean_fields(self, exclude=None):
+ """
+ Cleans all fields and raises a ValidationError containing message_dict
+ of all validation errors if any occur.
+ """
+ if exclude is None:
+ exclude = []
+
+ errors = {}
+ for f in self._meta.fields:
+ if f.name in exclude:
+ continue
+ # Skip validation for empty fields with blank=True. The developer
+ # is responsible for making sure they have a valid value.
+ raw_value = getattr(self, f.attname)
+ if f.blank and raw_value in validators.EMPTY_VALUES:
+ continue
+ try:
+ setattr(self, f.attname, f.clean(raw_value, self))
+ except ValidationError as e:
+ errors[f.name] = e.messages
+
+ if errors:
+ raise ValidationError(errors)
+
+
+############################################
+# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
+############################################
+
+# ORDERING METHODS #########################
+
+def method_set_order(ordered_obj, self, id_list, using=None):
+ if using is None:
+ using = DEFAULT_DB_ALIAS
+ rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
+ order_name = ordered_obj._meta.order_with_respect_to.name
+ # FIXME: It would be nice if there was an "update many" version of update
+ # for situations like this.
+ for i, j in enumerate(id_list):
+ ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i)
+ transaction.commit_unless_managed(using=using)
+
+
+def method_get_order(ordered_obj, self):
+ rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
+ order_name = ordered_obj._meta.order_with_respect_to.name
+ pk_name = ordered_obj._meta.pk.name
+ return [r[pk_name] for r in
+ ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)]
+
+
+##############################################
+# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
+##############################################
+
+def get_absolute_url(opts, func, self, *args, **kwargs):
+ return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs)
+
+
+########
+# MISC #
+########
+
+class Empty(object):
+ pass
+
+def simple_class_factory(model, attrs):
+ """Used to unpickle Models without deferred fields.
+
+ We need to do this the hard way, rather than just using
+ the default __reduce__ implementation, because of a
+ __deepcopy__ problem in Python 2.4
+ """
+ return model
+
+def model_unpickle(model, attrs, factory):
+ """
+ Used to unpickle Model subclasses with deferred fields.
+ """
+ cls = factory(model, attrs)
+ return cls.__new__(cls)
+model_unpickle.__safe_for_unpickle__ = True
+
+def subclass_exception(name, parents, module):
+ return type(name, parents, {'__module__': module})
diff --git a/samples/python/flask-view.py b/samples/python/flask-view.py
new file mode 100644
index 0000000..5192c1c
--- /dev/null
+++ b/samples/python/flask-view.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.views
+ ~~~~~~~~~~~
+
+ This module provides class-based views inspired by the ones in Django.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from .globals import request
+
+
+http_method_funcs = frozenset(['get', 'post', 'head', 'options',
+ 'delete', 'put', 'trace', 'patch'])
+
+
+class View(object):
+ """Alternative way to use view functions. A subclass has to implement
+ :meth:`dispatch_request` which is called with the view arguments from
+ the URL routing system. If :attr:`methods` is provided the methods
+ do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
+ method explicitly::
+
+ class MyView(View):
+ methods = ['GET']
+
+ def dispatch_request(self, name):
+ return 'Hello %s!' % name
+
+ app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
+
+ When you want to decorate a pluggable view you will have to either do that
+ when the view function is created (by wrapping the return value of
+ :meth:`as_view`) or you can use the :attr:`decorators` attribute::
+
+ class SecretView(View):
+ methods = ['GET']
+ decorators = [superuser_required]
+
+ def dispatch_request(self):
+ ...
+
+ The decorators stored in the decorators list are applied one after another
+ when the view function is created. Note that you can *not* use the class
+ based decorators since those would decorate the view class and not the
+ generated view function!
+ """
+
+ #: A for which methods this pluggable view can handle.
+ methods = None
+
+ #: The canonical way to decorate class-based views is to decorate the
+ #: return value of as_view(). However since this moves parts of the
+ #: logic from the class declaration to the place where it's hooked
+ #: into the routing system.
+ #:
+ #: You can place one or more decorators in this list and whenever the
+ #: view function is created the result is automatically decorated.
+ #:
+ #: .. versionadded:: 0.8
+ decorators = []
+
+ def dispatch_request(self):
+ """Subclasses have to override this method to implement the
+ actual view function code. This method is called with all
+ the arguments from the URL rule.
+ """
+ raise NotImplementedError()
+
+ @classmethod
+ def as_view(cls, name, *class_args, **class_kwargs):
+ """Converts the class into an actual view function that can be used
+ with the routing system. Internally this generates a function on the
+ fly which will instantiate the :class:`View` on each request and call
+ the :meth:`dispatch_request` method on it.
+
+ The arguments passed to :meth:`as_view` are forwarded to the
+ constructor of the class.
+ """
+ def view(*args, **kwargs):
+ self = view.view_class(*class_args, **class_kwargs)
+ return self.dispatch_request(*args, **kwargs)
+
+ if cls.decorators:
+ view.__name__ = name
+ view.__module__ = cls.__module__
+ for decorator in cls.decorators:
+ view = decorator(view)
+
+ # we attach the view class to the view function for two reasons:
+ # first of all it allows us to easily figure out what class-based
+ # view this thing came from, secondly it's also used for instantiating
+ # the view class so you can actually replace it with something else
+ # for testing purposes and debugging.
+ view.view_class = cls
+ view.__name__ = name
+ view.__doc__ = cls.__doc__
+ view.__module__ = cls.__module__
+ view.methods = cls.methods
+ return view
+
+
+class MethodViewType(type):
+
+ def __new__(cls, name, bases, d):
+ rv = type.__new__(cls, name, bases, d)
+ if 'methods' not in d:
+ methods = set(rv.methods or [])
+ for key in d:
+ if key in http_method_funcs:
+ methods.add(key.upper())
+ # if we have no method at all in there we don't want to
+ # add a method list. (This is for instance the case for
+ # the baseclass or another subclass of a base method view
+ # that does not introduce new methods).
+ if methods:
+ rv.methods = sorted(methods)
+ return rv
+
+
+class MethodView(View):
+ """Like a regular class-based view but that dispatches requests to
+ particular methods. For instance if you implement a method called
+ :meth:`get` it means you will response to ``'GET'`` requests and
+ the :meth:`dispatch_request` implementation will automatically
+ forward your request to that. Also :attr:`options` is set for you
+ automatically::
+
+ class CounterAPI(MethodView):
+
+ def get(self):
+ return session.get('counter', 0)
+
+ def post(self):
+ session['counter'] = session.get('counter', 0) + 1
+ return 'OK'
+
+ app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
+ """
+ __metaclass__ = MethodViewType
+
+ def dispatch_request(self, *args, **kwargs):
+ meth = getattr(self, request.method.lower(), None)
+ # if the request method is HEAD and we don't have a handler for it
+ # retry with GET
+ if meth is None and request.method == 'HEAD':
+ meth = getattr(self, 'get', None)
+ assert meth is not None, 'Unimplemented method %r' % request.method
+ return meth(*args, **kwargs)
diff --git a/samples/python/script.py b/samples/python/script.py
new file mode 100755
index 0000000..bcebcd4
--- /dev/null
+++ b/samples/python/script.py
@@ -0,0 +1,2 @@
+#!/usr/bin/env python2.4
+print "Python"
diff --git a/samples/python/tornado-httpserver.py b/samples/python/tornado-httpserver.py
new file mode 100644
index 0000000..5667ba1
--- /dev/null
+++ b/samples/python/tornado-httpserver.py
@@ -0,0 +1,486 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 Facebook
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""A non-blocking, single-threaded HTTP server.
+
+Typical applications have little direct interaction with the `HTTPServer`
+class except to start a server at the beginning of the process
+(and even that is often done indirectly via `tornado.web.Application.listen`).
+
+This module also defines the `HTTPRequest` class which is exposed via
+`tornado.web.RequestHandler.request`.
+"""
+
+from __future__ import absolute_import, division, with_statement
+
+import Cookie
+import logging
+import socket
+import time
+
+from tornado.escape import utf8, native_str, parse_qs_bytes
+from tornado import httputil
+from tornado import iostream
+from tornado.netutil import TCPServer
+from tornado import stack_context
+from tornado.util import b, bytes_type
+
+try:
+ import ssl # Python 2.6+
+except ImportError:
+ ssl = None
+
+
+class HTTPServer(TCPServer):
+ r"""A non-blocking, single-threaded HTTP server.
+
+ A server is defined by a request callback that takes an HTTPRequest
+ instance as an argument and writes a valid HTTP response with
+ `HTTPRequest.write`. `HTTPRequest.finish` finishes the request (but does
+ not necessarily close the connection in the case of HTTP/1.1 keep-alive
+ requests). A simple example server that echoes back the URI you
+ requested::
+
+ import httpserver
+ import ioloop
+
+ def handle_request(request):
+ message = "You requested %s\n" % request.uri
+ request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % (
+ len(message), message))
+ request.finish()
+
+ http_server = httpserver.HTTPServer(handle_request)
+ http_server.listen(8888)
+ ioloop.IOLoop.instance().start()
+
+ `HTTPServer` is a very basic connection handler. Beyond parsing the
+ HTTP request body and headers, the only HTTP semantics implemented
+ in `HTTPServer` is HTTP/1.1 keep-alive connections. We do not, however,
+ implement chunked encoding, so the request callback must provide a
+ ``Content-Length`` header or implement chunked encoding for HTTP/1.1
+ requests for the server to run correctly for HTTP/1.1 clients. If
+ the request handler is unable to do this, you can provide the
+ ``no_keep_alive`` argument to the `HTTPServer` constructor, which will
+ ensure the connection is closed on every request no matter what HTTP
+ version the client is using.
+
+ If ``xheaders`` is ``True``, we support the ``X-Real-Ip`` and ``X-Scheme``
+ headers, which override the remote IP and HTTP scheme for all requests.
+ These headers are useful when running Tornado behind a reverse proxy or
+ load balancer.
+
+ `HTTPServer` can serve SSL traffic with Python 2.6+ and OpenSSL.
+ To make this server serve SSL traffic, send the ssl_options dictionary
+ argument with the arguments required for the `ssl.wrap_socket` method,
+ including "certfile" and "keyfile"::
+
+ HTTPServer(applicaton, ssl_options={
+ "certfile": os.path.join(data_dir, "mydomain.crt"),
+ "keyfile": os.path.join(data_dir, "mydomain.key"),
+ })
+
+ `HTTPServer` initialization follows one of three patterns (the
+ initialization methods are defined on `tornado.netutil.TCPServer`):
+
+ 1. `~tornado.netutil.TCPServer.listen`: simple single-process::
+
+ server = HTTPServer(app)
+ server.listen(8888)
+ IOLoop.instance().start()
+
+ In many cases, `tornado.web.Application.listen` can be used to avoid
+ the need to explicitly create the `HTTPServer`.
+
+ 2. `~tornado.netutil.TCPServer.bind`/`~tornado.netutil.TCPServer.start`:
+ simple multi-process::
+
+ server = HTTPServer(app)
+ server.bind(8888)
+ server.start(0) # Forks multiple sub-processes
+ IOLoop.instance().start()
+
+ When using this interface, an `IOLoop` must *not* be passed
+ to the `HTTPServer` constructor. `start` will always start
+ the server on the default singleton `IOLoop`.
+
+ 3. `~tornado.netutil.TCPServer.add_sockets`: advanced multi-process::
+
+ sockets = tornado.netutil.bind_sockets(8888)
+ tornado.process.fork_processes(0)
+ server = HTTPServer(app)
+ server.add_sockets(sockets)
+ IOLoop.instance().start()
+
+ The `add_sockets` interface is more complicated, but it can be
+ used with `tornado.process.fork_processes` to give you more
+ flexibility in when the fork happens. `add_sockets` can
+ also be used in single-process servers if you want to create
+ your listening sockets in some way other than
+ `tornado.netutil.bind_sockets`.
+
+ """
+ def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
+ xheaders=False, ssl_options=None, **kwargs):
+ self.request_callback = request_callback
+ self.no_keep_alive = no_keep_alive
+ self.xheaders = xheaders
+ TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
+ **kwargs)
+
+ def handle_stream(self, stream, address):
+ HTTPConnection(stream, address, self.request_callback,
+ self.no_keep_alive, self.xheaders)
+
+
+class _BadRequestException(Exception):
+ """Exception class for malformed HTTP requests."""
+ pass
+
+
+class HTTPConnection(object):
+ """Handles a connection to an HTTP client, executing HTTP requests.
+
+ We parse HTTP headers and bodies, and execute the request callback
+ until the HTTP conection is closed.
+ """
+ def __init__(self, stream, address, request_callback, no_keep_alive=False,
+ xheaders=False):
+ self.stream = stream
+ self.address = address
+ self.request_callback = request_callback
+ self.no_keep_alive = no_keep_alive
+ self.xheaders = xheaders
+ self._request = None
+ self._request_finished = False
+ # Save stack context here, outside of any request. This keeps
+ # contexts from one request from leaking into the next.
+ self._header_callback = stack_context.wrap(self._on_headers)
+ self.stream.read_until(b("\r\n\r\n"), self._header_callback)
+ self._write_callback = None
+
+ def write(self, chunk, callback=None):
+ """Writes a chunk of output to the stream."""
+ assert self._request, "Request closed"
+ if not self.stream.closed():
+ self._write_callback = stack_context.wrap(callback)
+ self.stream.write(chunk, self._on_write_complete)
+
+ def finish(self):
+ """Finishes the request."""
+ assert self._request, "Request closed"
+ self._request_finished = True
+ if not self.stream.writing():
+ self._finish_request()
+
+ def _on_write_complete(self):
+ if self._write_callback is not None:
+ callback = self._write_callback
+ self._write_callback = None
+ callback()
+ # _on_write_complete is enqueued on the IOLoop whenever the
+ # IOStream's write buffer becomes empty, but it's possible for
+ # another callback that runs on the IOLoop before it to
+ # simultaneously write more data and finish the request. If
+ # there is still data in the IOStream, a future
+ # _on_write_complete will be responsible for calling
+ # _finish_request.
+ if self._request_finished and not self.stream.writing():
+ self._finish_request()
+
+ def _finish_request(self):
+ if self.no_keep_alive:
+ disconnect = True
+ else:
+ connection_header = self._request.headers.get("Connection")
+ if connection_header is not None:
+ connection_header = connection_header.lower()
+ if self._request.supports_http_1_1():
+ disconnect = connection_header == "close"
+ elif ("Content-Length" in self._request.headers
+ or self._request.method in ("HEAD", "GET")):
+ disconnect = connection_header != "keep-alive"
+ else:
+ disconnect = True
+ self._request = None
+ self._request_finished = False
+ if disconnect:
+ self.stream.close()
+ return
+ self.stream.read_until(b("\r\n\r\n"), self._header_callback)
+
+ def _on_headers(self, data):
+ try:
+ data = native_str(data.decode('latin1'))
+ eol = data.find("\r\n")
+ start_line = data[:eol]
+ try:
+ method, uri, version = start_line.split(" ")
+ except ValueError:
+ raise _BadRequestException("Malformed HTTP request line")
+ if not version.startswith("HTTP/"):
+ raise _BadRequestException("Malformed HTTP version in HTTP Request-Line")
+ headers = httputil.HTTPHeaders.parse(data[eol:])
+
+ # HTTPRequest wants an IP, not a full socket address
+ if getattr(self.stream.socket, 'family', socket.AF_INET) in (
+ socket.AF_INET, socket.AF_INET6):
+ # Jython 2.5.2 doesn't have the socket.family attribute,
+ # so just assume IP in that case.
+ remote_ip = self.address[0]
+ else:
+ # Unix (or other) socket; fake the remote address
+ remote_ip = '0.0.0.0'
+
+ self._request = HTTPRequest(
+ connection=self, method=method, uri=uri, version=version,
+ headers=headers, remote_ip=remote_ip)
+
+ content_length = headers.get("Content-Length")
+ if content_length:
+ content_length = int(content_length)
+ if content_length > self.stream.max_buffer_size:
+ raise _BadRequestException("Content-Length too long")
+ if headers.get("Expect") == "100-continue":
+ self.stream.write(b("HTTP/1.1 100 (Continue)\r\n\r\n"))
+ self.stream.read_bytes(content_length, self._on_request_body)
+ return
+
+ self.request_callback(self._request)
+ except _BadRequestException, e:
+ logging.info("Malformed HTTP request from %s: %s",
+ self.address[0], e)
+ self.stream.close()
+ return
+
+ def _on_request_body(self, data):
+ self._request.body = data
+ content_type = self._request.headers.get("Content-Type", "")
+ if self._request.method in ("POST", "PATCH", "PUT"):
+ if content_type.startswith("application/x-www-form-urlencoded"):
+ arguments = parse_qs_bytes(native_str(self._request.body))
+ for name, values in arguments.iteritems():
+ values = [v for v in values if v]
+ if values:
+ self._request.arguments.setdefault(name, []).extend(
+ values)
+ elif content_type.startswith("multipart/form-data"):
+ fields = content_type.split(";")
+ for field in fields:
+ k, sep, v = field.strip().partition("=")
+ if k == "boundary" and v:
+ httputil.parse_multipart_form_data(
+ utf8(v), data,
+ self._request.arguments,
+ self._request.files)
+ break
+ else:
+ logging.warning("Invalid multipart/form-data")
+ self.request_callback(self._request)
+
+
+class HTTPRequest(object):
+ """A single HTTP request.
+
+ All attributes are type `str` unless otherwise noted.
+
+ .. attribute:: method
+
+ HTTP request method, e.g. "GET" or "POST"
+
+ .. attribute:: uri
+
+ The requested uri.
+
+ .. attribute:: path
+
+ The path portion of `uri`
+
+ .. attribute:: query
+
+ The query portion of `uri`
+
+ .. attribute:: version
+
+ HTTP version specified in request, e.g. "HTTP/1.1"
+
+ .. attribute:: headers
+
+ `HTTPHeader` dictionary-like object for request headers. Acts like
+ a case-insensitive dictionary with additional methods for repeated
+ headers.
+
+ .. attribute:: body
+
+ Request body, if present, as a byte string.
+
+ .. attribute:: remote_ip
+
+ Client's IP address as a string. If `HTTPServer.xheaders` is set,
+ will pass along the real IP address provided by a load balancer
+ in the ``X-Real-Ip`` header
+
+ .. attribute:: protocol
+
+ The protocol used, either "http" or "https". If `HTTPServer.xheaders`
+ is set, will pass along the protocol used by a load balancer if
+ reported via an ``X-Scheme`` header.
+
+ .. attribute:: host
+
+ The requested hostname, usually taken from the ``Host`` header.
+
+ .. attribute:: arguments
+
+ GET/POST arguments are available in the arguments property, which
+ maps arguments names to lists of values (to support multiple values
+ for individual names). Names are of type `str`, while arguments
+ are byte strings. Note that this is different from
+ `RequestHandler.get_argument`, which returns argument values as
+ unicode strings.
+
+ .. attribute:: files
+
+ File uploads are available in the files property, which maps file
+ names to lists of :class:`HTTPFile`.
+
+ .. attribute:: connection
+
+ An HTTP request is attached to a single HTTP connection, which can
+ be accessed through the "connection" attribute. Since connections
+ are typically kept open in HTTP/1.1, multiple requests can be handled
+ sequentially on a single connection.
+ """
+ def __init__(self, method, uri, version="HTTP/1.0", headers=None,
+ body=None, remote_ip=None, protocol=None, host=None,
+ files=None, connection=None):
+ self.method = method
+ self.uri = uri
+ self.version = version
+ self.headers = headers or httputil.HTTPHeaders()
+ self.body = body or ""
+ if connection and connection.xheaders:
+ # Squid uses X-Forwarded-For, others use X-Real-Ip
+ self.remote_ip = self.headers.get(
+ "X-Real-Ip", self.headers.get("X-Forwarded-For", remote_ip))
+ if not self._valid_ip(self.remote_ip):
+ self.remote_ip = remote_ip
+ # AWS uses X-Forwarded-Proto
+ self.protocol = self.headers.get(
+ "X-Scheme", self.headers.get("X-Forwarded-Proto", protocol))
+ if self.protocol not in ("http", "https"):
+ self.protocol = "http"
+ else:
+ self.remote_ip = remote_ip
+ if protocol:
+ self.protocol = protocol
+ elif connection and isinstance(connection.stream,
+ iostream.SSLIOStream):
+ self.protocol = "https"
+ else:
+ self.protocol = "http"
+ self.host = host or self.headers.get("Host") or "127.0.0.1"
+ self.files = files or {}
+ self.connection = connection
+ self._start_time = time.time()
+ self._finish_time = None
+
+ self.path, sep, self.query = uri.partition('?')
+ arguments = parse_qs_bytes(self.query)
+ self.arguments = {}
+ for name, values in arguments.iteritems():
+ values = [v for v in values if v]
+ if values:
+ self.arguments[name] = values
+
+ def supports_http_1_1(self):
+ """Returns True if this request supports HTTP/1.1 semantics"""
+ return self.version == "HTTP/1.1"
+
+ @property
+ def cookies(self):
+ """A dictionary of Cookie.Morsel objects."""
+ if not hasattr(self, "_cookies"):
+ self._cookies = Cookie.SimpleCookie()
+ if "Cookie" in self.headers:
+ try:
+ self._cookies.load(
+ native_str(self.headers["Cookie"]))
+ except Exception:
+ self._cookies = {}
+ return self._cookies
+
+ def write(self, chunk, callback=None):
+ """Writes the given chunk to the response stream."""
+ assert isinstance(chunk, bytes_type)
+ self.connection.write(chunk, callback=callback)
+
+ def finish(self):
+ """Finishes this HTTP request on the open connection."""
+ self.connection.finish()
+ self._finish_time = time.time()
+
+ def full_url(self):
+ """Reconstructs the full URL for this request."""
+ return self.protocol + "://" + self.host + self.uri
+
+ def request_time(self):
+ """Returns the amount of time it took for this request to execute."""
+ if self._finish_time is None:
+ return time.time() - self._start_time
+ else:
+ return self._finish_time - self._start_time
+
+ def get_ssl_certificate(self):
+ """Returns the client's SSL certificate, if any.
+
+ To use client certificates, the HTTPServer must have been constructed
+ with cert_reqs set in ssl_options, e.g.::
+
+ server = HTTPServer(app,
+ ssl_options=dict(
+ certfile="foo.crt",
+ keyfile="foo.key",
+ cert_reqs=ssl.CERT_REQUIRED,
+ ca_certs="cacert.crt"))
+
+ The return value is a dictionary, see SSLSocket.getpeercert() in
+ the standard library for more details.
+ http://docs.python.org/library/ssl.html#sslsocket-objects
+ """
+ try:
+ return self.connection.stream.socket.getpeercert()
+ except ssl.SSLError:
+ return None
+
+ def __repr__(self):
+ attrs = ("protocol", "host", "method", "uri", "version", "remote_ip",
+ "body")
+ args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
+ return "%s(%s, headers=%s)" % (
+ self.__class__.__name__, args, dict(self.headers))
+
+ def _valid_ip(self, ip):
+ try:
+ res = socket.getaddrinfo(ip, 0, socket.AF_UNSPEC,
+ socket.SOCK_STREAM,
+ 0, socket.AI_NUMERICHOST)
+ return bool(res)
+ except socket.gaierror, e:
+ if e.args[0] == socket.EAI_NONAME:
+ return False
+ raise
+ return True
diff --git a/samples/r/hello-r.R b/samples/r/hello-r.R
new file mode 100644
index 0000000..30d715c
--- /dev/null
+++ b/samples/r/hello-r.R
@@ -0,0 +1,4 @@
+hello <- function() {
+ print("hello, world!")
+}
+hello()
diff --git a/samples/racket/scribble.scrbl b/samples/racket/scribble.scrbl
new file mode 100644
index 0000000..9c4d0e2
--- /dev/null
+++ b/samples/racket/scribble.scrbl
@@ -0,0 +1,33 @@
+#lang scribble/manual
+@(require scribble/bnf "utils.rkt")
+
+@title{Scribble: The Racket Documentation Tool}
+
+@author["Matthew Flatt" "Eli Barzilay"]
+
+Scribble is a collection of tools for creating prose
+documents---papers, books, library documentation, etc.---in HTML or
+PDF (via Latex) form. More generally, Scribble helps you write
+programs that are rich in textual content, whether the content is
+prose to be typeset or any other form of text to be generated
+programmatically.
+
+This document is itself written using Scribble. You can see its source
+at
+@(let ([url "http://git.racket-lang.org/plt/tree/HEAD:/collects/scribblings/scribble"])
+ (link url url)),
+starting with the @filepath{scribble.scrbl} file.
+
+@table-of-contents[]
+
+@; ------------------------------------------------------------------------
+@include-section["how-to-paper.scrbl"]
+@include-section["reader.scrbl"]
+@include-section["generic.scrbl"]
+@include-section["plt.scrbl"]
+@include-section["lp.scrbl"]
+@include-section["text.scrbl"]
+@include-section["internals.scrbl"]
+@include-section["running.scrbl"]
+
+@index-section[]
diff --git a/samples/racket/script.rkt b/samples/racket/script.rkt
new file mode 100755
index 0000000..bc5a8ca
--- /dev/null
+++ b/samples/racket/script.rkt
@@ -0,0 +1,7 @@
+#!/bin/sh
+#| -*- scheme -*-
+exec racket -um "$0" "$@"
+|#
+
+(require racket/file racket/path racket/list racket/string
+ (for-syntax racket/base))
diff --git a/samples/rebol/hello-rebol.r b/samples/rebol/hello-rebol.r
new file mode 100644
index 0000000..4c1b798
--- /dev/null
+++ b/samples/rebol/hello-rebol.r
@@ -0,0 +1,5 @@
+REBOL []
+hello: func [] [
+ print "hello, world!"
+]
+hello
diff --git a/samples/ruby/Capfile b/samples/ruby/Capfile
new file mode 100644
index 0000000..96291a7
--- /dev/null
+++ b/samples/ruby/Capfile
@@ -0,0 +1,3 @@
+load 'deploy'
+Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
+load 'config/deploy'
diff --git a/samples/ruby/Rakefile b/samples/ruby/Rakefile
new file mode 100644
index 0000000..9d51ff7
--- /dev/null
+++ b/samples/ruby/Rakefile
@@ -0,0 +1,3 @@
+task :default do
+ puts "Rake"
+end
diff --git a/samples/ruby/foo.rb b/samples/ruby/foo.rb
new file mode 100644
index 0000000..85026ed
--- /dev/null
+++ b/samples/ruby/foo.rb
@@ -0,0 +1,2 @@
+module Foo
+end
diff --git a/samples/ruby/formula.rb b/samples/ruby/formula.rb
new file mode 100644
index 0000000..541ee65
--- /dev/null
+++ b/samples/ruby/formula.rb
@@ -0,0 +1,703 @@
+require 'download_strategy'
+require 'dependencies'
+require 'formula_support'
+require 'hardware'
+require 'bottles'
+require 'extend/fileutils'
+require 'patches'
+require 'compilers'
+
+# Derive and define at least @url, see Library/Formula for examples
+class Formula
+ include FileUtils
+
+ attr_reader :name, :path, :url, :version, :homepage, :specs, :downloader
+ attr_reader :standard, :unstable, :head
+ attr_reader :bottle_version, :bottle_url, :bottle_sha1
+
+ # The build folder, usually in /tmp.
+ # Will only be non-nil during the stage method.
+ attr_reader :buildpath
+
+ # Homebrew determines the name
+ def initialize name='__UNKNOWN__', path=nil
+ set_instance_variable 'homepage'
+ set_instance_variable 'url'
+ set_instance_variable 'bottle_version'
+ set_instance_variable 'bottle_url'
+ set_instance_variable 'bottle_sha1'
+ set_instance_variable 'head'
+ set_instance_variable 'specs'
+ set_instance_variable 'standard'
+ set_instance_variable 'unstable'
+
+ if @head and (not @url or ARGV.build_head?)
+ @url = @head
+ @version = 'HEAD'
+ @spec_to_use = @unstable
+ else
+ if @standard.nil?
+ @spec_to_use = SoftwareSpecification.new(@url, @specs)
+ else
+ @spec_to_use = @standard
+ end
+ end
+
+ raise "No url provided for formula #{name}" if @url.nil?
+ @name = name
+ validate_variable :name
+
+ # If we got an explicit path, use that, else determine from the name
+ @path = path.nil? ? self.class.path(name) : Pathname.new(path)
+
+ # Use a provided version, if any
+ set_instance_variable 'version'
+ # Otherwise detect the version from the URL
+ @version ||= @spec_to_use.detect_version
+ # Only validate if a version was set; GitHubGistFormula needs to get
+ # the URL to determine the version
+ validate_variable :version if @version
+
+ CHECKSUM_TYPES.each { |type| set_instance_variable type }
+
+ @downloader = download_strategy.new @spec_to_use.url, name, version, @spec_to_use.specs
+
+ @bottle_url ||= bottle_base_url + bottle_filename(self) if @bottle_sha1
+ end
+
+ # if the dir is there, but it's empty we consider it not installed
+ def installed?
+ return installed_prefix.children.length > 0
+ rescue
+ return false
+ end
+
+ def explicitly_requested?
+ # `ARGV.formulae` will throw an exception if it comes up with an empty list.
+ # FIXME: `ARGV.formulae` shouldn't be throwing exceptions, see issue #8823
+ return false if ARGV.named.empty?
+ ARGV.formulae.include? self
+ end
+
+ def linked_keg
+ HOMEBREW_REPOSITORY/'Library/LinkedKegs'/@name
+ end
+
+ def installed_prefix
+ head_prefix = HOMEBREW_CELLAR+@name+'HEAD'
+ if @version == 'HEAD' || head_prefix.directory?
+ head_prefix
+ else
+ prefix
+ end
+ end
+
+ def prefix
+ validate_variable :name
+ validate_variable :version
+ HOMEBREW_CELLAR+@name+@version
+ end
+ def rack; prefix.parent end
+
+ def bin; prefix+'bin' end
+ def doc; prefix+'share/doc'+name end
+ def include; prefix+'include' end
+ def info; prefix+'share/info' end
+ def lib; prefix+'lib' end
+ def libexec; prefix+'libexec' end
+ def man; prefix+'share/man' end
+ def man1; man+'man1' end
+ def man2; man+'man2' end
+ def man3; man+'man3' end
+ def man4; man+'man4' end
+ def man5; man+'man5' end
+ def man6; man+'man6' end
+ def man7; man+'man7' end
+ def man8; man+'man8' end
+ def sbin; prefix+'sbin' end
+ def share; prefix+'share' end
+
+ # configuration needs to be preserved past upgrades
+ def etc; HOMEBREW_PREFIX+'etc' end
+ # generally we don't want var stuff inside the keg
+ def var; HOMEBREW_PREFIX+'var' end
+
+ # plist name, i.e. the name of the launchd service
+ def plist_name; 'homebrew.mxcl.'+name end
+ def plist_path; prefix+(plist_name+'.plist') end
+
+ # Use the @spec_to_use to detect the download strategy.
+ # Can be overriden to force a custom download strategy
+ def download_strategy
+ @spec_to_use.download_strategy
+ end
+
+ def cached_download
+ @downloader.cached_location
+ end
+
+ # tell the user about any caveats regarding this package, return a string
+ def caveats; nil end
+
+ # any e.g. configure options for this package
+ def options; [] end
+
+ # patches are automatically applied after extracting the tarball
+ # return an array of strings, or if you need a patch level other than -p1
+ # return a Hash eg.
+ # {
+ # :p0 => ['http://foo.com/patch1', 'http://foo.com/patch2'],
+ # :p1 => 'http://bar.com/patch2',
+ # :p2 => ['http://moo.com/patch5', 'http://moo.com/patch6']
+ # }
+ # The final option is to return DATA, then put a diff after __END__. You
+ # can still return a Hash with DATA as the value for a patch level key.
+ def patches; end
+
+ # rarely, you don't want your library symlinked into the main prefix
+ # see gettext.rb for an example
+ def keg_only?
+ self.class.keg_only_reason || false
+ end
+
+ def fails_with? cc
+ return false if self.class.cc_failures.nil?
+ cc = Compiler.new(cc) unless cc.is_a? Compiler
+ return self.class.cc_failures.find do |failure|
+ next unless failure.compiler == cc.name
+ failure.build.zero? or failure.build >= cc.build
+ end
+ end
+
+ # sometimes the clean process breaks things
+ # skip cleaning paths in a formula with a class method like this:
+ # skip_clean [bin+"foo", lib+"bar"]
+ # redefining skip_clean? now deprecated
+ def skip_clean? path
+ return true if self.class.skip_clean_all?
+ to_check = path.relative_path_from(prefix).to_s
+ self.class.skip_clean_paths.include? to_check
+ end
+
+ # yields self with current working directory set to the uncompressed tarball
+ def brew
+ validate_variable :name
+ validate_variable :version
+
+ stage do
+ begin
+ patch
+ # we allow formulas to do anything they want to the Ruby process
+ # so load any deps before this point! And exit asap afterwards
+ yield self
+ rescue Interrupt, RuntimeError, SystemCallError => e
+ puts if Interrupt === e # don't print next to the ^C
+ unless ARGV.debug?
+ %w(config.log CMakeCache.txt).select{|f| File.exist? f}.each do |f|
+ HOMEBREW_LOGS.install f
+ puts "#{f} was copied to #{HOMEBREW_LOGS}"
+ end
+ raise
+ end
+ onoe e.inspect
+ puts e.backtrace
+
+ ohai "Rescuing build..."
+ if (e.was_running_configure? rescue false) and File.exist? 'config.log'
+ puts "It looks like an autotools configure failed."
+ puts "Gist 'config.log' and any error output when reporting an issue."
+ puts
+ end
+
+ puts "When you exit this shell Homebrew will attempt to finalise the installation."
+ puts "If nothing is installed or the shell exits with a non-zero error code,"
+ puts "Homebrew will abort. The installation prefix is:"
+ puts prefix
+ interactive_shell self
+ end
+ end
+ end
+
+ def == b
+ name == b.name
+ end
+ def eql? b
+ self == b and self.class.equal? b.class
+ end
+ def hash
+ name.hash
+ end
+ def <=> b
+ name <=> b.name
+ end
+ def to_s
+ name
+ end
+
+ # Standard parameters for CMake builds.
+ # Using Build Type "None" tells cmake to use our CFLAGS,etc. settings.
+ # Setting it to Release would ignore our flags.
+ # Setting CMAKE_FIND_FRAMEWORK to "LAST" tells CMake to search for our
+ # libraries before trying to utilize Frameworks, many of which will be from
+ # 3rd party installs.
+ # Note: there isn't a std_autotools variant because autotools is a lot
+ # less consistent and the standard parameters are more memorable.
+ def std_cmake_args
+ %W[
+ -DCMAKE_INSTALL_PREFIX=#{prefix}
+ -DCMAKE_BUILD_TYPE=None
+ -DCMAKE_FIND_FRAMEWORK=LAST
+ -Wno-dev
+ ]
+ end
+
+ def self.class_s name
+ #remove invalid characters and then camelcase it
+ name.capitalize.gsub(/[-_.\s]([a-zA-Z0-9])/) { $1.upcase } \
+ .gsub('+', 'x')
+ end
+
+ # an array of all Formula names
+ def self.names
+ Dir["#{HOMEBREW_REPOSITORY}/Library/Formula/*.rb"].map{ |f| File.basename f, '.rb' }.sort
+ end
+
+ # an array of all Formula, instantiated
+ def self.all
+ map{ |f| f }
+ end
+ def self.map
+ rv = []
+ each{ |f| rv << yield(f) }
+ rv
+ end
+ def self.each
+ names.each do |n|
+ begin
+ yield Formula.factory(n)
+ rescue
+ # Don't let one broken formula break commands. But do complain.
+ onoe "Formula #{n} will not import."
+ end
+ end
+ end
+
+ def inspect
+ name
+ end
+
+ def self.aliases
+ Dir["#{HOMEBREW_REPOSITORY}/Library/Aliases/*"].map{ |f| File.basename f }.sort
+ end
+
+ def self.canonical_name name
+ name = name.to_s if name.kind_of? Pathname
+
+ formula_with_that_name = HOMEBREW_REPOSITORY+"Library/Formula/#{name}.rb"
+ possible_alias = HOMEBREW_REPOSITORY+"Library/Aliases/#{name}"
+ possible_cached_formula = HOMEBREW_CACHE_FORMULA+"#{name}.rb"
+
+ if name.include? "/"
+ if name =~ %r{(.+)/(.+)/(.+)}
+ tapd = HOMEBREW_REPOSITORY/"Library/Taps"/"#$1-#$2".downcase
+ tapd.find_formula do |relative_pathname|
+ return "#{tapd}/#{relative_pathname}" if relative_pathname.stem.to_s == $3
+ end if tapd.directory?
+ end
+ # Otherwise don't resolve paths or URLs
+ name
+ elsif formula_with_that_name.file? and formula_with_that_name.readable?
+ name
+ elsif possible_alias.file?
+ possible_alias.realpath.basename('.rb').to_s
+ elsif possible_cached_formula.file?
+ possible_cached_formula.to_s
+ else
+ name
+ end
+ end
+
+ def self.factory name
+ # If an instance of Formula is passed, just return it
+ return name if name.kind_of? Formula
+
+ # Otherwise, convert to String in case a Pathname comes in
+ name = name.to_s
+
+ # If a URL is passed, download to the cache and install
+ if name =~ %r[(https?|ftp)://]
+ url = name
+ name = Pathname.new(name).basename
+ target_file = HOMEBREW_CACHE_FORMULA+name
+ name = name.basename(".rb").to_s
+
+ HOMEBREW_CACHE_FORMULA.mkpath
+ FileUtils.rm target_file, :force => true
+ curl url, '-o', target_file
+
+ require target_file
+ install_type = :from_url
+ else
+ name = Formula.canonical_name(name)
+ # If name was a path or mapped to a cached formula
+ if name.include? "/"
+ require name
+
+ # require allows filenames to drop the .rb extension, but everything else
+ # in our codebase will require an exact and fullpath.
+ name = "#{name}.rb" unless name =~ /\.rb$/
+
+ path = Pathname.new(name)
+ name = path.stem
+ install_type = :from_path
+ target_file = path.to_s
+ else
+ # For names, map to the path and then require
+ require Formula.path(name)
+ install_type = :from_name
+ end
+ end
+
+ begin
+ klass_name = self.class_s(name)
+ klass = Object.const_get klass_name
+ rescue NameError
+ # TODO really this text should be encoded into the exception
+ # and only shown if the UI deems it correct to show it
+ onoe "class \"#{klass_name}\" expected but not found in #{name}.rb"
+ puts "Double-check the name of the class in that formula."
+ raise LoadError
+ end
+
+ return klass.new(name) if install_type == :from_name
+ return klass.new(name, target_file)
+ rescue LoadError
+ raise FormulaUnavailableError.new(name)
+ end
+
+ def tap
+ if path.realpath.to_s =~ %r{#{HOMEBREW_REPOSITORY}/Library/Taps/(\w+)-(\w+)}
+ "#$1/#$2"
+ else
+ # remotely installed formula are not mxcl/master but this will do for now
+ "mxcl/master"
+ end
+ end
+
+ def self.path name
+ HOMEBREW_REPOSITORY+"Library/Formula/#{name.downcase}.rb"
+ end
+
+ def mirrors; self.class.mirrors or []; end
+
+ def deps; self.class.dependencies.deps; end
+ def external_deps; self.class.dependencies.external_deps; end
+
+ # deps are in an installable order
+ # which means if a depends on b then b will be ordered before a in this list
+ def recursive_deps
+ Formula.expand_deps(self).flatten.uniq
+ end
+
+ def self.expand_deps f
+ f.deps.map do |dep|
+ f_dep = Formula.factory dep.to_s
+ expand_deps(f_dep) << f_dep
+ end
+ end
+
+protected
+
+ # Pretty titles the command and buffers stdout/stderr
+ # Throws if there's an error
+ def system cmd, *args
+ # remove "boring" arguments so that the important ones are more likely to
+ # be shown considering that we trim long ohai lines to the terminal width
+ pretty_args = args.dup
+ pretty_args.delete "--disable-dependency-tracking" if cmd == "./configure" and not ARGV.verbose?
+ ohai "#{cmd} #{pretty_args*' '}".strip
+
+ removed_ENV_variables = case if args.empty? then cmd.split(' ').first else cmd end
+ when "xcodebuild"
+ ENV.remove_cc_etc
+ end
+
+ if ARGV.verbose?
+ safe_system cmd, *args
+ else
+ rd, wr = IO.pipe
+ pid = fork do
+ rd.close
+ $stdout.reopen wr
+ $stderr.reopen wr
+ args.collect!{|arg| arg.to_s}
+ exec(cmd, *args) rescue nil
+ exit! 1 # never gets here unless exec threw or failed
+ end
+ wr.close
+ out = ''
+ out << rd.read until rd.eof?
+ Process.wait
+ unless $?.success?
+ puts out
+ raise
+ end
+ end
+
+ removed_ENV_variables.each do |key, value|
+ ENV[key] = value # ENV.kind_of? Hash # => false
+ end if removed_ENV_variables
+
+ rescue
+ raise BuildError.new(self, cmd, args, $?)
+ end
+
+public
+
+ # For brew-fetch and others.
+ def fetch
+ if install_bottle? self
+ downloader = CurlBottleDownloadStrategy.new bottle_url, name, version, nil
+ mirror_list = []
+ else
+ downloader = @downloader
+ # Don't attempt mirrors if this install is not pointed at a "stable" URL.
+ # This can happen when options like `--HEAD` are invoked.
+ mirror_list = @spec_to_use == @standard ? mirrors : []
+ end
+
+ # Ensure the cache exists
+ HOMEBREW_CACHE.mkpath
+
+ begin
+ fetched = downloader.fetch
+ rescue CurlDownloadStrategyError => e
+ raise e if mirror_list.empty?
+ puts "Trying a mirror..."
+ url, specs = mirror_list.shift.values_at :url, :specs
+ downloader = download_strategy.new url, name, version, specs
+ retry
+ end
+
+ return fetched, downloader
+ end
+
+ # Detect which type of checksum is being used, or nil if none
+ def checksum_type
+ CHECKSUM_TYPES.detect { |type| instance_variable_defined?("@#{type}") }
+ end
+
+ # For FormulaInstaller.
+ def verify_download_integrity fn, *args
+ require 'digest'
+ if args.length != 2
+ type = checksum_type || :md5
+ supplied = instance_variable_get("@#{type}")
+ # Convert symbol to readable string
+ type = type.to_s.upcase
+ else
+ supplied, type = args
+ end
+
+ hasher = Digest.const_get(type)
+ hash = fn.incremental_hash(hasher)
+
+ if supplied and not supplied.empty?
+ message = <<-EOF
+#{type} mismatch
+Expected: #{supplied}
+Got: #{hash}
+Archive: #{fn}
+(To retry an incomplete download, remove the file above.)
+EOF
+ raise message unless supplied.upcase == hash.upcase
+ else
+ opoo "Cannot verify package integrity"
+ puts "The formula did not provide a download checksum"
+ puts "For your reference the #{type} is: #{hash}"
+ end
+ end
+
+private
+
+ CHECKSUM_TYPES=[:md5, :sha1, :sha256].freeze
+
+ def stage
+ fetched, downloader = fetch
+ verify_download_integrity fetched if fetched.kind_of? Pathname
+ mktemp do
+ downloader.stage
+ # Set path after the downloader changes the working folder.
+ @buildpath = Pathname.pwd
+ yield
+ @buildpath = nil
+ end
+ end
+
+ def patch
+ patch_list = Patches.new(patches)
+ return if patch_list.empty?
+
+ if patch_list.external_patches?
+ ohai "Downloading patches"
+ patch_list.download!
+ end
+
+ ohai "Patching"
+ patch_list.each do |p|
+ case p.compression
+ when :gzip then safe_system "/usr/bin/gunzip", p.compressed_filename
+ when :bzip2 then safe_system "/usr/bin/bunzip2", p.compressed_filename
+ end
+ # -f means don't prompt the user if there are errors; just exit with non-zero status
+ safe_system '/usr/bin/patch', '-f', *(p.patch_args)
+ end
+ end
+
+ def validate_variable name
+ v = instance_variable_get("@#{name}")
+ raise "Invalid @#{name}" if v.to_s.empty? or v =~ /\s/
+ end
+
+ def set_instance_variable(type)
+ return if instance_variable_defined? "@#{type}"
+ class_value = self.class.send(type)
+ instance_variable_set("@#{type}", class_value) if class_value
+ end
+
+ def self.method_added method
+ raise 'You cannot override Formula.brew' if method == :brew
+ end
+
+ class << self
+ # The methods below define the formula DSL.
+ attr_reader :standard, :unstable
+
+ def self.attr_rw(*attrs)
+ attrs.each do |attr|
+ class_eval %Q{
+ def #{attr}(val=nil)
+ val.nil? ? @#{attr} : @#{attr} = val
+ end
+ }
+ end
+ end
+
+ attr_rw :version, :homepage, :mirrors, :specs
+ attr_rw :keg_only_reason, :skip_clean_all, :cc_failures
+ attr_rw :bottle_version, :bottle_url, :bottle_sha1
+ attr_rw(*CHECKSUM_TYPES)
+
+ def head val=nil, specs=nil
+ return @head if val.nil?
+ @unstable = SoftwareSpecification.new(val, specs)
+ @head = val
+ @specs = specs
+ end
+
+ def url val=nil, specs=nil
+ return @url if val.nil?
+ @standard = SoftwareSpecification.new(val, specs)
+ @url = val
+ @specs = specs
+ end
+
+ def stable &block
+ raise "url and md5 must be specified in a block" unless block_given?
+ instance_eval(&block) unless ARGV.build_devel? or ARGV.build_head?
+ end
+
+ def devel &block
+ raise "url and md5 must be specified in a block" unless block_given?
+ if ARGV.build_devel?
+ @mirrors = nil # clear out mirrors from the stable release
+ instance_eval(&block)
+ end
+ end
+
+ def bottle url=nil, &block
+ return unless block_given?
+
+ bottle_block = Class.new do
+ def self.version version
+ @version = version
+ end
+
+ def self.url url
+ @url = url
+ end
+
+ def self.sha1 sha1
+ case sha1
+ when Hash
+ key, value = sha1.shift
+ @sha1 = key if value == MacOS.cat
+ when String
+ @sha1 = sha1 if MacOS.lion?
+ end
+ end
+
+ def self.data
+ @version = 0 unless @version
+ return @version, @url, @sha1 if @sha1 && @url
+ return @version, nil, @sha1 if @sha1
+ end
+ end
+
+ bottle_block.instance_eval(&block)
+ @bottle_version, @bottle_url, @bottle_sha1 = bottle_block.data
+ end
+
+ def mirror val, specs=nil
+ @mirrors ||= []
+ @mirrors << {:url => val, :specs => specs}
+ # Added the uniq after some inspection with Pry---seems `mirror` gets
+ # called three times. The first two times only one copy of the input is
+ # left in `@mirrors`. On the final call, two copies are present. This
+ # happens with `@deps` as well. Odd.
+ @mirrors.uniq!
+ end
+
+ def dependencies
+ @dependencies ||= DependencyCollector.new
+ end
+
+ def depends_on dep
+ dependencies.add(dep)
+ end
+
+ def skip_clean paths
+ if paths == :all
+ @skip_clean_all = true
+ return
+ end
+ @skip_clean_paths ||= []
+ [paths].flatten.each do |p|
+ @skip_clean_paths << p.to_s unless @skip_clean_paths.include? p.to_s
+ end
+ end
+
+ def skip_clean_all?
+ @skip_clean_all
+ end
+
+ def skip_clean_paths
+ @skip_clean_paths or []
+ end
+
+ def keg_only reason, explanation=nil
+ @keg_only_reason = KegOnlyReason.new(reason, explanation.to_s.chomp)
+ end
+
+ def fails_with compiler, &block
+ @cc_failures ||= CompilerFailures.new
+ @cc_failures << if block_given?
+ CompilerFailure.new(compiler, &block)
+ else
+ CompilerFailure.new(compiler)
+ end
+ end
+ end
+end
+
+require 'formula_specialties'
diff --git a/samples/ruby/grit.rb b/samples/ruby/grit.rb
new file mode 100644
index 0000000..fc84753
--- /dev/null
+++ b/samples/ruby/grit.rb
@@ -0,0 +1,2 @@
+module Grit
+end
diff --git a/samples/ruby/inflector.rb b/samples/ruby/inflector.rb
new file mode 100644
index 0000000..2acc6dd
--- /dev/null
+++ b/samples/ruby/inflector.rb
@@ -0,0 +1,323 @@
+# encoding: utf-8
+
+require 'active_support/inflector/inflections'
+
+module ActiveSupport
+ # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
+ # in inflections.rb.
+ #
+ # The Rails core team has stated patches for the inflections library will not be accepted
+ # in order to avoid breaking legacy applications which may be relying on errant inflections.
+ # If you discover an incorrect inflection and require it for your application, you'll need
+ # to correct it yourself (explained below).
+ module Inflector
+ extend self
+
+ # Returns the plural form of the word in the string.
+ #
+ # "post".pluralize # => "posts"
+ # "octopus".pluralize # => "octopi"
+ # "sheep".pluralize # => "sheep"
+ # "words".pluralize # => "words"
+ # "CamelOctopus".pluralize # => "CamelOctopi"
+ def pluralize(word)
+ apply_inflections(word, inflections.plurals)
+ end
+
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
+ #
+ # "posts".singularize # => "post"
+ # "octopi".singularize # => "octopus"
+ # "sheep".singularize # => "sheep"
+ # "word".singularize # => "word"
+ # "CamelOctopi".singularize # => "CamelOctopus"
+ def singularize(word)
+ apply_inflections(word, inflections.singulars)
+ end
+
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
+ # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
+ #
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
+ #
+ # "active_model".camelize # => "ActiveModel"
+ # "active_model".camelize(:lower) # => "activeModel"
+ # "active_model/errors".camelize # => "ActiveModel::Errors"
+ # "active_model/errors".camelize(:lower) # => "activeModel::Errors"
+ #
+ # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
+ # though there are cases where that does not hold:
+ #
+ # "SSLError".underscore.camelize # => "SslError"
+ def camelize(term, uppercase_first_letter = true)
+ string = term.to_s
+ if uppercase_first_letter
+ string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
+ else
+ string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
+ end
+ string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
+ end
+
+ # Makes an underscored, lowercase form from the expression in the string.
+ #
+ # Changes '::' to '/' to convert namespaces to paths.
+ #
+ # "ActiveModel".underscore # => "active_model"
+ # "ActiveModel::Errors".underscore # => "active_model/errors"
+ #
+ # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
+ # though there are cases where that does not hold:
+ #
+ # "SSLError".underscore.camelize # => "SslError"
+ def underscore(camel_cased_word)
+ word = camel_cased_word.to_s.dup
+ word.gsub!('::', '/')
+ word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
+ word.tr!("-", "_")
+ word.downcase!
+ word
+ end
+
+ # Capitalizes the first word and turns underscores into spaces and strips a
+ # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
+ #
+ # "employee_salary" # => "Employee salary"
+ # "author_id" # => "Author"
+ def humanize(lower_case_and_underscored_word)
+ result = lower_case_and_underscored_word.to_s.dup
+ inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
+ result.gsub!(/_id$/, "")
+ result.tr!('_', ' ')
+ result.gsub(/([a-z\d]*)/i) { |match|
+ "#{inflections.acronyms[match] || match.downcase}"
+ }.gsub(/^\w/) { $&.upcase }
+ end
+
+ # Capitalizes all the words and replaces some characters in the string to create
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
+ # used in the Rails internals.
+ #
+ # +titleize+ is also aliased as +titlecase+.
+ #
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
+ # "TheManWithoutAPast".titleize # => "The Man Without A Past"
+ # "raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
+ def titleize(word)
+ humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
+ end
+
+ # Create the name of a table like Rails does for models to table names. This method
+ # uses the +pluralize+ method on the last word in the string.
+ #
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
+ # "egg_and_ham".tableize # => "egg_and_hams"
+ # "fancyCategory".tableize # => "fancy_categories"
+ def tableize(class_name)
+ pluralize(underscore(class_name))
+ end
+
+ # Create a class name from a plural table name like Rails does for table names to models.
+ # Note that this returns a string and not a Class. (To convert to an actual class
+ # follow +classify+ with +constantize+.)
+ #
+ # "egg_and_hams".classify # => "EggAndHam"
+ # "posts".classify # => "Post"
+ #
+ # Singular names are not handled correctly:
+ # "business".classify # => "Busines"
+ def classify(table_name)
+ # strip out any leading schema name
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
+ end
+
+ # Replaces underscores with dashes in the string.
+ #
+ # "puni_puni".dasherize # => "puni-puni"
+ def dasherize(underscored_word)
+ underscored_word.tr('_', '-')
+ end
+
+ # Removes the module part from the expression in the string:
+ #
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
+ # "Inflections".demodulize # => "Inflections"
+ #
+ # See also +deconstantize+.
+ def demodulize(path)
+ path = path.to_s
+ if i = path.rindex('::')
+ path[(i+2)..-1]
+ else
+ path
+ end
+ end
+
+ # Removes the rightmost segment from the constant expression in the string:
+ #
+ # "Net::HTTP".deconstantize # => "Net"
+ # "::Net::HTTP".deconstantize # => "::Net"
+ # "String".deconstantize # => ""
+ # "::String".deconstantize # => ""
+ # "".deconstantize # => ""
+ #
+ # See also +demodulize+.
+ def deconstantize(path)
+ path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
+ end
+
+ # Creates a foreign key name from a class name.
+ # +separate_class_name_and_id_with_underscore+ sets whether
+ # the method should put '_' between the name and 'id'.
+ #
+ # "Message".foreign_key # => "message_id"
+ # "Message".foreign_key(false) # => "messageid"
+ # "Admin::Post".foreign_key # => "post_id"
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
+ end
+
+ # Tries to find a constant with the name specified in the argument string:
+ #
+ # "Module".constantize # => Module
+ # "Test::Unit".constantize # => Test::Unit
+ #
+ # The name is assumed to be the one of a top-level constant, no matter whether
+ # it starts with "::" or not. No lexical context is taken into account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # "C".constantize # => 'outside', same as ::C
+ # end
+ #
+ # NameError is raised when the name is not in CamelCase or the constant is
+ # unknown.
+ def constantize(camel_cased_word)
+ names = camel_cased_word.split('::')
+ names.shift if names.empty? || names.first.empty?
+
+ names.inject(Object) do |constant, name|
+ if constant == Object
+ constant.const_get(name)
+ else
+ candidate = constant.const_get(name)
+ next candidate if constant.const_defined?(name, false)
+ next candidate unless Object.const_defined?(name)
+
+ # Go down the ancestors to check it it's owned
+ # directly before we reach Object or the end of ancestors.
+ constant = constant.ancestors.inject do |const, ancestor|
+ break const if ancestor == Object
+ break ancestor if ancestor.const_defined?(name, false)
+ const
+ end
+
+ # owner is in Object, so raise
+ constant.const_get(name, false)
+ end
+ end
+ end
+
+ # Tries to find a constant with the name specified in the argument string:
+ #
+ # "Module".safe_constantize # => Module
+ # "Test::Unit".safe_constantize # => Test::Unit
+ #
+ # The name is assumed to be the one of a top-level constant, no matter whether
+ # it starts with "::" or not. No lexical context is taken into account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # "C".safe_constantize # => 'outside', same as ::C
+ # end
+ #
+ # nil is returned when the name is not in CamelCase or the constant (or part of it) is
+ # unknown.
+ #
+ # "blargle".safe_constantize # => nil
+ # "UnknownModule".safe_constantize # => nil
+ # "UnknownModule::Foo::Bar".safe_constantize # => nil
+ #
+ def safe_constantize(camel_cased_word)
+ begin
+ constantize(camel_cased_word)
+ rescue NameError => e
+ raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
+ e.name.to_s == camel_cased_word.to_s
+ rescue ArgumentError => e
+ raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
+ end
+ end
+
+ # Returns the suffix that should be added to a number to denote the position
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # ordinal(1) # => "st"
+ # ordinal(2) # => "nd"
+ # ordinal(1002) # => "nd"
+ # ordinal(1003) # => "rd"
+ # ordinal(-11) # => "th"
+ # ordinal(-1021) # => "st"
+ def ordinal(number)
+ if (11..13).include?(number.to_i.abs % 100)
+ "th"
+ else
+ case number.to_i.abs % 10
+ when 1; "st"
+ when 2; "nd"
+ when 3; "rd"
+ else "th"
+ end
+ end
+ end
+
+ # Turns a number into an ordinal string used to denote the position in an
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # ordinalize(1) # => "1st"
+ # ordinalize(2) # => "2nd"
+ # ordinalize(1002) # => "1002nd"
+ # ordinalize(1003) # => "1003rd"
+ # ordinalize(-11) # => "-11th"
+ # ordinalize(-1021) # => "-1021st"
+ def ordinalize(number)
+ "#{number}#{ordinal(number)}"
+ end
+
+ private
+
+ # Mount a regular expression that will match part by part of the constant.
+ # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
+ def const_regexp(camel_cased_word) #:nodoc:
+ parts = camel_cased_word.split("::")
+ last = parts.pop
+
+ parts.reverse.inject(last) do |acc, part|
+ part.empty? ? acc : "#{part}(::#{acc})?"
+ end
+ end
+
+ # Applies inflection rules for +singularize+ and +pluralize+.
+ #
+ # apply_inflections("post", inflections.plurals) # => "posts"
+ # apply_inflections("posts", inflections.singulars) # => "post"
+ def apply_inflections(word, rules)
+ result = word.to_s.dup
+
+ if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
+ result
+ else
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
+ result
+ end
+ end
+ end
+end
diff --git a/samples/ruby/jekyll.rb b/samples/ruby/jekyll.rb
new file mode 100644
index 0000000..fd455fb
--- /dev/null
+++ b/samples/ruby/jekyll.rb
@@ -0,0 +1,138 @@
+$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
+
+# Require all of the Ruby files in the given directory.
+#
+# path - The String relative path from here to the directory.
+#
+# Returns nothing.
+def require_all(path)
+ glob = File.join(File.dirname(__FILE__), path, '*.rb')
+ Dir[glob].each do |f|
+ require f
+ end
+end
+
+# rubygems
+require 'rubygems'
+
+# stdlib
+require 'fileutils'
+require 'time'
+require 'yaml'
+require 'English'
+
+# 3rd party
+require 'liquid'
+require 'maruku'
+require 'albino'
+
+# internal requires
+require 'jekyll/core_ext'
+require 'jekyll/site'
+require 'jekyll/convertible'
+require 'jekyll/layout'
+require 'jekyll/page'
+require 'jekyll/post'
+require 'jekyll/filters'
+require 'jekyll/static_file'
+require 'jekyll/errors'
+
+# extensions
+require 'jekyll/plugin'
+require 'jekyll/converter'
+require 'jekyll/generator'
+require_all 'jekyll/converters'
+require_all 'jekyll/generators'
+require_all 'jekyll/tags'
+
+module Jekyll
+ VERSION = '0.11.2'
+
+ # Default options. Overriden by values in _config.yml or command-line opts.
+ # (Strings rather symbols used for compatability with YAML).
+ DEFAULTS = {
+ 'safe' => false,
+ 'auto' => false,
+ 'server' => false,
+ 'server_port' => 4000,
+
+ 'source' => Dir.pwd,
+ 'destination' => File.join(Dir.pwd, '_site'),
+ 'plugins' => File.join(Dir.pwd, '_plugins'),
+
+ 'future' => true,
+ 'lsi' => false,
+ 'pygments' => false,
+ 'markdown' => 'maruku',
+ 'permalink' => 'date',
+ 'include' => ['.htaccess'],
+ 'paginate_path' => 'page:num',
+
+ 'markdown_ext' => 'markdown,mkd,mkdn,md',
+ 'textile_ext' => 'textile',
+
+ 'maruku' => {
+ 'use_tex' => false,
+ 'use_divs' => false,
+ 'png_engine' => 'blahtex',
+ 'png_dir' => 'images/latex',
+ 'png_url' => '/images/latex'
+ },
+ 'rdiscount' => {
+ 'extensions' => []
+ },
+ 'redcarpet' => {
+ 'extensions' => []
+ },
+ 'kramdown' => {
+ 'auto_ids' => true,
+ 'footnote_nr' => 1,
+ 'entity_output' => 'as_char',
+ 'toc_levels' => '1..6',
+ 'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
+ 'use_coderay' => false,
+
+ 'coderay' => {
+ 'coderay_wrap' => 'div',
+ 'coderay_line_numbers' => 'inline',
+ 'coderay_line_number_start' => 1,
+ 'coderay_tab_width' => 4,
+ 'coderay_bold_every' => 10,
+ 'coderay_css' => 'style'
+ }
+ },
+ 'redcloth' => {
+ 'hard_breaks' => true
+ }
+ }
+
+ # Public: Generate a Jekyll configuration Hash by merging the default
+ # options with anything in _config.yml, and adding the given options on top.
+ #
+ # override - A Hash of config directives that override any options in both
+ # the defaults and the config file. See Jekyll::DEFAULTS for a
+ # list of option names and their defaults.
+ #
+ # Returns the final configuration Hash.
+ def self.configuration(override)
+ # _config.yml may override default source location, but until
+ # then, we need to know where to look for _config.yml
+ source = override['source'] || Jekyll::DEFAULTS['source']
+
+ # Get configuration from <source>/_config.yml
+ config_file = File.join(source, '_config.yml')
+ begin
+ config = YAML.load_file(config_file)
+ raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
+ $stdout.puts "Configuration from #{config_file}"
+ rescue => err
+ $stderr.puts "WARNING: Could not read configuration. " +
+ "Using defaults (and options)."
+ $stderr.puts "\t" + err.to_s
+ config = {}
+ end
+
+ # Merge DEFAULTS < _config.yml < override
+ Jekyll::DEFAULTS.deep_merge(config).deep_merge(override)
+ end
+end
diff --git a/samples/ruby/macruby-script b/samples/ruby/macruby-script
new file mode 100755
index 0000000..491be11
--- /dev/null
+++ b/samples/ruby/macruby-script
@@ -0,0 +1,2 @@
+#!/usr/bin/env macruby
+puts "MacRuby"
diff --git a/samples/ruby/resque.rb b/samples/ruby/resque.rb
new file mode 100644
index 0000000..1e36524
--- /dev/null
+++ b/samples/ruby/resque.rb
@@ -0,0 +1,385 @@
+require 'redis/namespace'
+
+require 'resque/version'
+
+require 'resque/errors'
+
+require 'resque/failure'
+require 'resque/failure/base'
+
+require 'resque/helpers'
+require 'resque/stat'
+require 'resque/job'
+require 'resque/worker'
+require 'resque/plugin'
+require 'resque/queue'
+require 'resque/multi_queue'
+require 'resque/coder'
+require 'resque/multi_json_coder'
+
+module Resque
+ include Helpers
+ extend self
+
+ # Accepts:
+ # 1. A 'hostname:port' String
+ # 2. A 'hostname:port:db' String (to select the Redis db)
+ # 3. A 'hostname:port/namespace' String (to set the Redis namespace)
+ # 4. A Redis URL String 'redis://host:port'
+ # 5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
+ # or `Redis::Namespace`.
+ def redis=(server)
+ case server
+ when String
+ if server =~ /redis\:\/\//
+ redis = Redis.connect(:url => server, :thread_safe => true)
+ else
+ server, namespace = server.split('/', 2)
+ host, port, db = server.split(':')
+ redis = Redis.new(:host => host, :port => port,
+ :thread_safe => true, :db => db)
+ end
+ namespace ||= :resque
+
+ @redis = Redis::Namespace.new(namespace, :redis => redis)
+ when Redis::Namespace
+ @redis = server
+ else
+ @redis = Redis::Namespace.new(:resque, :redis => server)
+ end
+ @queues = Hash.new { |h,name|
+ h[name] = Resque::Queue.new(name, @redis, coder)
+ }
+ end
+
+ # Encapsulation of encode/decode. Overwrite this to use it across Resque.
+ # This defaults to MultiJson for backwards compatibilty.
+ def coder
+ @coder ||= MultiJsonCoder.new
+ end
+ attr_writer :coder
+
+ # Returns the current Redis connection. If none has been created, will
+ # create a new one.
+ def redis
+ return @redis if @redis
+ self.redis = Redis.respond_to?(:connect) ? Redis.connect : "localhost:6379"
+ self.redis
+ end
+
+ def redis_id
+ # support 1.x versions of redis-rb
+ if redis.respond_to?(:server)
+ redis.server
+ elsif redis.respond_to?(:nodes) # distributed
+ redis.nodes.map { |n| n.id }.join(', ')
+ else
+ redis.client.id
+ end
+ end
+
+ # The `before_first_fork` hook will be run in the **parent** process
+ # only once, before forking to run the first job. Be careful- any
+ # changes you make will be permanent for the lifespan of the
+ # worker.
+ #
+ # Call with a block to set the hook.
+ # Call with no arguments to return the hook.
+ def before_first_fork(&block)
+ block ? (@before_first_fork = block) : @before_first_fork
+ end
+
+ # Set a proc that will be called in the parent process before the
+ # worker forks for the first time.
+ attr_writer :before_first_fork
+
+ # The `before_fork` hook will be run in the **parent** process
+ # before every job, so be careful- any changes you make will be
+ # permanent for the lifespan of the worker.
+ #
+ # Call with a block to set the hook.
+ # Call with no arguments to return the hook.
+ def before_fork(&block)
+ block ? (@before_fork = block) : @before_fork
+ end
+
+ # Set the before_fork proc.
+ attr_writer :before_fork
+
+ # The `after_fork` hook will be run in the child process and is passed
+ # the current job. Any changes you make, therefore, will only live as
+ # long as the job currently being processed.
+ #
+ # Call with a block to set the hook.
+ # Call with no arguments to return the hook.
+ def after_fork(&block)
+ block ? (@after_fork = block) : @after_fork
+ end
+
+ # Set the after_fork proc.
+ attr_writer :after_fork
+
+ def to_s
+ "Resque Client connected to #{redis_id}"
+ end
+
+ attr_accessor :inline
+
+ # If 'inline' is true Resque will call #perform method inline
+ # without queuing it into Redis and without any Resque callbacks.
+ # The 'inline' is false Resque jobs will be put in queue regularly.
+ alias :inline? :inline
+
+ #
+ # queue manipulation
+ #
+
+ # Pushes a job onto a queue. Queue name should be a string and the
+ # item should be any JSON-able Ruby object.
+ #
+ # Resque works generally expect the `item` to be a hash with the following
+ # keys:
+ #
+ # class - The String name of the job to run.
+ # args - An Array of arguments to pass the job. Usually passed
+ # via `class.to_class.perform(*args)`.
+ #
+ # Example
+ #
+ # Resque.push('archive', :class => 'Archive', :args => [ 35, 'tar' ])
+ #
+ # Returns nothing
+ def push(queue, item)
+ queue(queue) << item
+ end
+
+ # Pops a job off a queue. Queue name should be a string.
+ #
+ # Returns a Ruby object.
+ def pop(queue)
+ begin
+ queue(queue).pop(true)
+ rescue ThreadError
+ nil
+ end
+ end
+
+ # Returns an integer representing the size of a queue.
+ # Queue name should be a string.
+ def size(queue)
+ queue(queue).size
+ end
+
+ # Returns an array of items currently queued. Queue name should be
+ # a string.
+ #
+ # start and count should be integer and can be used for pagination.
+ # start is the item to begin, count is how many items to return.
+ #
+ # To get the 3rd page of a 30 item, paginatied list one would use:
+ # Resque.peek('my_list', 59, 30)
+ def peek(queue, start = 0, count = 1)
+ queue(queue).slice start, count
+ end
+
+ # Does the dirty work of fetching a range of items from a Redis list
+ # and converting them into Ruby objects.
+ def list_range(key, start = 0, count = 1)
+ if count == 1
+ decode redis.lindex(key, start)
+ else
+ Array(redis.lrange(key, start, start+count-1)).map do |item|
+ decode item
+ end
+ end
+ end
+
+ # Returns an array of all known Resque queues as strings.
+ def queues
+ Array(redis.smembers(:queues))
+ end
+
+ # Given a queue name, completely deletes the queue.
+ def remove_queue(queue)
+ queue(queue).destroy
+ @queues.delete(queue.to_s)
+ end
+
+ # Return the Resque::Queue object for a given name
+ def queue(name)
+ @queues[name.to_s]
+ end
+
+
+ #
+ # job shortcuts
+ #
+
+ # This method can be used to conveniently add a job to a queue.
+ # It assumes the class you're passing it is a real Ruby class (not
+ # a string or reference) which either:
+ #
+ # a) has a @queue ivar set
+ # b) responds to `queue`
+ #
+ # If either of those conditions are met, it will use the value obtained
+ # from performing one of the above operations to determine the queue.
+ #
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
+ #
+ # Returns true if the job was queued, nil if the job was rejected by a
+ # before_enqueue hook.
+ #
+ # This method is considered part of the `stable` API.
+ def enqueue(klass, *args)
+ enqueue_to(queue_from_class(klass), klass, *args)
+ end
+
+ # Just like `enqueue` but allows you to specify the queue you want to
+ # use. Runs hooks.
+ #
+ # `queue` should be the String name of the queue you're targeting.
+ #
+ # Returns true if the job was queued, nil if the job was rejected by a
+ # before_enqueue hook.
+ #
+ # This method is considered part of the `stable` API.
+ def enqueue_to(queue, klass, *args)
+ # Perform before_enqueue hooks. Don't perform enqueue if any hook returns false
+ before_hooks = Plugin.before_enqueue_hooks(klass).collect do |hook|
+ klass.send(hook, *args)
+ end
+ return nil if before_hooks.any? { |result| result == false }
+
+ Job.create(queue, klass, *args)
+
+ Plugin.after_enqueue_hooks(klass).each do |hook|
+ klass.send(hook, *args)
+ end
+
+ return true
+ end
+
+ # This method can be used to conveniently remove a job from a queue.
+ # It assumes the class you're passing it is a real Ruby class (not
+ # a string or reference) which either:
+ #
+ # a) has a @queue ivar set
+ # b) responds to `queue`
+ #
+ # If either of those conditions are met, it will use the value obtained
+ # from performing one of the above operations to determine the queue.
+ #
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
+ #
+ # If no args are given, this method will dequeue *all* jobs matching
+ # the provided class. See `Resque::Job.destroy` for more
+ # information.
+ #
+ # Returns the number of jobs destroyed.
+ #
+ # Example:
+ #
+ # # Removes all jobs of class `UpdateNetworkGraph`
+ # Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph)
+ #
+ # # Removes all jobs of class `UpdateNetworkGraph` with matching args.
+ # Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph, 'repo:135325')
+ #
+ # This method is considered part of the `stable` API.
+ def dequeue(klass, *args)
+ # Perform before_dequeue hooks. Don't perform dequeue if any hook returns false
+ before_hooks = Plugin.before_dequeue_hooks(klass).collect do |hook|
+ klass.send(hook, *args)
+ end
+ return if before_hooks.any? { |result| result == false }
+
+ Job.destroy(queue_from_class(klass), klass, *args)
+
+ Plugin.after_dequeue_hooks(klass).each do |hook|
+ klass.send(hook, *args)
+ end
+ end
+
+ # Given a class, try to extrapolate an appropriate queue based on a
+ # class instance variable or `queue` method.
+ def queue_from_class(klass)
+ klass.instance_variable_get(:@queue) ||
+ (klass.respond_to?(:queue) and klass.queue)
+ end
+
+ # This method will return a `Resque::Job` object or a non-true value
+ # depending on whether a job can be obtained. You should pass it the
+ # precise name of a queue: case matters.
+ #
+ # This method is considered part of the `stable` API.
+ def reserve(queue)
+ Job.reserve(queue)
+ end
+
+ # Validates if the given klass could be a valid Resque job
+ #
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
+ #
+ # If given klass is nil this method will raise a `Resque::NoClassError`
+ def validate(klass, queue = nil)
+ queue ||= queue_from_class(klass)
+
+ if !queue
+ raise NoQueueError.new("Jobs must be placed onto a queue.")
+ end
+
+ if klass.to_s.empty?
+ raise NoClassError.new("Jobs must be given a class.")
+ end
+ end
+
+
+ #
+ # worker shortcuts
+ #
+
+ # A shortcut to Worker.all
+ def workers
+ Worker.all
+ end
+
+ # A shortcut to Worker.working
+ def working
+ Worker.working
+ end
+
+ # A shortcut to unregister_worker
+ # useful for command line tool
+ def remove_worker(worker_id)
+ worker = Resque::Worker.find(worker_id)
+ worker.unregister_worker
+ end
+
+ #
+ # stats
+ #
+
+ # Returns a hash, similar to redis-rb's #info, of interesting stats.
+ def info
+ return {
+ :pending => queues.inject(0) { |m,k| m + size(k) },
+ :processed => Stat[:processed],
+ :queues => queues.size,
+ :workers => workers.size.to_i,
+ :working => working.size,
+ :failed => Stat[:failed],
+ :servers => [redis_id],
+ :environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
+ }
+ end
+
+ # Returns an array of all known Resque keys in Redis. Redis' KEYS operation
+ # is O(N) for the keyspace, so be careful - this can be slow for big databases.
+ def keys
+ redis.keys("*").map do |key|
+ key.sub("#{redis.namespace}:", '')
+ end
+ end
+end
+
diff --git a/samples/ruby/script.rake b/samples/ruby/script.rake
new file mode 100755
index 0000000..96992aa
--- /dev/null
+++ b/samples/ruby/script.rake
@@ -0,0 +1,4 @@
+#!/usr/bin/env rake
+task :default do
+ puts "Rake"
+end
diff --git a/samples/ruby/script.rb b/samples/ruby/script.rb
new file mode 100755
index 0000000..01a30f8
--- /dev/null
+++ b/samples/ruby/script.rb
@@ -0,0 +1,2 @@
+#!/usr/bin/env ruby
+puts "Ruby"
diff --git a/samples/ruby/script2.rb b/samples/ruby/script2.rb
new file mode 100644
index 0000000..40aeb7c
--- /dev/null
+++ b/samples/ruby/script2.rb
@@ -0,0 +1,2 @@
+#! /usr/bin/env ruby -w -Ilib:test
+echo "Ruby"
diff --git a/samples/ruby/sinatra.rb b/samples/ruby/sinatra.rb
new file mode 100644
index 0000000..36ec2a2
--- /dev/null
+++ b/samples/ruby/sinatra.rb
@@ -0,0 +1,1775 @@
+# external dependencies
+require 'rack'
+require 'tilt'
+require "rack/protection"
+
+# stdlib dependencies
+require 'thread'
+require 'time'
+require 'uri'
+
+# other files we need
+require 'sinatra/showexceptions'
+require 'sinatra/version'
+
+module Sinatra
+ # The request object. See Rack::Request for more info:
+ # http://rack.rubyforge.org/doc/classes/Rack/Request.html
+ class Request < Rack::Request
+ # Returns an array of acceptable media types for the response
+ def accept
+ @env['sinatra.accept'] ||= begin
+ entries = @env['HTTP_ACCEPT'].to_s.split(',')
+ entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
+ end
+ end
+
+ def preferred_type(*types)
+ return accept.first if types.empty?
+ types.flatten!
+ accept.detect do |pattern|
+ type = types.detect { |t| File.fnmatch(pattern, t) }
+ return type if type
+ end
+ end
+
+ alias accept? preferred_type
+ alias secure? ssl?
+
+ def forwarded?
+ @env.include? "HTTP_X_FORWARDED_HOST"
+ end
+
+ def safe?
+ get? or head? or options? or trace?
+ end
+
+ def idempotent?
+ safe? or put? or delete?
+ end
+
+ private
+
+ def accept_entry(entry)
+ type, *options = entry.delete(' ').split(';')
+ quality = 0 # we sort smallest first
+ options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
+ [type, [quality, type.count('*'), 1 - options.size]]
+ end
+ end
+
+ # The response object. See Rack::Response and Rack::ResponseHelpers for
+ # more info:
+ # http://rack.rubyforge.org/doc/classes/Rack/Response.html
+ # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
+ class Response < Rack::Response
+ def body=(value)
+ value = value.body while Rack::Response === value
+ @body = String === value ? [value.to_str] : value
+ end
+
+ def each
+ block_given? ? super : enum_for(:each)
+ end
+
+ def finish
+ if status.to_i / 100 == 1
+ headers.delete "Content-Length"
+ headers.delete "Content-Type"
+ elsif Array === body and not [204, 304].include?(status.to_i)
+ # if some other code has already set Content-Length, don't muck with it
+ # currently, this would be the static file-handler
+ headers["Content-Length"] ||= body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
+ end
+
+ # Rack::Response#finish sometimes returns self as response body. We don't want that.
+ status, headers, result = super
+ result = body if result == self
+ [status, headers, result]
+ end
+ end
+
+ # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
+ # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
+ # This middleware will detect an extended body object and will make sure it reaches the
+ # handler directly. We do this here, so our middleware and middleware set up by the app will
+ # still be able to run.
+ class ExtendedRack < Struct.new(:app)
+ def call(env)
+ result, callback = app.call(env), env['async.callback']
+ return result unless callback and async?(*result)
+ after_response { callback.call result }
+ setup_close(env, *result)
+ throw :async
+ end
+
+ private
+
+ def setup_close(env, status, header, body)
+ return unless body.respond_to? :close and env.include? 'async.close'
+ env['async.close'].callback { body.close }
+ env['async.close'].errback { body.close }
+ end
+
+ def after_response(&block)
+ raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
+ EventMachine.next_tick(&block)
+ end
+
+ def async?(status, headers, body)
+ return true if status == -1
+ body.respond_to? :callback and body.respond_to? :errback
+ end
+ end
+
+ # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
+ # if another CommonLogger is already in the middleware chane.
+ class CommonLogger < Rack::CommonLogger
+ def call(env)
+ env['sinatra.commonlogger'] ? @app.call(env) : super
+ end
+
+ superclass.class_eval do
+ alias call_without_check call unless method_defined? :call_without_check
+ def call(env)
+ env['sinatra.commonlogger'] = true
+ call_without_check(env)
+ end
+ end
+ end
+
+ class NotFound < NameError #:nodoc:
+ def http_status; 404 end
+ end
+
+ # Methods available to routes, before/after filters, and views.
+ module Helpers
+ # Set or retrieve the response status code.
+ def status(value=nil)
+ response.status = value if value
+ response.status
+ end
+
+ # Set or retrieve the response body. When a block is given,
+ # evaluation is deferred until the body is read with #each.
+ def body(value=nil, &block)
+ if block_given?
+ def block.each; yield(call) end
+ response.body = block
+ elsif value
+ response.body = value
+ else
+ response.body
+ end
+ end
+
+ # Halt processing and redirect to the URI provided.
+ def redirect(uri, *args)
+ if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
+ status 303
+ else
+ status 302
+ end
+
+ # According to RFC 2616 section 14.30, "the field value consists of a
+ # single absolute URI"
+ response['Location'] = uri(uri, settings.absolute_redirects?, settings.prefixed_redirects?)
+ halt(*args)
+ end
+
+ # Generates the absolute URI for a given path in the app.
+ # Takes Rack routers and reverse proxies into account.
+ def uri(addr = nil, absolute = true, add_script_name = true)
+ return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
+ uri = [host = ""]
+ if absolute
+ host << "http#{'s' if request.secure?}://"
+ if request.forwarded? or request.port != (request.secure? ? 443 : 80)
+ host << request.host_with_port
+ else
+ host << request.host
+ end
+ end
+ uri << request.script_name.to_s if add_script_name
+ uri << (addr ? addr : request.path_info).to_s
+ File.join uri
+ end
+
+ alias url uri
+ alias to uri
+
+ # Halt processing and return the error status provided.
+ def error(code, body=nil)
+ code, body = 500, code.to_str if code.respond_to? :to_str
+ response.body = body unless body.nil?
+ halt code
+ end
+
+ # Halt processing and return a 404 Not Found.
+ def not_found(body=nil)
+ error 404, body
+ end
+
+ # Set multiple response headers with Hash.
+ def headers(hash=nil)
+ response.headers.merge! hash if hash
+ response.headers
+ end
+
+ # Access the underlying Rack session.
+ def session
+ request.session
+ end
+
+ # Access shared logger object.
+ def logger
+ request.logger
+ end
+
+ # Look up a media type by file extension in Rack's mime registry.
+ def mime_type(type)
+ Base.mime_type(type)
+ end
+
+ # Set the Content-Type of the response body given a media type or file
+ # extension.
+ def content_type(type = nil, params={})
+ return response['Content-Type'] unless type
+ default = params.delete :default
+ mime_type = mime_type(type) || default
+ fail "Unknown media type: %p" % type if mime_type.nil?
+ mime_type = mime_type.dup
+ unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
+ params[:charset] = params.delete('charset') || settings.default_encoding
+ end
+ params.delete :charset if mime_type.include? 'charset'
+ unless params.empty?
+ mime_type << (mime_type.include?(';') ? ', ' : ';')
+ mime_type << params.map { |kv| kv.join('=') }.join(', ')
+ end
+ response['Content-Type'] = mime_type
+ end
+
+ # Set the Content-Disposition to "attachment" with the specified filename,
+ # instructing the user agents to prompt to save.
+ def attachment(filename=nil)
+ response['Content-Disposition'] = 'attachment'
+ if filename
+ params = '; filename="%s"' % File.basename(filename)
+ response['Content-Disposition'] << params
+ ext = File.extname(filename)
+ content_type(ext) unless response['Content-Type'] or ext.empty?
+ end
+ end
+
+ # Use the contents of the file at +path+ as the response body.
+ def send_file(path, opts={})
+ if opts[:type] or not response['Content-Type']
+ content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
+ end
+
+ if opts[:disposition] == 'attachment' || opts[:filename]
+ attachment opts[:filename] || path
+ elsif opts[:disposition] == 'inline'
+ response['Content-Disposition'] = 'inline'
+ end
+
+ last_modified opts[:last_modified] if opts[:last_modified]
+
+ file = Rack::File.new nil
+ file.path = path
+ result = file.serving env
+ result[1].each { |k,v| headers[k] ||= v }
+ headers['Content-Length'] = result[1]['Content-Length']
+ halt opts[:status] || result[0], result[2]
+ rescue Errno::ENOENT
+ not_found
+ end
+
+ # Class of the response body in case you use #stream.
+ #
+ # Three things really matter: The front and back block (back being the
+ # blog generating content, front the one sending it to the client) and
+ # the scheduler, integrating with whatever concurrency feature the Rack
+ # handler is using.
+ #
+ # Scheduler has to respond to defer and schedule.
+ class Stream
+ def self.schedule(*) yield end
+ def self.defer(*) yield end
+
+ def initialize(scheduler = self.class, keep_open = false, &back)
+ @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
+ @callbacks, @closed = [], false
+ end
+
+ def close
+ return if @closed
+ @closed = true
+ @scheduler.schedule { @callbacks.each { |c| c.call }}
+ end
+
+ def each(&front)
+ @front = front
+ @scheduler.defer do
+ begin
+ @back.call(self)
+ rescue Exception => e
+ @scheduler.schedule { raise e }
+ end
+ close unless @keep_open
+ end
+ end
+
+ def <<(data)
+ @scheduler.schedule { @front.call(data.to_s) }
+ self
+ end
+
+ def callback(&block)
+ return yield if @closed
+ @callbacks << block
+ end
+
+ alias errback callback
+ end
+
+ # Allows to start sending data to the client even though later parts of
+ # the response body have not yet been generated.
+ #
+ # The close parameter specifies whether Stream#close should be called
+ # after the block has been executed. This is only relevant for evented
+ # servers like Thin or Rainbows.
+ def stream(keep_open = false)
+ scheduler = env['async.callback'] ? EventMachine : Stream
+ current = @params.dup
+ body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
+ end
+
+ # Specify response freshness policy for HTTP caches (Cache-Control header).
+ # Any number of non-value directives (:public, :private, :no_cache,
+ # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
+ # a Hash of value directives (:max_age, :min_stale, :s_max_age).
+ #
+ # cache_control :public, :must_revalidate, :max_age => 60
+ # => Cache-Control: public, must-revalidate, max-age=60
+ #
+ # See RFC 2616 / 14.9 for more on standard cache control directives:
+ # http://tools.ietf.org/html/rfc2616#section-14.9.1
+ def cache_control(*values)
+ if values.last.kind_of?(Hash)
+ hash = values.pop
+ hash.reject! { |k,v| v == false }
+ hash.reject! { |k,v| values << k if v == true }
+ else
+ hash = {}
+ end
+
+ values.map! { |value| value.to_s.tr('_','-') }
+ hash.each do |key, value|
+ key = key.to_s.tr('_', '-')
+ value = value.to_i if key == "max-age"
+ values << [key, value].join('=')
+ end
+
+ response['Cache-Control'] = values.join(', ') if values.any?
+ end
+
+ # Set the Expires header and Cache-Control/max-age directive. Amount
+ # can be an integer number of seconds in the future or a Time object
+ # indicating when the response should be considered "stale". The remaining
+ # "values" arguments are passed to the #cache_control helper:
+ #
+ # expires 500, :public, :must_revalidate
+ # => Cache-Control: public, must-revalidate, max-age=60
+ # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
+ #
+ def expires(amount, *values)
+ values << {} unless values.last.kind_of?(Hash)
+
+ if amount.is_a? Integer
+ time = Time.now + amount.to_i
+ max_age = amount
+ else
+ time = time_for amount
+ max_age = time - Time.now
+ end
+
+ values.last.merge!(:max_age => max_age)
+ cache_control(*values)
+
+ response['Expires'] = time.httpdate
+ end
+
+ # Set the last modified time of the resource (HTTP 'Last-Modified' header)
+ # and halt if conditional GET matches. The +time+ argument is a Time,
+ # DateTime, or other object that responds to +to_time+.
+ #
+ # When the current request includes an 'If-Modified-Since' header that is
+ # equal or later than the time specified, execution is immediately halted
+ # with a '304 Not Modified' response.
+ def last_modified(time)
+ return unless time
+ time = time_for time
+ response['Last-Modified'] = time.httpdate
+ return if env['HTTP_IF_NONE_MATCH']
+
+ if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
+ # compare based on seconds since epoch
+ since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
+ halt 304 if since >= time.to_i
+ end
+
+ if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
+ # compare based on seconds since epoch
+ since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
+ halt 412 if since < time.to_i
+ end
+ rescue ArgumentError
+ end
+
+ # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
+ # GET matches. The +value+ argument is an identifier that uniquely
+ # identifies the current version of the resource. The +kind+ argument
+ # indicates whether the etag should be used as a :strong (default) or :weak
+ # cache validator.
+ #
+ # When the current request includes an 'If-None-Match' header with a
+ # matching etag, execution is immediately halted. If the request method is
+ # GET or HEAD, a '304 Not Modified' response is sent.
+ def etag(value, options = {})
+ # Before touching this code, please double check RFC 2616 14.24 and 14.26.
+ options = {:kind => options} unless Hash === options
+ kind = options[:kind] || :strong
+ new_resource = options.fetch(:new_resource) { request.post? }
+
+ unless [:strong, :weak].include?(kind)
+ raise ArgumentError, ":strong or :weak expected"
+ end
+
+ value = '"%s"' % value
+ value = 'W/' + value if kind == :weak
+ response['ETag'] = value
+
+ if success? or status == 304
+ if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
+ halt(request.safe? ? 304 : 412)
+ end
+
+ if env['HTTP_IF_MATCH']
+ halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
+ end
+ end
+ end
+
+ # Sugar for redirect (example: redirect back)
+ def back
+ request.referer
+ end
+
+ # whether or not the status is set to 1xx
+ def informational?
+ status.between? 100, 199
+ end
+
+ # whether or not the status is set to 2xx
+ def success?
+ status.between? 200, 299
+ end
+
+ # whether or not the status is set to 3xx
+ def redirect?
+ status.between? 300, 399
+ end
+
+ # whether or not the status is set to 4xx
+ def client_error?
+ status.between? 400, 499
+ end
+
+ # whether or not the status is set to 5xx
+ def server_error?
+ status.between? 500, 599
+ end
+
+ # whether or not the status is set to 404
+ def not_found?
+ status == 404
+ end
+
+ # Generates a Time object from the given value.
+ # Used by #expires and #last_modified.
+ def time_for(value)
+ if value.respond_to? :to_time
+ value.to_time
+ elsif value.is_a? Time
+ value
+ elsif value.respond_to? :new_offset
+ # DateTime#to_time does the same on 1.9
+ d = value.new_offset 0
+ t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
+ t.getlocal
+ elsif value.respond_to? :mday
+ # Date#to_time does the same on 1.9
+ Time.local(value.year, value.mon, value.mday)
+ elsif value.is_a? Numeric
+ Time.at value
+ else
+ Time.parse value.to_s
+ end
+ rescue ArgumentError => boom
+ raise boom
+ rescue Exception
+ raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
+ end
+
+ private
+
+ # Helper method checking if a ETag value list includes the current ETag.
+ def etag_matches?(list, new_resource = request.post?)
+ return !new_resource if list == '*'
+ list.to_s.split(/\s*,\s*/).include? response['ETag']
+ end
+
+ def with_params(temp_params)
+ original, @params = @params, temp_params
+ yield
+ ensure
+ @params = original if original
+ end
+ end
+
+ private
+
+ # Template rendering methods. Each method takes the name of a template
+ # to render as a Symbol and returns a String with the rendered output,
+ # as well as an optional hash with additional options.
+ #
+ # `template` is either the name or path of the template as symbol
+ # (Use `:'subdir/myview'` for views in subdirectories), or a string
+ # that will be rendered.
+ #
+ # Possible options are:
+ # :content_type The content type to use, same arguments as content_type.
+ # :layout If set to false, no layout is rendered, otherwise
+ # the specified layout is used (Ignored for `sass` and `less`)
+ # :layout_engine Engine to use for rendering the layout.
+ # :locals A hash with local variables that should be available
+ # in the template
+ # :scope If set, template is evaluate with the binding of the given
+ # object rather than the application instance.
+ # :views Views directory to use.
+ module Templates
+ module ContentTyped
+ attr_accessor :content_type
+ end
+
+ def initialize
+ super
+ @default_layout = :layout
+ end
+
+ def erb(template, options={}, locals={})
+ render :erb, template, options, locals
+ end
+
+ def erubis(template, options={}, locals={})
+ warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
+ "If you have Erubis installed, it will be used automatically."
+ render :erubis, template, options, locals
+ end
+
+ def haml(template, options={}, locals={})
+ render :haml, template, options, locals
+ end
+
+ def sass(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :sass, template, options, locals
+ end
+
+ def scss(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :scss, template, options, locals
+ end
+
+ def less(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :less, template, options, locals
+ end
+
+ def builder(template=nil, options={}, locals={}, &block)
+ options[:default_content_type] = :xml
+ render_ruby(:builder, template, options, locals, &block)
+ end
+
+ def liquid(template, options={}, locals={})
+ render :liquid, template, options, locals
+ end
+
+ def markdown(template, options={}, locals={})
+ render :markdown, template, options, locals
+ end
+
+ def textile(template, options={}, locals={})
+ render :textile, template, options, locals
+ end
+
+ def rdoc(template, options={}, locals={})
+ render :rdoc, template, options, locals
+ end
+
+ def radius(template, options={}, locals={})
+ render :radius, template, options, locals
+ end
+
+ def markaby(template=nil, options={}, locals={}, &block)
+ render_ruby(:mab, template, options, locals, &block)
+ end
+
+ def coffee(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :js
+ render :coffee, template, options, locals
+ end
+
+ def nokogiri(template=nil, options={}, locals={}, &block)
+ options[:default_content_type] = :xml
+ render_ruby(:nokogiri, template, options, locals, &block)
+ end
+
+ def slim(template, options={}, locals={})
+ render :slim, template, options, locals
+ end
+
+ def creole(template, options={}, locals={})
+ render :creole, template, options, locals
+ end
+
+ def yajl(template, options={}, locals={})
+ options[:default_content_type] = :json
+ render :yajl, template, options, locals
+ end
+
+ # Calls the given block for every possible template file in views,
+ # named name.ext, where ext is registered on engine.
+ def find_template(views, name, engine)
+ yield ::File.join(views, "#{name}.#{@preferred_extension}")
+ Tilt.mappings.each do |ext, engines|
+ next unless ext != @preferred_extension and engines.include? engine
+ yield ::File.join(views, "#{name}.#{ext}")
+ end
+ end
+
+ private
+ # logic shared between builder and nokogiri
+ def render_ruby(engine, template, options={}, locals={}, &block)
+ options, template = template, nil if template.is_a?(Hash)
+ template = Proc.new { block } if template.nil?
+ render engine, template, options, locals
+ end
+
+ def render(engine, data, options={}, locals={}, &block)
+ # merge app-level options
+ options = settings.send(engine).merge(options) if settings.respond_to?(engine)
+ options[:outvar] ||= '@_out_buf'
+ options[:default_encoding] ||= settings.default_encoding
+
+ # extract generic options
+ locals = options.delete(:locals) || locals || {}
+ views = options.delete(:views) || settings.views || "./views"
+ layout = options.delete(:layout)
+ eat_errors = layout.nil?
+ layout = @default_layout if layout.nil? or layout == true
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
+ layout_engine = options.delete(:layout_engine) || engine
+ scope = options.delete(:scope) || self
+
+ # compile and render template
+ begin
+ layout_was = @default_layout
+ @default_layout = false
+ template = compile_template(engine, data, options, views)
+ output = template.render(scope, locals, &block)
+ ensure
+ @default_layout = layout_was
+ end
+
+ # render layout
+ if layout
+ options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope)
+ catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
+ end
+
+ output.extend(ContentTyped).content_type = content_type if content_type
+ output
+ end
+
+ def compile_template(engine, data, options, views)
+ eat_errors = options.delete :eat_errors
+ template_cache.fetch engine, data, options do
+ template = Tilt[engine]
+ raise "Template engine not found: #{engine}" if template.nil?
+
+ case data
+ when Symbol
+ body, path, line = settings.templates[data]
+ if body
+ body = body.call if body.respond_to?(:call)
+ template.new(path, line.to_i, options) { body }
+ else
+ found = false
+ @preferred_extension = engine.to_s
+ find_template(views, data, template) do |file|
+ path ||= file # keep the initial path rather than the last one
+ if found = File.exists?(file)
+ path = file
+ break
+ end
+ end
+ throw :layout_missing if eat_errors and not found
+ template.new(path, 1, options)
+ end
+ when Proc, String
+ body = data.is_a?(String) ? Proc.new { data } : data
+ path, line = settings.caller_locations.first
+ template.new(path, line.to_i, options, &body)
+ else
+ raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
+ end
+ end
+ end
+ end
+
+ # Base class for all Sinatra applications and middleware.
+ class Base
+ include Rack::Utils
+ include Helpers
+ include Templates
+
+ attr_accessor :app
+ attr_reader :template_cache
+
+ def initialize(app=nil)
+ super()
+ @app = app
+ @template_cache = Tilt::Cache.new
+ yield self if block_given?
+ end
+
+ # Rack call interface.
+ def call(env)
+ dup.call!(env)
+ end
+
+ attr_accessor :env, :request, :response, :params
+
+ def call!(env) # :nodoc:
+ @env = env
+ @request = Request.new(env)
+ @response = Response.new
+ @params = indifferent_params(@request.params)
+ template_cache.clear if settings.reload_templates
+ force_encoding(@params)
+
+ @response['Content-Type'] = nil
+ invoke { dispatch! }
+ invoke { error_block!(response.status) }
+
+ unless @response['Content-Type']
+ if Array === body and body[0].respond_to? :content_type
+ content_type body[0].content_type
+ else
+ content_type :html
+ end
+ end
+
+ @response.finish
+ end
+
+ # Access settings defined with Base.set.
+ def self.settings
+ self
+ end
+
+ # Access settings defined with Base.set.
+ def settings
+ self.class.settings
+ end
+
+ def options
+ warn "Sinatra::Base#options is deprecated and will be removed, " \
+ "use #settings instead."
+ settings
+ end
+
+ # Exit the current block, halts any further processing
+ # of the request, and returns the specified response.
+ def halt(*response)
+ response = response.first if response.length == 1
+ throw :halt, response
+ end
+
+ # Pass control to the next matching route.
+ # If there are no more matching routes, Sinatra will
+ # return a 404 response.
+ def pass(&block)
+ throw :pass, block
+ end
+
+ # Forward the request to the downstream app -- middleware only.
+ def forward
+ fail "downstream app not set" unless @app.respond_to? :call
+ status, headers, body = @app.call env
+ @response.status = status
+ @response.body = body
+ @response.headers.merge! headers
+ nil
+ end
+
+ private
+ # Run filters defined on the class and all superclasses.
+ def filter!(type, base = settings)
+ filter! type, base.superclass if base.superclass.respond_to?(:filters)
+ base.filters[type].each { |args| process_route(*args) }
+ end
+
+ # Run routes defined on the class and all superclasses.
+ def route!(base = settings, pass_block=nil)
+ if routes = base.routes[@request.request_method]
+ routes.each do |pattern, keys, conditions, block|
+ pass_block = process_route(pattern, keys, conditions) do |*args|
+ route_eval { block[*args] }
+ end
+ end
+ end
+
+ # Run routes defined in superclass.
+ if base.superclass.respond_to?(:routes)
+ return route!(base.superclass, pass_block)
+ end
+
+ route_eval(&pass_block) if pass_block
+ route_missing
+ end
+
+ # Run a route block and throw :halt with the result.
+ def route_eval
+ throw :halt, yield
+ end
+
+ # If the current request matches pattern and conditions, fill params
+ # with keys and call the given block.
+ # Revert params afterwards.
+ #
+ # Returns pass block.
+ def process_route(pattern, keys, conditions, block = nil, values = [])
+ route = @request.path_info
+ route = '/' if route.empty? and not settings.empty_path_info?
+ return unless match = pattern.match(route)
+ values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
+
+ if values.any?
+ original, @params = params, params.merge('splat' => [], 'captures' => values)
+ keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
+ end
+
+ catch(:pass) do
+ conditions.each { |c| throw :pass if c.bind(self).call == false }
+ block ? block[self, values] : yield(self, values)
+ end
+ ensure
+ @params = original if original
+ end
+
+ # No matching route was found or all routes passed. The default
+ # implementation is to forward the request downstream when running
+ # as middleware (@app is non-nil); when no downstream app is set, raise
+ # a NotFound exception. Subclasses can override this method to perform
+ # custom route miss logic.
+ def route_missing
+ if @app
+ forward
+ else
+ raise NotFound
+ end
+ end
+
+ # Attempt to serve static files from public directory. Throws :halt when
+ # a matching file is found, returns nil otherwise.
+ def static!
+ return if (public_dir = settings.public_folder).nil?
+ public_dir = File.expand_path(public_dir)
+
+ path = File.expand_path(public_dir + unescape(request.path_info))
+ return unless path.start_with?(public_dir) and File.file?(path)
+
+ env['sinatra.static_file'] = path
+ cache_control(*settings.static_cache_control) if settings.static_cache_control?
+ send_file path, :disposition => nil
+ end
+
+ # Enable string or symbol key access to the nested params hash.
+ def indifferent_params(object)
+ case object
+ when Hash
+ new_hash = indifferent_hash
+ object.each { |key, value| new_hash[key] = indifferent_params(value) }
+ new_hash
+ when Array
+ object.map { |item| indifferent_params(item) }
+ else
+ object
+ end
+ end
+
+ # Creates a Hash with indifferent access.
+ def indifferent_hash
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
+ end
+
+ # Run the block with 'throw :halt' support and apply result to the response.
+ def invoke
+ res = catch(:halt) { yield }
+ res = [res] if Fixnum === res or String === res
+ if Array === res and Fixnum === res.first
+ status(res.shift)
+ body(res.pop)
+ headers(*res)
+ elsif res.respond_to? :each
+ body res
+ end
+ nil # avoid double setting the same response tuple twice
+ end
+
+ # Dispatch a request with error handling.
+ def dispatch!
+ invoke do
+ static! if settings.static? && (request.get? || request.head?)
+ filter! :before
+ route!
+ end
+ rescue ::Exception => boom
+ invoke { handle_exception!(boom) }
+ ensure
+ filter! :after unless env['sinatra.static_file']
+ end
+
+ # Error handling during requests.
+ def handle_exception!(boom)
+ @env['sinatra.error'] = boom
+
+ if boom.respond_to? :http_status
+ status(boom.http_status)
+ elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
+ status(boom.code)
+ else
+ status(500)
+ end
+
+ status(500) unless status.between? 400, 599
+
+ if server_error?
+ dump_errors! boom if settings.dump_errors?
+ raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
+ end
+
+ if not_found?
+ headers['X-Cascade'] = 'pass'
+ body '<h1>Not Found</h1>'
+ end
+
+ res = error_block!(boom.class, boom) || error_block!(status, boom)
+ return res if res or not server_error?
+ raise boom if settings.raise_errors? or settings.show_exceptions?
+ error_block! Exception, boom
+ end
+
+ # Find an custom error block for the key(s) specified.
+ def error_block!(key, *block_params)
+ base = settings
+ while base.respond_to?(:errors)
+ next base = base.superclass unless args_array = base.errors[key]
+ args_array.reverse_each do |args|
+ first = args == args_array.first
+ args += [block_params]
+ resp = process_route(*args)
+ return resp unless resp.nil? && !first
+ end
+ end
+ return false unless key.respond_to? :superclass and key.superclass < Exception
+ error_block!(key.superclass, *block_params)
+ end
+
+ def dump_errors!(boom)
+ msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
+ @env['rack.errors'].puts(msg)
+ end
+
+ class << self
+ attr_reader :routes, :filters, :templates, :errors
+
+ # Removes all routes, filters, middleware and extension hooks from the
+ # current class (not routes/filters/... defined by its superclass).
+ def reset!
+ @conditions = []
+ @routes = {}
+ @filters = {:before => [], :after => []}
+ @errors = {}
+ @middleware = []
+ @prototype = nil
+ @extensions = []
+
+ if superclass.respond_to?(:templates)
+ @templates = Hash.new { |hash,key| superclass.templates[key] }
+ else
+ @templates = {}
+ end
+ end
+
+ # Extension modules registered on this class and all superclasses.
+ def extensions
+ if superclass.respond_to?(:extensions)
+ (@extensions + superclass.extensions).uniq
+ else
+ @extensions
+ end
+ end
+
+ # Middleware used in this class and all superclasses.
+ def middleware
+ if superclass.respond_to?(:middleware)
+ superclass.middleware + @middleware
+ else
+ @middleware
+ end
+ end
+
+ # Sets an option to the given value. If the value is a proc,
+ # the proc will be called every time the option is accessed.
+ def set(option, value = (not_set = true), ignore_setter = false, &block)
+ raise ArgumentError if block and !not_set
+ value, not_set = block, false if block
+
+ if not_set
+ raise ArgumentError unless option.respond_to?(:each)
+ option.each { |k,v| set(k, v) }
+ return self
+ end
+
+ if respond_to?("#{option}=") and not ignore_setter
+ return __send__("#{option}=", value)
+ end
+
+ setter = proc { |val| set option, val, true }
+ getter = proc { value }
+
+ case value
+ when Proc
+ getter = value
+ when Symbol, Fixnum, FalseClass, TrueClass, NilClass
+ getter = value.inspect
+ when Hash
+ setter = proc do |val|
+ val = value.merge val if Hash === val
+ set option, val, true
+ end
+ end
+
+ define_singleton_method("#{option}=", setter) if setter
+ define_singleton_method(option, getter) if getter
+ define_singleton_method("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
+ self
+ end
+
+ # Same as calling `set :option, true` for each of the given options.
+ def enable(*opts)
+ opts.each { |key| set(key, true) }
+ end
+
+ # Same as calling `set :option, false` for each of the given options.
+ def disable(*opts)
+ opts.each { |key| set(key, false) }
+ end
+
+ # Define a custom error handler. Optionally takes either an Exception
+ # class, or an HTTP status code to specify which errors should be
+ # handled.
+ def error(*codes, &block)
+ args = compile! "ERROR", //, block
+ codes = codes.map { |c| Array(c) }.flatten
+ codes << Exception if codes.empty?
+ codes.each { |c| (@errors[c] ||= []) << args }
+ end
+
+ # Sugar for `error(404) { ... }`
+ def not_found(&block)
+ error 404, &block
+ end
+
+ # Define a named template. The block must return the template source.
+ def template(name, &block)
+ filename, line = caller_locations.first
+ templates[name] = [block, filename, line.to_i]
+ end
+
+ # Define the layout template. The block must return the template source.
+ def layout(name=:layout, &block)
+ template name, &block
+ end
+
+ # Load embeded templates from the file; uses the caller's __FILE__
+ # when no file is specified.
+ def inline_templates=(file=nil)
+ file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
+
+ begin
+ io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
+ app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
+ rescue Errno::ENOENT
+ app, data = nil
+ end
+
+ if data
+ if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
+ encoding = $2
+ else
+ encoding = settings.default_encoding
+ end
+ lines = app.count("\n") + 1
+ template = nil
+ force_encoding data, encoding
+ data.each_line do |line|
+ lines += 1
+ if line =~ /^@@\s*(.*\S)\s*$/
+ template = force_encoding('', encoding)
+ templates[$1.to_sym] = [template, file, lines]
+ elsif template
+ template << line
+ end
+ end
+ end
+ end
+
+ # Lookup or register a mime type in Rack's mime registry.
+ def mime_type(type, value=nil)
+ return type if type.nil? || type.to_s.include?('/')
+ type = ".#{type}" unless type.to_s[0] == ?.
+ return Rack::Mime.mime_type(type, nil) unless value
+ Rack::Mime::MIME_TYPES[type] = value
+ end
+
+ # provides all mime types matching type, including deprecated types:
+ # mime_types :html # => ['text/html']
+ # mime_types :js # => ['application/javascript', 'text/javascript']
+ def mime_types(type)
+ type = mime_type type
+ type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
+ end
+
+ # Define a before filter; runs before all requests within the same
+ # context as route handlers and may access/modify the request and
+ # response.
+ def before(path = nil, options = {}, &block)
+ add_filter(:before, path, options, &block)
+ end
+
+ # Define an after filter; runs after all requests within the same
+ # context as route handlers and may access/modify the request and
+ # response.
+ def after(path = nil, options = {}, &block)
+ add_filter(:after, path, options, &block)
+ end
+
+ # add a filter
+ def add_filter(type, path = nil, options = {}, &block)
+ path, options = //, path if path.respond_to?(:each_pair)
+ filters[type] << compile!(type, path || //, block, options)
+ end
+
+ # Add a route condition. The route is considered non-matching when the
+ # block returns false.
+ def condition(name = "#{caller.first[/`.*'/]} condition", &block)
+ @conditions << generate_method(name, &block)
+ end
+
+ def public=(value)
+ warn ":public is no longer used to avoid overloading Module#public, use :public_dir instead"
+ set(:public_folder, value)
+ end
+
+ def public_dir=(value)
+ self.public_folder = value
+ end
+
+ def public_dir
+ public_folder
+ end
+
+ private
+ # Dynamically defines a method on settings.
+ def define_singleton_method(name, content = Proc.new)
+ # replace with call to singleton_class once we're 1.9 only
+ (class << self; self; end).class_eval do
+ undef_method(name) if method_defined? name
+ String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
+ end
+ end
+
+ # Condition for matching host name. Parameter might be String or Regexp.
+ def host_name(pattern)
+ condition { pattern === request.host }
+ end
+
+ # Condition for matching user agent. Parameter should be Regexp.
+ # Will set params[:agent].
+ def user_agent(pattern)
+ condition do
+ if request.user_agent.to_s =~ pattern
+ @params[:agent] = $~[1..-1]
+ true
+ else
+ false
+ end
+ end
+ end
+ alias_method :agent, :user_agent
+
+ # Condition for matching mimetypes. Accepts file extensions.
+ def provides(*types)
+ types.map! { |t| mime_types(t) }
+ types.flatten!
+ condition do
+ if type = response['Content-Type']
+ types.include? type or types.include? type[/^[^;]+/]
+ elsif type = request.preferred_type(types)
+ content_type(type)
+ true
+ else
+ false
+ end
+ end
+ end
+
+ public
+ # Defining a `GET` handler also automatically defines
+ # a `HEAD` handler.
+ def get(path, opts={}, &block)
+ conditions = @conditions.dup
+ route('GET', path, opts, &block)
+
+ @conditions = conditions
+ route('HEAD', path, opts, &block)
+ end
+
+ def put(path, opts={}, &bk) route 'PUT', path, opts, &bk end
+ def post(path, opts={}, &bk) route 'POST', path, opts, &bk end
+ def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
+ def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
+ def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
+ def patch(path, opts={}, &bk) route 'PATCH', path, opts, &bk end
+
+ private
+ def route(verb, path, options={}, &block)
+ # Because of self.options.host
+ host_name(options.delete(:host)) if options.key?(:host)
+ enable :empty_path_info if path == "" and empty_path_info.nil?
+ signature = compile!(verb, path, block, options)
+ (@routes[verb] ||= []) << signature
+ invoke_hook(:route_added, verb, path, block)
+ signature
+ end
+
+ def invoke_hook(name, *args)
+ extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
+ end
+
+ def generate_method(method_name, &block)
+ define_method(method_name, &block)
+ method = instance_method method_name
+ remove_method method_name
+ method
+ end
+
+ def compile!(verb, path, block, options = {})
+ options.each_pair { |option, args| send(option, *args) }
+ method_name = "#{verb} #{path}"
+ unbound_method = generate_method(method_name, &block)
+ pattern, keys = compile path
+ conditions, @conditions = @conditions, []
+
+ [ pattern, keys, conditions, block.arity != 0 ?
+ proc { |a,p| unbound_method.bind(a).call(*p) } :
+ proc { |a,p| unbound_method.bind(a).call } ]
+ end
+
+ def compile(path)
+ keys = []
+ if path.respond_to? :to_str
+ pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
+ pattern.gsub!(/((:\w+)|\*)/) do |match|
+ if match == "*"
+ keys << 'splat'
+ "(.*?)"
+ else
+ keys << $2[1..-1]
+ "([^/?#]+)"
+ end
+ end
+ [/^#{pattern}$/, keys]
+ elsif path.respond_to?(:keys) && path.respond_to?(:match)
+ [path, path.keys]
+ elsif path.respond_to?(:names) && path.respond_to?(:match)
+ [path, path.names]
+ elsif path.respond_to? :match
+ [path, keys]
+ else
+ raise TypeError, path
+ end
+ end
+
+ URI = ::URI.const_defined?(:Parser) ? ::URI::Parser.new : ::URI
+
+ def encoded(char)
+ enc = URI.escape(char)
+ enc = "(?:#{Regexp.escape enc}|#{URI.escape char, /./})" if enc == char
+ enc = "(?:#{enc}|#{encoded('+')})" if char == " "
+ enc
+ end
+
+ public
+ # Makes the methods defined in the block and in the Modules given
+ # in `extensions` available to the handlers and templates
+ def helpers(*extensions, &block)
+ class_eval(&block) if block_given?
+ include(*extensions) if extensions.any?
+ end
+
+ # Register an extension. Alternatively take a block from which an
+ # extension will be created and registered on the fly.
+ def register(*extensions, &block)
+ extensions << Module.new(&block) if block_given?
+ @extensions += extensions
+ extensions.each do |extension|
+ extend extension
+ extension.registered(self) if extension.respond_to?(:registered)
+ end
+ end
+
+ def development?; environment == :development end
+ def production?; environment == :production end
+ def test?; environment == :test end
+
+ # Set configuration options for Sinatra and/or the app.
+ # Allows scoping of settings for certain environments.
+ def configure(*envs, &block)
+ yield self if envs.empty? || envs.include?(environment.to_sym)
+ end
+
+ # Use the specified Rack middleware
+ def use(middleware, *args, &block)
+ @prototype = nil
+ @middleware << [middleware, args, block]
+ end
+
+ def quit!(server, handler_name)
+ # Use Thin's hard #stop! if available, otherwise just #stop.
+ server.respond_to?(:stop!) ? server.stop! : server.stop
+ $stderr.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
+ end
+
+ # Run the Sinatra app as a self-hosted server using
+ # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
+ # with the constructed handler once we have taken the stage.
+ def run!(options={})
+ set options
+ handler = detect_rack_handler
+ handler_name = handler.name.gsub(/.*::/, '')
+ server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
+ handler.run self, server_settings.merge(:Port => port, :Host => bind) do |server|
+ unless handler_name =~ /cgi/i
+ $stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
+ "on #{port} for #{environment} with backup from #{handler_name}"
+ end
+ [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
+ server.threaded = settings.threaded if server.respond_to? :threaded=
+ set :running, true
+ yield server if block_given?
+ end
+ rescue Errno::EADDRINUSE
+ $stderr.puts "== Someone is already performing on port #{port}!"
+ end
+
+ # The prototype instance used to process requests.
+ def prototype
+ @prototype ||= new
+ end
+
+ # Create a new instance without middleware in front of it.
+ alias new! new unless method_defined? :new!
+
+ # Create a new instance of the class fronted by its middleware
+ # pipeline. The object is guaranteed to respond to #call but may not be
+ # an instance of the class new was called on.
+ def new(*args, &bk)
+ instance = new!(*args, &bk)
+ Wrapper.new(build(instance).to_app, instance)
+ end
+
+ # Creates a Rack::Builder instance with all the middleware set up and
+ # the given +app+ as end point.
+ def build(app)
+ builder = Rack::Builder.new
+ setup_default_middleware builder
+ setup_middleware builder
+ builder.run app
+ builder
+ end
+
+ def call(env)
+ synchronize { prototype.call(env) }
+ end
+
+ private
+ def setup_default_middleware(builder)
+ builder.use ExtendedRack
+ builder.use ShowExceptions if show_exceptions?
+ builder.use Rack::MethodOverride if method_override?
+ builder.use Rack::Head
+ setup_logging builder
+ setup_sessions builder
+ setup_protection builder
+ end
+
+ def setup_middleware(builder)
+ middleware.each { |c,a,b| builder.use(c, *a, &b) }
+ end
+
+ def setup_logging(builder)
+ if logging?
+ setup_common_logger(builder)
+ setup_custom_logger(builder)
+ elsif logging == false
+ setup_null_logger(builder)
+ end
+ end
+
+ def setup_null_logger(builder)
+ builder.use Rack::NullLogger
+ end
+
+ def setup_common_logger(builder)
+ builder.use Sinatra::CommonLogger
+ end
+
+ def setup_custom_logger(builder)
+ if logging.respond_to? :to_int
+ builder.use Rack::Logger, logging
+ else
+ builder.use Rack::Logger
+ end
+ end
+
+ def setup_protection(builder)
+ return unless protection?
+ options = Hash === protection ? protection.dup : {}
+ options[:except] = Array options[:except]
+ options[:except] += [:session_hijacking, :remote_token] unless sessions?
+ options[:reaction] ||= :drop_session
+ builder.use Rack::Protection, options
+ end
+
+ def setup_sessions(builder)
+ return unless sessions?
+ options = {}
+ options[:secret] = session_secret if session_secret?
+ options.merge! sessions.to_hash if sessions.respond_to? :to_hash
+ builder.use Rack::Session::Cookie, options
+ end
+
+ def detect_rack_handler
+ servers = Array(server)
+ servers.each do |server_name|
+ begin
+ return Rack::Handler.get(server_name.to_s)
+ rescue LoadError, NameError
+ end
+ end
+ fail "Server handler (#{servers.join(',')}) not found."
+ end
+
+ def inherited(subclass)
+ subclass.reset!
+ subclass.set :app_file, caller_files.first unless subclass.app_file?
+ super
+ end
+
+ @@mutex = Mutex.new
+ def synchronize(&block)
+ if lock?
+ @@mutex.synchronize(&block)
+ else
+ yield
+ end
+ end
+
+ public
+ CALLERS_TO_IGNORE = [ # :nodoc:
+ /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
+ /lib\/tilt.*\.rb$/, # all tilt code
+ /^\(.*\)$/, # generated code
+ /rubygems\/custom_require\.rb$/, # rubygems require hacks
+ /active_support/, # active_support require hacks
+ /bundler(\/runtime)?\.rb/, # bundler require hacks
+ /<internal:/, # internal in ruby >= 1.9.2
+ /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
+ ]
+
+ # contrary to what the comment said previously, rubinius never supported this
+ if defined?(RUBY_IGNORE_CALLERS)
+ warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
+ CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
+ end
+
+ # Like Kernel#caller but excluding certain magic entries and without
+ # line / method information; the resulting array contains filenames only.
+ def caller_files
+ cleaned_caller(1).flatten
+ end
+
+ # Like caller_files, but containing Arrays rather than strings with the
+ # first element being the file, and the second being the line.
+ def caller_locations
+ cleaned_caller 2
+ end
+
+ private
+ # used for deprecation warnings
+ def warn(message)
+ super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
+ end
+
+ # Like Kernel#caller but excluding certain magic entries
+ def cleaned_caller(keep = 3)
+ caller(1).
+ map { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
+ reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
+ end
+ end
+
+ # Fixes encoding issues by
+ # * defaulting to UTF-8
+ # * casting params to Encoding.default_external
+ #
+ # The latter might not be necessary if Rack handles it one day.
+ # Keep an eye on Rack's LH #100.
+ def force_encoding(*args) settings.force_encoding(*args) end
+ if defined? Encoding
+ def self.force_encoding(data, encoding = default_encoding)
+ return if data == settings || data.is_a?(Tempfile)
+ if data.respond_to? :force_encoding
+ data.force_encoding(encoding).encode!
+ elsif data.respond_to? :each_value
+ data.each_value { |v| force_encoding(v, encoding) }
+ elsif data.respond_to? :each
+ data.each { |v| force_encoding(v, encoding) }
+ end
+ data
+ end
+ else
+ def self.force_encoding(data, *) data end
+ end
+
+ reset!
+
+ set :environment, (ENV['RACK_ENV'] || :development).to_sym
+ set :raise_errors, Proc.new { test? }
+ set :dump_errors, Proc.new { !test? }
+ set :show_exceptions, Proc.new { development? }
+ set :sessions, false
+ set :logging, false
+ set :protection, true
+ set :method_override, false
+ set :use_code, false
+ set :default_encoding, "utf-8"
+ set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
+ settings.add_charset << /^text\//
+
+ # explicitly generating a session secret eagerly to play nice with preforking
+ begin
+ require 'securerandom'
+ set :session_secret, SecureRandom.hex(64)
+ rescue LoadError, NotImplementedError
+ # SecureRandom raises a NotImplementedError if no random device is available
+ set :session_secret, "%064x" % Kernel.rand(2**256-1)
+ end
+
+ class << self
+ alias_method :methodoverride?, :method_override?
+ alias_method :methodoverride=, :method_override=
+ end
+
+ set :run, false # start server via at-exit hook?
+ set :running, false # is the built-in server running now?
+ set :server, %w[http webrick]
+ set :bind, '0.0.0.0'
+ set :port, 4567
+
+ ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
+
+ if ruby_engine == 'macruby'
+ server.unshift 'controll_tower'
+ else
+ server.unshift 'mongrel' if ruby_engine.nil?
+ server.unshift 'puma' if ruby_engine != 'rbx'
+ server.unshift 'thin' if ruby_engine != 'jruby'
+ server.unshift 'puma' if ruby_engine == 'rbx'
+ server.unshift 'trinidad' if ruby_engine =='jruby'
+ end
+
+ set :absolute_redirects, true
+ set :prefixed_redirects, false
+ set :empty_path_info, nil
+
+ set :app_file, nil
+ set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
+ set :views, Proc.new { root && File.join(root, 'views') }
+ set :reload_templates, Proc.new { development? }
+ set :lock, false
+ set :threaded, true
+
+ set :public_folder, Proc.new { root && File.join(root, 'public') }
+ set :static, Proc.new { public_folder && File.exist?(public_folder) }
+ set :static_cache_control, false
+
+ error ::Exception do
+ response.status = 500
+ content_type 'text/html'
+ '<h1>Internal Server Error</h1>'
+ end
+
+ configure :development do
+ get '/__sinatra__/:image.png' do
+ filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
+ content_type :png
+ send_file filename
+ end
+
+ error NotFound do
+ content_type 'text/html'
+
+ (<<-HTML).gsub(/^ {8}/, '')
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <style type="text/css">
+ body { text-align:center;font-family:helvetica,arial;font-size:22px;
+ color:#888;margin:20px}
+ #c {margin:0 auto;width:500px;text-align:left}
+ </style>
+ </head>
+ <body>
+ <h2>Sinatra doesn&rsquo;t know this ditty.</h2>
+ <img src='#{uri "/__sinatra__/404.png"}'>
+ <div id="c">
+ Try this:
+ <pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
+ </div>
+ </body>
+ </html>
+ HTML
+ end
+ end
+ end
+
+ # Execution context for classic style (top-level) applications. All
+ # DSL methods executed on main are delegated to this class.
+ #
+ # The Application class should not be subclassed, unless you want to
+ # inherit all settings, routes, handlers, and error pages from the
+ # top-level. Subclassing Sinatra::Base is highly recommended for
+ # modular applications.
+ class Application < Base
+ set :logging, Proc.new { ! test? }
+ set :method_override, true
+ set :run, Proc.new { ! test? }
+ set :session_secret, Proc.new { super() unless development? }
+ set :app_file, nil
+
+ def self.register(*extensions, &block) #:nodoc:
+ added_methods = extensions.map {|m| m.public_instance_methods }.flatten
+ Delegator.delegate(*added_methods)
+ super(*extensions, &block)
+ end
+ end
+
+ # Sinatra delegation mixin. Mixing this module into an object causes all
+ # methods to be delegated to the Sinatra::Application class. Used primarily
+ # at the top-level.
+ module Delegator #:nodoc:
+ def self.delegate(*methods)
+ methods.each do |method_name|
+ define_method(method_name) do |*args, &block|
+ return super(*args, &block) if respond_to? method_name
+ Delegator.target.send(method_name, *args, &block)
+ end
+ private method_name
+ end
+ end
+
+ delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
+ :before, :after, :error, :not_found, :configure, :set, :mime_type,
+ :enable, :disable, :use, :development?, :test?, :production?,
+ :helpers, :settings
+
+ class << self
+ attr_accessor :target
+ end
+
+ self.target = Application
+ end
+
+ class Wrapper
+ def initialize(stack, instance)
+ @stack, @instance = stack, instance
+ end
+
+ def settings
+ @instance.settings
+ end
+
+ def helpers
+ @instance
+ end
+
+ def call(env)
+ @stack.call(env)
+ end
+
+ def inspect
+ "#<#{@instance.class} app_file=#{settings.app_file.inspect}>"
+ end
+ end
+
+ # Create a new Sinatra application. The block is evaluated in the new app's
+ # class scope.
+ def self.new(base=Base, options={}, &block)
+ base = Class.new(base)
+ base.class_eval(&block) if block_given?
+ base
+ end
+
+ # Extend the top-level DSL with the modules provided.
+ def self.register(*extensions, &block)
+ Delegator.target.register(*extensions, &block)
+ end
+
+ # Include the helper modules provided in Sinatra's request context.
+ def self.helpers(*extensions, &block)
+ Delegator.target.helpers(*extensions, &block)
+ end
+
+ # Use the middleware for classic applications.
+ def self.use(*args, &block)
+ Delegator.target.use(*args, &block)
+ end
+end
diff --git a/samples/ruby/wrong_shebang.rb b/samples/ruby/wrong_shebang.rb
new file mode 100644
index 0000000..22b4804
--- /dev/null
+++ b/samples/ruby/wrong_shebang.rb
@@ -0,0 +1,2 @@
+#!/usr/bin/env python
+puts "Not Python"
diff --git a/samples/rust/hello.rs b/samples/rust/hello.rs
new file mode 100644
index 0000000..5579f68
--- /dev/null
+++ b/samples/rust/hello.rs
@@ -0,0 +1,3 @@
+fn main() {
+ log "Hello, world!";
+}
diff --git a/samples/sass/screen.sass b/samples/sass/screen.sass
new file mode 100644
index 0000000..7182195
--- /dev/null
+++ b/samples/sass/screen.sass
@@ -0,0 +1,11 @@
+$blue: #3bbfce
+$margin: 16px
+
+.content-navigation
+ border-color: $blue
+ color: darken($blue, 9%)
+
+.border
+ padding: $margin / 2
+ margin: $margin / 2
+ border-color: $blue
diff --git a/samples/scala/build.sbt b/samples/scala/build.sbt
new file mode 100644
index 0000000..16e7902
--- /dev/null
+++ b/samples/scala/build.sbt
@@ -0,0 +1,169 @@
+// set the name of the project
+name := "My Project"
+
+version := "1.0"
+
+organization := "org.myproject"
+
+// add a test dependency on ScalaCheck
+libraryDependencies += "org.scala-tools.testing" %% "scalacheck" % "1.8" % "test"
+
+// add compile dependencies on some dispatch modules
+libraryDependencies ++= Seq(
+ "net.databinder" %% "dispatch-meetup" % "0.7.8",
+ "net.databinder" %% "dispatch-twitter" % "0.7.8"
+)
+
+// Set a dependency based partially on a val.
+{
+ val libosmVersion = "2.5.2-RC1"
+ libraryDependencies += "net.sf.travelingsales" % "osmlib" % libosmVersion from "http://downloads.sourceforge.net/project/travelingsales/libosm/"+libosmVersion+"/libosm-"+libosmVersion+".jar"
+}
+
+// reduce the maximum number of errors shown by the Scala compiler
+maxErrors := 20
+
+// increase the time between polling for file changes when using continuous execution
+pollInterval := 1000
+
+// append several options to the list of options passed to the Java compiler
+javacOptions ++= Seq("-source", "1.5", "-target", "1.5")
+
+// append -deprecation to the options passed to the Scala compiler
+scalacOptions += "-deprecation"
+
+// set the Scala version used for the project
+scalaVersion := "2.9.0-SNAPSHOT"
+
+// define the statements initially evaluated when entering 'console', 'console-quick', or 'console-project'
+initialCommands := """
+ import System.{currentTimeMillis => now}
+ def time[T](f: => T): T = {
+ val start = now
+ try { f } finally { println("Elapsed: " + (now - start)/1000.0 + " s") }
+ }
+"""
+
+// set the initial commands when entering 'console' only
+initialCommands in console := "import myproject._"
+
+// set the main class for packaging the main jar
+// 'run' will still auto-detect and prompt
+// change Compile to Test to set it for the test jar
+mainClass in (Compile, packageBin) := Some("myproject.MyMain")
+
+// set the main class for the main 'run' task
+// change Compile to Test to set it for 'test:run'
+mainClass in (Compile, run) := Some("myproject.MyMain")
+
+// add <base>/input to the files that '~' triggers on
+watchSources <+= baseDirectory map { _ / "input" }
+
+// add a maven-style repository
+resolvers += "name" at "url"
+
+// add a sequence of maven-style repositories
+resolvers ++= Seq("name" at "url")
+
+// define the repository to publish to
+publishTo := Some("name" at "url")
+
+// set Ivy logging to be at the highest level
+ivyLoggingLevel := UpdateLogging.Full
+
+// disable updating dynamic revisions (including -SNAPSHOT versions)
+offline := true
+
+// set the prompt (for this build) to include the project id.
+shellPrompt in ThisBuild := { state => Project.extract(state).currentRef.project + "> " }
+
+// set the prompt (for the current project) to include the username
+shellPrompt := { state => System.getProperty("user.name") + "> " }
+
+// disable printing timing information, but still print [success]
+showTiming := false
+
+// disable printing a message indicating the success or failure of running a task
+showSuccess := false
+
+// change the format used for printing task completion time
+timingFormat := {
+ import java.text.DateFormat
+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
+}
+
+// disable using the Scala version in output paths and artifacts
+crossPaths := false
+
+// fork a new JVM for 'run' and 'test:run'
+fork := true
+
+// fork a new JVM for 'test:run', but not 'run'
+fork in Test := true
+
+// add a JVM option to use when forking a JVM for 'run'
+javaOptions += "-Xmx2G"
+
+// only use a single thread for building
+parallelExecution := false
+
+// Execute tests in the current project serially
+// Tests from other projects may still run concurrently.
+parallelExecution in Test := false
+
+// set the location of the JDK to use for compiling Java code.
+// if 'fork' is true, this is used for 'run' as well
+javaHome := Some(file("/usr/lib/jvm/sun-jdk-1.6"))
+
+// Use Scala from a directory on the filesystem instead of retrieving from a repository
+scalaHome := Some(file("/home/user/scala/trunk/"))
+
+// don't aggregate clean (See FullConfiguration for aggregation details)
+aggregate in clean := false
+
+// only show warnings and errors on the screen for compilations.
+// this applies to both test:compile and compile and is Info by default
+logLevel in compile := Level.Warn
+
+// only show warnings and errors on the screen for all tasks (the default is Info)
+// individual tasks can then be more verbose using the previous setting
+logLevel := Level.Warn
+
+// only store messages at info and above (the default is Debug)
+// this is the logging level for replaying logging with 'last'
+persistLogLevel := Level.Debug
+
+// only show 10 lines of stack traces
+traceLevel := 10
+
+// only show stack traces up to the first sbt stack frame
+traceLevel := 0
+
+// add SWT to the unmanaged classpath
+unmanagedJars in Compile += file("/usr/share/java/swt.jar")
+
+// publish test jar, sources, and docs
+publishArtifact in Test := true
+
+// disable publishing of main docs
+publishArtifact in (Compile, packageDoc) := false
+
+// change the classifier for the docs artifact
+artifactClassifier in packageDoc := Some("doc")
+
+// Copy all managed dependencies to <build-root>/lib_managed/
+// This is essentially a project-local cache and is different
+// from the lib_managed/ in sbt 0.7.x. There is only one
+// lib_managed/ in the build root (not per-project).
+retrieveManaged := true
+
+/* Specify a file containing credentials for publishing. The format is:
+realm=Sonatype Nexus Repository Manager
+host=nexus.scala-tools.org
+user=admin
+password=admin123
+*/
+credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")
+
+// Directly specify credentials for publishing.
+credentials += Credentials("Sonatype Nexus Repository Manager", "nexus.scala-tools.org", "admin", "admin123")
diff --git a/samples/scala/script.scala b/samples/scala/script.scala
new file mode 100755
index 0000000..b026d24
--- /dev/null
+++ b/samples/scala/script.scala
@@ -0,0 +1,9 @@
+#!/bin/sh
+exec scala "$0" "$@"
+!#
+
+object HelloWorld {
+ def main(args: Array[String]) {
+ println("Hello, world!")
+ }
+}
diff --git a/samples/scheme/asteroids.sps b/samples/scheme/asteroids.sps
new file mode 100644
index 0000000..a4ae995
--- /dev/null
+++ b/samples/scheme/asteroids.sps
@@ -0,0 +1,435 @@
+(import (rnrs)
+ (only (surfage s1 lists) filter-map)
+ (gl)
+ (glut)
+ (dharmalab records define-record-type)
+ (dharmalab math basic)
+ (agave glu compat)
+ (agave geometry pt)
+ (agave glamour window)
+ (agave glamour misc)
+ (surfage s19 time)
+ (surfage s27 random-bits)
+ (surfage s42 eager-comprehensions))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; utilities
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (say . args)
+ (for-each display args)
+ (newline))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (gl-translate-pt p)
+ (glTranslated (pt-x p) (pt-y p) 0.0))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (radians x) (* x (/ pi 180)))
+
+(define (degrees x) (* x (/ 180 pi)))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (angle->pt a)
+ (pt (cos a)
+ (sin a)))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (current-time-in-nanoseconds)
+ (let ((val (current-time)))
+ (+ (* (time-second val) 1000000000)
+ (time-nanosecond val))))
+
+(define (current-time-in-seconds)
+ (/ (current-time-in-nanoseconds)
+ 1000.0 ;; micro
+ 1000.0 ;; milli
+ 1000.0))
+
+(define base-time (current-time-in-seconds))
+
+(define (time-step) (- (current-time-in-seconds) base-time))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define score 0)
+
+(define level 1)
+
+(define ships 3)
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; spaceship
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-record-type++ spaceship
+ (fields (mutable pos)
+ (mutable vel)
+ (mutable theta)
+ (mutable force)))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; particle
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-record-type++ particle
+ (fields (mutable pos)
+ (mutable vel)
+ (mutable birth)
+ (mutable lifetime)
+ (mutable color)))
+
+(define particles '())
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bullet
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-record-type++ bullet
+ (fields (mutable pos)
+ (mutable vel)
+ (mutable birth)))
+
+(define bullets '())
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; asteroid
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-record-type++ asteroid
+ (fields (mutable pos)
+ (mutable vel)
+ (mutable radius)))
+
+(define number-of-starting-asteroids 4)
+
+(define asteroids #f)
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bullet-pack
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-record-type++ bullet-pack
+ (fields (mutable pos)
+ (mutable vel)))
+
+(define pack #f)
+
+(is-bullet-pack pack)
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(initialize-glut)
+
+(window (size 800 400)
+ (title "Asteroids")
+ (reshape (width height)))
+
+(random-source-randomize! default-random-source)
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (pt-wrap p)
+ (pt (mod (pt-x p) width)
+ (mod (pt-y p) height)))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define ship
+ (make-spaceship (pt (/ width 2.0) (/ height 2.0))
+ (pt 0.0 0.0)
+ 0.0
+ 0.0))
+
+(is-spaceship ship)
+
+(define ammo 0)
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(set! asteroids
+ (list-ec (: i number-of-starting-asteroids)
+ (make-asteroid (pt (inexact (random-integer width))
+ (inexact (random-integer height)))
+ (pt (inexact (+ -50 (random-integer 100)))
+ (inexact (+ -50 (random-integer 100))))
+ 50.0)))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(set! pack (make-bullet-pack (pt (inexact (random-integer width))
+ (inexact (random-integer height)))
+ (pt (inexact (+ -50 (random-integer 100)))
+ (inexact (+ -50 (random-integer 100))))))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(buffered-display-procedure
+ (lambda ()
+ (background 0.0)
+
+ ;; ship
+
+ (glColor3f 0.0 1.0 0.0)
+
+ (gl-matrix-excursion
+ (gl-translate-pt ship.pos)
+ (glRotated 90.0 0.0 1.0 0.0)
+ (glRotated (degrees ship.theta) -1.0 0.0 0.0)
+ (glutWireCone 10.0 30.0 5 5))
+
+ ;; particles
+
+ (for-each
+ (lambda (par)
+
+ (let ((c (particle-color par)))
+ (glColor3f (vector-ref c 0)
+ (vector-ref c 1)
+ (vector-ref c 2)))
+
+ (gl-matrix-excursion
+ (gl-translate-pt (particle-pos par))
+ (glutWireSphere 2.0 5 5)))
+ particles)
+
+ ;; bullets
+
+ (glColor3f 0.0 0.0 1.0)
+
+ (for-each
+ (lambda (bullet)
+ (gl-matrix-excursion
+ (gl-translate-pt (bullet-pos bullet))
+ (glutWireSphere 5.0 10 10)))
+ bullets)
+
+ ;; asteroids
+
+ (glColor3f 1.0 0.0 0.0)
+
+ (for-each
+ (lambda (asteroid)
+ (gl-matrix-excursion
+ (gl-translate-pt (asteroid-pos asteroid))
+ (glutWireSphere (asteroid-radius asteroid) 10 10)))
+ asteroids)
+
+ ;; bullet-pack
+
+ (glColor3f 0.0 0.0 1.0)
+
+ (gl-matrix-excursion
+ (gl-translate-pt pack.pos)
+ (glutWireCube 10.0))
+
+ ))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define last-time (current-time-in-seconds))
+
+(define dt 0)
+
+(define (update-system)
+
+ (set! dt (- (current-time-in-seconds) last-time))
+
+ (set! last-time (current-time-in-seconds))
+
+ (ship.pos! (pt-wrap (pt+ ship.pos (pt*n ship.vel dt))))
+
+ (pack.pos! (pt-wrap (pt+ pack.pos (pt*n pack.vel dt))))
+
+ (set! particles
+ (filter-map
+ (lambda (par)
+ (is-particle par)
+ (cond ((> (- (current-time-in-seconds) par.birth) par.lifetime) #f)
+ (else (par.pos! (pt+ par.pos (pt*n par.vel dt)))
+ par)))
+ particles))
+
+ (set! bullets
+ (filter-map
+ (lambda (bullet)
+ (is-bullet bullet)
+ (cond ((> (- (current-time-in-seconds) bullet.birth) 2.0) #f)
+ (else (bullet.pos! (pt+ bullet.pos (pt*n bullet.vel dt)))
+ bullet)))
+ bullets))
+
+ (set! asteroids
+ (filter-map
+ (lambda (a)
+ (is-asteroid a)
+ (a.pos! (pt-wrap (pt+ a.pos (pt*n a.vel dt))))
+ (if (< a.radius 10.0) #f a))
+ asteroids))
+
+ ;; bullet asteroid contact
+
+ (for-each
+ (lambda (b)
+ (is-bullet b)
+ (for-each
+ (lambda (a)
+ (is-asteroid a)
+ (when (<= (pt-distance b.pos a.pos)
+ a.radius)
+
+ (begin (set! score (+ score 1))
+ (say "score: " score)
+ #f)
+
+ (set! asteroids
+ (append
+ (list-ec (: i 4)
+ (make-asteroid a.pos
+ (pt (+ -50.0 (random-integer 100))
+ (+ -50.0 (random-integer 100)))
+ (/ a.radius 2.0)))
+ asteroids))
+ (a.radius! 0.1)
+ (b.birth! 0.0)
+
+ (set! particles
+ (append (list-ec (: i 100)
+ (make-particle a.pos
+ (pt*n (angle->pt
+ (radians
+ (random-integer 360)))
+
+ (random-integer 100)
+
+ )
+ (current-time-in-seconds)
+ 1.0
+ (vector 1.0 1.0 1.0)))
+ particles))))
+ asteroids))
+ bullets)
+
+ (for-each
+ (lambda (a)
+ (is-asteroid a)
+ (when (<= (pt-distance a.pos ship.pos) a.radius)
+
+ (set! particles
+ (append (list-ec (: i 100)
+ (make-particle ship.pos
+ (pt*n (angle->pt
+ (radians
+ (random-integer 360)))
+ (random-integer 100))
+ (current-time-in-seconds)
+ 1.0
+ (vector 0.0 1.0 1.0)))
+ particles))
+
+ (set! ship (make-spaceship (pt (/ width 2.0) (/ height 2.0))
+ (pt 0.0 0.0)
+ 0.0
+ 0.0))
+
+ ))
+ asteroids)
+
+ (when (null? asteroids)
+ (set! level (+ level 1))
+ (display "level: ")
+ (display level)
+ (newline)
+ (set! asteroids
+ (list-ec (: i (+ number-of-starting-asteroids level))
+ (make-asteroid (pt (inexact (random-integer width))
+ (inexact (random-integer height)))
+ (pt (inexact (+ -50 (random-integer 100)))
+ (inexact (+ -50 (random-integer 100))))
+ 50.0))))
+
+ ;; ship pack contact
+
+ (when (<= (pt-distance ship.pos pack.pos) 10.0)
+ (set! ammo (+ ammo 5))
+ (set! pack (make-bullet-pack (pt (inexact (random-integer width))
+ (inexact (random-integer height)))
+ (pt (inexact (+ -50 (random-integer 100)))
+ (inexact (+ -50 (random-integer 100))))))
+ (say "ammo: " ammo))
+
+ )
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(glutIdleFunc
+ (lambda ()
+ (update-system)
+ (glutPostRedisplay)))
+
+(glutKeyboardFunc
+ (lambda (key x y)
+ (case (integer->char key)
+
+ ((#\w)
+
+ (ship.vel! (pt+ ship.vel (pt*n (angle->pt ship.theta) 50.0)))
+
+ (set! particles
+ (append (list-ec (: i 10)
+ (make-particle ship.pos
+ (pt*n
+ (angle->pt
+ (+ ship.theta
+ (radians 180.0)
+ (radians (+ -45 (random-integer 90)))
+ ))
+ (random-integer 50)
+ )
+ (current-time-in-seconds)
+ 1.0
+ (vector 1.0 1.0 0.0)))
+ particles))
+
+ )
+
+ ((#\a) (ship.theta! (+ ship.theta (radians 20.0))))
+ ((#\d) (ship.theta! (- ship.theta (radians 20.0))))
+
+ ((#\s) (ship.vel! (pt 0.0 0.0)))
+
+ ((#\x) (ship.theta! (+ ship.theta (radians 180.0))))
+
+ ((#\space)
+
+ (when (> ammo 0)
+
+ (set! ammo (- ammo 1))
+
+ (set! bullets
+ (cons
+ (make-bullet ship.pos
+ (pt+ ship.vel
+ (pt*n (angle->pt ship.theta) 400.0))
+ (current-time-in-seconds))
+ bullets)))
+
+ (say "ammo: " ammo)
+ )
+ )))
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(say "w - Thrusters")
+(say "a/d - Left/Right")
+(say "s - Stop")
+(say "x - Flip")
+(say "spc - Laser")
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(glutMainLoop)
diff --git a/samples/scilab/scilab_function.sci b/samples/scilab/scilab_function.sci
new file mode 100644
index 0000000..7c95174
--- /dev/null
+++ b/samples/scilab/scilab_function.sci
@@ -0,0 +1,14 @@
+// A comment with whites and tabulations
+// Email: <scilab.support@scilab.org>
+// Scilab editor: http://www.scilab.org/
+function [a, b] = myfunction(d, e, f)
+ a = 2.71828 + %pi + f($, :);
+ b = cos(a) + cosh(a);
+ if d == e then
+ b = 10 - e.field;
+ else
+ b = " test " + home
+ return
+ end
+ myvar = 1.23e-45;
+endfunction
diff --git a/samples/scilab/scilab_script.sce b/samples/scilab/scilab_script.sce
new file mode 100644
index 0000000..efa2922
--- /dev/null
+++ b/samples/scilab/scilab_script.sce
@@ -0,0 +1,2 @@
+disp(%pi);
+
diff --git a/samples/scilab/scilab_test.tst b/samples/scilab/scilab_test.tst
new file mode 100644
index 0000000..c48521f
--- /dev/null
+++ b/samples/scilab/scilab_test.tst
@@ -0,0 +1,3 @@
+assert_checkequal(1+1,2);
+assert_checkfalse(%pi==%e);
+
diff --git a/samples/scss/screen.scss b/samples/scss/screen.scss
new file mode 100644
index 0000000..7deebf3
--- /dev/null
+++ b/samples/scss/screen.scss
@@ -0,0 +1,14 @@
+$blue: #3bbfce;
+$margin: 16px;
+
+.content-navigation {
+ border-color: $blue;
+ color:
+ darken($blue, 9%);
+}
+
+.border {
+ padding: $margin / 2;
+ margin: $margin / 2;
+ border-color: $blue;
+}
diff --git a/samples/shell/.bash_profile b/samples/shell/.bash_profile
new file mode 100644
index 0000000..b7edb8a
--- /dev/null
+++ b/samples/shell/.bash_profile
@@ -0,0 +1 @@
+export PATH="/usr/local/bin:/usr/bin:/bin"
diff --git a/samples/shell/.bashrc b/samples/shell/.bashrc
new file mode 100644
index 0000000..b7edb8a
--- /dev/null
+++ b/samples/shell/.bashrc
@@ -0,0 +1 @@
+export PATH="/usr/local/bin:/usr/bin:/bin"
diff --git a/samples/shell/.profile b/samples/shell/.profile
new file mode 100644
index 0000000..b7edb8a
--- /dev/null
+++ b/samples/shell/.profile
@@ -0,0 +1 @@
+export PATH="/usr/local/bin:/usr/bin:/bin"
diff --git a/samples/shell/.zlogin b/samples/shell/.zlogin
new file mode 100644
index 0000000..b7edb8a
--- /dev/null
+++ b/samples/shell/.zlogin
@@ -0,0 +1 @@
+export PATH="/usr/local/bin:/usr/bin:/bin"
diff --git a/samples/shell/.zshrc b/samples/shell/.zshrc
new file mode 100644
index 0000000..b7edb8a
--- /dev/null
+++ b/samples/shell/.zshrc
@@ -0,0 +1 @@
+export PATH="/usr/local/bin:/usr/bin:/bin"
diff --git a/samples/shell/PKGBUILD b/samples/shell/PKGBUILD
new file mode 100644
index 0000000..854d2f1
--- /dev/null
+++ b/samples/shell/PKGBUILD
@@ -0,0 +1,43 @@
+# Maintainer: Daniel Micay <danielmicay@gmail.com>
+pkgname=stud-git
+pkgver=20120316
+pkgrel=1
+pkgdesc="The Scalable TLS Unwrapping Daemon"
+arch=(i686 x86_64)
+url="https://github.com/bumptech/stud"
+license=('BSD')
+depends=(libev openssl)
+makedepends=(git)
+provides=(stud)
+conflicts=(stud)
+
+_gitroot=https://github.com/bumptech/stud.git
+_gitname=stud
+
+build() {
+ cd "$srcdir"
+ msg "Connecting to GIT server...."
+
+ if [[ -d "$_gitname" ]]; then
+ cd "$_gitname" && git pull origin
+ msg "The local files are updated."
+ else
+ git clone "$_gitroot" "$_gitname"
+ fi
+
+ msg "GIT checkout done or server timeout"
+ msg "Starting build..."
+
+ rm -rf "$srcdir/$_gitname-build"
+ git clone "$srcdir/$_gitname" "$srcdir/$_gitname-build"
+ cd "$srcdir/$_gitname-build"
+
+ make
+}
+
+package() {
+ cd "$srcdir/$_gitname-build"
+ make PREFIX=/usr DESTDIR="$pkgdir/" install
+ install -Dm755 init.stud "$pkgdir/etc/rc.d/stud"
+ mkdir -p "$pkgdir/etc/stud"
+}
diff --git a/samples/shell/rbenv-sh-shell b/samples/shell/rbenv-sh-shell
new file mode 100755
index 0000000..a216cb2
--- /dev/null
+++ b/samples/shell/rbenv-sh-shell
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -e
+[ -n "$RBENV_DEBUG" ] && set -x
+
+# Provide rbenv completions
+if [ "$1" = "--complete" ]; then
+ echo --unset
+ echo system
+ exec rbenv-versions --bare
+fi
+
+version="$1"
+
+if [ -z "$version" ]; then
+ if [ -z "$RBENV_VERSION" ]; then
+ echo "rbenv: no shell-specific version configured" >&2
+ exit 1
+ else
+ echo "echo \"\$RBENV_VERSION\""
+ exit
+ fi
+fi
+
+if [ "$version" = "--unset" ]; then
+ echo "unset RBENV_VERSION"
+ exit 1
+fi
+
+# Make sure the specified version is installed.
+rbenv-prefix "$version" >/dev/null
+
+echo "export RBENV_VERSION=\"${version}\""
diff --git a/samples/shell/rvm b/samples/shell/rvm
new file mode 100755
index 0000000..4a9e9c3
--- /dev/null
+++ b/samples/shell/rvm
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+if (( ${rvm_ignore_rvmrc:=0} == 0 ))
+then
+ declare rvmrc
+
+ rvm_rvmrc_files=("/etc/rvmrc" "$HOME/.rvmrc")
+ if [[ -n "${rvm_prefix:-}" ]] && ! [[ "$HOME/.rvmrc" -ef "${rvm_prefix}/.rvmrc" ]]
+ then rvm_rvmrc_files+=( "${rvm_prefix}/.rvmrc" )
+ fi
+
+ for rvmrc in "${rvm_rvmrc_files[@]}"
+ do
+ if [[ -f "$rvmrc" ]]
+ then
+ if GREP_OPTIONS="" \grep '^\s*rvm .*$' "$rvmrc" >/dev/null 2>&1
+ then
+ printf "%b" "
+ Error:
+ $rvmrc is for rvm settings only.
+ rvm CLI may NOT be called from within $rvmrc.
+ Skipping the loading of $rvmrc
+"
+ exit 1
+ else
+ source "$rvmrc"
+ fi
+ fi
+ done
+ unset rvm_rvmrc_files
+ unset rvmrc
+fi
+
+export rvm_path
+if [[ -z "${rvm_path:-}" ]]
+then
+ if (( UID == 0 )) && [[ -d "/usr/local/rvm" ]]
+ then rvm_path="/usr/local/rvm"
+ elif [[ -d "${HOME}/.rvm" ]]
+ then rvm_path="${HOME}/.rvm"
+ elif [[ -d "/usr/local/rvm" ]]
+ then rvm_path="/usr/local/rvm"
+ else echo "Can't find rvm install!" 1>&2 ; exit 1
+ fi
+fi
+
+# allow disabling check temporary
+: rvm_is_not_a_shell_function:${rvm_is_not_a_shell_function:=1}
+
+# if to prevent fork-bomb
+if source "${rvm_scripts_path:="$rvm_path/scripts"}/rvm"
+then
+ rvm "$@"
+else
+ echo "Error sourcing RVM!" 1>&2
+ exit 1
+fi
diff --git a/samples/shell/script.bash b/samples/shell/script.bash
new file mode 100755
index 0000000..0c2172c
--- /dev/null
+++ b/samples/shell/script.bash
@@ -0,0 +1,2 @@
+#!/bin/bash
+echo "bash"
diff --git a/samples/shell/script.sh b/samples/shell/script.sh
new file mode 100755
index 0000000..e5ed467
--- /dev/null
+++ b/samples/shell/script.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo "sh"
diff --git a/samples/shell/script.zsh b/samples/shell/script.zsh
new file mode 100755
index 0000000..ed274ab
--- /dev/null
+++ b/samples/shell/script.zsh
@@ -0,0 +1,2 @@
+#!/bin/zsh
+echo "zsh"
diff --git a/samples/standard-ml/Foo.sig b/samples/standard-ml/Foo.sig
new file mode 100644
index 0000000..f881a5c
--- /dev/null
+++ b/samples/standard-ml/Foo.sig
@@ -0,0 +1,27 @@
+
+signature LAZY_BASE =
+ sig
+ type 'a lazy
+ exception Undefined
+ val force: 'a lazy -> 'a
+ val delay: (unit -> 'a) -> 'a lazy
+ val undefined: 'a lazy
+ end
+
+signature LAZY' =
+ sig
+ include LAZY_BASE
+ val isUndefined: 'a lazy -> bool
+ val inject : 'a -> 'a lazy
+ val toString: ('a -> string) -> 'a lazy -> string
+ val eq: ''a lazy * ''a lazy -> bool
+ val eqBy: ('a * 'a -> bool) -> 'a lazy * 'a lazy -> bool
+ val compare: ('a * 'a -> order) -> 'a lazy * 'a lazy -> order
+ val map: ('a -> 'b) -> 'a lazy -> 'b lazy
+
+ structure Ops:
+ sig
+ val ! : 'a lazy -> 'a (* force *)
+ val ? : 'a -> 'a lazy (* inject *)
+ end
+ end
diff --git a/samples/standard-ml/Foo.sml b/samples/standard-ml/Foo.sml
new file mode 100644
index 0000000..2bbb789
--- /dev/null
+++ b/samples/standard-ml/Foo.sml
@@ -0,0 +1,75 @@
+
+structure LazyBase:> LAZY_BASE =
+ struct
+ type 'a lazy = unit -> 'a
+
+ exception Undefined
+
+ fun delay f = f
+
+ fun force f = f()
+
+ val undefined = fn () => raise Undefined
+ end
+
+structure LazyMemoBase:> LAZY_BASE =
+ struct
+
+ datatype 'a susp = NotYet of unit -> 'a
+ | Done of 'a
+
+ type 'a lazy = unit -> 'a susp ref
+
+ exception Undefined
+
+ fun delay f =
+ let
+ val r = ref (NotYet f)
+ in
+ fn () => r
+ end
+
+ fun force f =
+ case f() of
+ ref (Done x) => x
+ | r as ref (NotYet f') =>
+ let
+ val a = f'()
+ in
+ r := Done a
+ ; a
+ end
+
+ val undefined = fn () => raise Undefined
+ end
+
+functor LazyFn(B: LAZY_BASE): LAZY' =
+ struct
+
+ open B
+
+ fun inject x = delay (fn () => x)
+
+ fun isUndefined x =
+ (ignore (force x)
+ ; false)
+ handle Undefined => true
+
+ fun toString f x = if isUndefined x then "_|_" else f (force x)
+
+ fun eqBy p (x,y) = p(force x,force y)
+ fun eq (x,y) = eqBy op= (x,y)
+ fun compare p (x,y) = p(force x,force y)
+
+ structure Ops =
+ struct
+ val ! = force
+ val ? = inject
+ end
+
+ fun map f x = delay (fn () => f (force x))
+
+ end
+
+structure Lazy' = LazyFn(LazyBase)
+structure LazyMemo = LazyFn(LazyMemoBase)
diff --git a/samples/supercollider/BCR2000.sc b/samples/supercollider/BCR2000.sc
new file mode 100644
index 0000000..f8f96a7
--- /dev/null
+++ b/samples/supercollider/BCR2000.sc
@@ -0,0 +1,114 @@
+BCR2000 {
+ var controls,
+ controlBuses,
+ rangedControlBuses,
+ responders
+ ;
+
+ *new {
+ ^super.new.init;
+ }
+
+ init {
+ controls = Dictionary.new(108);
+ controlBuses = Dictionary.new(108);
+ rangedControlBuses = Dictionary.new(108);
+
+ this.createCCResponders;
+ }
+
+ createCCResponders {
+ responders = Array.fill(108, {|i|
+ CCResponder({|src, chan, num, val|
+ [src, chan, num, val].postln;
+
+ // Write to controls
+ controls.put(i + 1, val);
+
+ // Write to bus (converted to scalar 0..1)
+ controlBuses.put(i + 1, Bus.control(Server.default));
+ controlBuses.at(i + 1).value = val / 127;
+ },
+ // Adjust values as/if needed
+ nil, // src
+ nil, // chan
+ nil, // num
+ nil // value
+ )
+ });
+ }
+
+ // Value from BCR
+ at {arg controlNum;
+ ^controls.at(controlNum)
+ }
+
+ // Convert to 0..1
+ scalarAt {arg controlNum;
+ ^controls.at(controlNum) / 127
+ }
+
+ // Get a bus
+ busAt {arg controlNum;
+ ^controlBuses.at(controlNum)
+ }
+
+ /*
+ busRangeAt(arg controlNum, lo, hi;
+ if (rangedControlBuses.at(controlNum).isNil, {
+ rangedControlBuses.put(controlNum, Bus.control(Server.default))
+ });
+
+ // Left to right order of operations
+ //rangedControlBuses.put(
+ bus.value = hi - lo * controls.at(controlNum) + lo;
+
+ ^bus
+ }
+ */
+}
+
+/* Scratch
+Dictionary
+b = BCR2000();
+b.at(4);
+b.scalarAt(4);
+b.controls[5].get;
+throw
+z = Dictionary.new(2);
+z.at(\1);
+Array.fill(10, {|i| i.postln;})
+(2 + 3).asSymbol;
+
+
+SynthDef(\x, {
+ arg amp = 0.01,
+ freq = 1200,
+ modDepth = 0.7,
+ modFreq = 2
+ ;
+
+ var
+ carrier,
+ modulator
+ ;
+
+ modulator = SinOsc.ar(modFreq, mul: modDepth);
+ carrier = Saw.ar(freq, add: modulator, mul: amp);
+
+ Out.ar([0,1], carrier)
+}).store;
+
+
+x = Synth(\x);
+x.set(\modDepth, 1);
+x.set(\modFreq, 64);
+
+x.map(\modFreq, b.busAt(
+
+
+
+ControlSpec
+*/
+
+
diff --git a/samples/tea/foo.tea b/samples/tea/foo.tea
new file mode 100644
index 0000000..60285e9
--- /dev/null
+++ b/samples/tea/foo.tea
@@ -0,0 +1 @@
+<% template foo() %> \ No newline at end of file
diff --git a/samples/tex/reedthesis.cls b/samples/tex/reedthesis.cls
new file mode 100644
index 0000000..adddc14
--- /dev/null
+++ b/samples/tex/reedthesis.cls
@@ -0,0 +1,245 @@
+%
+% This file is copyright (C) 2003 Sam Noble. It may be modified so long
+% as my name is not removed and the modifier adds his name to the file.
+% Redistribution permitted.
+%
+% 27 Jan 2004 Sam Noble Removed tocbibind dependency.
+% 04 Dec 2001 Sam Noble Class file
+% 03 Sep 1995 David Perkinson Title Page
+% Acknowledgements Page, David Perkinson & Sam Noble
+% May 2005 Patrick Carlisle Table of contents chapter definition
+% 2004-2005 Ben Salzberg (BTS) a variety of tweaks here and in the template
+%
+% Oddities:
+%
+% We *ASSUME* that any time \cleardoublepage is called
+% we actually want a blank back side with NO page number/heading
+%
+% Minor bug -- seems to be a more general LaTeX thing:
+% If you use \frontmatter \mainmatter without any chapters inbetween
+% be prepared to have the page numbering messed up. Not a big deal,
+% but I'm not sure how to fix it.
+%
+%
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesClass{reedthesis}[2004/01/27 The Reed College Thesis Class]
+\DeclareOption*{\PassOptionsToClass{\CurrentOption}{book}}
+\ProcessOptions\relax
+\LoadClass{book}
+\RequirePackage{fancyhdr}
+
+% This gives us rules below the headers
+\AtBeginDocument{%
+ \fancyhf{}
+ \fancyhead[LE,RO]{\thepage}
+% \fancyhead[RE]{\slshape \leftmark}
+% \fancyhead[LO]{\slshape \rightmark}
+% The above makes your headers in all caps. If you would like different headers, choose one of the following options (be sure to remove the % symbol from both the right and left headers):
+ \fancyhead[RE]{\slshape \nouppercase \leftmark} % This makes the headers on the RIGHT side pages be italic and use lowercase With Capitals When Specified.
+ \fancyhead[LO]{\slshape \nouppercase \rightmark} % This does the same thing to the LEFT side pages
+% or
+% \fancyhead[RE]{\scshape \leftmark} % The RIGHT headers will be in small caps.
+% \fancyhead[LO]{\scshape \rightmark} % And so will the LEFT headers
+ \pagestyle{fancy}
+
+% Psych majors: You do not need the following six lines, as it conflicts with apacite, so comment them out.
+ \let\oldthebibliography=\thebibliography
+ \let\endoldthebibliography=\endthebibliography
+ \renewenvironment{thebibliography}[1]{
+ \oldthebibliography{#1}
+ \addcontentsline{toc}{chapter}{\bibname}
+ }{\endoldthebibliography}
+ %%%%%% end of things for psych majors to comment out
+
+ \let\oldtheindex=\theindex
+ \let\endoldtheindex=\endtheindex
+ \renewenvironment{theindex}{
+ \oldtheindex
+ \addcontentsline{toc}{chapter}{\indexname}
+ }{\endoldtheindex}
+}
+
+% Stolen from book.cls and modified
+\let\RToldchapter\chapter
+\renewcommand{\chapter}{\if@openright\RTcleardoublepage
+ \else\clearpage\fi
+ \thispagestyle{empty}%
+ \global\@topnum\z@
+ \@afterindentfalse
+ \secdef\@chapter\@schapter}
+
+% Stolen from book.cls PBC 5/12/05
+% Using this to actually show "Chapter 1" in TOC instead of "1"
+\def\@chapter[#1]#2{\ifnum \c@secnumdepth >\m@ne
+ \if@mainmatter
+ \refstepcounter{chapter}%
+ \typeout{\@chapapp\space\thechapter.}%
+ \addcontentsline{toc}{chapter}%
+ {\@chapapp\space\thechapter:\space#1}%
+ \else
+ \addcontentsline{toc}{chapter}{#1}%
+ \fi
+ \else
+ \addcontentsline{toc}{chapter}{#1}%
+ \fi
+ \chaptermark{#1}%
+ \addtocontents{lof}{\protect\addvspace{10\p@}}%
+ \addtocontents{lot}{\protect\addvspace{10\p@}}%
+ \if@twocolumn
+ \@topnewpage[\@makechapterhead{#2}]%
+ \else
+ \@makechapterhead{#2}%
+ \@afterheading
+ \fi}
+
+\newcommand{\RTcleardoublepage}{
+ \clearpage\if@twoside \ifodd\c@page\else
+ \thispagestyle{empty}\hbox{}\newpage
+ \if@twocolumn\hbox{}\newpage\fi\fi\fi}
+
+\let\RToldcleardoublepage\cleardoublepage
+\renewcommand{\cleardoublepage}{\RTcleardoublepage}
+
+% adjust margins for binding (changed 2007-04-24 tgp)
+\setlength{\oddsidemargin}{.5in}
+\setlength{\evensidemargin}{0in}
+\setlength{\textwidth}{6.0in}
+\setlength{\textheight}{9.0in}
+\setlength\topmargin{0in}
+\addtolength\topmargin{-\headheight}
+\addtolength\topmargin{-\headsep}
+
+%\setlength{\oddsidemargin}{.6in}
+%\setlength{\evensidemargin}{0in}
+%\setlength{\textwidth}{5.9in}
+%\setlength\topmargin{0in}
+%\addtolength\headheight{2.5pt}
+%\addtolength\topmargin{-\headheight}
+%\addtolength\topmargin{-\headsep}
+%\addtolength\textheight{1in}
+%\addtolength\textheight{\headheight}
+%\addtolength\textheight{\headsep}
+
+\def\division#1{\gdef \@division{#1}}
+\def\@division{\@latex@warning@no@line{No \noexpand\division given}}
+\def\department#1{\gdef \@department{#1}}
+\def\@department{\@latex@warning@no@line{No \noexpand\department given}}
+\def\thedivisionof#1{\gdef \@thedivisionof{#1}}
+\def\@thedivisionof{The Division of}
+\def\approvedforthe#1{\gdef \@approvedforthe{#1}}
+\def\@approvedforthe{Division}
+\def\advisor#1{\gdef \@advisor{#1}}
+\def\@advisor{\@latex@warning@no@line{No \noexpand\advisor given}}
+\def\altadvisor#1{\gdef \@altadvisor{#1} \@altadvisortrue}
+\global\let\@altadvisor\@empty
+\newif\if@altadvisor
+\@altadvisorfalse
+
+\renewcommand{\contentsname}{Table of Contents}
+\renewcommand{\bibname}{References}
+
+\renewcommand\l@chapter[2]{%
+ \ifnum \c@tocdepth >\m@ne
+ \addpenalty{-\@highpenalty}%
+ \vskip 1.0em \@plus\p@
+ \setlength\@tempdima{1.5em}%
+ \begingroup
+ \parindent \z@ \rightskip \@pnumwidth
+ \parfillskip -\@pnumwidth
+ \leavevmode \bfseries
+ \advance\leftskip\@tempdima
+ \hskip -\leftskip
+ #1\nobreak\normalfont
+ \leaders\hbox{$\m@th \mkern \@dotsep mu\hbox{.}\mkern \@dotsep mu$}\hfill
+ \nobreak\hb@xt@\@pnumwidth{\bfseries \hss #2}\par
+ \penalty\@highpenalty
+ \endgroup
+ \fi}
+
+\newenvironment{abstract}{%
+ \if@twocolumn
+ \@restonecoltrue\onecolumn
+ \else
+ \@restonecolfalse
+ \fi
+ \chapter[Abstract]{}
+ \begin{center}
+ {\fontsize{14}{16}\selectfont \bfseries Abstract}
+ \end{center}
+ \fontsize{12}{14}\selectfont
+}{\clearpage \if@restonecol\twocolumn\fi}%
+
+\ifx\@pdfoutput\@undefined
+ \newcommand{\RTpercent}{\@percentchar\space}
+ \AtBeginDvi{\special{!\RTpercent Reed College LaTeX Thesis Class 2001/12/04 SN}}
+ \AtBeginDvi{\special{rawpostscript \RTpercent Reed College LaTeX Thesis Class 2001/12/04 SN}}
+\else
+ \AtEndDocument{\pdfinfo{/Creator (Reed College LaTeX Thesis Class 2001/12/04 SN)}}
+\fi
+
+% I hacked the title page to all be the same font size
+% as requested by the library, BTS 2005
+
+\renewcommand{\maketitle}{%
+ {\pagestyle{empty}
+ \fontsize{12}{14}\selectfont
+ \begin{titlepage}
+ \newpage
+ \let\footnotesize\small
+ \let\footnoterule\relax
+ \let \footnote \thanks
+
+ \baselineskip = 1.4\baselineskip
+ \setbox0=\hbox{of the Requirements for the Degree}
+
+ \begin{center}
+ \setcounter{page}{1}
+ \null\vfil
+ {\fontsize{12}{14}\selectfont \@title}
+ \vfil
+ \centerline{\hbox to \wd0 {\hbox{}\hrulefill\hbox{}}}
+ \vfil
+ A Thesis \\
+ Presented to \\
+ \@thedivisionof \ \@division \\
+ Reed College
+ \vfil
+ \centerline{\hbox to \wd0 {\hbox{}\hrulefill\hbox{}}}
+ \vfil
+ In Partial Fulfillment \\
+ of the Requirements for the Degree \\
+ Bachelor of Arts
+ \vfil
+ \centerline{\hbox to \wd0 {\hbox{}\hrulefill\hbox{}}}
+ \bigskip
+ \centerline{}
+ \bigskip
+ {\fontsize{12}{14}\selectfont \lineskip .75em
+ \begin{tabular}[t]{c}%
+ \@author
+ \end{tabular}\par}
+ \vskip 1.5em
+ {\fontsize{12}{14}\selectfont \@date \par}
+ \end{center}\par
+ \end{titlepage}
+
+%% Approved for the division page
+ \cleardoublepage
+ {\fontsize{12}{14}
+ \setbox0=\hbox{Approved for the \@approvedforthe}
+ \thispagestyle{empty}
+ \null\vfil % just below center of page
+ \par\vskip 6cm % below center, not center
+ \centerline{\copy0} % approved
+ \centerline{(\@department)} %major
+ \vskip 1cm %space to sign
+ \centerline{\makebox[\wd0][c]{\hrulefill}
+ \if@altadvisor \makebox[.5in]{} \makebox[\wd0][c]{\hrulefill} \fi}
+ \centerline{\makebox[\wd0][c]{\@advisor}
+ \if@altadvisor \makebox[.5in]{} \makebox[\wd0][c]{\@altadvisor} \fi}
+ \par\vfil\null}
+ \cleardoublepage
+ }
+}
+
diff --git a/samples/text/README b/samples/text/README
new file mode 100644
index 0000000..082a4aa
--- /dev/null
+++ b/samples/text/README
@@ -0,0 +1,2 @@
+README
+======
diff --git a/samples/text/blank b/samples/text/blank
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/text/blank
diff --git a/samples/text/defu.nkt b/samples/text/defu.nkt
new file mode 100644
index 0000000..0e8f207
--- /dev/null
+++ b/samples/text/defu.nkt
@@ -0,0 +1 @@
+defunkt
diff --git a/samples/text/dump.sql b/samples/text/dump.sql
new file mode 100644
index 0000000..2e959c0
--- /dev/null
+++ b/samples/text/dump.sql
@@ -0,0 +1 @@
+DROP ALL TABLES
diff --git a/samples/text/file.json b/samples/text/file.json
new file mode 100644
index 0000000..6d95903
--- /dev/null
+++ b/samples/text/file.json
@@ -0,0 +1 @@
+{"foo": "bar"}
diff --git a/samples/text/file.txt b/samples/text/file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/text/file.txt
diff --git a/samples/text/foo.txt b/samples/text/foo.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/samples/text/foo.txt
@@ -0,0 +1 @@
+foo
diff --git a/samples/text/md b/samples/text/md
new file mode 100644
index 0000000..3562498
--- /dev/null
+++ b/samples/text/md
@@ -0,0 +1,2 @@
+MD
+==
diff --git a/samples/text/script.foo b/samples/text/script.foo
new file mode 100755
index 0000000..f94d740
--- /dev/null
+++ b/samples/text/script.foo
@@ -0,0 +1,2 @@
+#!/bin/foo
+???
diff --git a/samples/text/txt b/samples/text/txt
new file mode 100644
index 0000000..3de705a
--- /dev/null
+++ b/samples/text/txt
@@ -0,0 +1 @@
+Text
diff --git a/samples/turing/turing.t b/samples/turing/turing.t
new file mode 100644
index 0000000..c68f8ee
--- /dev/null
+++ b/samples/turing/turing.t
@@ -0,0 +1,19 @@
+% Accepts a number and calculates its factorial
+
+function factorial (n: int) : real
+ if n = 0 then
+ result 1
+ else
+ result n * factorial (n - 1)
+ end if
+end factorial
+
+var n: int
+loop
+ put "Please input an integer: " ..
+ get n
+ exit when n >= 0
+ put "Input must be a non-negative integer."
+end loop
+
+put "The factorial of ", n, " is ", factorial (n)
diff --git a/samples/verilog/sha-256-functions.v b/samples/verilog/sha-256-functions.v
new file mode 100644
index 0000000..ec064dd
--- /dev/null
+++ b/samples/verilog/sha-256-functions.v
@@ -0,0 +1,86 @@
+/*
+*
+* Copyright (c) 2011 fpgaminer@bitcoin-mining.com
+*
+*
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+
+`timescale 1ns/1ps
+
+module e0 (x, y);
+
+ input [31:0] x;
+ output [31:0] y;
+
+ assign y = {x[1:0],x[31:2]} ^ {x[12:0],x[31:13]} ^ {x[21:0],x[31:22]};
+
+endmodule
+
+
+module e1 (x, y);
+
+ input [31:0] x;
+ output [31:0] y;
+
+ assign y = {x[5:0],x[31:6]} ^ {x[10:0],x[31:11]} ^ {x[24:0],x[31:25]};
+
+endmodule
+
+
+module ch (x, y, z, o);
+
+ input [31:0] x, y, z;
+ output [31:0] o;
+
+ assign o = z ^ (x & (y ^ z));
+
+endmodule
+
+
+module maj (x, y, z, o);
+
+ input [31:0] x, y, z;
+ output [31:0] o;
+
+ assign o = (x & y) | (z & (x | y));
+
+endmodule
+
+
+module s0 (x, y);
+
+ input [31:0] x;
+ output [31:0] y;
+
+ assign y[31:29] = x[6:4] ^ x[17:15];
+ assign y[28:0] = {x[3:0], x[31:7]} ^ {x[14:0],x[31:18]} ^ x[31:3];
+
+endmodule
+
+
+module s1 (x, y);
+
+ input [31:0] x;
+ output [31:0] y;
+
+ assign y[31:22] = x[16:7] ^ x[18:9];
+ assign y[21:0] = {x[6:0],x[31:17]} ^ {x[8:0],x[31:19]} ^ x[31:10];
+
+endmodule
+
+
diff --git a/samples/vhdl/foo.vhd b/samples/vhdl/foo.vhd
new file mode 100644
index 0000000..32ccc9b
--- /dev/null
+++ b/samples/vhdl/foo.vhd
@@ -0,0 +1,14 @@
+-- VHDL example file
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity inverter is
+ port(a : in std_logic;
+ b : out std_logic);
+end entity;
+
+architecture rtl of inverter is
+begin
+ b <= not a;
+end architecture;
diff --git a/samples/viml/.gvimrc b/samples/viml/.gvimrc
new file mode 100644
index 0000000..d4efd33
--- /dev/null
+++ b/samples/viml/.gvimrc
@@ -0,0 +1,2 @@
+" no toolbar
+set guioptions-=T
diff --git a/samples/viml/.vimrc b/samples/viml/.vimrc
new file mode 100644
index 0000000..2e9f626
--- /dev/null
+++ b/samples/viml/.vimrc
@@ -0,0 +1,8 @@
+set nocompatible
+set ignorecase
+set incsearch
+set smartcase
+set showmatch
+set showcmd
+
+syntax on
diff --git a/samples/visual-basic/cApplication.cls b/samples/visual-basic/cApplication.cls
new file mode 100644
index 0000000..5a58a6b
--- /dev/null
+++ b/samples/visual-basic/cApplication.cls
@@ -0,0 +1,240 @@
+VERSION 1.0 CLASS
+BEGIN
+ MultiUse = -1 'True
+ Persistable = 0 'NotPersistable
+ DataBindingBehavior = 0 'vbNone
+ DataSourceBehavior = 0 'vbNone
+ MTSTransactionMode = 0 'NotAnMTSObject
+END
+Attribute VB_Name = "cApplication"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = True
+Attribute VB_PredeclaredId = False
+Attribute VB_Exposed = False
+'*************************************************************************************************************************************************************************************************************************************************
+'
+' Copyright (c) David Briant 2009-2012 - All rights reserved
+'
+'*************************************************************************************************************************************************************************************************************************************************
+
+Option Explicit
+
+Private Declare Function apiSetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
+Private Declare Function apiGlobalAddAtom Lib "kernel32" Alias "GlobalAddAtomA" (ByVal lpString As String) As Long
+Private Declare Function apiSetForegroundWindow Lib "user32" Alias "SetForegroundWindow" (ByVal hwnd As Long) As Long
+
+Private myMouseEventsForm As fMouseEventsForm
+Private WithEvents myAST As cTP_AdvSysTray
+Attribute myAST.VB_VarHelpID = -1
+
+Private myClassName As String
+Private myWindowName As String
+Private Const TEN_MILLION As Single = 10000000
+
+Private WithEvents myListener As VLMessaging.VLMMMFileListener
+Attribute myListener.VB_VarHelpID = -1
+Private WithEvents myMMFileTransports As VLMessaging.VLMMMFileTransports
+Attribute myMMFileTransports.VB_VarHelpID = -1
+
+Private myMachineID As Long
+
+Private myRouterSeed As Long
+Private myRouterIDsByMMTransportID As New Dictionary
+Private myMMTransportIDsByRouterID As New Dictionary
+
+Private myDirectoryEntriesByIDString As New Dictionary
+
+Private Const GET_ROUTER_ID As String = "GET_ROUTER_ID"
+Private Const GET_ROUTER_ID_REPLY As String = "GET_ROUTER_ID_REPLY"
+Private Const REGISTER_SERVICE As String = "REGISTER_SERVICE"
+Private Const REGISTER_SERVICE_REPLY As String = "REGISTER_SERVICE_REPLY"
+Private Const UNREGISTER_SERVICE As String = "UNREGISTER_SERVICE"
+Private Const UNREGISTER_SERVICE_REPLY As String = "UNREGISTER_SERVICE_REPLY"
+Private Const GET_SERVICES As String = "GET_SERVICES"
+Private Const GET_SERVICES_REPLY As String = "GET_SERVICES_REPLY"
+
+
+'*************************************************************************************************************************************************************************************************************************************************
+' Initialize / Release
+'*************************************************************************************************************************************************************************************************************************************************
+
+Private Sub class_Initialize()
+ Dim atomID As Long
+ Randomize
+ ' hide us from the Applications list in the Windows Task Manager
+ App.TaskVisible = False
+
+ ' listen for connections
+ myClassName = "VLMMachineRouter" & CStr(Int(Rnd() * TEN_MILLION) + 1)
+ Randomize
+ myWindowName = "VLMMachineRouter" & CStr(Int(Rnd() * TEN_MILLION) + 1)
+ Set myListener = New VLMMMFileListener
+ myListener.listenViaNamedWindow myClassName, myWindowName, 1024 * 8
+ Set myMMFileTransports = New VLMMMFileTransports
+ myRouterSeed = 1
+
+ ' create tray icon
+ Set myMouseEventsForm = New fMouseEventsForm
+ Set myAST = New cTP_AdvSysTray
+ myAST.create myMouseEventsForm, myMouseEventsForm.icon, "VLM Directory"
+ 'myAST.showBalloon "Current Shell32.dll version is " & myAST.shellVersion & ".x", "AdvSysTray VB Class", NIIF_INFO
+
+ ' make myself easily found
+ apiSetProp myMouseEventsForm.hwnd, "IsVLMachineRouter", 1
+ apiSetProp myMouseEventsForm.hwnd, "WindowNameAtom", apiGlobalAddAtom(myWindowName)
+ apiSetProp myMouseEventsForm.hwnd, "ClassNameAtom", apiGlobalAddAtom(myClassName)
+
+End Sub
+
+Sub shutdown()
+ myAST.destroy
+ Set myAST = Nothing
+ Unload myMouseEventsForm
+ Set myMouseEventsForm = Nothing
+End Sub
+
+Private Sub myAST_RButtonUp()
+ Dim epm As New cTP_EasyPopupMenu, menuItemSelected As Long
+ 'SetForegroundWindow myMouseEventsForm.hwnd
+' epm.addMenuItem "Main form...", MF_STRING, 1
+' epm.createSubmenu "Radio items"
+' epm.addSubmenuItem "Radio item 1", MF_STRING, 2
+' epm.addSubmenuItem "Radio item 2", MF_STRING, 3
+' epm.addSubmenuItem "Radio item 3", MF_STRING, 4
+' epm.checkRadioItem 0, 2, 1
+' epm.addMenuItem "", MF_SEPARATOR, 0
+' epm.addMenuItem "Disabled item", MF_GRAYED, 5
+' epm.addMenuItem "Checked item", MF_CHECKED, 6
+' epm.addMenuItem "", MF_SEPARATOR, 0
+ epm.addMenuItem "Exit", MF_STRING, 12
+ apiSetForegroundWindow myMouseEventsForm.hwnd
+ menuItemSelected = epm.trackMenu(myMouseEventsForm.hwnd)
+ Select Case menuItemSelected
+ Case 12
+ Set epm = Nothing
+ globalShutdown
+ End Select
+End Sub
+
+Private Sub myListener_newConnection(ByVal newTransport As VLMessaging.VLMMMFileTransport, oReceived As Boolean)
+ Dim id As Long
+ oReceived = True
+ id = myMMFileTransports.addTransport(newTransport)
+End Sub
+
+Private Function messageFromBytes(buffer() As Byte) As VLMMessage
+ Dim i1 As Long, i2 As Long, messageArray As Variant, message As New VLMMessage
+ DBGetArrayBounds buffer, 1, i1, i2
+ messageArray = g_VLMUtils.BytesAsVariant(buffer, i2 + 1, 1)
+ message.fromMessageArray messageArray
+ Set messageFromBytes = message
+End Function
+
+Private Function messageToBytes(message As VLMMessage) As Byte()
+ Dim messageArray As Variant, length As Long, buffer() As Byte
+ message.toMessageArray messageArray
+ length = g_VLMUtils.LengthOfVariantAsBytes(messageArray)
+ DBCreateNewArrayOfBytes buffer, 1, length
+ g_VLMUtils.VariantAsBytes messageArray, buffer, length + 1, 1
+ messageToBytes = buffer
+End Function
+
+Private Sub myMMFileTransports_bytesArrived(ByVal id As Long, buffer() As Byte, oReceived As Boolean)
+ Dim message As VLMMessage, toAddress As VLMAddress
+ oReceived = True
+ Set message = messageFromBytes(buffer)
+ Set toAddress = message.toAddress
+ On Error GoTo errorHandler
+ If (toAddress.MachineID = myMachineID Or toAddress.MachineID = 0) And toAddress.RouterID = 1 And toAddress.AgentID = 1 Then
+ handleMessageToRouter id, message
+ Else
+ routeMessage message
+ End If
+Exit Sub
+errorHandler:
+ MsgBox Err.Description & ", " & Erl
+End Sub
+
+Sub handleMessageToRouter(MMFileTransportID As Long, message As VLMMessage)
+ Dim reply As VLMMessage, transport As VLMMMFileTransport, RouterID As Long, address As New VLMAddress
+ Dim IDString As String, vs As Variant, i As Long, entries As New Collection, answer1D As Variant
+
+ Select Case True
+
+ Case message.subject = GET_ROUTER_ID
+ If myRouterIDsByMMTransportID.Exists(MMFileTransportID) Then
+ RouterID = myRouterIDsByMMTransportID(MMFileTransportID)
+ Else
+ myRouterSeed = myRouterSeed + 1
+ RouterID = myRouterSeed
+ myRouterIDsByMMTransportID(MMFileTransportID) = RouterID
+ myMMTransportIDsByRouterID(RouterID) = MMFileTransportID
+ End If
+ Set reply = message.reply
+ reply.subject = GET_ROUTER_ID_REPLY
+ reply.Contents = RouterID
+ Set transport = myMMFileTransports.transport(MMFileTransportID)
+ transport.send messageToBytes(reply)
+
+ Case message.subject = REGISTER_SERVICE
+ address.initialise CLng(message.Contents(1)(1)), CLng(message.Contents(1)(2)), CLng(message.Contents(1)(3))
+ myDirectoryEntriesByIDString(directoryEntryIDString(CStr(message.Contents(2)), address)) = message.Contents
+ Set reply = message.reply
+ reply.subject = REGISTER_SERVICE_REPLY
+ Set transport = myMMFileTransports.transport(MMFileTransportID)
+ transport.send messageToBytes(reply)
+
+ Case message.subject = UNREGISTER_SERVICE
+ address.initialise CLng(message.Contents(1)(1)), CLng(message.Contents(1)(2)), CLng(message.Contents(1)(3))
+ IDString = directoryEntryIDString(CStr(message.Contents(2)), address)
+ If myDirectoryEntriesByIDString.Exists(IDString) Then myDirectoryEntriesByIDString.Remove IDString
+ Set reply = message.reply
+ reply.subject = UNREGISTER_SERVICE_REPLY
+ Set transport = myMMFileTransports.transport(MMFileTransportID)
+ transport.send messageToBytes(reply)
+
+ Case message.subject = GET_SERVICES
+ vs = myDirectoryEntriesByIDString.Items
+ For i = 0 To UBound(vs)
+ If IsEmpty(message.Contents) Then
+ entries.Add vs(i)
+ Else
+ If vs(i)(2) = message.Contents Then entries.Add vs(i)
+ End If
+ Next
+ If entries.Count > 0 Then
+ ReDim answer1D(1 To entries.Count)
+ For i = 1 To entries.Count
+ answer1D(i) = entries(i)
+ Next
+ End If
+ Set reply = message.reply
+ reply.subject = GET_SERVICES_REPLY
+ reply.Contents = answer1D
+ Set transport = myMMFileTransports.transport(MMFileTransportID)
+ transport.send messageToBytes(reply)
+
+ End Select
+
+End Sub
+
+Sub routeMessage(message As VLMMessage)
+ Dim buffer() As Byte, transport As VLMMMFileTransport
+ If message.toAddress.MachineID <> 0 And message.toAddress.MachineID <> myMachineID Then
+ ' route to a remote machine
+ Else
+ ' for the moment just route between MMFileTransports
+ If myMMTransportIDsByRouterID.Exists(message.toAddress.RouterID) Then
+ Set transport = myMMFileTransports(myMMTransportIDsByRouterID(message.toAddress.RouterID))
+ transport.send messageToBytes(message)
+ End If
+ End If
+End Sub
+
+Function directoryEntryIDString(serviceType As String, address As VLMAddress)
+ directoryEntryIDString = serviceType & "<" & address.MachineID & "," & address.RouterID & "," & address.AgentID & ">"
+End Function
+
+Private Sub myMMFileTransports_disconnecting(ByVal id As Long, oReceived As Boolean)
+ oReceived = True
+End Sub
diff --git a/samples/xml/net_docfile.xml b/samples/xml/net_docfile.xml
new file mode 100644
index 0000000..4cda7b9
--- /dev/null
+++ b/samples/xml/net_docfile.xml
@@ -0,0 +1,1121 @@
+<?xml version="1.0"?>
+<doc>
+ <assembly>
+ <name>ReactiveUI</name>
+ </assembly>
+ <members>
+ <member name="T:ReactiveUI.IObservedChange`2">
+ <summary>
+ IObservedChange is a generic interface that replaces the non-generic
+ PropertyChangedEventArgs. Note that it is used for both Changing (i.e.
+ 'before change') and Changed Observables. In the future, this interface
+ will be Covariant which will allow simpler casting between specific and
+ generic changes.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IObservedChange`2.Sender">
+ <summary>
+ The object that has raised the change.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IObservedChange`2.PropertyName">
+ <summary>
+ The name of the property that has changed on Sender.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IObservedChange`2.Value">
+ <summary>
+ The value of the property that has changed. IMPORTANT NOTE: This
+ property is often not set for performance reasons, unless you have
+ explicitly requested an Observable for a property via a method such
+ as ObservableForProperty. To retrieve the value for the property,
+ use the Value() extension method.
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.IReactiveNotifyPropertyChanged">
+ <summary>
+ IReactiveNotifyPropertyChanged represents an extended version of
+ INotifyPropertyChanged that also exposes Observables.
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.IEnableLogger">
+ <summary>
+ IEnableLogger is a dummy interface - attaching it to any class will give
+ you access to the Log() method.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.IReactiveNotifyPropertyChanged.SuppressChangeNotifications">
+ <summary>
+ When this method is called, an object will not fire change
+ notifications (neither traditional nor Observable notifications)
+ until the return value is disposed.
+ </summary>
+ <returns>An object that, when disposed, reenables change
+ notifications.</returns>
+ </member>
+ <member name="P:ReactiveUI.IReactiveNotifyPropertyChanged.Changing">
+ <summary>
+ Represents an Observable that fires *before* a property is about to
+ be changed. Note that this should not fire duplicate change notifications if a
+ property is set to the same value multiple times.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveNotifyPropertyChanged.Changed">
+ <summary>
+ Represents an Observable that fires *after* a property has changed.
+ Note that this should not fire duplicate change notifications if a
+ property is set to the same value multiple times.
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.IReactiveNotifyPropertyChanged`1">
+ <summary>
+ IReactiveNotifyPropertyChanged of TSender is a helper interface that adds
+ typed versions of Changing and Changed.
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.IReactiveCollection">
+ <summary>
+ IReactiveCollection represents a collection that can notify when its
+ contents are changed (either items are added/removed, or the object
+ itself changes).
+
+ It is important to implement the Changing/Changed from
+ IReactiveNotifyPropertyChanged semantically as "Fire when *anything* in
+ the collection or any of its items have changed, in any way".
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.ItemsAdded">
+ <summary>
+ Fires when items are added to the collection, once per item added.
+ Functions that add multiple items such AddRange should fire this
+ multiple times. The object provided is the item that was added.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.BeforeItemsAdded">
+ <summary>
+ Fires before an item is going to be added to the collection.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.ItemsRemoved">
+ <summary>
+ Fires once an item has been removed from a collection, providing the
+ item that was removed.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.BeforeItemsRemoved">
+ <summary>
+ Fires before an item will be removed from a collection, providing
+ the item that will be removed.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.CollectionCountChanged">
+ <summary>
+ Fires whenever the number of items in a collection has changed,
+ providing the new Count.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.CollectionCountChanging">
+ <summary>
+ Fires before a collection is about to change, providing the previous
+ Count.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.ItemChanging">
+ <summary>
+ Provides Item Changed notifications for any item in collection that
+ implements IReactiveNotifyPropertyChanged. This is only enabled when
+ ChangeTrackingEnabled is set to True.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.ItemChanged">
+ <summary>
+ Provides Item Changing notifications for any item in collection that
+ implements IReactiveNotifyPropertyChanged. This is only enabled when
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.IReactiveCollection.ChangeTrackingEnabled">
+ <summary>
+ Enables the ItemChanging and ItemChanged properties; when this is
+ enabled, whenever a property on any object implementing
+ IReactiveNotifyPropertyChanged changes, the change will be
+ rebroadcast through ItemChanging/ItemChanged.
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.IReactiveCollection`1">
+ <summary>
+ IReactiveCollection of T is the typed version of IReactiveCollection and
+ adds type-specified versions of Observables
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.IMessageBus">
+ <summary>
+ IMessageBus represents an object that can act as a "Message Bus", a
+ simple way for ViewModels and other objects to communicate with each
+ other in a loosely coupled way.
+
+ Specifying which messages go where is done via a combination of the Type
+ of the message as well as an additional "Contract" parameter; this is a
+ unique string used to distinguish between messages of the same Type, and
+ is arbitrarily set by the client.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.IMessageBus.Listen``1(System.String)">
+ <summary>
+ Listen provides an Observable that will fire whenever a Message is
+ provided for this object via RegisterMessageSource or SendMessage.
+ </summary>
+ <typeparam name="T">The type of the message to listen to.</typeparam>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <returns></returns>
+ </member>
+ <member name="M:ReactiveUI.IMessageBus.IsRegistered(System.Type,System.String)">
+ <summary>
+ Determins if a particular message Type is registered.
+ </summary>
+ <typeparam name="T">The type of the message.</typeparam>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <returns>True if messages have been posted for this message Type.</returns>
+ </member>
+ <member name="M:ReactiveUI.IMessageBus.RegisterMessageSource``1(System.IObservable{``0},System.String,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Registers an Observable representing the stream of messages to send.
+ Another part of the code can then call Listen to retrieve this
+ Observable.
+ </summary>
+ <typeparam name="T">The type of the message to listen to.</typeparam>
+ <param name="source">An Observable that will be subscribed to, and a
+ message sent out for each value provided.</param>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ </member>
+ <member name="M:ReactiveUI.IMessageBus.SendMessage``1(``0,System.String,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Sends a single message using the specified Type and contract.
+ Consider using RegisterMessageSource instead if you will be sending
+ messages in response to other changes such as property changes
+ or events.
+ </summary>
+ <typeparam name="T">The type of the message to send.</typeparam>
+ <param name="message">The actual message to send</param>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ </member>
+ <member name="M:ReactiveUI.EnableLoggerMixin.Log(ReactiveUI.IEnableLogger)">
+ <summary>
+ Log returns the current logger object, which allows the object to
+ log messages with the type name attached.
+ </summary>
+ <returns></returns>
+ </member>
+ <member name="T:ReactiveUI.MemoizingMRUCache`2">
+ <summary>
+ This data structure is a representation of a memoizing cache - i.e. a
+ class that will evaluate a function, but keep a cache of recently
+ evaluated parameters.
+
+ Since this is a memoizing cache, it is important that this function be a
+ "pure" function in the mathematical sense - that a key *always* maps to
+ a corresponding return value.
+ </summary>
+ <typeparam name="TParam">The type of the parameter to the calculation function.</typeparam>
+ <typeparam name="TVal">The type of the value returned by the calculation
+ function.</typeparam>
+ </member>
+ <member name="M:ReactiveUI.MemoizingMRUCache`2.#ctor(System.Func{`0,System.Object,`1},System.Int32,System.Action{`1})">
+ <summary>
+ Constructor
+ </summary>
+ <param name="calculationFunc">The function whose results you want to cache,
+ which is provided the key value, and an Tag object that is
+ user-defined</param>
+ <param name="maxSize">The size of the cache to maintain, after which old
+ items will start to be thrown out.</param>
+ <param name="onRelease">A function to call when a result gets
+ evicted from the cache (i.e. because Invalidate was called or the
+ cache is full)</param>
+ </member>
+ <member name="M:ReactiveUI.MemoizingMRUCache`2.Get(`0,System.Object)">
+ <summary>
+ Evaluates the function provided, returning the cached value if possible
+ </summary>
+ <param name="key">The value to pass to the calculation function.</param>
+ <param name="context">An additional optional user-specific parameter.</param>
+ <returns></returns>
+ </member>
+ <member name="M:ReactiveUI.MemoizingMRUCache`2.Invalidate(`0)">
+ <summary>
+ Ensure that the next time this key is queried, the calculation
+ function will be called.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.MemoizingMRUCache`2.InvalidateAll">
+ <summary>
+ Invalidate all items in the cache
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.MemoizingMRUCache`2.CachedValues">
+ <summary>
+ Returns all values currently in the cache
+ </summary>
+ <returns></returns>
+ </member>
+ <member name="T:ReactiveUI.MessageBus">
+ <summary>
+ MessageBus represents an object that can act as a "Message Bus", a
+ simple way for ViewModels and other objects to communicate with each
+ other in a loosely coupled way.
+
+ Specifying which messages go where is done via a combination of the Type
+ of the message as well as an additional "Contract" parameter; this is a
+ unique string used to distinguish between messages of the same Type, and
+ is arbitrarily set by the client.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.MessageBus.Listen``1(System.String)">
+ <summary>
+ Listen provides an Observable that will fire whenever a Message is
+ provided for this object via RegisterMessageSource or SendMessage.
+ </summary>
+ <typeparam name="T">The type of the message to listen to.</typeparam>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <returns>An Observable representing the notifications posted to the
+ message bus.</returns>
+ </member>
+ <member name="M:ReactiveUI.MessageBus.IsRegistered(System.Type,System.String)">
+ <summary>
+ Determins if a particular message Type is registered.
+ </summary>
+ <param name="type">The Type of the message to listen to.</param>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <returns>True if messages have been posted for this message Type.</returns>
+ </member>
+ <member name="M:ReactiveUI.MessageBus.RegisterMessageSource``1(System.IObservable{``0},System.String,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Registers an Observable representing the stream of messages to send.
+ Another part of the code can then call Listen to retrieve this
+ Observable.
+ </summary>
+ <typeparam name="T">The type of the message to listen to.</typeparam>
+ <param name="source">An Observable that will be subscribed to, and a
+ message sent out for each value provided.</param>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <param name="scheduler">The scheduler on which to post the
+ notifications, RxApp.DeferredScheduler by default.</param>
+ </member>
+ <member name="M:ReactiveUI.MessageBus.SendMessage``1(``0,System.String,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Sends a single message using the specified Type and contract.
+ Consider using RegisterMessageSource instead if you will be sending
+ messages in response to other changes such as property changes
+ or events.
+ </summary>
+ <typeparam name="T">The type of the message to send.</typeparam>
+ <param name="message">The actual message to send</param>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <param name="scheduler">The scheduler on which to post the
+ notifications, RxApp.DeferredScheduler by default.</param>
+ </member>
+ <member name="P:ReactiveUI.MessageBus.Current">
+ <summary>
+ Returns the Current MessageBus from the RxApp global object.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.MessageBusMixins.RegisterViewModel``1(ReactiveUI.IMessageBus,``0,System.String)">
+ <summary>
+ Registers a ViewModel object to send property change
+ messages; this allows a ViewModel to listen to another ViewModel's
+ changes in a loosely-typed manner.
+ </summary>
+ <param name="source">The ViewModel to register</param>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <exception cref="T:System.Exception"><c>Exception</c>The registered ViewModel
+ must be the only instance (i.e. not in an ItemsControl)</exception>
+ </member>
+ <member name="M:ReactiveUI.MessageBusMixins.ListenToViewModel``1(ReactiveUI.IMessageBus,System.String)">
+ <summary>
+ Listens to a registered ViewModel's property change notifications.
+ </summary>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <returns>An Observable that fires when an object changes and
+ provides the property name that has changed.</returns>
+ </member>
+ <member name="M:ReactiveUI.MessageBusMixins.ViewModelForType``1(ReactiveUI.IMessageBus,System.String)">
+ <summary>
+ Return the current instance of the ViewModel with the specified
+ type.
+ </summary>
+ <param name="contract">A unique string to distinguish messages with
+ identical types (i.e. "MyCoolViewModel") - if the message type is
+ only used for one purpose, leave this as null.</param>
+ <returns>The ViewModel object registered for this type.</returns>
+ </member>
+ <member name="T:ReactiveUI.ObservableAsPropertyHelper`1">
+ <summary>
+ ObservableAsPropertyHelper is a class to help ViewModels implement
+ "output properties", that is, a property that is backed by an
+ Observable. The property will be read-only, but will still fire change
+ notifications. This class can be created directly, but is more often created via the
+ ToProperty and ObservableToProperty extension methods.
+
+ This class is also an Observable itself, so that output properties can
+ be chained - for example a "Path" property and a chained
+ "PathFileNameOnly" property.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ObservableAsPropertyHelper`1.#ctor(System.IObservable{`0},System.Action{`0},`0,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Constructs an ObservableAsPropertyHelper object.
+ </summary>
+ <param name="observable">The Observable to base the property on.</param>
+ <param name="onChanged">The action to take when the property
+ changes, typically this will call the ViewModel's
+ RaisePropertyChanged method.</param>
+ <param name="initialValue">The initial value of the property.</param>
+ <param name="scheduler">The scheduler that the notifications will be
+ provided on - this should normally be a Dispatcher-based scheduler
+ (and is by default)</param>
+ </member>
+ <member name="M:ReactiveUI.ObservableAsPropertyHelper`1.Default(`0,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Constructs a "default" ObservableAsPropertyHelper object. This is
+ useful for when you will initialize the OAPH later, but don't want
+ bindings to access a null OAPH at startup.
+ </summary>
+ <param name="initialValue">The initial (and only) value of the property.</param>
+ <param name="scheduler">The scheduler that the notifications will be
+ provided on - this should normally be a Dispatcher-based scheduler
+ (and is by default)</param>
+ </member>
+ <member name="P:ReactiveUI.ObservableAsPropertyHelper`1.Value">
+ <summary>
+ The last provided value from the Observable.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ObservableAsPropertyHelper`1.BindingException">
+ <summary>
+ Returns the Exception which has been provided by the Observable; normally
+ steps should be taken to ensure that Observables provided to OAPH should
+ never complete or fail.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.OAPHCreationHelperMixin.ObservableToProperty``2(``0,System.IObservable{``1},System.Linq.Expressions.Expression{System.Func{``0,``1}},``1,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Converts an Observable to an ObservableAsPropertyHelper and
+ automatically provides the onChanged method to raise the property
+ changed notification. The ToProperty method is semantically
+ equivalent to this method and is often more convenient.
+ </summary>
+ <param name="observable">The Observable to base the property on.</param>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ <param name="initialValue">The initial value of the property.</param>
+ <param name="scheduler">The scheduler that the notifications will be
+ provided on - this should normally be a Dispatcher-based scheduler
+ (and is by default)</param>
+ <returns>An initialized ObservableAsPropertyHelper; use this as the
+ backing field for your property.</returns>
+ </member>
+ <member name="M:ReactiveUI.OAPHCreationHelperMixin.ToProperty``2(System.IObservable{``1},``0,System.Linq.Expressions.Expression{System.Func{``0,``1}},``1,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Converts an Observable to an ObservableAsPropertyHelper and
+ automatically provides the onChanged method to raise the property
+ changed notification.
+ </summary>
+ <param name="source">The ReactiveObject that has the property</param>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ <param name="initialValue">The initial value of the property.</param>
+ <param name="scheduler">The scheduler that the notifications will be
+ provided on - this should normally be a Dispatcher-based scheduler
+ (and is by default)</param>
+ <returns>An initialized ObservableAsPropertyHelper; use this as the
+ backing field for your property.</returns>
+ </member>
+ <member name="T:ReactiveUI.ObservableAsyncMRUCache`2">
+ <summary>
+ ObservableAsyncMRUCache implements memoization for asynchronous or
+ expensive to compute methods. This memoization is an MRU-based cache
+ with a fixed limit for the number of items in the cache.
+
+ This class guarantees that only one calculation for any given key is
+ in-flight at a time, subsequent requests will wait for the first one and
+ return its results (for example, an empty web image cache that receives
+ two concurrent requests for "Foo.jpg" will only issue one WebRequest -
+ this does not mean that a request for "Bar.jpg" will wait on "Foo.jpg").
+
+ Concurrency is also limited by the maxConcurrent parameter - when too
+ many in-flight operations are in progress, further operations will be
+ queued until a slot is available.
+ </summary>
+ <typeparam name="TParam">The key type.</typeparam>
+ <typeparam name="TVal">The type of the value to return from the cache.</typeparam>
+ </member>
+ <member name="M:ReactiveUI.ObservableAsyncMRUCache`2.#ctor(System.Func{`0,System.IObservable{`1}},System.Int32,System.Int32,System.Action{`1},System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Constructs an ObservableAsyncMRUCache object.
+ </summary>
+ <param name="calculationFunc">The function that performs the
+ expensive or asyncronous calculation and returns an async result -
+ for CPU-based operations, Observable.Return may be used to return
+ the result.
+
+ Note that this function *must* return an equivalently-same result given a
+ specific input - because the function is being memoized, if the
+ calculationFunc depends on other varables other than the input
+ value, the results will be unpredictable.
+ </param>
+ <param name="maxSize">The number of items to cache. When this limit
+ is reached, not recently used items will be discarded.</param>
+ <param name="maxConcurrent">The maximum number of concurrent
+ asynchronous operations regardless of key - this is important for
+ web-based caches to limit the number of concurrent requests to a
+ server. The default is 5.</param>
+ <param name="onRelease">This optional method is called when an item
+ is evicted from the cache - this can be used to clean up / manage an
+ on-disk cache; the calculationFunc can download a file and save it
+ to a temporary folder, and the onRelease action will delete the
+ file.</param>
+ <param name="sched">The scheduler to run asynchronous operations on
+ - defaults to TaskpoolScheduler</param>
+ </member>
+ <member name="M:ReactiveUI.ObservableAsyncMRUCache`2.AsyncGet(`0)">
+ <summary>
+ Issues an request to fetch the value for the specified key as an
+ async operation. The Observable returned will fire one time when the
+ async operation finishes. If the operation is cached, an Observable
+ that immediately fires upon subscribing will be returned.
+ </summary>
+ <param name="key">The key to provide to the calculation function.</param>
+ <returns>Returns an Observable representing the future result.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservableAsyncMRUCache`2.Get(`0)">
+ <summary>
+ The synchronous version of AsyncGet - it will issue a request for
+ the value of a specific key and wait until the value can be
+ provided.
+ </summary>
+ <param name="key">The key to provide to the calculation function.</param>
+ <returns>The resulting value.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservableCacheMixin.CachedSelectMany``2(System.IObservable{``0},System.Func{``0,System.IObservable{``1}},System.Int32,System.Int32,System.Reactive.Concurrency.IScheduler)">
+ <summary>
+ Works like SelectMany, but memoizes selector calls. In addition, it
+ guarantees that no more than 'maxConcurrent' selectors are running
+ concurrently and queues the rest. This is very important when using
+ web services to avoid potentially spamming the server with hundreds
+ of requests.
+ </summary>
+ <param name="selector">A selector similar to one you would pass as a
+ parameter passed to SelectMany. Note that similarly to
+ ObservableAsyncMRUCache.AsyncGet, a selector must return semantically
+ identical results given the same key - i.e. it must be a 'function' in
+ the mathematical sense.</param>
+ <param name="maxCached">The number of items to cache. When this limit
+ is reached, not recently used items will be discarded.</param>
+ <param name="maxConcurrent">The maximum number of concurrent
+ asynchronous operations regardless of key - this is important for
+ web-based caches to limit the number of concurrent requests to a
+ server. The default is 5.</param>
+ <param name="scheduler"></param>
+ <returns>An Observable representing the flattened results of the
+ selector.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservableCacheMixin.CachedSelectMany``2(System.IObservable{``0},ReactiveUI.ObservableAsyncMRUCache{``0,``1})">
+ <summary>
+ Works like SelectMany, but memoizes selector calls. In addition, it
+ guarantees that no more than 'maxConcurrent' selectors are running
+ concurrently and queues the rest. This is very important when using
+ web services to avoid potentially spamming the server with hundreds
+ of requests.
+
+ This overload is useful when making the same web service call in
+ several places in the code, to ensure that all of the code paths are
+ using the same cache.
+ </summary>
+ <param name="existingCache">An already-configured ObservableAsyncMRUCache.</param>
+ <returns>An Observable representing the flattened results of the
+ cache selector.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservedChangedMixin.GetValue``2(ReactiveUI.IObservedChange{``0,``1})">
+ <summary>
+ Returns the current value of a property given a notification that it has changed.
+ </summary>
+ <returns>The current value of the property</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservedChangedMixin.TryGetValue``2(ReactiveUI.IObservedChange{``0,``1},``1@)">
+ <summary>
+ Attempts to return the current value of a property given a
+ notification that it has changed. If any property in the
+ property expression is null, false is returned.
+ </summary>
+ <param name="changeValue">The value of the property expression.</param>
+ <returns>True if the entire expression was able to be followed, false otherwise</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservedChangedMixin.SetValueToProperty``3(ReactiveUI.IObservedChange{``0,``1},``2,System.Linq.Expressions.Expression{System.Func{``2,``1}})">
+ <summary>
+ Given a fully filled-out IObservedChange object, SetValueToProperty
+ will apply it to the specified object (i.e. it will ensure that
+ target.property == This.GetValue() and "replay" the observed change
+ onto another object)
+ </summary>
+ <param name="target">The target object to apply the change to.</param>
+ <param name="property">The target property to apply the change to.</param>
+ </member>
+ <member name="M:ReactiveUI.ObservedChangedMixin.Value``2(System.IObservable{ReactiveUI.IObservedChange{``0,``1}})">
+ <summary>
+ Given a stream of notification changes, this method will convert
+ the property changes to the current value of the property.
+ </summary>
+ <returns>An Observable representing the stream of current values of
+ the given change notification stream.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservedChangedMixin.ValueIfNotDefault``2(System.IObservable{ReactiveUI.IObservedChange{``0,``1}})">
+ <summary>
+ ValueIfNotDefault is similar to Value(), but filters out null values
+ from the stream.
+ </summary>
+ <returns>An Observable representing the stream of current values of
+ the given change notification stream.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservedChangedMixin.Value``3(System.IObservable{ReactiveUI.IObservedChange{``0,``1}})">
+ <summary>
+ Given a stream of notification changes, this method will convert
+ the property changes to the current value of the property.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.BindingMixins.BindTo``2(System.IObservable{``1},``0,System.Linq.Expressions.Expression{System.Func{``0,``1}})">
+ <summary>
+ BindTo takes an Observable stream and applies it to a target
+ property. Conceptually it is similar to "Subscribe(x =&gt;
+ target.property = x)", but allows you to use child properties
+ without the null checks.
+ </summary>
+ <param name="target">The target object whose property will be set.</param>
+ <param name="property">An expression representing the target
+ property to set. This can be a child property (i.e. x.Foo.Bar.Baz).</param>
+ <returns>An object that when disposed, disconnects the binding.</returns>
+ </member>
+ <member name="T:ReactiveUI.ReactiveCollection`1">
+ <summary>
+
+ </summary>
+ <typeparam name="T">The type of the objects in the collection.</typeparam>
+ </member>
+ <member name="M:ReactiveUI.ReactiveCollection`1.#ctor">
+ <summary>
+ Constructs a ReactiveCollection.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ReactiveCollection`1.#ctor(System.Collections.Generic.IEnumerable{`0})">
+ <summary>
+ Constructs a ReactiveCollection given an existing list.
+ </summary>
+ <param name="list">The existing list with which to populate the new
+ list.</param>
+ </member>
+ <member name="M:ReactiveUI.ReactiveCollection`1.SuppressChangeNotifications">
+ <summary>
+ When this method is called, an object will not fire change
+ notifications (neither traditional nor Observable notifications)
+ until the return value is disposed.
+ </summary>
+ <returns>An object that, when disposed, reenables change
+ notifications.</returns>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.ItemsAdded">
+ <summary>
+ Fires when items are added to the collection, once per item added.
+ Functions that add multiple items such as AddRange should fire this
+ multiple times. The object provided is the item that was added.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.BeforeItemsAdded">
+ <summary>
+ Fires before an item is going to be added to the collection.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.ItemsRemoved">
+ <summary>
+ Fires once an item has been removed from a collection, providing the
+ item that was removed.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.BeforeItemsRemoved">
+ <summary>
+ Fires before an item will be removed from a collection, providing
+ the item that will be removed.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.CollectionCountChanging">
+ <summary>
+ Fires before a collection is about to change, providing the previous
+ Count.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.CollectionCountChanged">
+ <summary>
+ Fires whenever the number of items in a collection has changed,
+ providing the new Count.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.ItemChanging">
+ <summary>
+ Provides Item Changed notifications for any item in collection that
+ implements IReactiveNotifyPropertyChanged. This is only enabled when
+ ChangeTrackingEnabled is set to True.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.ItemChanged">
+ <summary>
+ Provides Item Changing notifications for any item in collection that
+ implements IReactiveNotifyPropertyChanged. This is only enabled when
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.Changing">
+ <summary>
+ Fires when anything in the collection or any of its items (if Change
+ Tracking is enabled) are about to change.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.Changed">
+ <summary>
+ Fires when anything in the collection or any of its items (if Change
+ Tracking is enabled) have changed.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveCollection`1.ChangeTrackingEnabled">
+ <summary>
+ Enables the ItemChanging and ItemChanged properties; when this is
+ enabled, whenever a property on any object implementing
+ IReactiveNotifyPropertyChanged changes, the change will be
+ rebroadcast through ItemChanging/ItemChanged.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ReactiveCollectionMixins.CreateCollection``1(System.IObservable{``0},System.Nullable{System.TimeSpan})">
+ <summary>
+ Creates a collection based on an an Observable by adding items
+ provided until the Observable completes, optionally ensuring a
+ delay. Note that if the Observable never completes and withDelay is
+ set, this method will leak a Timer. This method also guarantees that
+ items are always added via the UI thread.
+ </summary>
+ <param name="fromObservable">The Observable whose items will be put
+ into the new collection.</param>
+ <param name="withDelay">If set, items will be populated in the
+ collection no faster than the delay provided.</param>
+ <returns>A new collection which will be populated with the
+ Observable.</returns>
+ </member>
+ <member name="M:ReactiveUI.ReactiveCollectionMixins.CreateCollection``2(System.IObservable{``0},System.Func{``0,``1},System.Nullable{System.TimeSpan})">
+ <summary>
+ Creates a collection based on an an Observable by adding items
+ provided until the Observable completes, optionally ensuring a
+ delay. Note that if the Observable never completes and withDelay is
+ set, this method will leak a Timer. This method also guarantees that
+ items are always added via the UI thread.
+ </summary>
+ <param name="fromObservable">The Observable whose items will be put
+ into the new collection.</param>
+ <param name="selector">A Select function that will be run on each
+ item.</param>
+ <param name="withDelay">If set, items will be populated in the
+ collection no faster than the delay provided.</param>
+ <returns>A new collection which will be populated with the
+ Observable.</returns>
+ </member>
+ <member name="M:ReactiveUI.ObservableCollectionMixin.CreateDerivedCollection``2(System.Collections.ObjectModel.ObservableCollection{``0},System.Func{``0,``1})">
+ <summary>
+ Creates a collection whose contents will "follow" another
+ collection; this method is useful for creating ViewModel collections
+ that are automatically updated when the respective Model collection
+ is updated.
+ </summary>
+ <param name="selector">A Select function that will be run on each
+ item.</param>
+ <returns>A new collection whose items are equivalent to
+ Collection.Select(selector) and will mirror the initial collection.</returns>
+ </member>
+ <member name="M:ReactiveUI.ReactiveNotifyPropertyChangedMixin.ObservableForProperty``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Boolean)">
+ <summary>
+ ObservableForProperty returns an Observable representing the
+ property change notifications for a specific property on a
+ ReactiveObject. This method (unlike other Observables that return
+ IObservedChange) guarantees that the Value property of
+ the IObservedChange is set.
+ </summary>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty.SomeOtherProperty'</param>
+ <param name="beforeChange">If True, the Observable will notify
+ immediately before a property is going to change.</param>
+ <returns>An Observable representing the property change
+ notifications for the given property.</returns>
+ </member>
+ <member name="M:ReactiveUI.ReactiveNotifyPropertyChangedMixin.ObservableForProperty``3(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Func{``1,``2},System.Boolean)">
+ <summary>
+ ObservableForProperty returns an Observable representing the
+ property change notifications for a specific property on a
+ ReactiveObject, running the IObservedChange through a Selector
+ function.
+ </summary>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ <param name="selector">A Select function that will be run on each
+ item.</param>
+ <param name="beforeChange">If True, the Observable will notify
+ immediately before a property is going to change.</param>
+ <returns>An Observable representing the property change
+ notifications for the given property.</returns>
+ </member>
+ <member name="T:ReactiveUI.ReactiveObject">
+ <summary>
+ ReactiveObject is the base object for ViewModel classes, and it
+ implements INotifyPropertyChanged. In addition, ReactiveObject provides
+ Changing and Changed Observables to monitor object changes.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObject.SuppressChangeNotifications">
+ <summary>
+ When this method is called, an object will not fire change
+ notifications (neither traditional nor Observable notifications)
+ until the return value is disposed.
+ </summary>
+ <returns>An object that, when disposed, reenables change
+ notifications.</returns>
+ </member>
+ <member name="P:ReactiveUI.ReactiveObject.Changing">
+ <summary>
+ Represents an Observable that fires *before* a property is about to
+ be changed.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.ReactiveObject.Changed">
+ <summary>
+ Represents an Observable that fires *after* a property has changed.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectExpressionMixin.RaiseAndSetIfChanged``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}},``1)">
+ <summary>
+ RaiseAndSetIfChanged fully implements a Setter for a read-write
+ property on a ReactiveObject, making the assumption that the
+ property has a backing field named "_NameOfProperty". To change this
+ assumption, set RxApp.GetFieldNameForPropertyNameFunc.
+ </summary>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ <param name="newValue">The new value to set the property to, almost
+ always the 'value' keyword.</param>
+ <returns>The newly set value, normally discarded.</returns>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectExpressionMixin.RaiseAndSetIfChanged``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}},``1@,``1)">
+ <summary>
+ RaiseAndSetIfChanged fully implements a Setter for a read-write
+ property on a ReactiveObject, making the assumption that the
+ property has a backing field named "_NameOfProperty". To change this
+ assumption, set RxApp.GetFieldNameForPropertyNameFunc. This
+ overload is intended for Silverlight and WP7 where reflection
+ cannot access the private backing field.
+ </summary>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ <param name="backingField">A Reference to the backing field for this
+ property.</param>
+ <param name="newValue">The new value to set the property to, almost
+ always the 'value' keyword.</param>
+ <returns>The newly set value, normally discarded.</returns>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectExpressionMixin.RaisePropertyChanging``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}})">
+ <summary>
+ Use this method in your ReactiveObject classes when creating custom
+ properties where raiseAndSetIfChanged doesn't suffice.
+ </summary>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectExpressionMixin.RaisePropertyChanged``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}})">
+ <summary>
+ Use this method in your ReactiveObject classes when creating custom
+ properties where raiseAndSetIfChanged doesn't suffice.
+ </summary>
+ <param name="property">An Expression representing the property (i.e.
+ 'x => x.SomeProperty'</param>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectTestMixin.RaisePropertyChanging(ReactiveUI.ReactiveObject,System.String)">
+ <summary>
+ RaisePropertyChanging is a helper method intended for test / mock
+ scenarios to manually fake a property change.
+ </summary>
+ <param name="target">The ReactiveObject to invoke
+ raisePropertyChanging on.</param>
+ <param name="property">The property that will be faking a change.</param>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectTestMixin.RaisePropertyChanging``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}})">
+ <summary>
+ RaisePropertyChanging is a helper method intended for test / mock
+ scenarios to manually fake a property change.
+ </summary>
+ <param name="target">The ReactiveObject to invoke
+ raisePropertyChanging on.</param>
+ <param name="property">The property that will be faking a change.</param>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectTestMixin.RaisePropertyChanged(ReactiveUI.ReactiveObject,System.String)">
+ <summary>
+ RaisePropertyChanged is a helper method intended for test / mock
+ scenarios to manually fake a property change.
+ </summary>
+ <param name="target">The ReactiveObject to invoke
+ raisePropertyChanging on.</param>
+ <param name="property">The property that will be faking a change.</param>
+ </member>
+ <member name="M:ReactiveUI.ReactiveObjectTestMixin.RaisePropertyChanged``2(``0,System.Linq.Expressions.Expression{System.Func{``0,``1}})">
+ <summary>
+ RaisePropertyChanged is a helper method intended for test / mock
+ scenarios to manually fake a property change.
+ </summary>
+ <param name="target">The ReactiveObject to invoke
+ raisePropertyChanging on.</param>
+ <param name="property">The property that will be faking a change.</param>
+ </member>
+ <member name="T:ReactiveUI.MakeObjectReactiveHelper">
+ <summary>
+ This class helps you take existing objects and make them compatible with
+ ReactiveUI and Rx.Net. To use this, declare an instance field of this
+ class in your class, initialize it in your Constructor, make your class
+ derive from IReactiveNotifyPropertyChanged, then implement all of the
+ properties/methods using MakeObjectReactiveHelper.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.RxApp.InUnitTestRunner">
+ <summary>
+ InUnitTestRunner attempts to determine heuristically if the current
+ application is running in a unit test framework.
+ </summary>
+ <returns>True if we have determined that a unit test framework is
+ currently running.</returns>
+ </member>
+ <member name="M:ReactiveUI.RxApp.EnableDebugMode">
+ <summary>
+
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.RxApp.GetFieldNameForProperty(System.String)">
+ <summary>
+ GetFieldNameForProperty returns the corresponding backing field name
+ for a given property name, using the convention specified in
+ GetFieldNameForPropertyNameFunc.
+ </summary>
+ <param name="propertyName">The name of the property whose backing
+ field needs to be found.</param>
+ <returns>The backing field name.</returns>
+ </member>
+ <member name="P:ReactiveUI.RxApp.DeferredScheduler">
+ <summary>
+ DeferredScheduler is the scheduler used to schedule work items that
+ should be run "on the UI thread". In normal mode, this will be
+ DispatcherScheduler, and in Unit Test mode this will be Immediate,
+ to simplify writing common unit tests.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.RxApp.TaskpoolScheduler">
+ <summary>
+ TaskpoolScheduler is the scheduler used to schedule work items to
+ run in a background thread. In both modes, this will run on the TPL
+ Task Pool (or the normal Threadpool on Silverlight).
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.RxApp.LoggerFactory">
+ <summary>
+ Set this property to implement a custom logger provider - the
+ string parameter is the 'prefix' (usually the class name of the log
+ entry)
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.RxApp.MessageBus">
+ <summary>
+ Set this property to implement a custom MessageBus for
+ MessageBus.Current.
+ </summary>
+ </member>
+ <member name="P:ReactiveUI.RxApp.GetFieldNameForPropertyNameFunc">
+ <summary>
+ Set this property to override the default field naming convention
+ of "_PropertyName" with a custom one.
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.ReactiveValidatedObject">
+ <summary>
+
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ReactiveValidatedObject.#ctor">
+ <summary>
+
+ </summary>
+ </member>
+ <member name="T:ReactiveUI.ValidationBase">
+ <summary>
+
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.ValidationBase.IsValid(System.Object,System.ComponentModel.DataAnnotations.ValidationContext)">
+ <summary>
+
+ </summary>
+ <param name="value"></param>
+ <param name="validationContext"></param>
+ <returns></returns>
+ </member>
+ <member name="M:ReactiveUI.ValidationBase.isValidViaNullOrBlank(System.Object)">
+ <summary>
+
+ </summary>
+ <param name="value"></param>
+ <returns></returns>
+ </member>
+ <member name="M:ReactiveUI.ValidationBase.isValidViaNullOrBlank(System.Object,System.ComponentModel.DataAnnotations.ValidationContext)">
+ <summary>
+
+ </summary>
+ <param name="value"></param>
+ <param name="ctx"></param>
+ <returns></returns>
+ </member>
+ <member name="M:ReactiveUI.ValidationBase.getStandardMessage(System.ComponentModel.DataAnnotations.ValidationContext)">
+ <summary>
+
+ </summary>
+ <param name="ctx"></param>
+ <returns></returns>
+ </member>
+ <member name="T:ReactiveUI.ValidatesViaMethodAttribute">
+ <summary>
+
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``3(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Func{ReactiveUI.IObservedChange{``0,``2},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``4(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``5(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``6(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``7(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``8(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``9(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Linq.Expressions.Expression{System.Func{``0,``8}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},ReactiveUI.IObservedChange{``0,``8},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``10(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Linq.Expressions.Expression{System.Func{``0,``8}},System.Linq.Expressions.Expression{System.Func{``0,``9}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},ReactiveUI.IObservedChange{``0,``8},ReactiveUI.IObservedChange{``0,``9},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``11(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Linq.Expressions.Expression{System.Func{``0,``8}},System.Linq.Expressions.Expression{System.Func{``0,``9}},System.Linq.Expressions.Expression{System.Func{``0,``10}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},ReactiveUI.IObservedChange{``0,``8},ReactiveUI.IObservedChange{``0,``9},ReactiveUI.IObservedChange{``0,``10},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``12(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Linq.Expressions.Expression{System.Func{``0,``8}},System.Linq.Expressions.Expression{System.Func{``0,``9}},System.Linq.Expressions.Expression{System.Func{``0,``10}},System.Linq.Expressions.Expression{System.Func{``0,``11}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},ReactiveUI.IObservedChange{``0,``8},ReactiveUI.IObservedChange{``0,``9},ReactiveUI.IObservedChange{``0,``10},ReactiveUI.IObservedChange{``0,``11},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``13(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Linq.Expressions.Expression{System.Func{``0,``8}},System.Linq.Expressions.Expression{System.Func{``0,``9}},System.Linq.Expressions.Expression{System.Func{``0,``10}},System.Linq.Expressions.Expression{System.Func{``0,``11}},System.Linq.Expressions.Expression{System.Func{``0,``12}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},ReactiveUI.IObservedChange{``0,``8},ReactiveUI.IObservedChange{``0,``9},ReactiveUI.IObservedChange{``0,``10},ReactiveUI.IObservedChange{``0,``11},ReactiveUI.IObservedChange{``0,``12},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ <member name="M:ReactiveUI.WhenAnyMixin.WhenAny``14(``0,System.Linq.Expressions.Expression{System.Func{``0,``2}},System.Linq.Expressions.Expression{System.Func{``0,``3}},System.Linq.Expressions.Expression{System.Func{``0,``4}},System.Linq.Expressions.Expression{System.Func{``0,``5}},System.Linq.Expressions.Expression{System.Func{``0,``6}},System.Linq.Expressions.Expression{System.Func{``0,``7}},System.Linq.Expressions.Expression{System.Func{``0,``8}},System.Linq.Expressions.Expression{System.Func{``0,``9}},System.Linq.Expressions.Expression{System.Func{``0,``10}},System.Linq.Expressions.Expression{System.Func{``0,``11}},System.Linq.Expressions.Expression{System.Func{``0,``12}},System.Linq.Expressions.Expression{System.Func{``0,``13}},System.Func{ReactiveUI.IObservedChange{``0,``2},ReactiveUI.IObservedChange{``0,``3},ReactiveUI.IObservedChange{``0,``4},ReactiveUI.IObservedChange{``0,``5},ReactiveUI.IObservedChange{``0,``6},ReactiveUI.IObservedChange{``0,``7},ReactiveUI.IObservedChange{``0,``8},ReactiveUI.IObservedChange{``0,``9},ReactiveUI.IObservedChange{``0,``10},ReactiveUI.IObservedChange{``0,``11},ReactiveUI.IObservedChange{``0,``12},ReactiveUI.IObservedChange{``0,``13},``1})">
+ <summary>
+ WhenAny allows you to observe whenever one or more properties on an
+ object have changed, providing an initial value when the Observable
+ is set up, unlike ObservableForProperty(). Use this method in
+ constructors to set up bindings between properties that also need an
+ initial setup.
+ </summary>
+ </member>
+ </members>
+</doc>
diff --git a/samples/xquery/xproc.xqm b/samples/xquery/xproc.xqm
new file mode 100644
index 0000000..13257b2
--- /dev/null
+++ b/samples/xquery/xproc.xqm
@@ -0,0 +1,70 @@
+(: -------------------------------------------------------------------------------------
+
+ xproc.xqm - core xqm contains entry points, primary eval-step function and
+ control functions.
+
+ ---------------------------------------------------------------------------------------- :)
+xquery version "3.0" encoding "UTF-8";
+
+module namespace xproc = "http://xproc.net/xproc";
+
+ (: declare namespaces :)
+ declare namespace p="http://www.w3.org/ns/xproc";
+ declare namespace c="http://www.w3.org/ns/xproc-step";
+ declare namespace err="http://www.w3.org/ns/xproc-error";
+
+ (: module imports :)
+(: import module namespace util = "http://xproc.net/xproc/util" at "util1.xqm"; :)
+ import module namespace const = "http://xproc.net/xproc/const" at "const.xqm";
+ import module namespace parse = "http://xproc.net/xproc/parse" at "parse.xqm";
+ import module namespace u = "http://xproc.net/xproc/util" at "util.xqm";
+
+ (: declare options :)
+ declare boundary-space preserve;
+ declare option saxon:output "indent=yes";
+
+ (: declare functions :)
+ declare variable $xproc:run-step := xproc:run#6;
+ declare variable $xproc:parse-and-eval := ();
+ declare variable $xproc:declare-step := ();
+ declare variable $xproc:choose := ();
+ declare variable $xproc:try := ();
+ declare variable $xproc:catch := ();
+ declare variable $xproc:group := ();
+ declare variable $xproc:for-each := ();
+ declare variable $xproc:viewport := ();
+ declare variable $xproc:library := ();
+ declare variable $xproc:pipeline := ();
+ declare variable $xproc:variable := ();
+
+
+ (: list all declared namespaces :)
+ (: -------------------------------------------------------------------------- :)
+ declare function xproc:enum-namespaces($pipeline){
+ (: -------------------------------------------------------------------------- :)
+ <namespace name="{$pipeline/@name}">{u:enum-ns(<dummy>{$pipeline}</dummy>)}</namespace>
+ };
+
+ (: entry point :)
+ (: -------------------------------------------------------------------------- :)
+ declare function xproc:run($pipeline,$stdin,$dflag,$tflag,$bindings,$options){
+ (: -------------------------------------------------------------------------- :)
+
+ (: STEP I: preprocess :)
+ let $validate := ()
+ let $namespaces := xproc:enum-namespaces($pipeline)
+ let $parse := parse:explicit-bindings( parse:AST(parse:explicit-name(parse:explicit-type($pipeline))))
+ let $ast := element p:declare-step {$parse/@*,
+ parse:pipeline-step-sort( $parse/*, () )
+ }
+
+ (: STEP II: eval AST :)
+ let $eval_result := ()
+
+ (: STEP III: serialize and return results :)
+ let $serialized_result := $pipeline
+
+ return
+ $serialized_result
+ };
+
diff --git a/samples/xslt/test.xslt b/samples/xslt/test.xslt
new file mode 100644
index 0000000..234a079
--- /dev/null
+++ b/samples/xslt/test.xslt
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/">
+ <html>
+ <body>
+ <h2>My CD Collection</h2>
+ <table border="1">
+ <tr bgcolor="#9acd32">
+ <th>Title</th>
+ <th>Artist</th>
+ </tr>
+ <xsl:for-each select="catalog/cd">
+ <tr>
+ <td><xsl:value-of select="title"/></td>
+ <td><xsl:value-of select="artist"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/samples/yaml/.gemrc b/samples/yaml/.gemrc
new file mode 100644
index 0000000..821e54a
--- /dev/null
+++ b/samples/yaml/.gemrc
@@ -0,0 +1,5 @@
+gem: --local --gen-rdoc --run-tests
+rdoc: --inline-source --line-numbers
+gempath:
+ - /usr/local/rubygems
+ - /home/gavin/.rubygems