FAQ
Repository: couchdb
Updated Branches:
   refs/heads/master 9c53cff2d -> 9950caae6


Add share/www to .gitignore


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/e16b51d7
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/e16b51d7
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/e16b51d7

Branch: refs/heads/master
Commit: e16b51d7025fe8a3db9b8a4962a588970b345327
Parents: 72cc66f
Author: Jan Lehnardt <jan@apache.org>
Authored: Fri Oct 10 21:17:47 2014 +0200
Committer: Alexander Shorin <kxepal@apache.org>
Committed: Wed Dec 10 14:07:01 2014 +0300

----------------------------------------------------------------------
  .gitignore | 1 +
  1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/e16b51d7/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index eaffbe1..f0af739 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ dev/logs/

  share/server/main-coffee.js
  share/server/main.js
+share/www

  src/couch/priv/couch_js/config.h
  src/couch/priv/couchjs

Search Discussions

  • Kxepal at Dec 10, 2014 at 11:08 am
    Add Fauxton build process to Makefile

    `make` alone now builds Fauxton along with the rest.

    `make fauxton` to only do the fauxton build.

    `make distclean` cleans the Fauxton build.

    Fauxton is deployed into share/www. `make distclean` simply removes
    share/www.

    If share/www exists, `make [fauxton]` skips building Fauxton.

    Includes a letter for Noah ;)


    Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
    Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/72cc66fe
    Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/72cc66fe
    Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/72cc66fe

    Branch: refs/heads/master
    Commit: 72cc66fe139fc149ced05b3911b8970d695e67a4
    Parents: 78f367c
    Author: Jan Lehnardt <jan@apache.org>
    Authored: Fri Oct 10 21:08:11 2014 +0200
    Committer: Alexander Shorin <kxepal@apache.org>
    Committed: Wed Dec 10 14:07:01 2014 +0300

    ----------------------------------------------------------------------
      Makefile | 7 ++++++-
      1 file changed, 6 insertions(+), 1 deletion(-)
    ----------------------------------------------------------------------


    http://git-wip-us.apache.org/repos/asf/couchdb/blob/72cc66fe/Makefile
    ----------------------------------------------------------------------
    diff --git a/Makefile b/Makefile
    index 986d2dc..3d6ccbf 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -18,7 +18,7 @@ config.erl:
       @echo
       @false

    -compile: config.erl
    +compile: config.erl fauxton
       @rebar compile
       @cp src/couch/priv/couchjs bin/

    @@ -34,6 +34,7 @@ dist: compile

      distclean: clean
       @rm -rf rel/couchdb
    + @rm -rf share/www

      devclean:
       @rm -rf dev/lib/*/data
    @@ -69,3 +70,7 @@ eunit: compile

      javascript: compile
       @dev/run -q test/javascript/run
    +
    +fauxton:
    +# This next line so Noah throws his arms up in dispair and teaches me proper Make again -- Love, Jan
    + @if [ ! -d share/www ]; then echo "Building Fauxton" && cd src/fauxton && npm install && grunt couchdb; fi
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/lorem.txt
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/lorem.txt b/test/javascript/tests/lorem.txt
    new file mode 100644
    index 0000000..0ef85ba
    --- /dev/null
    +++ b/test/javascript/tests/lorem.txt
    @@ -0,0 +1,103 @@
    +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nunc sapien, porta id pellentesque at, elementum et felis. Curabitur condimentum ante in metus iaculis quis congue diam commodo. Donec eleifend ante sed nulla dapibus convallis. Ut cursus aliquam neque, vel porttitor tellus interdum ut. Sed pharetra lacinia adipiscing. In tristique tristique felis non tincidunt. Nulla auctor mauris a velit cursus ultricies. In at libero quis justo consectetur laoreet. Nullam id ultrices nunc. Donec non turpis nulla, eu lacinia ante. Nunc eu orci et turpis pretium venenatis. Nam molestie, lacus at dignissim elementum, ante libero consectetur libero, ut lacinia lacus urna et purus. Nullam lorem ipsum, dapibus vel ullamcorper a, malesuada a metus. Sed porta adipiscing magna, quis pulvinar purus mattis fringilla. Integer pellentesque sapien in neque tristique ac iaculis libero ultricies. Ut eget pharetra purus.
    +
    +Nulla in convallis tellus. Proin tincidunt suscipit vulputate. Suspendisse potenti. Nullam tristique justo mi, a tristique ligula. Duis convallis aliquam iaculis. Nulla dictum fringilla congue. Suspendisse ac leo lectus, ac aliquam justo. Ut porttitor commodo mi sed luctus. Nulla at enim lorem. Nunc eu justo sapien, a blandit odio. Curabitur faucibus sollicitudin dolor, id lacinia sem auctor in. Donec varius nunc at lectus sagittis nec luctus arcu pharetra. Nunc sed metus justo. Cras vel mauris diam. Ut feugiat felis eget neque pharetra vestibulum consectetur massa facilisis. Quisque consectetur luctus nisi quis tincidunt. Vivamus cursus cursus quam non blandit. Pellentesque et velit lacus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
    +
    +In et dolor vitae orci adipiscing congue. Aliquam gravida nibh at nisl gravida molestie. Curabitur a bibendum sapien. Aliquam tincidunt, nulla nec pretium lobortis, odio augue tincidunt arcu, a lobortis odio sem ut purus. Donec accumsan mattis nunc vitae lacinia. Suspendisse potenti. Integer commodo nisl quis nibh interdum non fringilla dui sodales. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In hac habitasse platea dictumst. Etiam ullamcorper, mi id feugiat bibendum, purus neque cursus mauris, id sodales quam nisi id velit. Sed lectus leo, tincidunt vel rhoncus imperdiet, blandit in leo. Integer quis magna nulla. Donec vel nisl magna, ut rhoncus dui. Aliquam gravida, nulla nec eleifend luctus, neque nibh pharetra ante, quis egestas elit metus a mi. Nunc nec augue quam. Morbi tincidunt tristique varius. Suspendisse iaculis elit feugiat magna pellentesque ultricies. Vestibulum aliquam tortor non ante ullamcorper fringilla. Donec iaculis
       mi quis mauris ornare vestibulum.
    +
    +In a magna nisi, a ultricies massa. Donec elit neque, viverra non tempor quis, fringilla in metus. Integer odio odio, euismod vitae mollis sed, sodales eget libero. Donec nec massa in felis ornare pharetra at nec tellus. Nunc lorem dolor, pretium vel auctor in, volutpat vitae felis. Maecenas rhoncus, orci vel blandit euismod, turpis erat tincidunt ante, elementum adipiscing nisl urna in nisi. Phasellus sagittis, enim sed accumsan consequat, urna augue lobortis erat, non malesuada quam metus sollicitudin ante. In leo purus, dignissim quis varius vel, pellentesque et nibh. In sed tortor iaculis libero mollis pellentesque id vitae lectus. In hac habitasse platea dictumst. Phasellus mauris enim, posuere eget luctus ac, iaculis et quam. Vivamus et nibh diam, elementum egestas tellus. Aenean vulputate malesuada est. Sed posuere porta diam a sodales. Proin eu sem non velit facilisis venenatis sed a turpis.
    +
    +Pellentesque sed risus a ante vulputate lobortis sit amet eu nisl. Suspendisse ut eros mi, a rhoncus lacus. Curabitur fermentum vehicula tellus, a ornare mi condimentum vel. Integer molestie volutpat viverra. Integer posuere euismod venenatis. Proin ac mauris sed nulla pharetra porttitor. Duis vel dui in risus sodales auctor sit amet non enim. Maecenas mollis lacus at ligula faucibus sodales. Cras vel neque arcu. Sed tincidunt tortor pretium nisi interdum quis dictum arcu laoreet. Morbi pretium ultrices feugiat. Maecenas convallis augue nec felis malesuada malesuada scelerisque mauris placerat. Sed at magna enim, at fringilla dolor. Quisque ut mattis dui. Praesent consectetur ante viverra nisi blandit pharetra. Quisque metus elit, dignissim vitae fermentum sit amet, fringilla imperdiet odio. Cras eget purus eget tellus feugiat luctus a ac purus. Cras vitae nisl vel augue rhoncus porttitor sit amet quis lorem. Donec interdum pellentesque adipiscing. Phasellus neque libero, aliquam in
       mattis vitae, consectetur adipiscing nibh.
    +
    +Donec nec nulla urna, ac sagittis lectus. Suspendisse non elit sed mi auctor facilisis vitae et lectus. Fusce ac vulputate mauris. Morbi condimentum ultrices metus, et accumsan purus malesuada at. Maecenas lobortis ante sed massa dictum vitae venenatis elit commodo. Proin tellus eros, adipiscing sed dignissim vitae, tempor eget ante. Aenean id tellus nec magna cursus pharetra vitae vel enim. Morbi vestibulum pharetra est in vulputate. Aliquam vitae metus arcu, id aliquet nulla. Phasellus ligula est, hendrerit nec iaculis ut, volutpat vel eros. Suspendisse vitae urna turpis, placerat adipiscing diam. Phasellus feugiat vestibulum neque eu dapibus. Nulla facilisi. Duis tortor felis, euismod sit amet aliquet in, volutpat nec turpis. Mauris rhoncus ipsum ut purus eleifend ut lobortis lectus dapibus. Quisque non erat lorem. Vivamus posuere imperdiet iaculis. Ut ligula lacus, eleifend at tempor id, auctor eu leo.
    +
    +Donec mi enim, laoreet pulvinar mollis eu, malesuada viverra nunc. In vitae metus vitae neque tempor dapibus. Maecenas tincidunt purus a felis aliquam placerat. Nulla facilisi. Suspendisse placerat pharetra mattis. Integer tempor malesuada justo at tempus. Maecenas vehicula lorem a sapien bibendum vel iaculis risus feugiat. Pellentesque diam erat, dapibus et pellentesque quis, molestie ut massa. Vivamus iaculis interdum massa id bibendum. Quisque ut mauris dui, sit amet varius elit. Vestibulum elit lorem, rutrum non consectetur ut, laoreet nec nunc. Donec nec mauris ante. Curabitur ut est sed odio pharetra laoreet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur purus risus, laoreet sed porta id, sagittis vel ipsum. Maecenas nibh diam, cursus et varius sit amet, fringilla sed magna. Nullam id neque eu leo faucibus mollis. Duis nec adipiscing mauris. Suspendisse sollicitudin, enim eu pulvinar commodo, erat augue ultrices mi, a tristique magna sem non libero.
    +
    +Sed in metus nulla. Praesent nec adipiscing sapien. Donec laoreet, velit non rutrum vestibulum, ligula neque adipiscing turpis, at auctor sapien elit ut massa. Nullam aliquam, enim vel posuere rutrum, justo erat laoreet est, vel fringilla lacus nisi non lectus. Etiam lectus nunc, laoreet et placerat at, venenatis quis libero. Praesent in placerat elit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque fringilla augue eu nibh placerat dictum. Nunc porttitor tristique diam, eu aliquam enim aliquet vel. Aliquam lacinia interdum ipsum, in posuere metus luctus vel. Vivamus et nisl a eros semper elementum. Donec venenatis orci at diam tristique sollicitudin. In eu eros sed odio rutrum luctus non nec tellus.
    +
    +Nulla nec felis elit. Nullam in ipsum in ipsum consequat fringilla quis vel tortor. Phasellus non massa nisi, sit amet aliquam urna. Sed fermentum nibh vitae lacus tincidunt nec tincidunt massa bibendum. Etiam elit dui, facilisis sit amet vehicula nec, iaculis at sapien. Ut at massa id dui ultrices volutpat ut ac libero. Fusce ipsum mi, bibendum a lacinia et, pulvinar eget mauris. Proin faucibus urna ut lorem elementum vulputate. Duis quam leo, malesuada non euismod ut, blandit facilisis mauris. Suspendisse sit amet magna id velit tincidunt aliquet nec eu dolor. Curabitur bibendum lorem vel felis tempus dapibus. Aliquam erat volutpat. Aenean cursus tortor nec dui aliquet porta. Aenean commodo iaculis suscipit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque sit amet ornare elit. Nam ligula risus, vestibulum nec mattis in, condimentum ac ante. Donec fringilla, justo et ultrices faucibus, tellus est volutpat massa, vitae commodo sapien d
      iam non risus. Vivamus at arcu gravida purus mollis feugiat.
    +
    +Nulla a turpis quis sapien commodo dignissim eu quis justo. Maecenas eu lorem odio, ut hendrerit velit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin facilisis porttitor ullamcorper. Praesent mollis dignissim massa, laoreet aliquet velit pellentesque non. Nunc facilisis convallis tristique. Mauris porttitor ante at tellus convallis placerat. Morbi aliquet nisi ac nisl pulvinar id dictum nisl mollis. Sed ornare sem et risus placerat lobortis id eget elit. Integer consequat, magna id suscipit pharetra, nulla velit suscipit orci, ut interdum augue augue quis quam. Fusce pretium aliquet vulputate. Mauris blandit dictum molestie. Proin nulla nibh, bibendum eu placerat at, tincidunt ac nisl. Nullam vulputate metus ut libero rutrum ultricies. Nunc sit amet dui mauris. Suspendisse adipiscing lacus in augue eleifend mollis.
    +
    +Duis pretium ultrices mattis. Nam euismod risus a erat lacinia bibendum. Morbi massa tortor, consectetur id eleifend id, pellentesque vel tortor. Praesent urna lorem, porttitor at condimentum vitae, luctus eget elit. Maecenas fringilla quam convallis est hendrerit viverra. Etiam vehicula, sapien non pulvinar adipiscing, nisi massa vestibulum est, id interdum mauris velit eu est. Vestibulum est arcu, facilisis at ultricies non, vulputate id sapien. Vestibulum ipsum metus, pharetra nec pellentesque id, facilisis id sapien. Donec rutrum odio et lacus ultricies ullamcorper. Integer sed est ut mi posuere tincidunt quis non leo. Morbi tellus justo, ultricies sit amet ultrices quis, facilisis vitae magna. Donec ligula metus, pellentesque non tristique ac, vestibulum sed erat. Aliquam erat volutpat.
    +
    +Nam dignissim, nisl eget consequat euismod, sem lectus auctor orci, ut porttitor lacus dui ac neque. In hac habitasse platea dictumst. Fusce egestas porta facilisis. In hac habitasse platea dictumst. Mauris cursus rhoncus risus ac euismod. Quisque vitae risus a tellus venenatis convallis. Curabitur laoreet sapien eu quam luctus lobortis. Vivamus sollicitudin sodales dolor vitae sodales. Suspendisse pharetra laoreet aliquet. Maecenas ullamcorper orci vel tortor luctus iaculis ut vitae metus. Vestibulum ut arcu ac tellus mattis eleifend eget vehicula elit.
    +
    +In sed feugiat eros. Donec bibendum ullamcorper diam, eu faucibus mauris dictum sed. Duis tincidunt justo in neque accumsan dictum. Maecenas in rutrum sapien. Ut id feugiat lacus. Nulla facilisi. Nunc ac lorem id quam varius cursus a et elit. Aenean posuere libero eu tortor vehicula ut ullamcorper odio consequat. Sed in dignissim dui. Curabitur iaculis tempor quam nec placerat. Aliquam venenatis nibh et justo iaculis lacinia. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque tempus magna sed mi aliquet eget varius odio congue.
    +
    +Integer sem sem, semper in vestibulum vitae, lobortis quis erat. Duis ante lectus, fermentum sed tempor sit amet, placerat sit amet sem. Mauris congue tincidunt ipsum. Ut viverra, lacus vel varius pharetra, purus enim pulvinar ipsum, non pellentesque enim justo non erat. Fusce ipsum orci, ultrices sed pellentesque at, hendrerit laoreet enim. Nunc blandit mollis pretium. Ut mollis, nulla aliquam sodales vestibulum, libero lorem tempus tortor, a pellentesque nibh elit a ipsum. Phasellus fermentum ligula at neque adipiscing sollicitudin. Suspendisse id ipsum arcu. Sed tincidunt placerat viverra. Donec libero augue, porttitor sit amet varius eget, rutrum nec lacus. Proin blandit orci sit amet diam dictum id porttitor risus iaculis. Integer lacinia feugiat leo, vitae auctor turpis eleifend vel. Suspendisse lorem quam, pretium id bibendum sed, viverra vitae tortor. Nullam ultricies libero eu risus convallis eget ullamcorper nisi elementum. Mauris nulla elit, bibendum id vulputate vitae, i
      mperdiet rutrum lorem. Curabitur eget dignissim orci. Sed semper tellus ipsum, at blandit dui. Integer dapibus facilisis sodales. Vivamus sollicitudin varius est, quis ornare justo cursus id.
    +
    +Nunc vel ullamcorper mi. Suspendisse potenti. Nunc et urna a augue scelerisque ultrices non quis mi. In quis porttitor elit. Aenean quis erat nulla, a venenatis tellus. Fusce vestibulum nisi sed leo adipiscing dignissim. Nunc interdum, lorem et lacinia vestibulum, quam est mattis magna, sit amet volutpat elit augue at libero. Cras gravida dui quis velit lobortis condimentum et eleifend ligula. Phasellus ac metus quam, id venenatis mi. Aliquam ut turpis ac tellus dapibus dapibus eu in mi. Quisque eget nibh eros. Fusce consectetur leo velit.
    +
    +Vestibulum semper egestas mauris. Morbi vestibulum sem sem. Aliquam venenatis, felis sed eleifend porta, mauris diam semper arcu, sit amet ultricies est sapien sit amet libero. Vestibulum dui orci, ornare condimentum mollis nec, molestie ac eros. Proin vitae mollis velit. Praesent eget felis mi. Maecenas eu vulputate nisi. Vestibulum varius, arcu in ultricies vestibulum, nibh leo sagittis odio, ut bibendum nisl mi nec diam. Integer at enim feugiat nulla semper bibendum ut a velit. Proin at nisi ut lorem aliquam varius eget quis elit. Nullam nec odio vel lectus congue consequat adipiscing ac mi. Fusce vitae laoreet libero. Curabitur sit amet sem neque, nec posuere enim. Curabitur at massa a sem gravida iaculis nec et nibh. Sed vitae dui vitae leo tincidunt pretium a aliquam erat. Suspendisse ultricies odio at metus tempor in pellentesque arcu ultricies.
    +
    +Sed aliquam mattis quam, in vulputate sapien ultrices in. Pellentesque quis velit sed dui hendrerit cursus. Pellentesque non nunc lacus, a semper metus. Fusce euismod velit quis diam suscipit consequat. Praesent commodo accumsan neque. Proin viverra, ipsum non tristique ultrices, velit velit facilisis lorem, vel rutrum neque eros ac nisi. Suspendisse felis massa, faucibus in volutpat ac, dapibus et odio. Pellentesque id tellus sit amet risus ultricies ullamcorper non nec sapien. Nam placerat viverra ullamcorper. Nam placerat porttitor sapien nec pulvinar. Curabitur vel odio sit amet odio accumsan aliquet vitae a lectus. Pellentesque lobortis viverra consequat. Mauris elementum cursus nulla, sit amet hendrerit justo dictum sed. Maecenas diam odio, fringilla ac congue quis, adipiscing ut elit.
    +
    +Aliquam lorem eros, pharetra nec egestas vitae, mattis nec risus. Mauris arcu massa, sodales eget gravida sed, viverra vitae turpis. Ut ligula urna, euismod ac tincidunt eu, faucibus sed felis. Praesent mollis, ipsum quis rhoncus dignissim, odio sem venenatis nulla, at consequat felis augue vel erat. Nam fermentum feugiat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam vitae dui in nisi adipiscing ultricies non eu justo. Donec tristique ultricies adipiscing. Nulla sodales, nunc a tristique elementum, erat neque egestas nisl, at hendrerit orci sapien sed libero. Vivamus a mauris turpis, quis laoreet ipsum. Nunc nec mi et nisl pellentesque scelerisque. Vivamus volutpat, justo tristique lacinia condimentum, erat justo ultrices urna, elementum viverra eros augue non libero. Sed mollis mollis arcu, at fermentum diam suscipit quis.
    +
    +Etiam sit amet nibh justo, posuere volutpat nunc. Morbi pellentesque neque in orci volutpat eu scelerisque lorem dictum. Mauris mollis iaculis est, nec sagittis sapien consequat id. Nunc nec malesuada odio. Duis quis suscipit odio. Mauris purus dui, sodales id mattis sit amet, posuere in arcu. Phasellus porta elementum convallis. Maecenas at orci et mi vulputate sollicitudin in in turpis. Pellentesque cursus adipiscing neque sit amet commodo. Fusce ut mi eu lectus porttitor volutpat et nec felis.
    +
    +Curabitur scelerisque eros quis nisl viverra vel ultrices velit vestibulum. Sed lobortis pulvinar sapien ac venenatis. Sed ante nibh, rhoncus eget dictum in, mollis ut nisi. Phasellus facilisis mi non lorem tristique non eleifend sem fringilla. Integer ut augue est. In venenatis tincidunt scelerisque. Etiam ante dui, posuere quis malesuada vitae, malesuada a arcu. Aenean faucibus venenatis sapien, ut facilisis nisi blandit vel. Aenean ac lorem eu sem fermentum placerat. Proin neque purus, aliquet ut tincidunt ut, convallis sit amet eros. Phasellus vehicula ullamcorper enim non vehicula. Etiam porta odio ut ipsum adipiscing egestas id a odio. Pellentesque blandit, sapien ut pulvinar interdum, mi nulla hendrerit elit, in tempor diam enim a urna. In tellus odio, ornare sed condimentum a, mattis eu augue.
    +
    +Fusce hendrerit porttitor euismod. Donec malesuada egestas turpis, et ultricies felis elementum vitae. Nullam in sem nibh. Nullam ultricies hendrerit justo sit amet lobortis. Sed tincidunt, mauris at ornare laoreet, sapien purus elementum elit, nec porttitor nisl purus et erat. Donec felis nisi, rutrum ullamcorper gravida ac, tincidunt sit amet urna. Proin vel justo vitae eros sagittis bibendum a ut nibh. Phasellus sodales laoreet tincidunt. Maecenas odio massa, condimentum id aliquet ut, rhoncus vel lectus. Duis pharetra consectetur sapien. Phasellus posuere ultricies massa, non rhoncus risus aliquam tempus.
    +
    +Praesent venenatis magna id sem dictum eu vehicula ipsum vulputate. Sed a convallis sapien. Sed justo dolor, rhoncus vel rutrum mattis, sollicitudin ut risus. Nullam sit amet convallis est. Etiam non tincidunt ligula. Fusce suscipit pretium elit at ullamcorper. Quisque sollicitudin, diam id interdum porta, metus ipsum volutpat libero, id venenatis felis orci non velit. Suspendisse potenti. Mauris rutrum, tortor sit amet pellentesque tincidunt, erat quam ultricies odio, id aliquam elit leo nec leo. Pellentesque justo eros, rutrum at feugiat nec, porta et tellus. Aenean eget metus lectus.
    +
    +Praesent euismod, turpis quis laoreet consequat, neque ante imperdiet quam, ac semper tortor nibh in nulla. Integer scelerisque eros vehicula urna lacinia ac facilisis mauris accumsan. Phasellus at mauris nibh. Curabitur enim ante, rutrum sed adipiscing hendrerit, pellentesque non augue. In hac habitasse platea dictumst. Nam tempus euismod massa a dictum. Donec sit amet justo ac diam ultricies ultricies. Sed tincidunt erat quis quam tempus vel interdum erat rhoncus. In hac habitasse platea dictumst. Vestibulum vehicula varius sem eget interdum. Cras bibendum leo nec felis venenatis sed pharetra sem feugiat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed quam orci, mollis eget sagittis accumsan, vulputate sit amet dui. Praesent eu elementum arcu.
    +
    +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nisl metus, hendrerit ut laoreet sed, consectetur at purus. Duis interdum congue lobortis. Nullam sed massa porta felis eleifend consequat sit amet nec metus. Aliquam placerat dictum erat at eleifend. Vestibulum libero ante, ullamcorper a porttitor suscipit, accumsan vel nisi. Donec et magna neque. Nam elementum ultrices justo, eget sollicitudin sapien imperdiet eget. Nullam auctor dictum nunc, at feugiat odio vestibulum a. Sed erat nulla, viverra hendrerit commodo id, ullamcorper ac orci. Phasellus pellentesque feugiat suscipit. Etiam egestas fermentum enim. Etiam gravida interdum tellus ac laoreet. Morbi mattis aliquet eros, non tempor erat ullamcorper in. Etiam pulvinar interdum turpis ac vehicula. Sed quam justo, accumsan id consectetur a, aliquet sed leo. Aenean vitae blandit mauris.
    +
    +In sed eros augue, non rutrum odio. Etiam vitae dui neque, in tristique massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas dictum elit at lectus tempor non pharetra nisl hendrerit. Sed sed quam eu lectus ultrices malesuada tincidunt a est. Nam vel eros risus. Maecenas eros elit, blandit fermentum tempor eget, lobortis id diam. Vestibulum lacinia lacus vitae magna volutpat eu dignissim eros convallis. Vivamus ac velit tellus, a congue neque. Integer mi nulla, varius non luctus in, dictum sit amet sem. Ut laoreet, sapien sit amet scelerisque porta, purus sapien vestibulum nibh, sed luctus libero massa ac elit. Donec iaculis odio eget odio sagittis nec venenatis lorem blandit.
    +
    +Aliquam imperdiet tellus posuere justo vehicula sed vestibulum ante tristique. Fusce feugiat faucibus purus nec molestie. Nulla tempor neque id magna iaculis quis sollicitudin eros semper. Praesent viverra sagittis luctus. Morbi sit amet magna sed odio gravida varius. Ut nisi libero, vulputate feugiat pretium tempus, egestas sit amet justo. Pellentesque consequat tempor nisi in lobortis. Sed fermentum convallis dui ac sollicitudin. Integer auctor augue eget tellus tempus fringilla. Proin nec dolor sapien, nec tristique nibh. Aliquam a velit at mi mattis aliquet.
    +
    +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam ultrices erat non turpis auctor id ornare mauris sagittis. Quisque porttitor, tellus ut convallis sagittis, mi libero feugiat tellus, rhoncus placerat ipsum tortor id risus. Donec tincidunt feugiat leo. Cras id mi neque, eu malesuada eros. Ut molestie magna quis libero placerat malesuada. Aliquam erat volutpat. Aliquam non mauris lorem, in adipiscing metus. Donec eget ipsum in elit commodo ornare bibendum a nibh. Vivamus odio erat, placerat ac vestibulum eget, malesuada ut nisi. Etiam suscipit sollicitudin leo semper sollicitudin. Sed rhoncus risus sit amet sem eleifend dictum pretium sapien egestas. Nulla at urna nunc, vel aliquet leo. Praesent ultricies, mi eu pretium lobortis, erat nibh euismod leo, sit amet gravida sapien eros et turpis. Donec lacinia venenatis lectus, non lacinia mi hendrerit sit amet. Integer sed felis vel orci aliquam pulvinar. Phasellus et risus id erat euis
      mod tincidunt. Sed luctus tempor nisi, nec tempor ipsum elementum eget. Integer nisl tortor, viverra in dapibus at, mattis ac erat. Curabitur nec dui lectus.
    +
    +Phasellus suscipit, tortor eu varius fringilla, sapien magna egestas risus, ut suscipit dui mauris quis velit. Cras a sapien quis sapien hendrerit tristique a sit amet elit. Pellentesque dui arcu, malesuada et sodales sit amet, dapibus vel quam. Sed non adipiscing ligula. Ut vulputate purus at nisl posuere sodales. Maecenas diam velit, tincidunt id mattis eu, aliquam ac nisi. Maecenas pretium, augue a sagittis suscipit, leo ligula eleifend dolor, mollis feugiat odio augue non eros. Pellentesque scelerisque orci pretium quam mollis at lobortis dui facilisis. Morbi congue metus id tortor porta fringilla. Sed lorem mi, molestie fermentum sagittis at, gravida a nisi. Donec eu vestibulum velit. In viverra, enim eu elementum sodales, enim odio dapibus urna, eget commodo nisl mauris ut odio. Curabitur nec enim nulla. In nec elit ipsum. Nunc in massa suscipit magna elementum faucibus in nec ipsum. Nullam suscipit malesuada elementum. Etiam sed mi in nibh ultricies venenatis nec pharetra mag
      na. In purus ante, rhoncus vel placerat sed, fermentum sit amet dui. Sed at sodales velit.
    +
    +Duis suscipit pellentesque pellentesque. Praesent porta lobortis cursus. Quisque sagittis velit non tellus bibendum at sollicitudin lacus aliquet. Sed nibh risus, blandit a aliquet eget, vehicula et est. Suspendisse facilisis bibendum aliquam. Fusce consectetur convallis erat, eget mollis diam fermentum sollicitudin. Quisque tincidunt porttitor pretium. Nullam id nisl et urna vulputate dapibus. Donec quis lorem urna. Quisque id justo nec nunc blandit convallis. Nunc volutpat, massa sollicitudin adipiscing vestibulum, massa urna congue lectus, sit amet ultricies augue orci convallis turpis. Nulla at lorem elit. Nunc tristique, quam facilisis commodo porttitor, lacus ligula accumsan nisi, et laoreet justo ante vitae eros. Curabitur sed augue arcu. Phasellus porttitor vestibulum felis, ut consectetur arcu tempor non. In justo risus, semper et suscipit id, ullamcorper at urna. Quisque tincidunt, urna nec aliquam tristique, nibh odio faucibus augue, in ornare enim turpis accumsan dolor.
      Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse sodales varius turpis eu fermentum.
    +
    +Morbi ultricies diam eget massa posuere lobortis. Aliquam volutpat pellentesque enim eu porttitor. Donec lacus felis, consectetur a pretium vitae, bibendum non enim. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam ut nibh a quam pellentesque auctor ut id velit. Duis lacinia justo eget mi placerat bibendum. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec velit tortor, tempus nec tristique id, aliquet sit amet turpis. Praesent et neque nec magna porta fringilla. Morbi id egestas eros. Donec semper tincidunt ullamcorper. Phasellus tempus lacinia hendrerit. Quisque faucibus pretium neque non convallis. Nunc malesuada accumsan rhoncus. Cras lobortis, sem sed fringilla convallis, augue velit semper nisl, commodo varius nisi diam ac leo.
    +
    +Quisque interdum tellus ac ante posuere ut cursus lorem egestas. Nulla facilisi. Aenean sed massa nec nisi scelerisque vulputate. Etiam convallis consectetur iaculis. Maecenas ac purus ut ante dignissim auctor ac quis lorem. Pellentesque suscipit tincidunt orci. Fusce aliquam dapibus orci, at bibendum ipsum adipiscing eget. Morbi pellentesque hendrerit quam, nec placerat urna vulputate sed. Quisque vel diam lorem. Praesent id diam quis enim elementum rhoncus sagittis eget purus. Quisque fringilla bibendum leo in laoreet. Vestibulum id nibh risus, non elementum metus. Ut a felis diam, non mollis nisl. Cras elit ante, ullamcorper quis iaculis eu, sodales vel est. Curabitur quis lobortis dolor. Aliquam mattis gravida metus pellentesque vulputate.
    +
    +Ut id augue id dolor luctus euismod et quis velit. Maecenas enim dolor, tempus sit amet hendrerit eu, faucibus vitae neque. Proin sit amet varius elit. Proin varius felis ullamcorper purus dignissim consequat. Cras cursus tempus eros. Nunc ultrices venenatis ullamcorper. Aliquam et feugiat tellus. Phasellus sit amet vestibulum elit. Phasellus ac purus lacus, et accumsan eros. Morbi ultrices, purus a porta sodales, odio metus posuere neque, nec elementum risus turpis sit amet magna. Sed est quam, ultricies at congue adipiscing, lobortis in justo. Proin iaculis dictum nunc, eu laoreet quam varius vitae. Donec sit amet feugiat turpis. Mauris sit amet magna quam, ac consectetur dui. Curabitur eget magna tellus, eu pharetra felis. Donec sit amet tortor nisl. Aliquam et tortor facilisis lacus tincidunt commodo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur nunc magna, ultricies id convallis at, ullamcorper vitae massa.
    +
    +Phasellus viverra iaculis placerat. Nulla consequat dolor sit amet erat dignissim posuere. Nulla lacinia augue vitae mi tempor gravida. Phasellus non tempor tellus. Quisque non enim semper tortor sagittis facilisis. Aliquam urna felis, egestas at posuere nec, aliquet eu nibh. Praesent sed vestibulum enim. Mauris iaculis velit dui, et fringilla enim. Nulla nec nisi orci. Sed volutpat, justo eget fringilla adipiscing, nisl nulla condimentum libero, sed sodales est est et odio. Cras ipsum dui, varius eu elementum consequat, faucibus in leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
    +
    +Ut malesuada molestie eleifend. Curabitur id enim dui, eu tincidunt nibh. Mauris sit amet ante leo. Duis turpis ipsum, bibendum sed mattis sit amet, accumsan quis dolor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean a imperdiet metus. Quisque sollicitudin felis id neque tempor scelerisque. Donec at orci felis. Vivamus tempus convallis auctor. Donec interdum euismod lobortis. Sed at lacus nec odio dignissim mollis. Sed sapien orci, porttitor tempus accumsan vel, tincidunt nec ante. Nunc rhoncus egestas dapibus. Suspendisse fermentum dictum fringilla. Nullam nisi justo, eleifend a consectetur convallis, porttitor et tortor. Proin vitae lorem non dolor suscipit lacinia eu eget nulla.
    +
    +Suspendisse egestas, sapien sit amet blandit scelerisque, nulla arcu tristique dui, a porta justo quam vitae arcu. In metus libero, bibendum non volutpat ut, laoreet vel turpis. Nunc faucibus velit eu ipsum commodo nec iaculis eros volutpat. Vivamus congue auctor elit sed suscipit. Duis commodo, libero eu vestibulum feugiat, leo mi dapibus tellus, in placerat nisl dui at est. Vestibulum viverra tristique lorem, ornare egestas erat rutrum a. Nullam at augue massa, ut consectetur ipsum. Pellentesque malesuada, velit ut lobortis sagittis, nisi massa semper odio, malesuada semper purus nisl vel lectus. Nunc dui sem, mattis vitae laoreet vitae, sollicitudin ac leo. Nulla vel fermentum est.
    +
    +Vivamus in odio a nisi dignissim rhoncus in in lacus. Donec et nisl tortor. Donec sagittis consequat mi, vel placerat tellus convallis id. Aliquam facilisis rutrum nisl sed pretium. Donec et lacinia nisl. Aliquam erat volutpat. Curabitur ac pulvinar tellus. Nullam varius lobortis porta. Cras dapibus, ligula ut porta ultricies, leo lacus viverra purus, quis mollis urna risus eu leo. Nunc malesuada consectetur purus, vel auctor lectus scelerisque posuere. Maecenas dui massa, vestibulum bibendum blandit non, interdum eget mauris. Phasellus est ante, pulvinar at imperdiet quis, imperdiet vel urna. Quisque eget volutpat orci. Quisque et arcu purus, ut faucibus velit.
    +
    +Praesent sed ipsum urna. Praesent sagittis varius magna, id commodo dolor malesuada ac. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque sit amet nunc eu sem ornare tempor. Mauris id dolor nec erat convallis porta in lobortis nisi. Curabitur hendrerit rhoncus tortor eu hendrerit. Pellentesque eu ante vel elit luctus eleifend quis viverra nulla. Suspendisse odio diam, euismod eu porttitor molestie, sollicitudin sit amet nulla. Sed ante urna, dictum bibendum rhoncus et, blandit nec ante. Suspendisse tortor augue, accumsan quis suscipit id, accumsan sit amet erat. Donec pharetra varius lobortis. Maecenas ipsum diam, faucibus eu tempus id, convallis nec enim. Duis arcu turpis, fringilla nec egestas ut, dignissim tristique nulla. Curabitur suscipit dui non justo ultrices pharetra. Aliquam erat volutpat. Nulla facilisi. Quisque id felis eu sem aliquam fringilla.
    +
    +Etiam quis augue in tellus consequat eleifend. Aenean dignissim congue felis id elementum. Duis fringilla varius ipsum, nec suscipit leo semper vel. Ut sollicitudin, orci a tincidunt accumsan, diam lectus laoreet lacus, vel fermentum quam est vel eros. Aliquam fringilla sapien ac sapien faucibus convallis. Aliquam id nunc eu justo consequat tincidunt. Quisque nec nisl dui. Phasellus augue lectus, varius vitae auctor vel, rutrum at risus. Vivamus lacinia leo quis neque ultrices nec elementum felis fringilla. Proin vel porttitor lectus.
    +
    +Curabitur sapien lorem, mollis ut accumsan non, ultricies et metus. Curabitur vel lorem quis sapien fringilla laoreet. Morbi id urna ac orci elementum blandit eget volutpat neque. Pellentesque sem odio, iaculis eu pharetra vitae, cursus in quam. Nulla molestie ligula id massa luctus et pulvinar nisi pulvinar. Nunc fermentum augue a lacus fringilla rhoncus porttitor erat dictum. Nunc sit amet tellus et dui viverra auctor euismod at nisl. In sed congue magna. Proin et tortor ut augue placerat dignissim a eu justo. Morbi porttitor porta lobortis. Pellentesque nibh lacus, adipiscing ut tristique quis, consequat vitae velit. Maecenas ut luctus libero. Vivamus auctor odio et erat semper sagittis. Vivamus interdum velit in risus mattis quis dictum ante rhoncus. In sagittis porttitor eros, at lobortis metus ultrices vel. Curabitur non aliquam nisl. Vestibulum luctus feugiat suscipit. Etiam non lacus vel nulla egestas iaculis id quis risus.
    +
    +Etiam in auctor urna. Fusce ultricies molestie convallis. In hac habitasse platea dictumst. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris iaculis lorem faucibus purus gravida at convallis turpis sollicitudin. Suspendisse at velit lorem, a fermentum ipsum. Etiam condimentum, dui vel condimentum elementum, sapien sem blandit sapien, et pharetra leo neque et lectus. Nunc viverra urna iaculis augue ultrices ac porttitor lacus dignissim. Aliquam ut turpis dui. Sed eget aliquet felis. In bibendum nibh sit amet sapien accumsan accumsan pharetra magna molestie.
    +
    +Mauris aliquet urna eget lectus adipiscing at congue turpis consequat. Vivamus tincidunt fermentum risus et feugiat. Nulla molestie ullamcorper nibh sed facilisis. Phasellus et cursus purus. Nam cursus, dui dictum ultrices viverra, erat risus varius elit, eu molestie dui eros quis quam. Aliquam et ante neque, ac consectetur dui. Donec condimentum erat id elit dictum sed accumsan leo sagittis. Proin consequat congue risus, vel tincidunt leo imperdiet eu. Vestibulum malesuada turpis eu metus imperdiet pretium. Aliquam condimentum ultrices nibh, eu semper enim eleifend a. Etiam condimentum nisl quam.
    +
    +Pellentesque id molestie nisl. Maecenas et lectus at justo molestie viverra sit amet sit amet ligula. Nullam non porttitor magna. Quisque elementum arcu cursus tortor rutrum lobortis. Morbi sit amet lectus vitae enim euismod dignissim eget at neque. Vivamus consequat vehicula dui, vitae auctor augue dignissim in. In tempus sem quis justo tincidunt sit amet auctor turpis lobortis. Pellentesque non est nunc. Vestibulum mollis fringilla interdum. Maecenas ipsum dolor, pharetra id tristique mattis, luctus vitae urna. Ut ullamcorper arcu eget elit convallis mollis. Pellentesque condimentum, massa ac hendrerit tempor, mauris purus blandit justo, et pharetra leo justo a est. Duis arcu augue, facilisis vel dignissim sed, aliquam quis magna. Quisque non consequat dolor. Suspendisse a ultrices leo.
    +
    +Donec vitae pretium nibh. Maecenas bibendum bibendum diam in placerat. Ut accumsan, mi vitae vestibulum euismod, nunc justo vulputate nisi, non placerat mi urna et diam. Maecenas malesuada lorem ut arcu mattis mollis. Nulla facilisi. Donec est leo, bibendum eu pulvinar in, cursus vel metus. Aliquam erat volutpat. Nullam feugiat porttitor neque in vulputate. Quisque nec mi eu magna consequat cursus non at arcu. Etiam risus metus, sollicitudin et ultrices at, tincidunt sed nunc. Sed eget scelerisque augue. Ut fringilla venenatis sem non eleifend. Nunc mattis, risus sit amet vulputate varius, risus justo egestas mauris, id interdum odio ipsum et nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi id erat odio, nec pulvinar enim.
    +
    +Curabitur ac fermentum quam. Morbi eu eros sapien, vitae tempus dolor. Mauris vestibulum blandit enim ut venenatis. Aliquam egestas, eros at consectetur tincidunt, lorem augue iaculis est, nec mollis felis arcu in nunc. Sed in odio sed libero pellentesque volutpat vitae a ante. Morbi commodo volutpat tellus, ut viverra purus placerat fermentum. Integer iaculis facilisis arcu, at gravida lorem bibendum at. Aenean id eros eget est sagittis convallis sed et dui. Donec eu pulvinar tellus. Nunc dignissim rhoncus tellus, at pellentesque metus luctus at. Sed ornare aliquam diam, a porttitor leo sollicitudin sed. Nam vitae lectus lacus. Integer adipiscing quam neque, blandit posuere libero. Sed libero nunc, egestas sodales tempus sed, cursus blandit tellus. Vestibulum mi purus, ultricies quis placerat vel, molestie at dui.
    +
    +Nulla commodo odio justo. Pellentesque non ornare diam. In consectetur sapien ac nunc sagittis malesuada. Morbi ullamcorper tempor erat nec rutrum. Duis ut commodo justo. Cras est orci, consectetur sed interdum sed, scelerisque sit amet nulla. Vestibulum justo nulla, pellentesque a tempus et, dapibus et arcu. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi tristique, eros nec congue adipiscing, ligula sem rhoncus felis, at ornare tellus mauris ac risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin mauris dui, tempor fermentum dictum et, cursus a leo. Maecenas nec nisl a tellus pellentesque rhoncus. Nullam ultrices euismod dui eu congue.
    +
    +In nec tempor risus. In faucibus nisi eget diam dignissim consequat. Donec pulvinar ante nec enim mattis rutrum. Vestibulum leo augue, molestie nec dapibus in, dictum at enim. Integer aliquam, lorem eu vulputate lacinia, mi orci tempor enim, eget mattis ligula magna a magna. Praesent sed erat ut tortor interdum viverra. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla facilisi. Maecenas sit amet lectus lacus. Nunc vitae purus id ligula laoreet condimentum. Duis auctor tortor vel dui pulvinar a facilisis arcu dignissim. In hac habitasse platea dictumst. Donec sollicitudin pellentesque egestas. Sed sed sem justo. Maecenas laoreet hendrerit mauris, ut porttitor lorem iaculis ac. Quisque molestie sem quis lorem tempor rutrum. Phasellus nibh mauris, rhoncus in consectetur non, aliquet eu massa.
    +
    +Curabitur velit arcu, pretium porta placerat quis, varius ut metus. Vestibulum vulputate tincidunt justo, vitae porttitor lectus imperdiet sit amet. Vivamus enim dolor, sollicitudin ut semper non, ornare ornare dui. Aliquam tempor fermentum sapien eget condimentum. Curabitur laoreet bibendum ante, in euismod lacus lacinia eu. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse potenti. Sed at libero eu tortor tempus scelerisque. Nulla facilisi. Nullam vitae neque id justo viverra rhoncus pretium at libero. Etiam est urna, aliquam vel pulvinar non, ornare vel purus.
    +
    +Nulla varius, nisi eget condimentum semper, metus est dictum odio, vel mattis risus est sed velit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc non est nec tellus ultricies mattis ut eget velit. Integer condimentum ante id lorem blandit lacinia. Donec vel tortor augue, in condimentum nisi. Pellentesque pellentesque nulla ut nulla porttitor quis sodales enim rutrum. Sed augue risus, euismod a aliquet at, vulputate non libero. Nullam nibh odio, dignissim fermentum pulvinar ac, congue eu mi. Duis tincidunt, nibh id venenatis placerat, diam turpis gravida leo, sit amet mollis massa dolor quis mauris. Vivamus scelerisque sodales arcu et dapibus. Suspendisse potenti. Cras quis tellus arcu, quis laoreet sem. Fusce porttitor, sapien vel tristique sodales, velit leo porta arcu, quis pellentesque nunc metus non odio. Nam arcu libero, ullamcorper ut pharetra non, dignissim et velit. Quisque dolor lorem, vehicula sit amet scelerisque in, varius at n
      ulla. Pellentesque vitae sem eget tortor iaculis pulvinar. Sed nunc justo, euismod gravida pulvinar eget, gravida eget turpis. Cras vel dictum nisi. Nullam nulla libero, gravida sit amet aliquam quis, commodo vitae odio. Cras vitae nibh nec dui placerat semper.
    +
    +Vivamus at fringilla eros. Vivamus at nisl id massa commodo feugiat quis non massa. Morbi tellus urna, auctor sit amet elementum sed, rutrum non lectus. Nulla feugiat dui in sapien ornare et imperdiet est ornare. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum semper rutrum tempor. Sed in felis nibh, sed aliquam enim. Curabitur ut quam scelerisque velit placerat dictum. Donec eleifend vehicula purus, eu vestibulum sapien rutrum eu. Vivamus in odio vel est vulputate iaculis. Nunc rutrum feugiat pretium.
    +
    +Maecenas ipsum neque, auctor quis lacinia vitae, euismod ac orci. Donec molestie massa consequat est porta ac porta purus tincidunt. Nam bibendum leo nec lacus mollis non condimentum dolor rhoncus. Nulla ac volutpat lorem. Nullam erat purus, convallis eget commodo id, varius quis augue. Nullam aliquam egestas mi, vel suscipit nisl mattis consequat. Quisque vel egestas sapien. Nunc lorem velit, convallis nec laoreet et, aliquet eget massa. Nam et nibh ac dui vehicula aliquam quis eu augue. Cras vel magna ut elit rhoncus interdum iaculis volutpat nisl. Suspendisse arcu lorem, varius rhoncus tempor id, pulvinar sed tortor. Pellentesque ultricies laoreet odio ac dignissim. Aliquam diam arcu, placerat quis egestas eget, facilisis eu nunc. Mauris vulputate, nisl sit amet mollis interdum, risus tortor ornare orci, sed egestas orci eros non diam. Vestibulum hendrerit, metus quis placerat pellentesque, enim purus faucibus dui, sit amet ultricies lectus ipsum id lorem. Class aptent taciti soc
      iosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent eget diam odio, eu bibendum elit. In vestibulum orci eu erat tincidunt tristique.
    +
    +Cras consectetur ante eu turpis placerat sollicitudin. Mauris et lacus tortor, eget pharetra velit. Donec accumsan ultrices tempor. Donec at nibh a elit condimentum dapibus. Integer sit amet vulputate ante. Suspendisse potenti. In sodales laoreet massa vitae lacinia. Morbi vel lacus feugiat arcu vulputate molestie. Aliquam massa magna, ullamcorper accumsan gravida quis, rhoncus pulvinar nulla. Praesent sit amet ipsum diam, sit amet lacinia neque. In et sapien augue. Etiam enim elit, ultrices vel rutrum id, scelerisque non enim.
    +
    +Proin et egestas neque. Praesent et ipsum dolor. Nunc non varius nisl. Fusce in tortor nisi. Maecenas convallis neque in ligula blandit quis vehicula leo mollis. Pellentesque sagittis blandit leo, dapibus pellentesque leo ultrices ac. Curabitur ac egestas libero. Donec pretium pharetra pretium. Fusce imperdiet, turpis eu aliquam porta, ante elit eleifend risus, luctus auctor arcu ante ut nunc. Vivamus in leo felis, vitae eleifend lacus. Donec tempus aliquam purus porttitor tristique. Suspendisse diam neque, suscipit feugiat fringilla non, eleifend sit nullam.
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/rev_stemming.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/rev_stemming.js b/test/javascript/tests/rev_stemming.js
    new file mode 100644
    index 0000000..954da79
    --- /dev/null
    +++ b/test/javascript/tests/rev_stemming.js
    @@ -0,0 +1,110 @@
    +// 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.
    +
    +couchTests.rev_stemming = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + var db = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
    + dbB.deleteDb();
    + dbB.createDb();
    + if (debug) debugger;
    +
    + var newLimit = 5;
    +
    + T(db.getDbProperty("_revs_limit") == 1000);
    +
    + // Make an invalid request to _revs_limit
    + // Should return 400
    + var xhr = CouchDB.request("PUT", "/test_suite_db/_revs_limit", {body:"\"foo\""});
    + T(xhr.status == 400);
    + var result = JSON.parse(xhr.responseText);
    + T(result.error == "bad_request");
    + T(result.reason == "Rev limit has to be an integer");
    +
    + var doc = {_id:"foo",foo:0}
    + for( var i=0; i < newLimit + 1; i++) {
    + doc.foo++;
    + T(db.save(doc).ok);
    + }
    + var doc0 = db.open("foo", {revs:true});
    + T(doc0._revisions.ids.length == newLimit + 1);
    +
    + var docBar = {_id:"bar",foo:0}
    + for( var i=0; i < newLimit + 1; i++) {
    + docBar.foo++;
    + T(db.save(docBar).ok);
    + }
    + T(db.open("bar", {revs:true})._revisions.ids.length == newLimit + 1);
    +
    + T(db.setDbProperty("_revs_limit", newLimit).ok);
    +
    + for( var i=0; i < newLimit + 1; i++) {
    + doc.foo++;
    + T(db.save(doc).ok);
    + }
    + doc0 = db.open("foo", {revs:true});
    + T(doc0._revisions.ids.length == newLimit);
    +
    +
    + // If you replicate after you make more edits than the limit, you'll
    + // cause a spurious edit conflict.
    + CouchDB.replicate("test_suite_db_a", "test_suite_db_b");
    + var docB1 = dbB.open("foo",{conflicts:true})
    + T(docB1._conflicts == null);
    +
    + for( var i=0; i < newLimit - 1; i++) {
    + doc.foo++;
    + T(db.save(doc).ok);
    + }
    +
    + // one less edit than limit, no conflict
    + CouchDB.replicate("test_suite_db_a", "test_suite_db_b");
    + var docB1 = dbB.open("foo",{conflicts:true})
    + T(docB1._conflicts == null);
    +
    + //now we hit the limit
    + for( var i=0; i < newLimit; i++) {
    + doc.foo++;
    + T(db.save(doc).ok);
    + }
    +
    + CouchDB.replicate("test_suite_db_a", "test_suite_db_b");
    +
    + var docB2 = dbB.open("foo",{conflicts:true});
    +
    + // we have a conflict, but the previous replicated rev is always the losing
    + // conflict
    + T(docB2._conflicts[0] == docB1._rev)
    +
    + // We having already updated bar before setting the limit, so it's still got
    + // a long rev history. compact to stem the revs.
    +
    + T(db.open("bar", {revs:true})._revisions.ids.length == newLimit + 1);
    +
    + T(db.compact().ok);
    +
    + // compaction isn't instantaneous, loop until done
    + while (db.info().compact_running) {};
    +
    + // force reload because ETags don't honour compaction
    + var req = db.request("GET", "/test_suite_db_a/bar?revs=true", {
    + headers:{"if-none-match":"pommes"}
    + });
    +
    + var finalDoc = JSON.parse(req.responseText);
    + TEquals(newLimit, finalDoc._revisions.ids.length,
    + "should return a truncated revision list");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/rewrite.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/rewrite.js b/test/javascript/tests/rewrite.js
    new file mode 100644
    index 0000000..5c56fa5
    --- /dev/null
    +++ b/test/javascript/tests/rewrite.js
    @@ -0,0 +1,505 @@
    +// 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.
    +
    +
    +
    +couchTests.rewrite = function(debug) {
    + if (debug) debugger;
    + var dbNames = ["test_suite_db", "test_suite_db/with_slashes"];
    + for (var i=0; i < dbNames.length; i++) {
    + var db = new CouchDB(dbNames[i]);
    + var dbName = encodeURIComponent(dbNames[i]);
    + db.deleteDb();
    + db.createDb();
    +
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "authentication_handlers",
    + value: "{couch_httpd_auth, special_test_authentication_handler}"},
    + {section:"httpd",
    + key: "WWW-Authenticate",
    + value: "X-Couch-Test-Auth"}],
    +
    + function(){
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + },
    + rewrites: [
    + {
    + "from": "foo",
    + "to": "foo.txt"
    + },
    + {
    + "from": "foo2",
    + "to": "foo.txt",
    + "method": "GET"
    + },
    + {
    + "from": "hello/:id",
    + "to": "_update/hello/:id",
    + "method": "PUT"
    + },
    + {
    + "from": "/welcome",
    + "to": "_show/welcome"
    + },
    + {
    + "from": "/welcome/:name",
    + "to": "_show/welcome",
    + "query": {
    + "name": ":name"
    + }
    + },
    + {
    + "from": "/welcome2",
    + "to": "_show/welcome",
    + "query": {
    + "name": "user"
    + }
    + },
    + {
    + "from": "/welcome3/:name",
    + "to": "_update/welcome2/:name",
    + "method": "PUT"
    + },
    + {
    + "from": "/welcome3/:name",
    + "to": "_show/welcome2/:name",
    + "method": "GET"
    + },
    + {
    + "from": "/welcome4/*",
    + "to" : "_show/welcome3",
    + "query": {
    + "name": "*"
    + }
    + },
    + {
    + "from": "/welcome5/*",
    + "to" : "_show/*",
    + "query": {
    + "name": "*"
    + }
    + },
    + {
    + "from": "basicView",
    + "to": "_view/basicView",
    + },
    + {
    + "from": "simpleForm/basicView",
    + "to": "_list/simpleForm/basicView",
    + },
    + {
    + "from": "simpleForm/basicViewFixed",
    + "to": "_list/simpleForm/basicView",
    + "query": {
    + "startkey": 3,
    + "endkey": 8
    + }
    + },
    + {
    + "from": "simpleForm/basicViewPath/:start/:end",
    + "to": "_list/simpleForm/basicView",
    + "query": {
    + "startkey": ":start",
    + "endkey": ":end"
    + },
    + "formats": {
    + "start": "int",
    + "end": "int"
    + }
    + },
    + {
    + "from": "simpleForm/complexView",
    + "to": "_list/simpleForm/complexView",
    + "query": {
    + "key": [1, 2]
    + }
    + },
    + {
    + "from": "simpleForm/complexView2",
    + "to": "_list/simpleForm/complexView",
    + "query": {
    + "key": ["test", {}]
    + }
    + },
    + {
    + "from": "simpleForm/complexView3",
    + "to": "_list/simpleForm/complexView",
    + "query": {
    + "key": ["test", ["test", "essai"]]
    + }
    + },
    + {
    + "from": "simpleForm/complexView4",
    + "to": "_list/simpleForm/complexView2",
    + "query": {
    + "key": {"c": 1}
    + }
    + },
    + {
    + "from": "simpleForm/complexView5/:a/:b",
    + "to": "_list/simpleForm/complexView3",
    + "query": {
    + "key": [":a", ":b"]
    + }
    + },
    + {
    + "from": "simpleForm/complexView6",
    + "to": "_list/simpleForm/complexView3",
    + "query": {
    + "key": [":a", ":b"]
    + }
    + },
    + {
    + "from": "simpleForm/complexView7/:a/:b",
    + "to": "_view/complexView3",
    + "query": {
    + "key": [":a", ":b"],
    + "include_docs": ":doc"
    + },
    + "format": {
    + "doc": "bool"
    + }
    +
    + },
    + {
    + "from": "/",
    + "to": "_view/basicView",
    + },
    + {
    + "from": "/db/*",
    + "to": "../../*"
    + }
    + ],
    + lists: {
    + simpleForm: stringFun(function(head, req) {
    + log("simpleForm");
    + send('<ul>');
    + var row, row_number = 0, prevKey, firstKey = null;
    + while (row = getRow()) {
    + row_number += 1;
    + if (!firstKey) firstKey = row.key;
    + prevKey = row.key;
    + send('\n<li>Key: '+row.key
    + +' Value: '+row.value
    + +' LineNo: '+row_number+'</li>');
    + }
    + return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
    + }),
    + },
    + shows: {
    + "welcome": stringFun(function(doc,req) {
    + return "Welcome " + req.query["name"];
    + }),
    + "welcome2": stringFun(function(doc, req) {
    + return "Welcome " + doc.name;
    + }),
    + "welcome3": stringFun(function(doc,req) {
    + return "Welcome " + req.query["name"];
    + })
    + },
    + updates: {
    + "hello" : stringFun(function(doc, req) {
    + if (!doc) {
    + if (req.id) {
    + return [{
    + _id : req.id
    + }, "New World"]
    + }
    + return [null, "Empty World"];
    + }
    + doc.world = "hello";
    + doc.edited_by = req.userCtx;
    + return [doc, "hello doc"];
    + }),
    + "welcome2": stringFun(function(doc, req) {
    + if (!doc) {
    + if (req.id) {
    + return [{
    + _id: req.id,
    + name: req.id
    + }, "New World"]
    + }
    + return [null, "Empty World"];
    + }
    + return [doc, "hello doc"];
    + })
    + },
    + views : {
    + basicView : {
    + map : stringFun(function(doc) {
    + if (doc.integer) {
    + emit(doc.integer, doc.string);
    + }
    +
    + })
    + },
    + complexView: {
    + map: stringFun(function(doc) {
    + if (doc.type == "complex") {
    + emit([doc.a, doc.b], doc.string);
    + }
    + })
    + },
    + complexView2: {
    + map: stringFun(function(doc) {
    + if (doc.type == "complex") {
    + emit(doc.a, doc.string);
    + }
    + })
    + },
    + complexView3: {
    + map: stringFun(function(doc) {
    + if (doc.type == "complex") {
    + emit(doc.b, doc.string);
    + }
    + })
    + }
    + }
    + }
    +
    + db.save(designDoc);
    +
    + var docs = makeDocs(0, 10);
    + db.bulkSave(docs);
    +
    + var docs2 = [
    + {"a": 1, "b": 1, "string": "doc 1", "type": "complex"},
    + {"a": 1, "b": 2, "string": "doc 2", "type": "complex"},
    + {"a": "test", "b": {}, "string": "doc 3", "type": "complex"},
    + {"a": "test", "b": ["test", "essai"], "string": "doc 4", "type": "complex"},
    + {"a": {"c": 1}, "b": "", "string": "doc 5", "type": "complex"}
    + ];
    +
    + db.bulkSave(docs2);
    +
    + // test simple rewriting
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/foo");
    + T(req.responseText == "This is a base64 encoded text");
    + T(req.getResponseHeader("Content-Type") == "text/plain");
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/foo2");
    + T(req.responseText == "This is a base64 encoded text");
    + T(req.getResponseHeader("Content-Type") == "text/plain");
    +
    +
    + // test POST
    + // hello update world
    +
    + var doc = {"word":"plankton", "name":"Rusty"}
    + var resp = db.save(doc);
    + T(resp.ok);
    + var docid = resp.id;
    +
    + xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test/_rewrite/hello/"+docid);
    + T(xhr.status == 201);
    + T(xhr.responseText == "hello doc");
    + T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))
    +
    + doc = db.open(docid);
    + T(doc.world == "hello");
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome?name=user");
    + T(req.responseText == "Welcome user");
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome/user");
    + T(req.responseText == "Welcome user");
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome2");
    + T(req.responseText == "Welcome user");
    +
    + xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test/_rewrite/welcome3/test");
    + T(xhr.status == 201);
    + T(xhr.responseText == "New World");
    + T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome3/test");
    + T(xhr.responseText == "Welcome test");
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome4/user");
    + T(req.responseText == "Welcome user");
    +
    + req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome5/welcome3");
    + T(req.responseText == "Welcome welcome3");
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/basicView");
    + T(xhr.status == 200, "view call");
    + T(/{"total_rows":9/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/");
    + T(xhr.status == 200, "view call");
    + T(/{"total_rows":9/.test(xhr.responseText));
    +
    +
    + // get with query params
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/basicView?startkey=3&endkey=8");
    + T(xhr.status == 200, "with query params");
    + T(!(/Key: 1/.test(xhr.responseText)));
    + T(/FirstKey: 3/.test(xhr.responseText));
    + T(/LastKey: 8/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/basicViewFixed");
    + T(xhr.status == 200, "with query params");
    + T(!(/Key: 1/.test(xhr.responseText)));
    + T(/FirstKey: 3/.test(xhr.responseText));
    + T(/LastKey: 8/.test(xhr.responseText));
    +
    + // get with query params
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/basicViewFixed?startkey=4");
    + T(xhr.status == 200, "with query params");
    + T(!(/Key: 1/.test(xhr.responseText)));
    + T(/FirstKey: 3/.test(xhr.responseText));
    + T(/LastKey: 8/.test(xhr.responseText));
    +
    + // get with query params
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/basicViewPath/3/8");
    + T(xhr.status == 200, "with query params");
    + T(!(/Key: 1/.test(xhr.responseText)));
    + T(/FirstKey: 3/.test(xhr.responseText));
    + T(/LastKey: 8/.test(xhr.responseText));
    +
    + // get with query params
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView");
    + T(xhr.status == 200, "with query params");
    + T(/FirstKey: [1, 2]/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView2");
    + T(xhr.status == 200, "with query params");
    + T(/Value: doc 3/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView3");
    + T(xhr.status == 200, "with query params");
    + T(/Value: doc 4/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView4");
    + T(xhr.status == 200, "with query params");
    + T(/Value: doc 5/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView5/test/essai");
    + T(xhr.status == 200, "with query params");
    + T(/Value: doc 4/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView6?a=test&b=essai");
    + T(xhr.status == 200, "with query params");
    + T(/Value: doc 4/.test(xhr.responseText));
    +
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView7/test/essai?doc=true");
    + T(xhr.status == 200, "with query params");
    + var result = JSON.parse(xhr.responseText);
    + T(typeof(result.rows[0].doc) === "object");
    +
    + // COUCHDB-2031 - path normalization versus qs params
    + xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/db/_design/test?meta=true");
    + T(xhr.status == 200, "path normalization works with qs params");
    + var result = JSON.parse(xhr.responseText);
    + T(result['_id'] == "_design/test");
    + T(typeof(result['_revs_info']) === "object");
    +
    + // test path relative to server
    + designDoc.rewrites.push({
    + "from": "uuids",
    + "to": "../../../_uuids"
    + });
    + T(db.save(designDoc).ok);
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/uuids");
    + T(xhr.status == 500);
    + var result = JSON.parse(xhr.responseText);
    + T(result.error == "insecure_rewrite_rule");
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "secure_rewrites",
    + value: "false"}],
    + function() {
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/uuids?cache=bust");
    + T(xhr.status == 200);
    + var result = JSON.parse(xhr.responseText);
    + T(result.uuids.length == 1);
    + var first = result.uuids[0];
    + });
    + });
    +
    + // test invalid rewrites
    + // string
    + var ddoc = {
    + _id: "_design/invalid",
    + rewrites: "[{\"from\":\"foo\",\"to\":\"bar\"}]"
    + }
    + db.save(ddoc);
    + var res = CouchDB.request("GET", "/"+dbName+"/_design/invalid/_rewrite/foo");
    + TEquals(400, res.status, "should return 400");
    +
    + var ddoc_requested_path = {
    + _id: "_design/requested_path",
    + rewrites:[
    + {"from": "show", "to": "_show/origin/0"},
    + {"from": "show_rewritten", "to": "_rewrite/show"}
    + ],
    + shows: {
    + origin: stringFun(function(doc, req) {
    + return req.headers["x-couchdb-requested-path"];
    + })}
    + };
    +
    + db.save(ddoc_requested_path);
    + var url = "/"+dbName+"/_design/requested_path/_rewrite/show";
    + var res = CouchDB.request("GET", url);
    + TEquals(url, res.responseText, "should return the original url");
    +
    + var url = "/"+dbName+"/_design/requested_path/_rewrite/show_rewritten";
    + var res = CouchDB.request("GET", url);
    + TEquals(url, res.responseText, "returned the original url");
    +
    + var ddoc_loop = {
    + _id: "_design/loop",
    + rewrites: [{ "from": "loop", "to": "_rewrite/loop"}]
    + };
    + db.save(ddoc_loop);
    +
    + // Assert loop detection
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "rewrite_limit",
    + value: "2"}],
    + function(){
    + var url = "/"+dbName+"/_design/loop/_rewrite/loop";
    + var xhr = CouchDB.request("GET", url);
    + TEquals(400, xhr.status);
    + });
    +
    + // Assert serial execution is not spuriously counted as loop
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "rewrite_limit",
    + value: "2"},
    + {section: "httpd",
    + key: "secure_rewrites",
    + value: "false"}],
    + function(){
    + var url = "/"+dbName+"/_design/test/_rewrite/foo";
    + for (var i=0; i < 5; i++) {
    + var xhr = CouchDB.request("GET", url);
    + TEquals(200, xhr.status);
    + }
    + });
    + }
    +}

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/security_validation.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/security_validation.js b/test/javascript/tests/security_validation.js
    new file mode 100644
    index 0000000..14e5d04
    --- /dev/null
    +++ b/test/javascript/tests/security_validation.js
    @@ -0,0 +1,338 @@
    +// 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.
    +
    +couchTests.security_validation = function(debug) {
    + // This tests couchdb's security and validation features. This does
    + // not test authentication, except to use test authentication code made
    + // specifically for this testing. It is a WWW-Authenticate scheme named
    + // X-Couch-Test-Auth, and the user names and passwords are hard coded
    + // on the server-side.
    + //
    + // We could have used Basic authentication, however the XMLHttpRequest
    + // implementation for Firefox and Safari, and probably other browsers are
    + // broken (Firefox always prompts the user on 401 failures, Safari gives
    + // odd security errors when using different name/passwords, perhaps due
    + // to cross site scripting prevention). These problems essentially make Basic
    + // authentication testing in the browser impossible. But while hard to
    + // test automated in the browser, Basic auth may still useful for real
    + // world use where these bugs/behaviors don't matter.
    + //
    + // So for testing purposes we are using this custom X-Couch-Test-Auth.
    + // It's identical to Basic auth, except it doesn't even base64 encode
    + // the "username:password" string, it's sent completely plain text.
    + // Firefox and Safari both deal with this correctly (which is to say
    + // they correctly do nothing special).
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "authentication_handlers",
    + value: "{couch_httpd_auth, special_test_authentication_handler}"},
    + {section:"httpd",
    + key: "WWW-Authenticate",
    + value: "X-Couch-Test-Auth"}],
    +
    + function () {
    + // try saving document using the wrong credentials
    + var wrongPasswordDb = new CouchDB("test_suite_db",
    + {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
    + );
    +
    + try {
    + wrongPasswordDb.save({foo:1,author:"Damien Katz"});
    + T(false && "Can't get here. Should have thrown an error 1");
    + } catch (e) {
    + T(e.error == "unauthorized");
    + T(wrongPasswordDb.last_req.status == 401);
    + }
    +
    + // test force basic login
    + var resp = wrongPasswordDb.request("GET", "/_session?basic=true");
    + var err = JSON.parse(resp.responseText);
    + T(err.error == "unauthorized");
    + T(resp.status == 401);
    +
    + // Create the design doc that will run custom validation code
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + validate_doc_update: stringFun(function (newDoc, oldDoc, userCtx, secObj) {
    + if (secObj.admin_override) {
    + if (userCtx.roles.indexOf('_admin') != -1) {
    + // user is admin, they can do anything
    + return true;
    + }
    + }
    + // docs should have an author field.
    + if (!newDoc._deleted && !newDoc.author) {
    + throw {forbidden:
    + "Documents must have an author field"};
    + }
    + if (oldDoc && oldDoc.author != userCtx.name) {
    + throw {unauthorized:
    + "You are not the author of this document. You jerk."};
    + }
    + })
    + }
    +
    + // Save a document normally
    + var userDb = new CouchDB("test_suite_db",
    + {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
    + );
    +
    + T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
    +
    + // Attempt to save the design as a non-admin
    + try {
    + userDb.save(designDoc);
    + T(false && "Can't get here. Should have thrown an error on design doc");
    + } catch (e) {
    + T(e.error == "unauthorized");
    + T(userDb.last_req.status == 401);
    + }
    +
    + // set user as the admin
    + T(db.setSecObj({
    + admins : {names : ["Damien Katz"]}
    + }).ok);
    +
    + T(userDb.save(designDoc).ok);
    +
    + var user2Db = new CouchDB("test_suite_db",
    + {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
    + );
    + // Attempt to save the design as a non-admin (in replication scenario)
    + designDoc.foo = "bar";
    + designDoc._rev = "2-642e20f96624a0aae6025b4dba0c6fb2";
    + try {
    + user2Db.save(designDoc, {new_edits : false});
    + T(false && "Can't get here. Should have thrown an error on design doc");
    + } catch (e) {
    + T(e.error == "unauthorized");
    + T(user2Db.last_req.status == 401);
    + }
    +
    + // test the _session API
    + var resp = userDb.request("GET", "/_session");
    + var user = JSON.parse(resp.responseText).userCtx;
    + T(user.name == "Damien Katz");
    + // test that the roles are listed properly
    + TEquals(user.roles, []);
    +
    +
    + // update the document
    + var doc = userDb.open("testdoc");
    + doc.foo=2;
    + T(userDb.save(doc).ok);
    +
    + // Save a document that's missing an author field (before and after compaction)
    + for (var i=0; i<2; i++) {
    + try {
    + userDb.save({foo:1});
    + T(false && "Can't get here. Should have thrown an error 2");
    + } catch (e) {
    + T(e.error == "forbidden");
    + T(userDb.last_req.status == 403);
    + }
    + // compact.
    + T(db.compact().ok);
    + T(db.last_req.status == 202);
    + // compaction isn't instantaneous, loop until done
    + while (db.info().compact_running) {};
    + }
    +
    + // Now attempt to update the document as a different user, Jan
    + var doc = user2Db.open("testdoc");
    + doc.foo=3;
    + try {
    + user2Db.save(doc);
    + T(false && "Can't get here. Should have thrown an error 3");
    + } catch (e) {
    + T(e.error == "unauthorized");
    + T(user2Db.last_req.status == 401);
    + }
    +
    + // Now have Damien change the author to Jan
    + doc = userDb.open("testdoc");
    + doc.author="Jan Lehnardt";
    + T(userDb.save(doc).ok);
    +
    + // Now update the document as Jan
    + doc = user2Db.open("testdoc");
    + doc.foo = 3;
    + T(user2Db.save(doc).ok);
    +
    + // Damien can't delete it
    + try {
    + userDb.deleteDoc(doc);
    + T(false && "Can't get here. Should have thrown an error 4");
    + } catch (e) {
    + T(e.error == "unauthorized");
    + T(userDb.last_req.status == 401);
    + }
    +
    + // admin must save with author field unless admin override
    + var resp = db.request("GET", "/_session");
    + var user = JSON.parse(resp.responseText).userCtx;
    + T(user.name == null);
    + // test that we are admin
    + TEquals(user.roles, ["_admin"]);
    +
    + // can't save the doc even though we are admin
    + var doc = db.open("testdoc");
    + doc.foo=3;
    + try {
    + db.save(doc);
    + T(false && "Can't get here. Should have thrown an error 3");
    + } catch (e) {
    + T(e.error == "unauthorized");
    + T(db.last_req.status == 401);
    + }
    +
    + // now turn on admin override
    + T(db.setDbProperty("_security", {admin_override : true}).ok);
    + T(db.save(doc).ok);
    +
    + // try to do something lame
    + try {
    + db.setDbProperty("_security", ["foo"]);
    + T(false && "can't do this");
    + } catch(e) {}
    +
    + // go back to normal
    + T(db.setDbProperty("_security", {admin_override : false}).ok);
    +
    + // Now delete document
    + T(user2Db.deleteDoc(doc).ok);
    +
    + // now test bulk docs
    + var docs = [{_id:"bahbah",author:"Damien Katz",foo:"bar"},{_id:"fahfah",foo:"baz"}];
    +
    + // Create the docs
    + var results = db.bulkSave(docs);
    +
    + T(results[0].rev)
    + T(results[0].error == undefined)
    + T(results[1].rev === undefined)
    + T(results[1].error == "forbidden")
    +
    + T(db.open("bahbah"));
    + T(db.open("fahfah") == null);
    +
    +
    + // now all or nothing with a failure
    + var docs = [{_id:"booboo",author:"Damien Katz",foo:"bar"},{_id:"foofoo",foo:"baz"}];
    +
    + // Create the docs
    + var results = db.bulkSave(docs, {all_or_nothing:true});
    +
    + T(results.errors.length == 1);
    + T(results.errors[0].error == "forbidden");
    + T(db.open("booboo") == null);
    + T(db.open("foofoo") == null);
    +
    + // Now test replication
    + var AuthHeaders = {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"};
    + var host = CouchDB.host;
    + var dbPairs = [
    + {source:"test_suite_db_a",
    + target:"test_suite_db_b"},
    +
    + {source:"test_suite_db_a",
    + target:{url: CouchDB.protocol + host + "/test_suite_db_b",
    + headers: AuthHeaders}},
    +
    + {source:{url:CouchDB.protocol + host + "/test_suite_db_a",
    + headers: AuthHeaders},
    + target:"test_suite_db_b"},
    +
    + {source:{url:CouchDB.protocol + host + "/test_suite_db_a",
    + headers: AuthHeaders},
    + target:{url:CouchDB.protocol + host + "/test_suite_db_b",
    + headers: AuthHeaders}},
    + ]
    + var adminDbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
    + var adminDbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
    + var dbA = new CouchDB("test_suite_db_a",
    + {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
    + var dbB = new CouchDB("test_suite_db_b",
    + {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
    + var xhr;
    + for (var testPair = 0; testPair < dbPairs.length; testPair++) {
    + var A = dbPairs[testPair].source
    + var B = dbPairs[testPair].target
    +
    + adminDbA.deleteDb();
    + adminDbA.createDb();
    + adminDbB.deleteDb();
    + adminDbB.createDb();
    +
    + // save and replicate a documents that will and will not pass our design
    + // doc validation function.
    + dbA.save({_id:"foo1",value:"a",author:"Noah Slater"});
    + dbA.save({_id:"foo2",value:"a",author:"Christopher Lenz"});
    + dbA.save({_id:"bad1",value:"a"});
    +
    + T(CouchDB.replicate(A, B, {headers:AuthHeaders}).ok);
    + T(CouchDB.replicate(B, A, {headers:AuthHeaders}).ok);
    +
    + T(dbA.open("foo1"));
    + T(dbB.open("foo1"));
    + T(dbA.open("foo2"));
    + T(dbB.open("foo2"));
    +
    + // save the design doc to dbA
    + delete designDoc._rev; // clear rev from previous saves
    + adminDbA.save(designDoc);
    +
    + // no affect on already saved docs
    + T(dbA.open("bad1"));
    +
    + // Update some docs on dbB. Since the design hasn't replicated, anything
    + // is allowed.
    +
    + // this edit will fail validation on replication to dbA (no author)
    + T(dbB.save({_id:"bad2",value:"a"}).ok);
    +
    + // this edit will fail security on replication to dbA (wrong author
    + // replicating the change)
    + var foo1 = dbB.open("foo1");
    + foo1.value = "b";
    + dbB.save(foo1);
    +
    + // this is a legal edit
    + var foo2 = dbB.open("foo2");
    + foo2.value = "b";
    + dbB.save(foo2);
    +
    + var results = CouchDB.replicate(B, A, {headers:AuthHeaders});
    +
    + T(results.ok);
    +
    + T(results.history[0].docs_written == 1);
    + T(results.history[0].doc_write_failures == 2);
    +
    + // bad2 should not be on dbA
    + T(dbA.open("bad2") == null);
    +
    + // The edit to foo1 should not have replicated.
    + T(dbA.open("foo1").value == "a");
    +
    + // The edit to foo2 should have replicated.
    + T(dbA.open("foo2").value == "b");
    + }
    + });
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/show_documents.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/show_documents.js b/test/javascript/tests/show_documents.js
    new file mode 100644
    index 0000000..618925f
    --- /dev/null
    +++ b/test/javascript/tests/show_documents.js
    @@ -0,0 +1,420 @@
    +// 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.
    +
    +couchTests.show_documents = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var designDoc = {
    + _id:"_design/template",
    + language: "javascript",
    + shows: {
    + "hello" : stringFun(function(doc, req) {
    + log("hello fun");
    + if (doc) {
    + return "Hello World";
    + } else {
    + if(req.id) {
    + return "New World";
    + } else {
    + return "Empty World";
    + }
    + }
    + }),
    + "just-name" : stringFun(function(doc, req) {
    + if (doc) {
    + return {
    + body : "Just " + doc.name
    + };
    + } else {
    + return {
    + body : "No such doc",
    + code : 404
    + };
    + }
    + }),
    + "json" : stringFun(function(doc, req) {
    + return {
    + json : doc
    + }
    + }),
    + "req-info" : stringFun(function(doc, req) {
    + return {
    + json : req
    + }
    + }),
    + "show-deleted" : stringFun(function(doc, req) {
    + if(doc) {
    + return doc._id;
    + } else {
    + return "No doc " + req.id;
    + }
    + }),
    + "render-error" : stringFun(function(doc, req) {
    + return noSuchVariable;
    + }),
    + "empty" : stringFun(function(doc, req) {
    + return "";
    + }),
    + "fail" : stringFun(function(doc, req) {
    + return doc._id;
    + }),
    + "no-set-etag" : stringFun(function(doc, req) {
    + return {
    + headers : {
    + "Etag" : "skipped"
    + },
    + "body" : "something"
    + }
    + }),
    + "list-api" : stringFun(function(doc, req) {
    + start({"X-Couch-Test-Header": "Yeah"});
    + send("Hey");
    + }),
    + "list-api-provides" : stringFun(function(doc, req) {
    + provides("text", function(){
    + send("foo, ");
    + send("bar, ");
    + send("baz!");
    + })
    + }),
    + "list-api-provides-and-return" : stringFun(function(doc, req) {
    + provides("text", function(){
    + send("4, ");
    + send("5, ");
    + send("6, ");
    + return "7!";
    + })
    + send("1, ");
    + send("2, ");
    + return "3, ";
    + }),
    + "list-api-mix" : stringFun(function(doc, req) {
    + start({"X-Couch-Test-Header": "Yeah"});
    + send("Hey ");
    + return "Dude";
    + }),
    + "list-api-mix-with-header" : stringFun(function(doc, req) {
    + start({"X-Couch-Test-Header": "Yeah"});
    + send("Hey ");
    + return {
    + headers: {
    + "X-Couch-Test-Header-Awesome": "Oh Yeah!"
    + },
    + body: "Dude"
    + };
    + }),
    + "accept-switch" : stringFun(function(doc, req) {
    + if (req.headers["Accept"].match(/image/)) {
    + return {
    + // a 16x16 px version of the CouchDB logo
    + "base64" :
    +["iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV",
    +"BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/",
    +"AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7",
    +"/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6",
    +"wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA",
    +"AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5",
    +"zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx",
    +"vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT",
    +"LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII="].join(''),
    + headers : {
    + "Content-Type" : "image/png",
    + "Vary" : "Accept" // we set this for proxy caches
    + }
    + };
    + } else {
    + return {
    + "body" : "accepting text requests",
    + headers : {
    + "Content-Type" : "text/html",
    + "Vary" : "Accept"
    + }
    + };
    + }
    + }),
    + "provides" : stringFun(function(doc, req) {
    + registerType("foo", "application/foo","application/x-foo");
    +
    + provides("html", function() {
    + return "Ha ha, you said \"" + doc.word + "\".";
    + });
    +
    + provides("foo", function() {
    + return "foofoo";
    + });
    + }),
    + "withSlash": stringFun(function(doc, req) {
    + return { json: doc }
    + }),
    + "secObj": stringFun(function(doc, req) {
    + return { json: req.secObj };
    + })
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var doc = {"word":"plankton", "name":"Rusty"}
    + var resp = db.save(doc);
    + T(resp.ok);
    + var docid = resp.id;
    +
    + // show error
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/");
    + T(xhr.status == 404, 'Should be missing');
    + T(JSON.parse(xhr.responseText).reason == "Invalid path.");
    +
    + // hello template world
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/"+docid);
    + T(xhr.responseText == "Hello World", "hello");
    + T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));
    +
    +
    + // Fix for COUCHDB-379
    + T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB"));
    +
    + // // error stacktraces
    + // xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/render-error/"+docid);
    + // T(JSON.parse(xhr.responseText).error == "render_error");
    +
    + // hello template world (no docid)
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello");
    + T(xhr.responseText == "Empty World");
    +
    + // hello template world (no docid)
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/empty");
    + T(xhr.responseText == "");
    +
    + // // hello template world (non-existing docid)
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/fail/nonExistingDoc");
    + T(xhr.status == 404);
    + var resp = JSON.parse(xhr.responseText);
    + T(resp.error == "not_found");
    +
    + // show with doc
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid);
    + T(xhr.responseText == "Just Rusty");
    +
    + // show with missing doc
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/missingdoc");
    + T(xhr.status == 404);
    + TEquals("No such doc", xhr.responseText);
    +
    + // show with missing func
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/missing/"+docid);
    + T(xhr.status == 404, "function is missing");
    +
    + // missing design doc
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/missingddoc/_show/just-name/"+docid);
    + T(xhr.status == 404);
    + var resp = JSON.parse(xhr.responseText);
    + T(resp.error == "not_found");
    +
    + // query parameters
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/req-info/"+docid+"?foo=bar", {
    + headers: {
    + "Accept": "text/html;text/plain;*/*",
    + "X-Foo" : "bar"
    + }
    + });
    + var resp = JSON.parse(xhr.responseText);
    + T(equals(resp.headers["X-Foo"], "bar"));
    + T(equals(resp.query, {foo:"bar"}));
    + T(equals(resp.method, "GET"));
    + T(equals(resp.path[5], docid));
    + T(equals(resp.info.db_name, "test_suite_db"));
    +
    + // accept header switching
    + // different mime has different etag
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/accept-switch/"+docid, {
    + headers: {"Accept": "text/html;text/plain;*/*"}
    + });
    + var ct = xhr.getResponseHeader("Content-Type");
    + T(/text\/html/.test(ct))
    + T("Accept" == xhr.getResponseHeader("Vary"));
    + var etag = xhr.getResponseHeader("etag");
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/accept-switch/"+docid, {
    + headers: {"Accept": "image/png;*/*"}
    + });
    + T(xhr.responseText.match(/PNG/))
    + T("image/png" == xhr.getResponseHeader("Content-Type"));
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag2 != etag);
    +
    + // proper etags
    + // show with doc
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid);
    + // extract the ETag header values
    + etag = xhr.getResponseHeader("etag");
    + // get again with etag in request
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
    + headers: {"if-none-match": etag}
    + });
    + // should be 304
    + T(xhr.status == 304);
    +
    + // update the doc
    + doc.name = "Crusty";
    + resp = db.save(doc);
    + T(resp.ok);
    + // req with same etag
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
    + headers: {"if-none-match": etag}
    + });
    + // status is 200
    + T(xhr.status == 200);
    +
    + // get new etag and request again
    + etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
    + headers: {"if-none-match": etag}
    + });
    + // should be 304
    + T(xhr.status == 304);
    +
    + // update design doc (but not function)
    + designDoc.isChanged = true;
    + T(db.save(designDoc).ok);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
    + headers: {"if-none-match": etag}
    + });
    + // should not be 304 if we change the doc
    + T(xhr.status != 304, "changed ddoc");
    +
    + // update design doc function
    + designDoc.shows["just-name"] = stringFun(function(doc, req) {
    + return {
    + body : "Just old " + doc.name
    + };
    + });
    + T(db.save(designDoc).ok);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
    + headers: {"if-none-match": etag}
    + });
    + // status is 200
    + T(xhr.status == 200);
    +
    +
    + // JS can't set etag
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/no-set-etag/"+docid);
    + // extract the ETag header values
    + etag = xhr.getResponseHeader("etag");
    + T(etag != "skipped")
    +
    + // test the provides mime matcher
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+docid, {
    + headers: {
    + "Accept": 'text/html,application/atom+xml; q=0.9'
    + }
    + });
    + var ct = xhr.getResponseHeader("Content-Type");
    + T(/charset=utf-8/.test(ct))
    + T(/text\/html/.test(ct))
    + T(xhr.responseText == "Ha ha, you said \"plankton\".");
    +
    + // registering types works
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+docid, {
    + headers: {
    + "Accept": "application/x-foo"
    + }
    + });
    + T(xhr.getResponseHeader("Content-Type") == "application/x-foo");
    + T(xhr.responseText.match(/foofoo/));
    +
    + // test the provides mime matcher without a match
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+docid, {
    + headers: {
    + "Accept": 'text/monkeys'
    + }
    + });
    + var rs = JSON.parse(xhr.responseText);
    + T(rs.error == "not_acceptable")
    +
    +
    + // test inclusion of conflict state
    + var doc1 = {_id:"foo", a:1};
    + var doc2 = {_id:"foo", a:2};
    + db.save(doc1);
    +
    + // create the conflict with an all_or_nothing bulk docs request
    + var docs = [doc2];
    + db.bulkSave(docs, {all_or_nothing:true});
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/json/foo");
    + TEquals(1, JSON.parse(xhr.responseText)._conflicts.length);
    +
    + var doc3 = {_id:"a/b/c", a:1};
    + db.save(doc3);
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/withSlash/a/b/c");
    + T(xhr.status == 200);
    +
    + // hello template world (non-existing docid)
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/nonExistingDoc");
    + T(xhr.responseText == "New World");
    +
    + // test list() compatible API
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api/foo");
    + T(xhr.responseText == "Hey");
    + TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool");
    +
    + // test list() compatible API with provides function
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-provides/foo?format=text");
    + TEquals(xhr.responseText, "foo, bar, baz!", "should join chunks to response body");
    +
    + // should keep next result order: chunks + return value + provided chunks + provided return value
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-provides-and-return/foo?format=text");
    + TEquals(xhr.responseText, "1, 2, 3, 4, 5, 6, 7!", "should not break 1..7 range");
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-mix/foo");
    + T(xhr.responseText == "Hey Dude");
    + TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool");
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-mix-with-header/foo");
    + T(xhr.responseText == "Hey Dude");
    + TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool");
    + TEquals("Oh Yeah!", xhr.getResponseHeader("X-Couch-Test-Header-Awesome"), "header should be cool");
    +
    + // test deleted docs
    + var doc = {_id:"testdoc",foo:1};
    + db.save(doc);
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc");
    + TEquals("testdoc", xhr.responseText, "should return 'testdoc'");
    +
    + db.deleteDoc(doc);
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc");
    + TEquals("No doc testdoc", xhr.responseText, "should return 'no doc testdoc'");
    +
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "authentication_handlers",
    + value: "{couch_httpd_auth, special_test_authentication_handler}"},
    + {section:"httpd",
    + key: "WWW-Authenticate",
    + value: "X-Couch-Test-Auth"}],
    +
    + function() {
    + T(db.setDbProperty("_security", {foo: true}).ok);
    + T(db.save({_id:"testdoc",foo:1}).ok);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/secObj");
    + var resp = JSON.parse(xhr.responseText);
    + T(resp.foo == true);
    + }
    + );
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/stats.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/stats.js b/test/javascript/tests/stats.js
    new file mode 100644
    index 0000000..87440b3
    --- /dev/null
    +++ b/test/javascript/tests/stats.js
    @@ -0,0 +1,348 @@
    +// 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.
    +
    +couchTests.stats = function(debug) {
    +
    + function newDb(name, doSetup) {
    + var db = new CouchDB(name, {"X-Couch-Full-Commit": "false"});
    + if(doSetup) {
    + db.deleteDb();
    + db.createDb();
    + }
    + return db;
    + };
    +
    + function getStat(path) {
    + var stat = CouchDB.requestStats(path, true);
    + return stat ? stat.value : null;
    + };
    +
    + function doView(db) {
    + var designDoc = {
    + _id:"_design/test", // turn off couch.js id escaping?
    + language: "javascript",
    + views: {
    + all_docs: {map: "function(doc) {emit(doc.integer, null);}"}
    + }
    + };
    + db.save(designDoc);
    + db.view("test/all_docs");
    + };
    +
    + function runTest(path, funcs) {
    + var db = newDb("test_suite_db", true);
    + if(funcs.setup) funcs.setup(db);
    + var before = getStat(path);
    + if(funcs.run) funcs.run(db);
    + var after = getStat(path);
    + if(funcs.test) funcs.test(before, after);
    + }
    +
    + if (debug) debugger;
    +
    + (function() {
    + var db = newDb("test_suite_db");
    + db.deleteDb();
    +
    + var before = getStat(["couchdb", "open_databases"]);
    + db.createDb();
    + var after = getStat(["couchdb", "open_databases"]);
    + TEquals(before+1, after, "Creating a db increments open db count.");
    + })();
    +
    + runTest(["couchdb", "open_databases"], {
    + setup: function() {restartServer();},
    + run: function(db) {db.open("123");},
    + test: function(before, after) {
    + TEquals(before+1, after, "Opening a db increments open db count.");
    + }
    + });
    +
    + runTest(["couchdb", "open_databases"], {
    + run: function(db) {db.deleteDb();},
    + test: function(before, after) {
    + TEquals(before-1, after, "Deleting a db decrements open db count.");
    + }
    + });
    +
    + (function() {
    + restartServer();
    + var max = 5;
    +
    + var testFun = function() {
    + var pre_dbs = getStat(["couchdb", "open_databases"]) || 0;
    + var pre_files = getStat(["couchdb", "open_os_files"]) || 0;
    +
    + var triggered = false;
    + var db = null;
    + for(var i = 0; i < max*2; i++) {
    + while (true) {
    + try {
    + db = newDb("test_suite_db_" + i, true);
    + break;
    + } catch(e) {
    + // all_dbs_active error!
    + triggered = true;
    + }
    + }
    +
    + // Trigger a delayed commit
    + db.save({_id: "" + i, "lang": "Awesome!"});
    + }
    + T(triggered, "We managed to force a all_dbs_active error.");
    +
    + var open_dbs = getStat(["couchdb", "open_databases"]);
    + TEquals(open_dbs > 0, true, "We actually opened some dbs.");
    + TEquals(max, open_dbs, "We only have max db's open.");
    +
    + for(var i = 0; i < max * 2; i++) {
    + newDb("test_suite_db_" + i).deleteDb();
    + }
    +
    + var post_dbs = getStat(["couchdb", "open_databases"]);
    + var post_files = getStat(["couchdb", "open_os_files"]);
    + TEquals(pre_dbs, post_dbs, "We have the same number of open dbs.");
    + TEquals(pre_files, post_files, "We have the same number of open files.");
    + };
    +
    + run_on_modified_server(
    + [{section: "couchdb", key: "max_dbs_open", value: "5"}],
    + testFun
    + );
    + })();
    +
    + // Just fetching the before value is the extra +1 in test
    + runTest(["couchdb", "httpd", "requests"], {
    + run: function() {CouchDB.request("GET", "/");},
    + test: function(before, after) {
    + TEquals(before+2, after, "Request counts are incremented properly.");
    + }
    + });
    +
    + runTest(["couchdb", "database_reads"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {db.open("test");},
    + test: function(before, after) {
    + TEquals(before+1, after, "Reading a doc increments docs reads.");
    + }
    + });
    +
    + runTest(["couchdb", "database_reads"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {db.request("GET", "/");},
    + test: function(before, after) {
    + TEquals(before, after, "Only doc reads increment doc reads.");
    + }
    + });
    +
    + runTest(["couchdb", "database_reads"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {db.open("test", {"open_revs": "all"});},
    + test: function(before, after) {
    + TEquals(before+1, after, "Reading doc revs increments docs reads.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + run: function(db) {db.save({"a": "1"});},
    + test: function(before, after) {
    + TEquals(before+1, after, "Saving docs incrememnts doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + run: function(db) {
    + CouchDB.request("POST", "/test_suite_db", {
    + headers: {"Content-Type": "application/json"},
    + body: '{"a": "1"}'
    + });
    + },
    + test: function(before, after) {
    + TEquals(before+1, after, "POST'ing new docs increments doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {var doc = db.open("test"); db.save(doc);},
    + test: function(before, after) {
    + TEquals(before+1, after, "Updating docs incrememnts doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {var doc = db.open("test"); db.deleteDoc(doc);},
    + test: function(before, after) {
    + TEquals(before+1, after, "Deleting docs increments doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {
    + CouchDB.request("COPY", "/test_suite_db/test", {
    + headers: {"Destination": "copy_of_test"}
    + });
    + },
    + test: function(before, after) {
    + TEquals(before+1, after, "Copying docs increments doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + run: function() {
    + CouchDB.request("PUT", "/test_suite_db/bin_doc2/foo2.txt", {
    + body: "This is no base64 encoded test",
    + headers: {"Content-Type": "text/plain;charset=utf-8"}
    + });
    + },
    + test: function(before, after) {
    + TEquals(before+1, after, "Create with attachment increments doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "database_writes"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {
    + var doc = db.open("test");
    + CouchDB.request("PUT", "/test_suite_db/test/foo2.txt?rev=" + doc._rev, {
    + body: "This is no base64 encoded text",
    + headers: {"Content-Type": "text/plainn;charset=utf-8"}
    + });
    + },
    + test: function(before, after) {
    + TEquals(before+1, after, "Adding attachment increments doc writes.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd", "bulk_requests"], {
    + run: function(db) {db.bulkSave(makeDocs(5));},
    + test: function(before, after) {
    + TEquals(before+1, after, "The bulk_requests counter is incremented.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd", "view_reads"], {
    + run: function(db) {doView(db);},
    + test: function(before, after) {
    + TEquals(before+1, after, "Reading a view increments view reads.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd", "view_reads"], {
    + setup: function(db) {db.save({"_id": "test"});},
    + run: function(db) {db.open("test");},
    + test: function(before, after) {
    + TEquals(before, after, "Reading a doc doesn't increment view reads.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd", "temporary_view_reads"], {
    + run: function(db) { db.query(function(doc) { emit(doc._id); }); },
    + test: function(before, after) {
    + TEquals(before+1, after, "Temporary views have their own counter.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd", "temporary_view_reads"], {
    + run: function(db) {doView(db);},
    + test: function(before, after) {
    + TEquals(before, after, "Permanent views don't affect temporary views.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd", "view_reads"], {
    + run: function(db) { db.query(function(doc) { emit(doc._id); }); },
    + test: function(before, after) {
    + TEquals(before, after, "Temporary views don't affect permanent views.");
    + }
    + });
    +
    + // Relies on getting the stats values being GET requests.
    + runTest(["couchdb", "httpd_request_methods", "GET"], {
    + test: function(before, after) {
    + TEquals(before+1, after, "Get requests are incremented properly.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd_request_methods", "GET"], {
    + run: function() {CouchDB.request("POST", "/");},
    + test: function(before, after) {
    + TEquals(before+1, after, "POST requests don't affect GET counter.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd_request_methods", "POST"], {
    + run: function() {CouchDB.request("POST", "/");},
    + test: function(before, after) {
    + TEquals(before+1, after, "POST requests are incremented properly.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd_status_codes", "404"], {
    + run: function() {CouchDB.request("GET", "/nonexistant_db");},
    + test: function(before, after) {
    + TEquals(before+1, after, "Increments 404 counter on db not found.");
    + }
    + });
    +
    + runTest(["couchdb", "httpd_status_codes", "404"], {
    + run: function() {CouchDB.request("GET", "/");},
    + test: function(before, after) {
    + TEquals(before, after, "Getting DB info doesn't increment 404's");
    + }
    + });
    +
    + var test_metric = function(metric, expected_fields) {
    + for (var k in metric) {
    + T(expected_fields.indexOf(k) >= 0, "Unknown property name: " + k);
    + }
    + for (var k in expected_fields) {
    + T(metric[expected_fields[k]] !== undefined, "Missing required property: " + k);
    + }
    + };
    +
    + var test_histogram = function(histo) {
    + test_metric(histo, ["value", "type", "desc"]);
    + test_metric(histo.value, ["min", "max", "arithmetic_mean",
    + "geometric_mean", "harmonic_mean", "median", "variance",
    + "standard_deviation", "skewness", "kurtosis", "percentile",
    + "histogram", "n"]);
    + };
    +
    + var test_counter = function(counter) {
    + test_metric(counter, ["value", "desc", "type"]);
    + };
    +
    + var test_metrics = function(metrics) {
    + if (metrics.type === "counter") {
    + test_counter(metrics);
    + } else if (metrics.type === "gauge") {
    + test_counter(metrics);
    + } else if (metrics.type === "histogram") {
    + test_histogram(metrics);
    + } else if (metrics.type === undefined) {
    + for (var k in metrics) {
    + test_metrics(metrics[k]);
    + }
    + }
    + };
    +
    + (function() {
    + var summary = JSON.parse(CouchDB.request("GET", "/_stats", {
    + headers: {"Accept": "application/json"}
    + }).responseText);
    + T(typeof(summary) === 'object');
    + test_metrics(summary);
    + })();
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/update_documents.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/update_documents.js b/test/javascript/tests/update_documents.js
    new file mode 100644
    index 0000000..bdb7a99
    --- /dev/null
    +++ b/test/javascript/tests/update_documents.js
    @@ -0,0 +1,235 @@
    +// 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.
    +
    +
    +couchTests.update_documents = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var designDoc = {
    + _id:"_design/update",
    + language: "javascript",
    + updates: {
    + "hello" : stringFun(function(doc, req) {
    + log(doc);
    + log(req);
    + if (!doc) {
    + if (req.id) {
    + return [
    + // Creates a new document with the PUT docid,
    + { _id : req.id,
    + reqs : [req] },
    + // and returns an HTML response to the client.
    + "<p>New World</p>"];
    + };
    + //
    + return [null, "<p>Empty World</p>"];
    + };
    + // we can update the document inline
    + doc.world = "hello";
    + // we can record aspects of the request or use them in application logic.
    + doc.reqs && doc.reqs.push(req);
    + doc.edited_by = req.userCtx;
    + return [doc, "<p>hello doc</p>"];
    + }),
    + "in-place" : stringFun(function(doc, req) {
    + var field = req.query.field;
    + var value = req.query.value;
    + var message = "set "+field+" to "+value;
    + doc[field] = value;
    + return [doc, message];
    + }),
    + "form-update" : stringFun(function(doc, req) {
    + for (var field in req.form) {
    + doc[field] = req.form[field];
    + }
    + var message = "updated doc from form";
    + return [doc, message];
    + }),
    + "bump-counter" : stringFun(function(doc, req) {
    + if (!doc.counter) doc.counter = 0;
    + doc.counter += 1;
    + var message = "<h1>bumped it!</h1>";
    + return [doc, message];
    + }),
    + "error" : stringFun(function(doc, req) {
    + superFail.badCrash;
    + }),
    + "get-uuid" : stringFun(function(doc, req) {
    + return [null, req.uuid];
    + }),
    + "code-n-bump" : stringFun(function(doc,req) {
    + if (!doc.counter) doc.counter = 0;
    + doc.counter += 1;
    + var message = "<h1>bumped it!</h1>";
    + resp = {"code": 302, "body": message}
    + return [doc, resp];
    + }),
    + "resp-code" : stringFun(function(doc,req) {
    + resp = {"code": 302}
    + return [null, resp];
    + }),
    + "resp-code-and-json" : stringFun(function(doc,req) {
    + resp = {"code": 302, "json": {"ok": true}}
    + return [{"_id": req["uuid"]}, resp];
    + }),
    + "binary" : stringFun(function(doc, req) {
    + var resp = {
    + "headers" : {
    + "Content-Type" : "application/octet-stream"
    + },
    + "base64" : "aGVsbG8gd29ybGQh" // "hello world!" encoded
    + };
    + return [doc, resp];
    + }),
    + "empty" : stringFun(function(doc, req) {
    + return [{}, 'oops'];
    + })
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var doc = {"word":"plankton", "name":"Rusty"}
    + var resp = db.save(doc);
    + T(resp.ok);
    + var docid = resp.id;
    +
    + // update error
    + var xhr = CouchDB.request("POST", "/test_suite_db/_design/update/_update/");
    + T(xhr.status == 404, 'Should be missing');
    + T(JSON.parse(xhr.responseText).reason == "Invalid path.");
    +
    + // hello update world
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/hello/"+docid);
    + T(xhr.status == 201);
    + T(xhr.responseText == "<p>hello doc</p>");
    + T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));
    + T(equals(docid, xhr.getResponseHeader("X-Couch-Id")));
    +
    + doc = db.open(docid);
    + T(doc.world == "hello");
    +
    + // Fix for COUCHDB-379
    + T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB"));
    +
    + // hello update world (no docid)
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/update/_update/hello");
    + T(xhr.status == 200);
    + T(xhr.responseText == "<p>Empty World</p>");
    +
    + // no GET allowed
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/update/_update/hello");
    + // T(xhr.status == 405); // TODO allow qs to throw error code as well as error message
    + T(JSON.parse(xhr.responseText).error == "method_not_allowed");
    +
    + // // hello update world (non-existing docid)
    + xhr = CouchDB.request("GET", "/test_suite_db/nonExistingDoc");
    + T(xhr.status == 404);
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/hello/nonExistingDoc");
    + T(xhr.status == 201);
    + T(xhr.responseText == "<p>New World</p>");
    + xhr = CouchDB.request("GET", "/test_suite_db/nonExistingDoc");
    + T(xhr.status == 200);
    +
    + // in place update
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/in-place/"+docid+'?field=title&value=test');
    + T(xhr.status == 201);
    + T(xhr.responseText == "set title to test");
    + doc = db.open(docid);
    + T(doc.title == "test");
    +
    + // form update via application/x-www-form-urlencoded
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/form-update/"+docid, {
    + headers : {"Content-Type":"application/x-www-form-urlencoded"},
    + body : "formfoo=bar&formbar=foo"
    + });
    + TEquals(201, xhr.status);
    + TEquals("updated doc from form", xhr.responseText);
    + doc = db.open(docid);
    + TEquals("bar", doc.formfoo);
    + TEquals("foo", doc.formbar);
    +
    + // bump counter
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/bump-counter/"+docid, {
    + headers : {"X-Couch-Full-Commit":"true"}
    + });
    + T(xhr.status == 201);
    + T(xhr.responseText == "<h1>bumped it!</h1>");
    + doc = db.open(docid);
    + T(doc.counter == 1);
    +
    + // _update honors full commit if you need it to
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/bump-counter/"+docid, {
    + headers : {"X-Couch-Full-Commit":"true"}
    + });
    +
    + var NewRev = xhr.getResponseHeader("X-Couch-Update-NewRev");
    + doc = db.open(docid);
    + T(doc['_rev'] == NewRev);
    +
    +
    + T(doc.counter == 2);
    +
    + // Server provides UUID when POSTing without an ID in the URL
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/update/_update/get-uuid/");
    + T(xhr.status == 200);
    + T(xhr.responseText.length == 32);
    +
    + // COUCHDB-1229 - allow slashes in doc ids for update handlers
    + // /db/_design/doc/_update/handler/doc/id
    +
    + var doc = {
    + _id:"with/slash",
    + counter:1
    + };
    + db.save(doc);
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/bump-counter/with/slash");
    + TEquals(201, xhr.status, "should return a 200 status");
    + TEquals("<h1>bumped it!</h1>", xhr.responseText, "should report bumping");
    +
    + var doc = db.open("with/slash");
    + TEquals(2, doc.counter, "counter should be 2");
    +
    + // COUCHDB-648 - the code in the JSON response should be honored
    +
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/code-n-bump/"+docid, {
    + headers : {"X-Couch-Full-Commit":"true"}
    + });
    + T(xhr.status == 302);
    + T(xhr.responseText == "<h1>bumped it!</h1>");
    + doc = db.open(docid);
    + T(doc.counter == 3);
    +
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/update/_update/resp-code/");
    + T(xhr.status == 302);
    +
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/update/_update/resp-code-and-json/");
    + TEquals(302, xhr.status);
    + T(JSON.parse(xhr.responseText).ok);
    +
    + // base64 response
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/binary/"+docid, {
    + headers : {"X-Couch-Full-Commit":"false"},
    + body : 'rubbish'
    + });
    + T(xhr.status == 201);
    + T(xhr.responseText == "hello world!");
    + T(/application\/octet-stream/.test(xhr.getResponseHeader("Content-Type")));
    +
    + // Insert doc with empty id
    + xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/empty/foo");
    + TEquals(400, xhr.status);
    + TEquals("Document id must not be empty", JSON.parse(xhr.responseText).reason);
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/users_db.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/users_db.js b/test/javascript/tests/users_db.js
    new file mode 100644
    index 0000000..56dae6b
    --- /dev/null
    +++ b/test/javascript/tests/users_db.js
    @@ -0,0 +1,173 @@
    +// 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.
    +
    +couchTests.users_db = function(debug) {
    + // This tests the users db, especially validations
    + // this should also test that you can log into the couch
    +
    + var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    +
    + // test that you can treat "_user" as a db-name
    + // this can complicate people who try to secure the users db with
    + // an http proxy and fail to get both the actual db and the _user path
    + // maybe it's not the right approach...
    + // hard to know what else to do, as we don't let non-admins inspect the config
    + // to determine the actual users db name.
    +
    + function testFun() {
    + // test that the validation function is installed
    + var ddoc = usersDb.open("_design/_auth");
    + T(ddoc.validate_doc_update);
    +
    + // test that you can login as a user using basic auth
    + var jchrisUserDoc = CouchDB.prepareUserDoc({
    + name: "jchris@apache.org"
    + }, "funnybone");
    + T(usersDb.save(jchrisUserDoc).ok);
    +
    + T(CouchDB.session().userCtx.name == null);
    +
    + // test that you can use basic auth aginst the users db
    + var s = CouchDB.session({
    + headers : {
    + // base64_encode("jchris@apache.org:funnybone")
    + "Authorization" : "Basic amNocmlzQGFwYWNoZS5vcmc6ZnVubnlib25l"
    + }
    + });
    + T(s.userCtx.name == "jchris@apache.org");
    + T(s.info.authenticated == "default");
    + T(s.info.authentication_db == "test_suite_users");
    + TEquals(["oauth", "cookie", "default"], s.info.authentication_handlers);
    + var s = CouchDB.session({
    + headers : {
    + "Authorization" : "Basic Xzpf" // name and pass of _:_
    + }
    + });
    + T(s.name == null);
    + T(s.info.authenticated == "default");
    +
    +
    + // ok, now create a conflicting edit on the jchris doc, and make sure there's no login.
    + var jchrisUser2 = JSON.parse(JSON.stringify(jchrisUserDoc));
    + jchrisUser2.foo = "bar";
    + T(usersDb.save(jchrisUser2).ok);
    + try {
    + usersDb.save(jchrisUserDoc);
    + T(false && "should be an update conflict");
    + } catch(e) {
    + T(true);
    + }
    + // save as bulk with new_edits=false to force conflict save
    + var resp = usersDb.bulkSave([jchrisUserDoc],{all_or_nothing : true});
    +
    + var jchrisWithConflict = usersDb.open(jchrisUserDoc._id, {conflicts : true});
    + T(jchrisWithConflict._conflicts.length == 1);
    +
    + // no login with conflicted user doc
    + try {
    + var s = CouchDB.session({
    + headers : {
    + "Authorization" : "Basic amNocmlzQGFwYWNoZS5vcmc6ZnVubnlib25l"
    + }
    + });
    + T(false && "this will throw");
    + } catch(e) {
    + T(e.error == "unauthorized");
    + T(/conflict/.test(e.reason));
    + }
    +
    + // you can delete a user doc
    + s = CouchDB.session().userCtx;
    + T(s.name == null);
    + T(s.roles.indexOf("_admin") !== -1);
    + T(usersDb.deleteDoc(jchrisWithConflict).ok);
    +
    + // you can't change doc from type "user"
    + jchrisUserDoc = usersDb.open(jchrisUserDoc._id);
    + jchrisUserDoc.type = "not user";
    + try {
    + usersDb.save(jchrisUserDoc);
    + T(false && "should only allow us to save doc when type == 'user'");
    + } catch(e) {
    + T(e.reason == "doc.type must be user");
    + }
    + jchrisUserDoc.type = "user";
    +
    + // "roles" must be an array
    + jchrisUserDoc.roles = "not an array";
    + try {
    + usersDb.save(jchrisUserDoc);
    + T(false && "should only allow us to save doc when roles is an array");
    + } catch(e) {
    + T(e.reason == "doc.roles must be an array");
    + }
    + jchrisUserDoc.roles = [];
    +
    + // "roles" must be an array of strings
    + jchrisUserDoc.roles = [12];
    + try {
    + usersDb.save(jchrisUserDoc);
    + T(false && "should only allow us to save doc when roles is an array of strings");
    + } catch(e) {
    + TEquals(e.reason, "doc.roles can only contain strings");
    + }
    + jchrisUserDoc.roles = [];
    +
    + // "roles" must exist
    + delete jchrisUserDoc.roles;
    + try {
    + usersDb.save(jchrisUserDoc);
    + T(false && "should only allow us to save doc when roles exists");
    + } catch(e) {
    + T(e.reason == "doc.roles must exist");
    + }
    + jchrisUserDoc.roles = [];
    +
    + // character : is not allowed in usernames
    + var joeUserDoc = CouchDB.prepareUserDoc({
    + name: "joe:erlang"
    + }, "qwerty");
    + try {
    + usersDb.save(joeUserDoc);
    + T(false, "shouldn't allow : in usernames");
    + } catch(e) {
    + TEquals("Character `:` is not allowed in usernames.", e.reason);
    + }
    +
    + // test that you can login as a user with a password starting with :
    + var doc = CouchDB.prepareUserDoc({
    + name: "foo@example.org"
    + }, ":bar");
    + T(usersDb.save(doc).ok);
    +
    + T(CouchDB.session().userCtx.name == null);
    +
    + // test that you can use basic auth aginst the users db
    + var s = CouchDB.session({
    + headers : {
    + // base64_encode("foo@example.org::bar")
    + "Authorization" : "Basic Zm9vQGV4YW1wbGUub3JnOjpiYXI="
    + }
    + });
    + T(s.userCtx.name == "foo@example.org");
    +
    + };
    +
    + usersDb.deleteDb();
    + run_on_modified_server(
    + [{section: "couch_httpd_auth",
    + key: "authentication_db", value: usersDb.name}],
    + testFun
    + );
    + usersDb.deleteDb(); // cleanup
    +
    +}
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/lorem_b64.txt
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/lorem_b64.txt b/test/javascript/tests/lorem_b64.txt
    new file mode 100644
    index 0000000..8a21d79
    --- /dev/null
    +++ b/test/javascript/tests/lorem_b64.txt
    @@ -0,0 +1 @@
    +TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUGhhc2VsbHVzIG51bmMgc2FwaWVuLCBwb3J0YSBpZCBwZWxsZW50ZXNxdWUgYXQsIGVsZW1lbnR1bSBldCBmZWxpcy4gQ3VyYWJpdHVyIGNvbmRpbWVudHVtIGFudGUgaW4gbWV0dXMgaWFjdWxpcyBxdWlzIGNvbmd1ZSBkaWFtIGNvbW1vZG8uIERvbmVjIGVsZWlmZW5kIGFudGUgc2VkIG51bGxhIGRhcGlidXMgY29udmFsbGlzLiBVdCBjdXJzdXMgYWxpcXVhbSBuZXF1ZSwgdmVsIHBvcnR0aXRvciB0ZWxsdXMgaW50ZXJkdW0gdXQuIFNlZCBwaGFyZXRyYSBsYWNpbmlhIGFkaXBpc2NpbmcuIEluIHRyaXN0aXF1ZSB0cmlzdGlxdWUgZmVsaXMgbm9uIHRpbmNpZHVudC4gTnVsbGEgYXVjdG9yIG1hdXJpcyBhIHZlbGl0IGN1cnN1cyB1bHRyaWNpZXMuIEluIGF0IGxpYmVybyBxdWlzIGp1c3RvIGNvbnNlY3RldHVyIGxhb3JlZXQuIE51bGxhbSBpZCB1bHRyaWNlcyBudW5jLiBEb25lYyBub24gdHVycGlzIG51bGxhLCBldSBsYWNpbmlhIGFudGUuIE51bmMgZXUgb3JjaSBldCB0dXJwaXMgcHJldGl1bSB2ZW5lbmF0aXMuIE5hbSBtb2xlc3RpZSwgbGFjdXMgYXQgZGlnbmlzc2ltIGVsZW1lbnR1bSwgYW50ZSBsaWJlcm8gY29uc2VjdGV0dXIgbGliZXJvLCB1dCBsYWNpbmlhIGxhY3VzIHVybmEgZXQgcHVydXMuIE51bGxhbSBsb3JlbSBpcHN1bSwgZGFwaWJ1cyB2ZWwgdWxsYW1jb3JwZXIgYSwgbWFsZXN1YWRhIGEgb
      WV0dXMuIFNlZCBwb3J0YSBhZGlwaXNjaW5nIG1hZ25hLCBxdWlzIHB1bHZpbmFyIHB1cnVzIG1hdHRpcyBmcmluZ2lsbGEuIEludGVnZXIgcGVsbGVudGVzcXVlIHNhcGllbiBpbiBuZXF1ZSB0cmlzdGlxdWUgYWMgaWFjdWxpcyBsaWJlcm8gdWx0cmljaWVzLiBVdCBlZ2V0IHBoYXJldHJhIHB1cnVzLgoKTnVsbGEgaW4gY29udmFsbGlzIHRlbGx1cy4gUHJvaW4gdGluY2lkdW50IHN1c2NpcGl0IHZ1bHB1dGF0ZS4gU3VzcGVuZGlzc2UgcG90ZW50aS4gTnVsbGFtIHRyaXN0aXF1ZSBqdXN0byBtaSwgYSB0cmlzdGlxdWUgbGlndWxhLiBEdWlzIGNvbnZhbGxpcyBhbGlxdWFtIGlhY3VsaXMuIE51bGxhIGRpY3R1bSBmcmluZ2lsbGEgY29uZ3VlLiBTdXNwZW5kaXNzZSBhYyBsZW8gbGVjdHVzLCBhYyBhbGlxdWFtIGp1c3RvLiBVdCBwb3J0dGl0b3IgY29tbW9kbyBtaSBzZWQgbHVjdHVzLiBOdWxsYSBhdCBlbmltIGxvcmVtLiBOdW5jIGV1IGp1c3RvIHNhcGllbiwgYSBibGFuZGl0IG9kaW8uIEN1cmFiaXR1ciBmYXVjaWJ1cyBzb2xsaWNpdHVkaW4gZG9sb3IsIGlkIGxhY2luaWEgc2VtIGF1Y3RvciBpbi4gRG9uZWMgdmFyaXVzIG51bmMgYXQgbGVjdHVzIHNhZ2l0dGlzIG5lYyBsdWN0dXMgYXJjdSBwaGFyZXRyYS4gTnVuYyBzZWQgbWV0dXMganVzdG8uIENyYXMgdmVsIG1hdXJpcyBkaWFtLiBVdCBmZXVnaWF0IGZlbGlzIGVnZXQgbmVxdWUgcGhhcmV0cmEgdmVzdGlidWx1bSBjb25zZWN0ZXR1ciBtYXNzYSBmYW
      NpbGlzaXMuIFF1aXNxdWUgY29uc2VjdGV0dXIgbHVjdHVzIG5pc2kgcXVpcyB0aW5jaWR1bnQuIFZpdmFtdXMgY3Vyc3VzIGN1cnN1cyBxdWFtIG5vbiBibGFuZGl0LiBQZWxsZW50ZXNxdWUgZXQgdmVsaXQgbGFjdXMuIFBlbGxlbnRlc3F1ZSBoYWJpdGFudCBtb3JiaSB0cmlzdGlxdWUgc2VuZWN0dXMgZXQgbmV0dXMgZXQgbWFsZXN1YWRhIGZhbWVzIGFjIHR1cnBpcyBlZ2VzdGFzLgoKSW4gZXQgZG9sb3Igdml0YWUgb3JjaSBhZGlwaXNjaW5nIGNvbmd1ZS4gQWxpcXVhbSBncmF2aWRhIG5pYmggYXQgbmlzbCBncmF2aWRhIG1vbGVzdGllLiBDdXJhYml0dXIgYSBiaWJlbmR1bSBzYXBpZW4uIEFsaXF1YW0gdGluY2lkdW50LCBudWxsYSBuZWMgcHJldGl1bSBsb2JvcnRpcywgb2RpbyBhdWd1ZSB0aW5jaWR1bnQgYXJjdSwgYSBsb2JvcnRpcyBvZGlvIHNlbSB1dCBwdXJ1cy4gRG9uZWMgYWNjdW1zYW4gbWF0dGlzIG51bmMgdml0YWUgbGFjaW5pYS4gU3VzcGVuZGlzc2UgcG90ZW50aS4gSW50ZWdlciBjb21tb2RvIG5pc2wgcXVpcyBuaWJoIGludGVyZHVtIG5vbiBmcmluZ2lsbGEgZHVpIHNvZGFsZXMuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIEV0aWFtIHVsbGFtY29ycGVyLCBtaSBpZCBmZXVnaWF0IGJpYmVuZHVtLCBwdXJ1cyB
      uZXF1ZSBjdXJzdXMgbWF1cmlzLCBpZCBzb2RhbGVzIHF1YW0gbmlzaSBpZCB2ZWxpdC4gU2VkIGxlY3R1cyBsZW8sIHRpbmNpZHVudCB2ZWwgcmhvbmN1cyBpbXBlcmRpZXQsIGJsYW5kaXQgaW4gbGVvLiBJbnRlZ2VyIHF1aXMgbWFnbmEgbnVsbGEuIERvbmVjIHZlbCBuaXNsIG1hZ25hLCB1dCByaG9uY3VzIGR1aS4gQWxpcXVhbSBncmF2aWRhLCBudWxsYSBuZWMgZWxlaWZlbmQgbHVjdHVzLCBuZXF1ZSBuaWJoIHBoYXJldHJhIGFudGUsIHF1aXMgZWdlc3RhcyBlbGl0IG1ldHVzIGEgbWkuIE51bmMgbmVjIGF1Z3VlIHF1YW0uIE1vcmJpIHRpbmNpZHVudCB0cmlzdGlxdWUgdmFyaXVzLiBTdXNwZW5kaXNzZSBpYWN1bGlzIGVsaXQgZmV1Z2lhdCBtYWduYSBwZWxsZW50ZXNxdWUgdWx0cmljaWVzLiBWZXN0aWJ1bHVtIGFsaXF1YW0gdG9ydG9yIG5vbiBhbnRlIHVsbGFtY29ycGVyIGZyaW5naWxsYS4gRG9uZWMgaWFjdWxpcyBtaSBxdWlzIG1hdXJpcyBvcm5hcmUgdmVzdGlidWx1bS4KCkluIGEgbWFnbmEgbmlzaSwgYSB1bHRyaWNpZXMgbWFzc2EuIERvbmVjIGVsaXQgbmVxdWUsIHZpdmVycmEgbm9uIHRlbXBvciBxdWlzLCBmcmluZ2lsbGEgaW4gbWV0dXMuIEludGVnZXIgb2RpbyBvZGlvLCBldWlzbW9kIHZpdGFlIG1vbGxpcyBzZWQsIHNvZGFsZXMgZWdldCBsaWJlcm8uIERvbmVjIG5lYyBtYXNzYSBpbiBmZWxpcyBvcm5hcmUgcGhhcmV0cmEgYXQgbmVjIHRlbGx1cy4gTnVuYyBsb3JlbSBkb2xvciwgcHJl
      dGl1bSB2ZWwgYXVjdG9yIGluLCB2b2x1dHBhdCB2aXRhZSBmZWxpcy4gTWFlY2VuYXMgcmhvbmN1cywgb3JjaSB2ZWwgYmxhbmRpdCBldWlzbW9kLCB0dXJwaXMgZXJhdCB0aW5jaWR1bnQgYW50ZSwgZWxlbWVudHVtIGFkaXBpc2NpbmcgbmlzbCB1cm5hIGluIG5pc2kuIFBoYXNlbGx1cyBzYWdpdHRpcywgZW5pbSBzZWQgYWNjdW1zYW4gY29uc2VxdWF0LCB1cm5hIGF1Z3VlIGxvYm9ydGlzIGVyYXQsIG5vbiBtYWxlc3VhZGEgcXVhbSBtZXR1cyBzb2xsaWNpdHVkaW4gYW50ZS4gSW4gbGVvIHB1cnVzLCBkaWduaXNzaW0gcXVpcyB2YXJpdXMgdmVsLCBwZWxsZW50ZXNxdWUgZXQgbmliaC4gSW4gc2VkIHRvcnRvciBpYWN1bGlzIGxpYmVybyBtb2xsaXMgcGVsbGVudGVzcXVlIGlkIHZpdGFlIGxlY3R1cy4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIFBoYXNlbGx1cyBtYXVyaXMgZW5pbSwgcG9zdWVyZSBlZ2V0IGx1Y3R1cyBhYywgaWFjdWxpcyBldCBxdWFtLiBWaXZhbXVzIGV0IG5pYmggZGlhbSwgZWxlbWVudHVtIGVnZXN0YXMgdGVsbHVzLiBBZW5lYW4gdnVscHV0YXRlIG1hbGVzdWFkYSBlc3QuIFNlZCBwb3N1ZXJlIHBvcnRhIGRpYW0gYSBzb2RhbGVzLiBQcm9pbiBldSBzZW0gbm9uIHZlbGl0IGZhY2lsaXNpcyB2ZW5lbmF0aXMgc2VkIGEgdHVycGlzLgoKUGVsbGVudGVzcXVlIHNlZCByaXN1cyBhIGFudGUgdnVscHV0YXRlIGxvYm9ydGlzIHNpdCBhbWV0IGV1IG5pc2wuIFN1c3BlbmRpc
      3NlIHV0IGVyb3MgbWksIGEgcmhvbmN1cyBsYWN1cy4gQ3VyYWJpdHVyIGZlcm1lbnR1bSB2ZWhpY3VsYSB0ZWxsdXMsIGEgb3JuYXJlIG1pIGNvbmRpbWVudHVtIHZlbC4gSW50ZWdlciBtb2xlc3RpZSB2b2x1dHBhdCB2aXZlcnJhLiBJbnRlZ2VyIHBvc3VlcmUgZXVpc21vZCB2ZW5lbmF0aXMuIFByb2luIGFjIG1hdXJpcyBzZWQgbnVsbGEgcGhhcmV0cmEgcG9ydHRpdG9yLiBEdWlzIHZlbCBkdWkgaW4gcmlzdXMgc29kYWxlcyBhdWN0b3Igc2l0IGFtZXQgbm9uIGVuaW0uIE1hZWNlbmFzIG1vbGxpcyBsYWN1cyBhdCBsaWd1bGEgZmF1Y2lidXMgc29kYWxlcy4gQ3JhcyB2ZWwgbmVxdWUgYXJjdS4gU2VkIHRpbmNpZHVudCB0b3J0b3IgcHJldGl1bSBuaXNpIGludGVyZHVtIHF1aXMgZGljdHVtIGFyY3UgbGFvcmVldC4gTW9yYmkgcHJldGl1bSB1bHRyaWNlcyBmZXVnaWF0LiBNYWVjZW5hcyBjb252YWxsaXMgYXVndWUgbmVjIGZlbGlzIG1hbGVzdWFkYSBtYWxlc3VhZGEgc2NlbGVyaXNxdWUgbWF1cmlzIHBsYWNlcmF0LiBTZWQgYXQgbWFnbmEgZW5pbSwgYXQgZnJpbmdpbGxhIGRvbG9yLiBRdWlzcXVlIHV0IG1hdHRpcyBkdWkuIFByYWVzZW50IGNvbnNlY3RldHVyIGFudGUgdml2ZXJyYSBuaXNpIGJsYW5kaXQgcGhhcmV0cmEuIFF1aXNxdWUgbWV0dXMgZWxpdCwgZGlnbmlzc2ltIHZpdGFlIGZlcm1lbnR1bSBzaXQgYW1ldCwgZnJpbmdpbGxhIGltcGVyZGlldCBvZGlvLiBDcmFzIGVnZXQgcHVydXMgZWdldC
      B0ZWxsdXMgZmV1Z2lhdCBsdWN0dXMgYSBhYyBwdXJ1cy4gQ3JhcyB2aXRhZSBuaXNsIHZlbCBhdWd1ZSByaG9uY3VzIHBvcnR0aXRvciBzaXQgYW1ldCBxdWlzIGxvcmVtLiBEb25lYyBpbnRlcmR1bSBwZWxsZW50ZXNxdWUgYWRpcGlzY2luZy4gUGhhc2VsbHVzIG5lcXVlIGxpYmVybywgYWxpcXVhbSBpbiBtYXR0aXMgdml0YWUsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgbmliaC4KCkRvbmVjIG5lYyBudWxsYSB1cm5hLCBhYyBzYWdpdHRpcyBsZWN0dXMuIFN1c3BlbmRpc3NlIG5vbiBlbGl0IHNlZCBtaSBhdWN0b3IgZmFjaWxpc2lzIHZpdGFlIGV0IGxlY3R1cy4gRnVzY2UgYWMgdnVscHV0YXRlIG1hdXJpcy4gTW9yYmkgY29uZGltZW50dW0gdWx0cmljZXMgbWV0dXMsIGV0IGFjY3Vtc2FuIHB1cnVzIG1hbGVzdWFkYSBhdC4gTWFlY2VuYXMgbG9ib3J0aXMgYW50ZSBzZWQgbWFzc2EgZGljdHVtIHZpdGFlIHZlbmVuYXRpcyBlbGl0IGNvbW1vZG8uIFByb2luIHRlbGx1cyBlcm9zLCBhZGlwaXNjaW5nIHNlZCBkaWduaXNzaW0gdml0YWUsIHRlbXBvciBlZ2V0IGFudGUuIEFlbmVhbiBpZCB0ZWxsdXMgbmVjIG1hZ25hIGN1cnN1cyBwaGFyZXRyYSB2aXRhZSB2ZWwgZW5pbS4gTW9yYmkgdmVzdGlidWx1bSBwaGFyZXRyYSBlc3QgaW4gdnVscHV0YXRlLiBBbGlxdWFtIHZpdGFlIG1ldHVzIGFyY3UsIGlkIGFsaXF1ZXQgbnVsbGEuIFBoYXNlbGx1cyBsaWd1bGEgZXN0LCBoZW5kcmVyaXQgbmVjIGlhY3VsaXMgdXQ
      sIHZvbHV0cGF0IHZlbCBlcm9zLiBTdXNwZW5kaXNzZSB2aXRhZSB1cm5hIHR1cnBpcywgcGxhY2VyYXQgYWRpcGlzY2luZyBkaWFtLiBQaGFzZWxsdXMgZmV1Z2lhdCB2ZXN0aWJ1bHVtIG5lcXVlIGV1IGRhcGlidXMuIE51bGxhIGZhY2lsaXNpLiBEdWlzIHRvcnRvciBmZWxpcywgZXVpc21vZCBzaXQgYW1ldCBhbGlxdWV0IGluLCB2b2x1dHBhdCBuZWMgdHVycGlzLiBNYXVyaXMgcmhvbmN1cyBpcHN1bSB1dCBwdXJ1cyBlbGVpZmVuZCB1dCBsb2JvcnRpcyBsZWN0dXMgZGFwaWJ1cy4gUXVpc3F1ZSBub24gZXJhdCBsb3JlbS4gVml2YW11cyBwb3N1ZXJlIGltcGVyZGlldCBpYWN1bGlzLiBVdCBsaWd1bGEgbGFjdXMsIGVsZWlmZW5kIGF0IHRlbXBvciBpZCwgYXVjdG9yIGV1IGxlby4KCkRvbmVjIG1pIGVuaW0sIGxhb3JlZXQgcHVsdmluYXIgbW9sbGlzIGV1LCBtYWxlc3VhZGEgdml2ZXJyYSBudW5jLiBJbiB2aXRhZSBtZXR1cyB2aXRhZSBuZXF1ZSB0ZW1wb3IgZGFwaWJ1cy4gTWFlY2VuYXMgdGluY2lkdW50IHB1cnVzIGEgZmVsaXMgYWxpcXVhbSBwbGFjZXJhdC4gTnVsbGEgZmFjaWxpc2kuIFN1c3BlbmRpc3NlIHBsYWNlcmF0IHBoYXJldHJhIG1hdHRpcy4gSW50ZWdlciB0ZW1wb3IgbWFsZXN1YWRhIGp1c3RvIGF0IHRlbXB1cy4gTWFlY2VuYXMgdmVoaWN1bGEgbG9yZW0gYSBzYXBpZW4gYmliZW5kdW0gdmVsIGlhY3VsaXMgcmlzdXMgZmV1Z2lhdC4gUGVsbGVudGVzcXVlIGRpYW0gZXJhdCwgZGFwaWJ1
      cyBldCBwZWxsZW50ZXNxdWUgcXVpcywgbW9sZXN0aWUgdXQgbWFzc2EuIFZpdmFtdXMgaWFjdWxpcyBpbnRlcmR1bSBtYXNzYSBpZCBiaWJlbmR1bS4gUXVpc3F1ZSB1dCBtYXVyaXMgZHVpLCBzaXQgYW1ldCB2YXJpdXMgZWxpdC4gVmVzdGlidWx1bSBlbGl0IGxvcmVtLCBydXRydW0gbm9uIGNvbnNlY3RldHVyIHV0LCBsYW9yZWV0IG5lYyBudW5jLiBEb25lYyBuZWMgbWF1cmlzIGFudGUuIEN1cmFiaXR1ciB1dCBlc3Qgc2VkIG9kaW8gcGhhcmV0cmEgbGFvcmVldC4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3VyYWJpdHVyIHB1cnVzIHJpc3VzLCBsYW9yZWV0IHNlZCBwb3J0YSBpZCwgc2FnaXR0aXMgdmVsIGlwc3VtLiBNYWVjZW5hcyBuaWJoIGRpYW0sIGN1cnN1cyBldCB2YXJpdXMgc2l0IGFtZXQsIGZyaW5naWxsYSBzZWQgbWFnbmEuIE51bGxhbSBpZCBuZXF1ZSBldSBsZW8gZmF1Y2lidXMgbW9sbGlzLiBEdWlzIG5lYyBhZGlwaXNjaW5nIG1hdXJpcy4gU3VzcGVuZGlzc2Ugc29sbGljaXR1ZGluLCBlbmltIGV1IHB1bHZpbmFyIGNvbW1vZG8sIGVyYXQgYXVndWUgdWx0cmljZXMgbWksIGEgdHJpc3RpcXVlIG1hZ25hIHNlbSBub24gbGliZXJvLgoKU2VkIGluIG1ldHVzIG51bGxhLiBQcmFlc2VudCBuZWMgYWRpcGlzY2luZyBzYXBpZW4uIERvbmVjIGxhb3JlZXQsIHZlbGl0IG5vbiBydXRydW0gdmVzdGlidWx1bSwgbGlndWxhIG5lcXVlIGFka
      XBpc2NpbmcgdHVycGlzLCBhdCBhdWN0b3Igc2FwaWVuIGVsaXQgdXQgbWFzc2EuIE51bGxhbSBhbGlxdWFtLCBlbmltIHZlbCBwb3N1ZXJlIHJ1dHJ1bSwganVzdG8gZXJhdCBsYW9yZWV0IGVzdCwgdmVsIGZyaW5naWxsYSBsYWN1cyBuaXNpIG5vbiBsZWN0dXMuIEV0aWFtIGxlY3R1cyBudW5jLCBsYW9yZWV0IGV0IHBsYWNlcmF0IGF0LCB2ZW5lbmF0aXMgcXVpcyBsaWJlcm8uIFByYWVzZW50IGluIHBsYWNlcmF0IGVsaXQuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gUGVsbGVudGVzcXVlIGZyaW5naWxsYSBhdWd1ZSBldSBuaWJoIHBsYWNlcmF0IGRpY3R1bS4gTnVuYyBwb3J0dGl0b3IgdHJpc3RpcXVlIGRpYW0sIGV1IGFsaXF1YW0gZW5pbSBhbGlxdWV0IHZlbC4gQWxpcXVhbSBsYWNpbmlhIGludGVyZHVtIGlwc3VtLCBpbiBwb3N1ZXJlIG1ldHVzIGx1Y3R1cyB2ZWwuIFZpdmFtdXMgZXQgbmlzbCBhIGVyb3Mgc2VtcGVyIGVsZW1lbnR1bS4gRG9uZWMgdmVuZW5hdGlzIG9yY2kgYXQgZGlhbSB0cmlzdGlxdWUgc29sbGljaXR1ZGluLiBJbiBldSBlcm9zIHNlZCBvZGlvIHJ1dHJ1bSBsdWN0dXMgbm9uIG5lYyB0ZWxsdXMuCgpOdWxsYSBuZWMgZmVsaXMgZWxpdC4gTnVsbGFtIGluIGlwc3VtIGluIGlwc3VtIGNvbnNlcXVhdCBmcmluZ2lsbGEgcXVpcyB2ZWwgdG9ydG9yLiBQaGFzZWxsdX
      Mgbm9uIG1hc3NhIG5pc2ksIHNpdCBhbWV0IGFsaXF1YW0gdXJuYS4gU2VkIGZlcm1lbnR1bSBuaWJoIHZpdGFlIGxhY3VzIHRpbmNpZHVudCBuZWMgdGluY2lkdW50IG1hc3NhIGJpYmVuZHVtLiBFdGlhbSBlbGl0IGR1aSwgZmFjaWxpc2lzIHNpdCBhbWV0IHZlaGljdWxhIG5lYywgaWFjdWxpcyBhdCBzYXBpZW4uIFV0IGF0IG1hc3NhIGlkIGR1aSB1bHRyaWNlcyB2b2x1dHBhdCB1dCBhYyBsaWJlcm8uIEZ1c2NlIGlwc3VtIG1pLCBiaWJlbmR1bSBhIGxhY2luaWEgZXQsIHB1bHZpbmFyIGVnZXQgbWF1cmlzLiBQcm9pbiBmYXVjaWJ1cyB1cm5hIHV0IGxvcmVtIGVsZW1lbnR1bSB2dWxwdXRhdGUuIER1aXMgcXVhbSBsZW8sIG1hbGVzdWFkYSBub24gZXVpc21vZCB1dCwgYmxhbmRpdCBmYWNpbGlzaXMgbWF1cmlzLiBTdXNwZW5kaXNzZSBzaXQgYW1ldCBtYWduYSBpZCB2ZWxpdCB0aW5jaWR1bnQgYWxpcXVldCBuZWMgZXUgZG9sb3IuIEN1cmFiaXR1ciBiaWJlbmR1bSBsb3JlbSB2ZWwgZmVsaXMgdGVtcHVzIGRhcGlidXMuIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gQWVuZWFuIGN1cnN1cyB0b3J0b3IgbmVjIGR1aSBhbGlxdWV0IHBvcnRhLiBBZW5lYW4gY29tbW9kbyBpYWN1bGlzIHN1c2NpcGl0LiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgUXVpc3F1ZSBzaXQgYW1ldCBvcm5hcmUgZWxpdC4gTmF
      tIGxpZ3VsYSByaXN1cywgdmVzdGlidWx1bSBuZWMgbWF0dGlzIGluLCBjb25kaW1lbnR1bSBhYyBhbnRlLiBEb25lYyBmcmluZ2lsbGEsIGp1c3RvIGV0IHVsdHJpY2VzIGZhdWNpYnVzLCB0ZWxsdXMgZXN0IHZvbHV0cGF0IG1hc3NhLCB2aXRhZSBjb21tb2RvIHNhcGllbiBkaWFtIG5vbiByaXN1cy4gVml2YW11cyBhdCBhcmN1IGdyYXZpZGEgcHVydXMgbW9sbGlzIGZldWdpYXQuCgpOdWxsYSBhIHR1cnBpcyBxdWlzIHNhcGllbiBjb21tb2RvIGRpZ25pc3NpbSBldSBxdWlzIGp1c3RvLiBNYWVjZW5hcyBldSBsb3JlbSBvZGlvLCB1dCBoZW5kcmVyaXQgdmVsaXQuIEN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0dXIgcmlkaWN1bHVzIG11cy4gUHJvaW4gZmFjaWxpc2lzIHBvcnR0aXRvciB1bGxhbWNvcnBlci4gUHJhZXNlbnQgbW9sbGlzIGRpZ25pc3NpbSBtYXNzYSwgbGFvcmVldCBhbGlxdWV0IHZlbGl0IHBlbGxlbnRlc3F1ZSBub24uIE51bmMgZmFjaWxpc2lzIGNvbnZhbGxpcyB0cmlzdGlxdWUuIE1hdXJpcyBwb3J0dGl0b3IgYW50ZSBhdCB0ZWxsdXMgY29udmFsbGlzIHBsYWNlcmF0LiBNb3JiaSBhbGlxdWV0IG5pc2kgYWMgbmlzbCBwdWx2aW5hciBpZCBkaWN0dW0gbmlzbCBtb2xsaXMuIFNlZCBvcm5hcmUgc2VtIGV0IHJpc3VzIHBsYWNlcmF0IGxvYm9ydGlzIGlkIGVnZXQgZWxpdC4gSW50ZWdlciBjb25zZXF1YXQsIG1hZ25h
      IGlkIHN1c2NpcGl0IHBoYXJldHJhLCBudWxsYSB2ZWxpdCBzdXNjaXBpdCBvcmNpLCB1dCBpbnRlcmR1bSBhdWd1ZSBhdWd1ZSBxdWlzIHF1YW0uIEZ1c2NlIHByZXRpdW0gYWxpcXVldCB2dWxwdXRhdGUuIE1hdXJpcyBibGFuZGl0IGRpY3R1bSBtb2xlc3RpZS4gUHJvaW4gbnVsbGEgbmliaCwgYmliZW5kdW0gZXUgcGxhY2VyYXQgYXQsIHRpbmNpZHVudCBhYyBuaXNsLiBOdWxsYW0gdnVscHV0YXRlIG1ldHVzIHV0IGxpYmVybyBydXRydW0gdWx0cmljaWVzLiBOdW5jIHNpdCBhbWV0IGR1aSBtYXVyaXMuIFN1c3BlbmRpc3NlIGFkaXBpc2NpbmcgbGFjdXMgaW4gYXVndWUgZWxlaWZlbmQgbW9sbGlzLgoKRHVpcyBwcmV0aXVtIHVsdHJpY2VzIG1hdHRpcy4gTmFtIGV1aXNtb2QgcmlzdXMgYSBlcmF0IGxhY2luaWEgYmliZW5kdW0uIE1vcmJpIG1hc3NhIHRvcnRvciwgY29uc2VjdGV0dXIgaWQgZWxlaWZlbmQgaWQsIHBlbGxlbnRlc3F1ZSB2ZWwgdG9ydG9yLiBQcmFlc2VudCB1cm5hIGxvcmVtLCBwb3J0dGl0b3IgYXQgY29uZGltZW50dW0gdml0YWUsIGx1Y3R1cyBlZ2V0IGVsaXQuIE1hZWNlbmFzIGZyaW5naWxsYSBxdWFtIGNvbnZhbGxpcyBlc3QgaGVuZHJlcml0IHZpdmVycmEuIEV0aWFtIHZlaGljdWxhLCBzYXBpZW4gbm9uIHB1bHZpbmFyIGFkaXBpc2NpbmcsIG5pc2kgbWFzc2EgdmVzdGlidWx1bSBlc3QsIGlkIGludGVyZHVtIG1hdXJpcyB2ZWxpdCBldSBlc3QuIFZlc3RpYnVsdW0gZXN0IGFyY3UsI
      GZhY2lsaXNpcyBhdCB1bHRyaWNpZXMgbm9uLCB2dWxwdXRhdGUgaWQgc2FwaWVuLiBWZXN0aWJ1bHVtIGlwc3VtIG1ldHVzLCBwaGFyZXRyYSBuZWMgcGVsbGVudGVzcXVlIGlkLCBmYWNpbGlzaXMgaWQgc2FwaWVuLiBEb25lYyBydXRydW0gb2RpbyBldCBsYWN1cyB1bHRyaWNpZXMgdWxsYW1jb3JwZXIuIEludGVnZXIgc2VkIGVzdCB1dCBtaSBwb3N1ZXJlIHRpbmNpZHVudCBxdWlzIG5vbiBsZW8uIE1vcmJpIHRlbGx1cyBqdXN0bywgdWx0cmljaWVzIHNpdCBhbWV0IHVsdHJpY2VzIHF1aXMsIGZhY2lsaXNpcyB2aXRhZSBtYWduYS4gRG9uZWMgbGlndWxhIG1ldHVzLCBwZWxsZW50ZXNxdWUgbm9uIHRyaXN0aXF1ZSBhYywgdmVzdGlidWx1bSBzZWQgZXJhdC4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LgoKTmFtIGRpZ25pc3NpbSwgbmlzbCBlZ2V0IGNvbnNlcXVhdCBldWlzbW9kLCBzZW0gbGVjdHVzIGF1Y3RvciBvcmNpLCB1dCBwb3J0dGl0b3IgbGFjdXMgZHVpIGFjIG5lcXVlLiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4gRnVzY2UgZWdlc3RhcyBwb3J0YSBmYWNpbGlzaXMuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBNYXVyaXMgY3Vyc3VzIHJob25jdXMgcmlzdXMgYWMgZXVpc21vZC4gUXVpc3F1ZSB2aXRhZSByaXN1cyBhIHRlbGx1cyB2ZW5lbmF0aXMgY29udmFsbGlzLiBDdXJhYml0dXIgbGFvcmVldCBzYXBpZW4gZXUgcXVhbSBsdWN0dXMgbG9ib3J0aXMuIFZpdmFtdX
      Mgc29sbGljaXR1ZGluIHNvZGFsZXMgZG9sb3Igdml0YWUgc29kYWxlcy4gU3VzcGVuZGlzc2UgcGhhcmV0cmEgbGFvcmVldCBhbGlxdWV0LiBNYWVjZW5hcyB1bGxhbWNvcnBlciBvcmNpIHZlbCB0b3J0b3IgbHVjdHVzIGlhY3VsaXMgdXQgdml0YWUgbWV0dXMuIFZlc3RpYnVsdW0gdXQgYXJjdSBhYyB0ZWxsdXMgbWF0dGlzIGVsZWlmZW5kIGVnZXQgdmVoaWN1bGEgZWxpdC4KCkluIHNlZCBmZXVnaWF0IGVyb3MuIERvbmVjIGJpYmVuZHVtIHVsbGFtY29ycGVyIGRpYW0sIGV1IGZhdWNpYnVzIG1hdXJpcyBkaWN0dW0gc2VkLiBEdWlzIHRpbmNpZHVudCBqdXN0byBpbiBuZXF1ZSBhY2N1bXNhbiBkaWN0dW0uIE1hZWNlbmFzIGluIHJ1dHJ1bSBzYXBpZW4uIFV0IGlkIGZldWdpYXQgbGFjdXMuIE51bGxhIGZhY2lsaXNpLiBOdW5jIGFjIGxvcmVtIGlkIHF1YW0gdmFyaXVzIGN1cnN1cyBhIGV0IGVsaXQuIEFlbmVhbiBwb3N1ZXJlIGxpYmVybyBldSB0b3J0b3IgdmVoaWN1bGEgdXQgdWxsYW1jb3JwZXIgb2RpbyBjb25zZXF1YXQuIFNlZCBpbiBkaWduaXNzaW0gZHVpLiBDdXJhYml0dXIgaWFjdWxpcyB0ZW1wb3IgcXVhbSBuZWMgcGxhY2VyYXQuIEFsaXF1YW0gdmVuZW5hdGlzIG5pYmggZXQganVzdG8gaWFjdWxpcyBsYWNpbmlhLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gTG9yZW0gaXB
      zdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUGVsbGVudGVzcXVlIHRlbXB1cyBtYWduYSBzZWQgbWkgYWxpcXVldCBlZ2V0IHZhcml1cyBvZGlvIGNvbmd1ZS4KCkludGVnZXIgc2VtIHNlbSwgc2VtcGVyIGluIHZlc3RpYnVsdW0gdml0YWUsIGxvYm9ydGlzIHF1aXMgZXJhdC4gRHVpcyBhbnRlIGxlY3R1cywgZmVybWVudHVtIHNlZCB0ZW1wb3Igc2l0IGFtZXQsIHBsYWNlcmF0IHNpdCBhbWV0IHNlbS4gTWF1cmlzIGNvbmd1ZSB0aW5jaWR1bnQgaXBzdW0uIFV0IHZpdmVycmEsIGxhY3VzIHZlbCB2YXJpdXMgcGhhcmV0cmEsIHB1cnVzIGVuaW0gcHVsdmluYXIgaXBzdW0sIG5vbiBwZWxsZW50ZXNxdWUgZW5pbSBqdXN0byBub24gZXJhdC4gRnVzY2UgaXBzdW0gb3JjaSwgdWx0cmljZXMgc2VkIHBlbGxlbnRlc3F1ZSBhdCwgaGVuZHJlcml0IGxhb3JlZXQgZW5pbS4gTnVuYyBibGFuZGl0IG1vbGxpcyBwcmV0aXVtLiBVdCBtb2xsaXMsIG51bGxhIGFsaXF1YW0gc29kYWxlcyB2ZXN0aWJ1bHVtLCBsaWJlcm8gbG9yZW0gdGVtcHVzIHRvcnRvciwgYSBwZWxsZW50ZXNxdWUgbmliaCBlbGl0IGEgaXBzdW0uIFBoYXNlbGx1cyBmZXJtZW50dW0gbGlndWxhIGF0IG5lcXVlIGFkaXBpc2Npbmcgc29sbGljaXR1ZGluLiBTdXNwZW5kaXNzZSBpZCBpcHN1bSBhcmN1LiBTZWQgdGluY2lkdW50IHBsYWNlcmF0IHZpdmVycmEuIERvbmVjIGxpYmVybyBhdWd1ZSwgcG9ydHRpdG9yIHNp
      dCBhbWV0IHZhcml1cyBlZ2V0LCBydXRydW0gbmVjIGxhY3VzLiBQcm9pbiBibGFuZGl0IG9yY2kgc2l0IGFtZXQgZGlhbSBkaWN0dW0gaWQgcG9ydHRpdG9yIHJpc3VzIGlhY3VsaXMuIEludGVnZXIgbGFjaW5pYSBmZXVnaWF0IGxlbywgdml0YWUgYXVjdG9yIHR1cnBpcyBlbGVpZmVuZCB2ZWwuIFN1c3BlbmRpc3NlIGxvcmVtIHF1YW0sIHByZXRpdW0gaWQgYmliZW5kdW0gc2VkLCB2aXZlcnJhIHZpdGFlIHRvcnRvci4gTnVsbGFtIHVsdHJpY2llcyBsaWJlcm8gZXUgcmlzdXMgY29udmFsbGlzIGVnZXQgdWxsYW1jb3JwZXIgbmlzaSBlbGVtZW50dW0uIE1hdXJpcyBudWxsYSBlbGl0LCBiaWJlbmR1bSBpZCB2dWxwdXRhdGUgdml0YWUsIGltcGVyZGlldCBydXRydW0gbG9yZW0uIEN1cmFiaXR1ciBlZ2V0IGRpZ25pc3NpbSBvcmNpLiBTZWQgc2VtcGVyIHRlbGx1cyBpcHN1bSwgYXQgYmxhbmRpdCBkdWkuIEludGVnZXIgZGFwaWJ1cyBmYWNpbGlzaXMgc29kYWxlcy4gVml2YW11cyBzb2xsaWNpdHVkaW4gdmFyaXVzIGVzdCwgcXVpcyBvcm5hcmUganVzdG8gY3Vyc3VzIGlkLgoKTnVuYyB2ZWwgdWxsYW1jb3JwZXIgbWkuIFN1c3BlbmRpc3NlIHBvdGVudGkuIE51bmMgZXQgdXJuYSBhIGF1Z3VlIHNjZWxlcmlzcXVlIHVsdHJpY2VzIG5vbiBxdWlzIG1pLiBJbiBxdWlzIHBvcnR0aXRvciBlbGl0LiBBZW5lYW4gcXVpcyBlcmF0IG51bGxhLCBhIHZlbmVuYXRpcyB0ZWxsdXMuIEZ1c2NlIHZlc3RpYnVsdW0gbmlza
      SBzZWQgbGVvIGFkaXBpc2NpbmcgZGlnbmlzc2ltLiBOdW5jIGludGVyZHVtLCBsb3JlbSBldCBsYWNpbmlhIHZlc3RpYnVsdW0sIHF1YW0gZXN0IG1hdHRpcyBtYWduYSwgc2l0IGFtZXQgdm9sdXRwYXQgZWxpdCBhdWd1ZSBhdCBsaWJlcm8uIENyYXMgZ3JhdmlkYSBkdWkgcXVpcyB2ZWxpdCBsb2JvcnRpcyBjb25kaW1lbnR1bSBldCBlbGVpZmVuZCBsaWd1bGEuIFBoYXNlbGx1cyBhYyBtZXR1cyBxdWFtLCBpZCB2ZW5lbmF0aXMgbWkuIEFsaXF1YW0gdXQgdHVycGlzIGFjIHRlbGx1cyBkYXBpYnVzIGRhcGlidXMgZXUgaW4gbWkuIFF1aXNxdWUgZWdldCBuaWJoIGVyb3MuIEZ1c2NlIGNvbnNlY3RldHVyIGxlbyB2ZWxpdC4KClZlc3RpYnVsdW0gc2VtcGVyIGVnZXN0YXMgbWF1cmlzLiBNb3JiaSB2ZXN0aWJ1bHVtIHNlbSBzZW0uIEFsaXF1YW0gdmVuZW5hdGlzLCBmZWxpcyBzZWQgZWxlaWZlbmQgcG9ydGEsIG1hdXJpcyBkaWFtIHNlbXBlciBhcmN1LCBzaXQgYW1ldCB1bHRyaWNpZXMgZXN0IHNhcGllbiBzaXQgYW1ldCBsaWJlcm8uIFZlc3RpYnVsdW0gZHVpIG9yY2ksIG9ybmFyZSBjb25kaW1lbnR1bSBtb2xsaXMgbmVjLCBtb2xlc3RpZSBhYyBlcm9zLiBQcm9pbiB2aXRhZSBtb2xsaXMgdmVsaXQuIFByYWVzZW50IGVnZXQgZmVsaXMgbWkuIE1hZWNlbmFzIGV1IHZ1bHB1dGF0ZSBuaXNpLiBWZXN0aWJ1bHVtIHZhcml1cywgYXJjdSBpbiB1bHRyaWNpZXMgdmVzdGlidWx1bSwgbmliaCBsZW8gc2FnaXR0aX
      Mgb2RpbywgdXQgYmliZW5kdW0gbmlzbCBtaSBuZWMgZGlhbS4gSW50ZWdlciBhdCBlbmltIGZldWdpYXQgbnVsbGEgc2VtcGVyIGJpYmVuZHVtIHV0IGEgdmVsaXQuIFByb2luIGF0IG5pc2kgdXQgbG9yZW0gYWxpcXVhbSB2YXJpdXMgZWdldCBxdWlzIGVsaXQuIE51bGxhbSBuZWMgb2RpbyB2ZWwgbGVjdHVzIGNvbmd1ZSBjb25zZXF1YXQgYWRpcGlzY2luZyBhYyBtaS4gRnVzY2Ugdml0YWUgbGFvcmVldCBsaWJlcm8uIEN1cmFiaXR1ciBzaXQgYW1ldCBzZW0gbmVxdWUsIG5lYyBwb3N1ZXJlIGVuaW0uIEN1cmFiaXR1ciBhdCBtYXNzYSBhIHNlbSBncmF2aWRhIGlhY3VsaXMgbmVjIGV0IG5pYmguIFNlZCB2aXRhZSBkdWkgdml0YWUgbGVvIHRpbmNpZHVudCBwcmV0aXVtIGEgYWxpcXVhbSBlcmF0LiBTdXNwZW5kaXNzZSB1bHRyaWNpZXMgb2RpbyBhdCBtZXR1cyB0ZW1wb3IgaW4gcGVsbGVudGVzcXVlIGFyY3UgdWx0cmljaWVzLgoKU2VkIGFsaXF1YW0gbWF0dGlzIHF1YW0sIGluIHZ1bHB1dGF0ZSBzYXBpZW4gdWx0cmljZXMgaW4uIFBlbGxlbnRlc3F1ZSBxdWlzIHZlbGl0IHNlZCBkdWkgaGVuZHJlcml0IGN1cnN1cy4gUGVsbGVudGVzcXVlIG5vbiBudW5jIGxhY3VzLCBhIHNlbXBlciBtZXR1cy4gRnVzY2UgZXVpc21vZCB2ZWxpdCBxdWlzIGRpYW0gc3VzY2lwaXQgY29uc2VxdWF0LiBQcmFlc2VudCBjb21tb2RvIGFjY3Vtc2FuIG5lcXVlLiBQcm9pbiB2aXZlcnJhLCBpcHN1bSBub24gdHJpc3RpcXVlIHV
      sdHJpY2VzLCB2ZWxpdCB2ZWxpdCBmYWNpbGlzaXMgbG9yZW0sIHZlbCBydXRydW0gbmVxdWUgZXJvcyBhYyBuaXNpLiBTdXNwZW5kaXNzZSBmZWxpcyBtYXNzYSwgZmF1Y2lidXMgaW4gdm9sdXRwYXQgYWMsIGRhcGlidXMgZXQgb2Rpby4gUGVsbGVudGVzcXVlIGlkIHRlbGx1cyBzaXQgYW1ldCByaXN1cyB1bHRyaWNpZXMgdWxsYW1jb3JwZXIgbm9uIG5lYyBzYXBpZW4uIE5hbSBwbGFjZXJhdCB2aXZlcnJhIHVsbGFtY29ycGVyLiBOYW0gcGxhY2VyYXQgcG9ydHRpdG9yIHNhcGllbiBuZWMgcHVsdmluYXIuIEN1cmFiaXR1ciB2ZWwgb2RpbyBzaXQgYW1ldCBvZGlvIGFjY3Vtc2FuIGFsaXF1ZXQgdml0YWUgYSBsZWN0dXMuIFBlbGxlbnRlc3F1ZSBsb2JvcnRpcyB2aXZlcnJhIGNvbnNlcXVhdC4gTWF1cmlzIGVsZW1lbnR1bSBjdXJzdXMgbnVsbGEsIHNpdCBhbWV0IGhlbmRyZXJpdCBqdXN0byBkaWN0dW0gc2VkLiBNYWVjZW5hcyBkaWFtIG9kaW8sIGZyaW5naWxsYSBhYyBjb25ndWUgcXVpcywgYWRpcGlzY2luZyB1dCBlbGl0LgoKQWxpcXVhbSBsb3JlbSBlcm9zLCBwaGFyZXRyYSBuZWMgZWdlc3RhcyB2aXRhZSwgbWF0dGlzIG5lYyByaXN1cy4gTWF1cmlzIGFyY3UgbWFzc2EsIHNvZGFsZXMgZWdldCBncmF2aWRhIHNlZCwgdml2ZXJyYSB2aXRhZSB0dXJwaXMuIFV0IGxpZ3VsYSB1cm5hLCBldWlzbW9kIGFjIHRpbmNpZHVudCBldSwgZmF1Y2lidXMgc2VkIGZlbGlzLiBQcmFlc2VudCBtb2xsaXMsIGlwc3Vt
      IHF1aXMgcmhvbmN1cyBkaWduaXNzaW0sIG9kaW8gc2VtIHZlbmVuYXRpcyBudWxsYSwgYXQgY29uc2VxdWF0IGZlbGlzIGF1Z3VlIHZlbCBlcmF0LiBOYW0gZmVybWVudHVtIGZldWdpYXQgdm9sdXRwYXQuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gRXRpYW0gdml0YWUgZHVpIGluIG5pc2kgYWRpcGlzY2luZyB1bHRyaWNpZXMgbm9uIGV1IGp1c3RvLiBEb25lYyB0cmlzdGlxdWUgdWx0cmljaWVzIGFkaXBpc2NpbmcuIE51bGxhIHNvZGFsZXMsIG51bmMgYSB0cmlzdGlxdWUgZWxlbWVudHVtLCBlcmF0IG5lcXVlIGVnZXN0YXMgbmlzbCwgYXQgaGVuZHJlcml0IG9yY2kgc2FwaWVuIHNlZCBsaWJlcm8uIFZpdmFtdXMgYSBtYXVyaXMgdHVycGlzLCBxdWlzIGxhb3JlZXQgaXBzdW0uIE51bmMgbmVjIG1pIGV0IG5pc2wgcGVsbGVudGVzcXVlIHNjZWxlcmlzcXVlLiBWaXZhbXVzIHZvbHV0cGF0LCBqdXN0byB0cmlzdGlxdWUgbGFjaW5pYSBjb25kaW1lbnR1bSwgZXJhdCBqdXN0byB1bHRyaWNlcyB1cm5hLCBlbGVtZW50dW0gdml2ZXJyYSBlcm9zIGF1Z3VlIG5vbiBsaWJlcm8uIFNlZCBtb2xsaXMgbW9sbGlzIGFyY3UsIGF0IGZlcm1lbnR1bSBkaWFtIHN1c2NpcGl0IHF1aXMuCgpFdGlhbSBzaXQgYW1ldCBuaWJoIGp1c3RvLCBwb3N1ZXJlIHZvbHV0cGF0IG51bmMuIE1vcmJpIHBlbGxlbnRlc
      3F1ZSBuZXF1ZSBpbiBvcmNpIHZvbHV0cGF0IGV1IHNjZWxlcmlzcXVlIGxvcmVtIGRpY3R1bS4gTWF1cmlzIG1vbGxpcyBpYWN1bGlzIGVzdCwgbmVjIHNhZ2l0dGlzIHNhcGllbiBjb25zZXF1YXQgaWQuIE51bmMgbmVjIG1hbGVzdWFkYSBvZGlvLiBEdWlzIHF1aXMgc3VzY2lwaXQgb2Rpby4gTWF1cmlzIHB1cnVzIGR1aSwgc29kYWxlcyBpZCBtYXR0aXMgc2l0IGFtZXQsIHBvc3VlcmUgaW4gYXJjdS4gUGhhc2VsbHVzIHBvcnRhIGVsZW1lbnR1bSBjb252YWxsaXMuIE1hZWNlbmFzIGF0IG9yY2kgZXQgbWkgdnVscHV0YXRlIHNvbGxpY2l0dWRpbiBpbiBpbiB0dXJwaXMuIFBlbGxlbnRlc3F1ZSBjdXJzdXMgYWRpcGlzY2luZyBuZXF1ZSBzaXQgYW1ldCBjb21tb2RvLiBGdXNjZSB1dCBtaSBldSBsZWN0dXMgcG9ydHRpdG9yIHZvbHV0cGF0IGV0IG5lYyBmZWxpcy4KCkN1cmFiaXR1ciBzY2VsZXJpc3F1ZSBlcm9zIHF1aXMgbmlzbCB2aXZlcnJhIHZlbCB1bHRyaWNlcyB2ZWxpdCB2ZXN0aWJ1bHVtLiBTZWQgbG9ib3J0aXMgcHVsdmluYXIgc2FwaWVuIGFjIHZlbmVuYXRpcy4gU2VkIGFudGUgbmliaCwgcmhvbmN1cyBlZ2V0IGRpY3R1bSBpbiwgbW9sbGlzIHV0IG5pc2kuIFBoYXNlbGx1cyBmYWNpbGlzaXMgbWkgbm9uIGxvcmVtIHRyaXN0aXF1ZSBub24gZWxlaWZlbmQgc2VtIGZyaW5naWxsYS4gSW50ZWdlciB1dCBhdWd1ZSBlc3QuIEluIHZlbmVuYXRpcyB0aW5jaWR1bnQgc2NlbGVyaXNxdWUuIEV0aWFtIG
      FudGUgZHVpLCBwb3N1ZXJlIHF1aXMgbWFsZXN1YWRhIHZpdGFlLCBtYWxlc3VhZGEgYSBhcmN1LiBBZW5lYW4gZmF1Y2lidXMgdmVuZW5hdGlzIHNhcGllbiwgdXQgZmFjaWxpc2lzIG5pc2kgYmxhbmRpdCB2ZWwuIEFlbmVhbiBhYyBsb3JlbSBldSBzZW0gZmVybWVudHVtIHBsYWNlcmF0LiBQcm9pbiBuZXF1ZSBwdXJ1cywgYWxpcXVldCB1dCB0aW5jaWR1bnQgdXQsIGNvbnZhbGxpcyBzaXQgYW1ldCBlcm9zLiBQaGFzZWxsdXMgdmVoaWN1bGEgdWxsYW1jb3JwZXIgZW5pbSBub24gdmVoaWN1bGEuIEV0aWFtIHBvcnRhIG9kaW8gdXQgaXBzdW0gYWRpcGlzY2luZyBlZ2VzdGFzIGlkIGEgb2Rpby4gUGVsbGVudGVzcXVlIGJsYW5kaXQsIHNhcGllbiB1dCBwdWx2aW5hciBpbnRlcmR1bSwgbWkgbnVsbGEgaGVuZHJlcml0IGVsaXQsIGluIHRlbXBvciBkaWFtIGVuaW0gYSB1cm5hLiBJbiB0ZWxsdXMgb2Rpbywgb3JuYXJlIHNlZCBjb25kaW1lbnR1bSBhLCBtYXR0aXMgZXUgYXVndWUuCgpGdXNjZSBoZW5kcmVyaXQgcG9ydHRpdG9yIGV1aXNtb2QuIERvbmVjIG1hbGVzdWFkYSBlZ2VzdGFzIHR1cnBpcywgZXQgdWx0cmljaWVzIGZlbGlzIGVsZW1lbnR1bSB2aXRhZS4gTnVsbGFtIGluIHNlbSBuaWJoLiBOdWxsYW0gdWx0cmljaWVzIGhlbmRyZXJpdCBqdXN0byBzaXQgYW1ldCBsb2JvcnRpcy4gU2VkIHRpbmNpZHVudCwgbWF1cmlzIGF0IG9ybmFyZSBsYW9yZWV0LCBzYXBpZW4gcHVydXMgZWxlbWVudHVtIGVsaXQ
      sIG5lYyBwb3J0dGl0b3IgbmlzbCBwdXJ1cyBldCBlcmF0LiBEb25lYyBmZWxpcyBuaXNpLCBydXRydW0gdWxsYW1jb3JwZXIgZ3JhdmlkYSBhYywgdGluY2lkdW50IHNpdCBhbWV0IHVybmEuIFByb2luIHZlbCBqdXN0byB2aXRhZSBlcm9zIHNhZ2l0dGlzIGJpYmVuZHVtIGEgdXQgbmliaC4gUGhhc2VsbHVzIHNvZGFsZXMgbGFvcmVldCB0aW5jaWR1bnQuIE1hZWNlbmFzIG9kaW8gbWFzc2EsIGNvbmRpbWVudHVtIGlkIGFsaXF1ZXQgdXQsIHJob25jdXMgdmVsIGxlY3R1cy4gRHVpcyBwaGFyZXRyYSBjb25zZWN0ZXR1ciBzYXBpZW4uIFBoYXNlbGx1cyBwb3N1ZXJlIHVsdHJpY2llcyBtYXNzYSwgbm9uIHJob25jdXMgcmlzdXMgYWxpcXVhbSB0ZW1wdXMuCgpQcmFlc2VudCB2ZW5lbmF0aXMgbWFnbmEgaWQgc2VtIGRpY3R1bSBldSB2ZWhpY3VsYSBpcHN1bSB2dWxwdXRhdGUuIFNlZCBhIGNvbnZhbGxpcyBzYXBpZW4uIFNlZCBqdXN0byBkb2xvciwgcmhvbmN1cyB2ZWwgcnV0cnVtIG1hdHRpcywgc29sbGljaXR1ZGluIHV0IHJpc3VzLiBOdWxsYW0gc2l0IGFtZXQgY29udmFsbGlzIGVzdC4gRXRpYW0gbm9uIHRpbmNpZHVudCBsaWd1bGEuIEZ1c2NlIHN1c2NpcGl0IHByZXRpdW0gZWxpdCBhdCB1bGxhbWNvcnBlci4gUXVpc3F1ZSBzb2xsaWNpdHVkaW4sIGRpYW0gaWQgaW50ZXJkdW0gcG9ydGEsIG1ldHVzIGlwc3VtIHZvbHV0cGF0IGxpYmVybywgaWQgdmVuZW5hdGlzIGZlbGlzIG9yY2kgbm9uIHZlbGl0LiBT
      dXNwZW5kaXNzZSBwb3RlbnRpLiBNYXVyaXMgcnV0cnVtLCB0b3J0b3Igc2l0IGFtZXQgcGVsbGVudGVzcXVlIHRpbmNpZHVudCwgZXJhdCBxdWFtIHVsdHJpY2llcyBvZGlvLCBpZCBhbGlxdWFtIGVsaXQgbGVvIG5lYyBsZW8uIFBlbGxlbnRlc3F1ZSBqdXN0byBlcm9zLCBydXRydW0gYXQgZmV1Z2lhdCBuZWMsIHBvcnRhIGV0IHRlbGx1cy4gQWVuZWFuIGVnZXQgbWV0dXMgbGVjdHVzLgoKUHJhZXNlbnQgZXVpc21vZCwgdHVycGlzIHF1aXMgbGFvcmVldCBjb25zZXF1YXQsIG5lcXVlIGFudGUgaW1wZXJkaWV0IHF1YW0sIGFjIHNlbXBlciB0b3J0b3IgbmliaCBpbiBudWxsYS4gSW50ZWdlciBzY2VsZXJpc3F1ZSBlcm9zIHZlaGljdWxhIHVybmEgbGFjaW5pYSBhYyBmYWNpbGlzaXMgbWF1cmlzIGFjY3Vtc2FuLiBQaGFzZWxsdXMgYXQgbWF1cmlzIG5pYmguIEN1cmFiaXR1ciBlbmltIGFudGUsIHJ1dHJ1bSBzZWQgYWRpcGlzY2luZyBoZW5kcmVyaXQsIHBlbGxlbnRlc3F1ZSBub24gYXVndWUuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBOYW0gdGVtcHVzIGV1aXNtb2QgbWFzc2EgYSBkaWN0dW0uIERvbmVjIHNpdCBhbWV0IGp1c3RvIGFjIGRpYW0gdWx0cmljaWVzIHVsdHJpY2llcy4gU2VkIHRpbmNpZHVudCBlcmF0IHF1aXMgcXVhbSB0ZW1wdXMgdmVsIGludGVyZHVtIGVyYXQgcmhvbmN1cy4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIFZlc3RpYnVsdW0gdmVoaWN1bGEgd
      mFyaXVzIHNlbSBlZ2V0IGludGVyZHVtLiBDcmFzIGJpYmVuZHVtIGxlbyBuZWMgZmVsaXMgdmVuZW5hdGlzIHNlZCBwaGFyZXRyYSBzZW0gZmV1Z2lhdC4gQ3VtIHNvY2lpcyBuYXRvcXVlIHBlbmF0aWJ1cyBldCBtYWduaXMgZGlzIHBhcnR1cmllbnQgbW9udGVzLCBuYXNjZXR1ciByaWRpY3VsdXMgbXVzLiBTZWQgcXVhbSBvcmNpLCBtb2xsaXMgZWdldCBzYWdpdHRpcyBhY2N1bXNhbiwgdnVscHV0YXRlIHNpdCBhbWV0IGR1aS4gUHJhZXNlbnQgZXUgZWxlbWVudHVtIGFyY3UuCgpMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBWZXN0aWJ1bHVtIG5pc2wgbWV0dXMsIGhlbmRyZXJpdCB1dCBsYW9yZWV0IHNlZCwgY29uc2VjdGV0dXIgYXQgcHVydXMuIER1aXMgaW50ZXJkdW0gY29uZ3VlIGxvYm9ydGlzLiBOdWxsYW0gc2VkIG1hc3NhIHBvcnRhIGZlbGlzIGVsZWlmZW5kIGNvbnNlcXVhdCBzaXQgYW1ldCBuZWMgbWV0dXMuIEFsaXF1YW0gcGxhY2VyYXQgZGljdHVtIGVyYXQgYXQgZWxlaWZlbmQuIFZlc3RpYnVsdW0gbGliZXJvIGFudGUsIHVsbGFtY29ycGVyIGEgcG9ydHRpdG9yIHN1c2NpcGl0LCBhY2N1bXNhbiB2ZWwgbmlzaS4gRG9uZWMgZXQgbWFnbmEgbmVxdWUuIE5hbSBlbGVtZW50dW0gdWx0cmljZXMganVzdG8sIGVnZXQgc29sbGljaXR1ZGluIHNhcGllbiBpbXBlcmRpZXQgZWdldC4gTnVsbGFtIGF1Y3RvciBkaWN0dW0gbnVuYywgYXQgZmV1Z2
      lhdCBvZGlvIHZlc3RpYnVsdW0gYS4gU2VkIGVyYXQgbnVsbGEsIHZpdmVycmEgaGVuZHJlcml0IGNvbW1vZG8gaWQsIHVsbGFtY29ycGVyIGFjIG9yY2kuIFBoYXNlbGx1cyBwZWxsZW50ZXNxdWUgZmV1Z2lhdCBzdXNjaXBpdC4gRXRpYW0gZWdlc3RhcyBmZXJtZW50dW0gZW5pbS4gRXRpYW0gZ3JhdmlkYSBpbnRlcmR1bSB0ZWxsdXMgYWMgbGFvcmVldC4gTW9yYmkgbWF0dGlzIGFsaXF1ZXQgZXJvcywgbm9uIHRlbXBvciBlcmF0IHVsbGFtY29ycGVyIGluLiBFdGlhbSBwdWx2aW5hciBpbnRlcmR1bSB0dXJwaXMgYWMgdmVoaWN1bGEuIFNlZCBxdWFtIGp1c3RvLCBhY2N1bXNhbiBpZCBjb25zZWN0ZXR1ciBhLCBhbGlxdWV0IHNlZCBsZW8uIEFlbmVhbiB2aXRhZSBibGFuZGl0IG1hdXJpcy4KCkluIHNlZCBlcm9zIGF1Z3VlLCBub24gcnV0cnVtIG9kaW8uIEV0aWFtIHZpdGFlIGR1aSBuZXF1ZSwgaW4gdHJpc3RpcXVlIG1hc3NhLiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgTWFlY2VuYXMgZGljdHVtIGVsaXQgYXQgbGVjdHVzIHRlbXBvciBub24gcGhhcmV0cmEgbmlzbCBoZW5kcmVyaXQuIFNlZCBzZWQgcXVhbSBldSBsZWN0dXMgdWx0cmljZXMgbWFsZXN1YWRhIHRpbmNpZHVudCBhIGVzdC4gTmFtIHZlbCBlcm9zIHJpc3VzLiBNYWVjZW5hcyBlcm9zIGVsaXQsIGJsYW5kaXQgZmVybWVudHVtIHR
      lbXBvciBlZ2V0LCBsb2JvcnRpcyBpZCBkaWFtLiBWZXN0aWJ1bHVtIGxhY2luaWEgbGFjdXMgdml0YWUgbWFnbmEgdm9sdXRwYXQgZXUgZGlnbmlzc2ltIGVyb3MgY29udmFsbGlzLiBWaXZhbXVzIGFjIHZlbGl0IHRlbGx1cywgYSBjb25ndWUgbmVxdWUuIEludGVnZXIgbWkgbnVsbGEsIHZhcml1cyBub24gbHVjdHVzIGluLCBkaWN0dW0gc2l0IGFtZXQgc2VtLiBVdCBsYW9yZWV0LCBzYXBpZW4gc2l0IGFtZXQgc2NlbGVyaXNxdWUgcG9ydGEsIHB1cnVzIHNhcGllbiB2ZXN0aWJ1bHVtIG5pYmgsIHNlZCBsdWN0dXMgbGliZXJvIG1hc3NhIGFjIGVsaXQuIERvbmVjIGlhY3VsaXMgb2RpbyBlZ2V0IG9kaW8gc2FnaXR0aXMgbmVjIHZlbmVuYXRpcyBsb3JlbSBibGFuZGl0LgoKQWxpcXVhbSBpbXBlcmRpZXQgdGVsbHVzIHBvc3VlcmUganVzdG8gdmVoaWN1bGEgc2VkIHZlc3RpYnVsdW0gYW50ZSB0cmlzdGlxdWUuIEZ1c2NlIGZldWdpYXQgZmF1Y2lidXMgcHVydXMgbmVjIG1vbGVzdGllLiBOdWxsYSB0ZW1wb3IgbmVxdWUgaWQgbWFnbmEgaWFjdWxpcyBxdWlzIHNvbGxpY2l0dWRpbiBlcm9zIHNlbXBlci4gUHJhZXNlbnQgdml2ZXJyYSBzYWdpdHRpcyBsdWN0dXMuIE1vcmJpIHNpdCBhbWV0IG1hZ25hIHNlZCBvZGlvIGdyYXZpZGEgdmFyaXVzLiBVdCBuaXNpIGxpYmVybywgdnVscHV0YXRlIGZldWdpYXQgcHJldGl1bSB0ZW1wdXMsIGVnZXN0YXMgc2l0IGFtZXQganVzdG8uIFBlbGxlbnRlc3F1ZSBjb25zZXF1
      YXQgdGVtcG9yIG5pc2kgaW4gbG9ib3J0aXMuIFNlZCBmZXJtZW50dW0gY29udmFsbGlzIGR1aSBhYyBzb2xsaWNpdHVkaW4uIEludGVnZXIgYXVjdG9yIGF1Z3VlIGVnZXQgdGVsbHVzIHRlbXB1cyBmcmluZ2lsbGEuIFByb2luIG5lYyBkb2xvciBzYXBpZW4sIG5lYyB0cmlzdGlxdWUgbmliaC4gQWxpcXVhbSBhIHZlbGl0IGF0IG1pIG1hdHRpcyBhbGlxdWV0LgoKUGVsbGVudGVzcXVlIGhhYml0YW50IG1vcmJpIHRyaXN0aXF1ZSBzZW5lY3R1cyBldCBuZXR1cyBldCBtYWxlc3VhZGEgZmFtZXMgYWMgdHVycGlzIGVnZXN0YXMuIEFsaXF1YW0gdWx0cmljZXMgZXJhdCBub24gdHVycGlzIGF1Y3RvciBpZCBvcm5hcmUgbWF1cmlzIHNhZ2l0dGlzLiBRdWlzcXVlIHBvcnR0aXRvciwgdGVsbHVzIHV0IGNvbnZhbGxpcyBzYWdpdHRpcywgbWkgbGliZXJvIGZldWdpYXQgdGVsbHVzLCByaG9uY3VzIHBsYWNlcmF0IGlwc3VtIHRvcnRvciBpZCByaXN1cy4gRG9uZWMgdGluY2lkdW50IGZldWdpYXQgbGVvLiBDcmFzIGlkIG1pIG5lcXVlLCBldSBtYWxlc3VhZGEgZXJvcy4gVXQgbW9sZXN0aWUgbWFnbmEgcXVpcyBsaWJlcm8gcGxhY2VyYXQgbWFsZXN1YWRhLiBBbGlxdWFtIGVyYXQgdm9sdXRwYXQuIEFsaXF1YW0gbm9uIG1hdXJpcyBsb3JlbSwgaW4gYWRpcGlzY2luZyBtZXR1cy4gRG9uZWMgZWdldCBpcHN1bSBpbiBlbGl0IGNvbW1vZG8gb3JuYXJlIGJpYmVuZHVtIGEgbmliaC4gVml2YW11cyBvZGlvIGVyYXQsIHBsY
      WNlcmF0IGFjIHZlc3RpYnVsdW0gZWdldCwgbWFsZXN1YWRhIHV0IG5pc2kuIEV0aWFtIHN1c2NpcGl0IHNvbGxpY2l0dWRpbiBsZW8gc2VtcGVyIHNvbGxpY2l0dWRpbi4gU2VkIHJob25jdXMgcmlzdXMgc2l0IGFtZXQgc2VtIGVsZWlmZW5kIGRpY3R1bSBwcmV0aXVtIHNhcGllbiBlZ2VzdGFzLiBOdWxsYSBhdCB1cm5hIG51bmMsIHZlbCBhbGlxdWV0IGxlby4gUHJhZXNlbnQgdWx0cmljaWVzLCBtaSBldSBwcmV0aXVtIGxvYm9ydGlzLCBlcmF0IG5pYmggZXVpc21vZCBsZW8sIHNpdCBhbWV0IGdyYXZpZGEgc2FwaWVuIGVyb3MgZXQgdHVycGlzLiBEb25lYyBsYWNpbmlhIHZlbmVuYXRpcyBsZWN0dXMsIG5vbiBsYWNpbmlhIG1pIGhlbmRyZXJpdCBzaXQgYW1ldC4gSW50ZWdlciBzZWQgZmVsaXMgdmVsIG9yY2kgYWxpcXVhbSBwdWx2aW5hci4gUGhhc2VsbHVzIGV0IHJpc3VzIGlkIGVyYXQgZXVpc21vZCB0aW5jaWR1bnQuIFNlZCBsdWN0dXMgdGVtcG9yIG5pc2ksIG5lYyB0ZW1wb3IgaXBzdW0gZWxlbWVudHVtIGVnZXQuIEludGVnZXIgbmlzbCB0b3J0b3IsIHZpdmVycmEgaW4gZGFwaWJ1cyBhdCwgbWF0dGlzIGFjIGVyYXQuIEN1cmFiaXR1ciBuZWMgZHVpIGxlY3R1cy4KClBoYXNlbGx1cyBzdXNjaXBpdCwgdG9ydG9yIGV1IHZhcml1cyBmcmluZ2lsbGEsIHNhcGllbiBtYWduYSBlZ2VzdGFzIHJpc3VzLCB1dCBzdXNjaXBpdCBkdWkgbWF1cmlzIHF1aXMgdmVsaXQuIENyYXMgYSBzYXBpZW4gcXVpcyBzYX
      BpZW4gaGVuZHJlcml0IHRyaXN0aXF1ZSBhIHNpdCBhbWV0IGVsaXQuIFBlbGxlbnRlc3F1ZSBkdWkgYXJjdSwgbWFsZXN1YWRhIGV0IHNvZGFsZXMgc2l0IGFtZXQsIGRhcGlidXMgdmVsIHF1YW0uIFNlZCBub24gYWRpcGlzY2luZyBsaWd1bGEuIFV0IHZ1bHB1dGF0ZSBwdXJ1cyBhdCBuaXNsIHBvc3VlcmUgc29kYWxlcy4gTWFlY2VuYXMgZGlhbSB2ZWxpdCwgdGluY2lkdW50IGlkIG1hdHRpcyBldSwgYWxpcXVhbSBhYyBuaXNpLiBNYWVjZW5hcyBwcmV0aXVtLCBhdWd1ZSBhIHNhZ2l0dGlzIHN1c2NpcGl0LCBsZW8gbGlndWxhIGVsZWlmZW5kIGRvbG9yLCBtb2xsaXMgZmV1Z2lhdCBvZGlvIGF1Z3VlIG5vbiBlcm9zLiBQZWxsZW50ZXNxdWUgc2NlbGVyaXNxdWUgb3JjaSBwcmV0aXVtIHF1YW0gbW9sbGlzIGF0IGxvYm9ydGlzIGR1aSBmYWNpbGlzaXMuIE1vcmJpIGNvbmd1ZSBtZXR1cyBpZCB0b3J0b3IgcG9ydGEgZnJpbmdpbGxhLiBTZWQgbG9yZW0gbWksIG1vbGVzdGllIGZlcm1lbnR1bSBzYWdpdHRpcyBhdCwgZ3JhdmlkYSBhIG5pc2kuIERvbmVjIGV1IHZlc3RpYnVsdW0gdmVsaXQuIEluIHZpdmVycmEsIGVuaW0gZXUgZWxlbWVudHVtIHNvZGFsZXMsIGVuaW0gb2RpbyBkYXBpYnVzIHVybmEsIGVnZXQgY29tbW9kbyBuaXNsIG1hdXJpcyB1dCBvZGlvLiBDdXJhYml0dXIgbmVjIGVuaW0gbnVsbGEuIEluIG5lYyBlbGl0IGlwc3VtLiBOdW5jIGluIG1hc3NhIHN1c2NpcGl0IG1hZ25hIGVsZW1lbnR1bSB
      mYXVjaWJ1cyBpbiBuZWMgaXBzdW0uIE51bGxhbSBzdXNjaXBpdCBtYWxlc3VhZGEgZWxlbWVudHVtLiBFdGlhbSBzZWQgbWkgaW4gbmliaCB1bHRyaWNpZXMgdmVuZW5hdGlzIG5lYyBwaGFyZXRyYSBtYWduYS4gSW4gcHVydXMgYW50ZSwgcmhvbmN1cyB2ZWwgcGxhY2VyYXQgc2VkLCBmZXJtZW50dW0gc2l0IGFtZXQgZHVpLiBTZWQgYXQgc29kYWxlcyB2ZWxpdC4KCkR1aXMgc3VzY2lwaXQgcGVsbGVudGVzcXVlIHBlbGxlbnRlc3F1ZS4gUHJhZXNlbnQgcG9ydGEgbG9ib3J0aXMgY3Vyc3VzLiBRdWlzcXVlIHNhZ2l0dGlzIHZlbGl0IG5vbiB0ZWxsdXMgYmliZW5kdW0gYXQgc29sbGljaXR1ZGluIGxhY3VzIGFsaXF1ZXQuIFNlZCBuaWJoIHJpc3VzLCBibGFuZGl0IGEgYWxpcXVldCBlZ2V0LCB2ZWhpY3VsYSBldCBlc3QuIFN1c3BlbmRpc3NlIGZhY2lsaXNpcyBiaWJlbmR1bSBhbGlxdWFtLiBGdXNjZSBjb25zZWN0ZXR1ciBjb252YWxsaXMgZXJhdCwgZWdldCBtb2xsaXMgZGlhbSBmZXJtZW50dW0gc29sbGljaXR1ZGluLiBRdWlzcXVlIHRpbmNpZHVudCBwb3J0dGl0b3IgcHJldGl1bS4gTnVsbGFtIGlkIG5pc2wgZXQgdXJuYSB2dWxwdXRhdGUgZGFwaWJ1cy4gRG9uZWMgcXVpcyBsb3JlbSB1cm5hLiBRdWlzcXVlIGlkIGp1c3RvIG5lYyBudW5jIGJsYW5kaXQgY29udmFsbGlzLiBOdW5jIHZvbHV0cGF0LCBtYXNzYSBzb2xsaWNpdHVkaW4gYWRpcGlzY2luZyB2ZXN0aWJ1bHVtLCBtYXNzYSB1cm5hIGNvbmd1
      ZSBsZWN0dXMsIHNpdCBhbWV0IHVsdHJpY2llcyBhdWd1ZSBvcmNpIGNvbnZhbGxpcyB0dXJwaXMuIE51bGxhIGF0IGxvcmVtIGVsaXQuIE51bmMgdHJpc3RpcXVlLCBxdWFtIGZhY2lsaXNpcyBjb21tb2RvIHBvcnR0aXRvciwgbGFjdXMgbGlndWxhIGFjY3Vtc2FuIG5pc2ksIGV0IGxhb3JlZXQganVzdG8gYW50ZSB2aXRhZSBlcm9zLiBDdXJhYml0dXIgc2VkIGF1Z3VlIGFyY3UuIFBoYXNlbGx1cyBwb3J0dGl0b3IgdmVzdGlidWx1bSBmZWxpcywgdXQgY29uc2VjdGV0dXIgYXJjdSB0ZW1wb3Igbm9uLiBJbiBqdXN0byByaXN1cywgc2VtcGVyIGV0IHN1c2NpcGl0IGlkLCB1bGxhbWNvcnBlciBhdCB1cm5hLiBRdWlzcXVlIHRpbmNpZHVudCwgdXJuYSBuZWMgYWxpcXVhbSB0cmlzdGlxdWUsIG5pYmggb2RpbyBmYXVjaWJ1cyBhdWd1ZSwgaW4gb3JuYXJlIGVuaW0gdHVycGlzIGFjY3Vtc2FuIGRvbG9yLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gU3VzcGVuZGlzc2Ugc29kYWxlcyB2YXJpdXMgdHVycGlzIGV1IGZlcm1lbnR1bS4KCk1vcmJpIHVsdHJpY2llcyBkaWFtIGVnZXQgbWFzc2EgcG9zdWVyZSBsb2JvcnRpcy4gQWxpcXVhbSB2b2x1dHBhdCBwZWxsZW50ZXNxdWUgZW5pbSBldSBwb3J0dGl0b3IuIERvbmVjIGxhY3VzIGZlbGlzLCBjb25zZWN0ZXR1ciBhIHByZXRpdW0gdml0YWUsI
      GJpYmVuZHVtIG5vbiBlbmltLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gRXRpYW0gdXQgbmliaCBhIHF1YW0gcGVsbGVudGVzcXVlIGF1Y3RvciB1dCBpZCB2ZWxpdC4gRHVpcyBsYWNpbmlhIGp1c3RvIGVnZXQgbWkgcGxhY2VyYXQgYmliZW5kdW0uIEN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0dXIgcmlkaWN1bHVzIG11cy4gRG9uZWMgdmVsaXQgdG9ydG9yLCB0ZW1wdXMgbmVjIHRyaXN0aXF1ZSBpZCwgYWxpcXVldCBzaXQgYW1ldCB0dXJwaXMuIFByYWVzZW50IGV0IG5lcXVlIG5lYyBtYWduYSBwb3J0YSBmcmluZ2lsbGEuIE1vcmJpIGlkIGVnZXN0YXMgZXJvcy4gRG9uZWMgc2VtcGVyIHRpbmNpZHVudCB1bGxhbWNvcnBlci4gUGhhc2VsbHVzIHRlbXB1cyBsYWNpbmlhIGhlbmRyZXJpdC4gUXVpc3F1ZSBmYXVjaWJ1cyBwcmV0aXVtIG5lcXVlIG5vbiBjb252YWxsaXMuIE51bmMgbWFsZXN1YWRhIGFjY3Vtc2FuIHJob25jdXMuIENyYXMgbG9ib3J0aXMsIHNlbSBzZWQgZnJpbmdpbGxhIGNvbnZhbGxpcywgYXVndWUgdmVsaXQgc2VtcGVyIG5pc2wsIGNvbW1vZG8gdmFyaXVzIG5pc2kgZGlhbSBhYyBsZW8uCgpRdWlzcXVlIGludGVyZHVtIHRlbGx1cyBhYyBhbnRlIHBvc3VlcmUgdXQgY3Vyc3VzIGxvcmVtIG
      VnZXN0YXMuIE51bGxhIGZhY2lsaXNpLiBBZW5lYW4gc2VkIG1hc3NhIG5lYyBuaXNpIHNjZWxlcmlzcXVlIHZ1bHB1dGF0ZS4gRXRpYW0gY29udmFsbGlzIGNvbnNlY3RldHVyIGlhY3VsaXMuIE1hZWNlbmFzIGFjIHB1cnVzIHV0IGFudGUgZGlnbmlzc2ltIGF1Y3RvciBhYyBxdWlzIGxvcmVtLiBQZWxsZW50ZXNxdWUgc3VzY2lwaXQgdGluY2lkdW50IG9yY2kuIEZ1c2NlIGFsaXF1YW0gZGFwaWJ1cyBvcmNpLCBhdCBiaWJlbmR1bSBpcHN1bSBhZGlwaXNjaW5nIGVnZXQuIE1vcmJpIHBlbGxlbnRlc3F1ZSBoZW5kcmVyaXQgcXVhbSwgbmVjIHBsYWNlcmF0IHVybmEgdnVscHV0YXRlIHNlZC4gUXVpc3F1ZSB2ZWwgZGlhbSBsb3JlbS4gUHJhZXNlbnQgaWQgZGlhbSBxdWlzIGVuaW0gZWxlbWVudHVtIHJob25jdXMgc2FnaXR0aXMgZWdldCBwdXJ1cy4gUXVpc3F1ZSBmcmluZ2lsbGEgYmliZW5kdW0gbGVvIGluIGxhb3JlZXQuIFZlc3RpYnVsdW0gaWQgbmliaCByaXN1cywgbm9uIGVsZW1lbnR1bSBtZXR1cy4gVXQgYSBmZWxpcyBkaWFtLCBub24gbW9sbGlzIG5pc2wuIENyYXMgZWxpdCBhbnRlLCB1bGxhbWNvcnBlciBxdWlzIGlhY3VsaXMgZXUsIHNvZGFsZXMgdmVsIGVzdC4gQ3VyYWJpdHVyIHF1aXMgbG9ib3J0aXMgZG9sb3IuIEFsaXF1YW0gbWF0dGlzIGdyYXZpZGEgbWV0dXMgcGVsbGVudGVzcXVlIHZ1bHB1dGF0ZS4KClV0IGlkIGF1Z3VlIGlkIGRvbG9yIGx1Y3R1cyBldWlzbW9kIGV0IHF1aXMgdmVsaXQ
      uIE1hZWNlbmFzIGVuaW0gZG9sb3IsIHRlbXB1cyBzaXQgYW1ldCBoZW5kcmVyaXQgZXUsIGZhdWNpYnVzIHZpdGFlIG5lcXVlLiBQcm9pbiBzaXQgYW1ldCB2YXJpdXMgZWxpdC4gUHJvaW4gdmFyaXVzIGZlbGlzIHVsbGFtY29ycGVyIHB1cnVzIGRpZ25pc3NpbSBjb25zZXF1YXQuIENyYXMgY3Vyc3VzIHRlbXB1cyBlcm9zLiBOdW5jIHVsdHJpY2VzIHZlbmVuYXRpcyB1bGxhbWNvcnBlci4gQWxpcXVhbSBldCBmZXVnaWF0IHRlbGx1cy4gUGhhc2VsbHVzIHNpdCBhbWV0IHZlc3RpYnVsdW0gZWxpdC4gUGhhc2VsbHVzIGFjIHB1cnVzIGxhY3VzLCBldCBhY2N1bXNhbiBlcm9zLiBNb3JiaSB1bHRyaWNlcywgcHVydXMgYSBwb3J0YSBzb2RhbGVzLCBvZGlvIG1ldHVzIHBvc3VlcmUgbmVxdWUsIG5lYyBlbGVtZW50dW0gcmlzdXMgdHVycGlzIHNpdCBhbWV0IG1hZ25hLiBTZWQgZXN0IHF1YW0sIHVsdHJpY2llcyBhdCBjb25ndWUgYWRpcGlzY2luZywgbG9ib3J0aXMgaW4ganVzdG8uIFByb2luIGlhY3VsaXMgZGljdHVtIG51bmMsIGV1IGxhb3JlZXQgcXVhbSB2YXJpdXMgdml0YWUuIERvbmVjIHNpdCBhbWV0IGZldWdpYXQgdHVycGlzLiBNYXVyaXMgc2l0IGFtZXQgbWFnbmEgcXVhbSwgYWMgY29uc2VjdGV0dXIgZHVpLiBDdXJhYml0dXIgZWdldCBtYWduYSB0ZWxsdXMsIGV1IHBoYXJldHJhIGZlbGlzLiBEb25lYyBzaXQgYW1ldCB0b3J0b3IgbmlzbC4gQWxpcXVhbSBldCB0b3J0b3IgZmFjaWxpc2lzIGxhY3Vz
      IHRpbmNpZHVudCBjb21tb2RvLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gQ3VyYWJpdHVyIG51bmMgbWFnbmEsIHVsdHJpY2llcyBpZCBjb252YWxsaXMgYXQsIHVsbGFtY29ycGVyIHZpdGFlIG1hc3NhLgoKUGhhc2VsbHVzIHZpdmVycmEgaWFjdWxpcyBwbGFjZXJhdC4gTnVsbGEgY29uc2VxdWF0IGRvbG9yIHNpdCBhbWV0IGVyYXQgZGlnbmlzc2ltIHBvc3VlcmUuIE51bGxhIGxhY2luaWEgYXVndWUgdml0YWUgbWkgdGVtcG9yIGdyYXZpZGEuIFBoYXNlbGx1cyBub24gdGVtcG9yIHRlbGx1cy4gUXVpc3F1ZSBub24gZW5pbSBzZW1wZXIgdG9ydG9yIHNhZ2l0dGlzIGZhY2lsaXNpcy4gQWxpcXVhbSB1cm5hIGZlbGlzLCBlZ2VzdGFzIGF0IHBvc3VlcmUgbmVjLCBhbGlxdWV0IGV1IG5pYmguIFByYWVzZW50IHNlZCB2ZXN0aWJ1bHVtIGVuaW0uIE1hdXJpcyBpYWN1bGlzIHZlbGl0IGR1aSwgZXQgZnJpbmdpbGxhIGVuaW0uIE51bGxhIG5lYyBuaXNpIG9yY2kuIFNlZCB2b2x1dHBhdCwganVzdG8gZWdldCBmcmluZ2lsbGEgYWRpcGlzY2luZywgbmlzbCBudWxsYSBjb25kaW1lbnR1bSBsaWJlcm8sIHNlZCBzb2RhbGVzIGVzdCBlc3QgZXQgb2Rpby4gQ3JhcyBpcHN1bSBkdWksIHZhcml1cyBldSBlbGVtZW50dW0gY29uc2VxdWF0LCBmYXVjaWJ1cyBpbiBsZW8uIFBlbGxlbnRlc3F1ZSBoY
      WJpdGFudCBtb3JiaSB0cmlzdGlxdWUgc2VuZWN0dXMgZXQgbmV0dXMgZXQgbWFsZXN1YWRhIGZhbWVzIGFjIHR1cnBpcyBlZ2VzdGFzLgoKVXQgbWFsZXN1YWRhIG1vbGVzdGllIGVsZWlmZW5kLiBDdXJhYml0dXIgaWQgZW5pbSBkdWksIGV1IHRpbmNpZHVudCBuaWJoLiBNYXVyaXMgc2l0IGFtZXQgYW50ZSBsZW8uIER1aXMgdHVycGlzIGlwc3VtLCBiaWJlbmR1bSBzZWQgbWF0dGlzIHNpdCBhbWV0LCBhY2N1bXNhbiBxdWlzIGRvbG9yLiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgQWVuZWFuIGEgaW1wZXJkaWV0IG1ldHVzLiBRdWlzcXVlIHNvbGxpY2l0dWRpbiBmZWxpcyBpZCBuZXF1ZSB0ZW1wb3Igc2NlbGVyaXNxdWUuIERvbmVjIGF0IG9yY2kgZmVsaXMuIFZpdmFtdXMgdGVtcHVzIGNvbnZhbGxpcyBhdWN0b3IuIERvbmVjIGludGVyZHVtIGV1aXNtb2QgbG9ib3J0aXMuIFNlZCBhdCBsYWN1cyBuZWMgb2RpbyBkaWduaXNzaW0gbW9sbGlzLiBTZWQgc2FwaWVuIG9yY2ksIHBvcnR0aXRvciB0ZW1wdXMgYWNjdW1zYW4gdmVsLCB0aW5jaWR1bnQgbmVjIGFudGUuIE51bmMgcmhvbmN1cyBlZ2VzdGFzIGRhcGlidXMuIFN1c3BlbmRpc3NlIGZlcm1lbnR1bSBkaWN0dW0gZnJpbmdpbGxhLiBOdWxsYW0gbmlzaSBqdXN0bywgZWxlaWZlbmQgYSBjb25zZWN0ZXR1ciBjb252YWxsaXMsIHBvcnR0aXRvci
      BldCB0b3J0b3IuIFByb2luIHZpdGFlIGxvcmVtIG5vbiBkb2xvciBzdXNjaXBpdCBsYWNpbmlhIGV1IGVnZXQgbnVsbGEuCgpTdXNwZW5kaXNzZSBlZ2VzdGFzLCBzYXBpZW4gc2l0IGFtZXQgYmxhbmRpdCBzY2VsZXJpc3F1ZSwgbnVsbGEgYXJjdSB0cmlzdGlxdWUgZHVpLCBhIHBvcnRhIGp1c3RvIHF1YW0gdml0YWUgYXJjdS4gSW4gbWV0dXMgbGliZXJvLCBiaWJlbmR1bSBub24gdm9sdXRwYXQgdXQsIGxhb3JlZXQgdmVsIHR1cnBpcy4gTnVuYyBmYXVjaWJ1cyB2ZWxpdCBldSBpcHN1bSBjb21tb2RvIG5lYyBpYWN1bGlzIGVyb3Mgdm9sdXRwYXQuIFZpdmFtdXMgY29uZ3VlIGF1Y3RvciBlbGl0IHNlZCBzdXNjaXBpdC4gRHVpcyBjb21tb2RvLCBsaWJlcm8gZXUgdmVzdGlidWx1bSBmZXVnaWF0LCBsZW8gbWkgZGFwaWJ1cyB0ZWxsdXMsIGluIHBsYWNlcmF0IG5pc2wgZHVpIGF0IGVzdC4gVmVzdGlidWx1bSB2aXZlcnJhIHRyaXN0aXF1ZSBsb3JlbSwgb3JuYXJlIGVnZXN0YXMgZXJhdCBydXRydW0gYS4gTnVsbGFtIGF0IGF1Z3VlIG1hc3NhLCB1dCBjb25zZWN0ZXR1ciBpcHN1bS4gUGVsbGVudGVzcXVlIG1hbGVzdWFkYSwgdmVsaXQgdXQgbG9ib3J0aXMgc2FnaXR0aXMsIG5pc2kgbWFzc2Egc2VtcGVyIG9kaW8sIG1hbGVzdWFkYSBzZW1wZXIgcHVydXMgbmlzbCB2ZWwgbGVjdHVzLiBOdW5jIGR1aSBzZW0sIG1hdHRpcyB2aXRhZSBsYW9yZWV0IHZpdGFlLCBzb2xsaWNpdHVkaW4gYWMgbGVvLiBOdWxsYSB
      2ZWwgZmVybWVudHVtIGVzdC4KClZpdmFtdXMgaW4gb2RpbyBhIG5pc2kgZGlnbmlzc2ltIHJob25jdXMgaW4gaW4gbGFjdXMuIERvbmVjIGV0IG5pc2wgdG9ydG9yLiBEb25lYyBzYWdpdHRpcyBjb25zZXF1YXQgbWksIHZlbCBwbGFjZXJhdCB0ZWxsdXMgY29udmFsbGlzIGlkLiBBbGlxdWFtIGZhY2lsaXNpcyBydXRydW0gbmlzbCBzZWQgcHJldGl1bS4gRG9uZWMgZXQgbGFjaW5pYSBuaXNsLiBBbGlxdWFtIGVyYXQgdm9sdXRwYXQuIEN1cmFiaXR1ciBhYyBwdWx2aW5hciB0ZWxsdXMuIE51bGxhbSB2YXJpdXMgbG9ib3J0aXMgcG9ydGEuIENyYXMgZGFwaWJ1cywgbGlndWxhIHV0IHBvcnRhIHVsdHJpY2llcywgbGVvIGxhY3VzIHZpdmVycmEgcHVydXMsIHF1aXMgbW9sbGlzIHVybmEgcmlzdXMgZXUgbGVvLiBOdW5jIG1hbGVzdWFkYSBjb25zZWN0ZXR1ciBwdXJ1cywgdmVsIGF1Y3RvciBsZWN0dXMgc2NlbGVyaXNxdWUgcG9zdWVyZS4gTWFlY2VuYXMgZHVpIG1hc3NhLCB2ZXN0aWJ1bHVtIGJpYmVuZHVtIGJsYW5kaXQgbm9uLCBpbnRlcmR1bSBlZ2V0IG1hdXJpcy4gUGhhc2VsbHVzIGVzdCBhbnRlLCBwdWx2aW5hciBhdCBpbXBlcmRpZXQgcXVpcywgaW1wZXJkaWV0IHZlbCB1cm5hLiBRdWlzcXVlIGVnZXQgdm9sdXRwYXQgb3JjaS4gUXVpc3F1ZSBldCBhcmN1IHB1cnVzLCB1dCBmYXVjaWJ1cyB2ZWxpdC4KClByYWVzZW50IHNlZCBpcHN1bSB1cm5hLiBQcmFlc2VudCBzYWdpdHRpcyB2YXJpdXMgbWFnbmEs
      IGlkIGNvbW1vZG8gZG9sb3IgbWFsZXN1YWRhIGFjLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gUXVpc3F1ZSBzaXQgYW1ldCBudW5jIGV1IHNlbSBvcm5hcmUgdGVtcG9yLiBNYXVyaXMgaWQgZG9sb3IgbmVjIGVyYXQgY29udmFsbGlzIHBvcnRhIGluIGxvYm9ydGlzIG5pc2kuIEN1cmFiaXR1ciBoZW5kcmVyaXQgcmhvbmN1cyB0b3J0b3IgZXUgaGVuZHJlcml0LiBQZWxsZW50ZXNxdWUgZXUgYW50ZSB2ZWwgZWxpdCBsdWN0dXMgZWxlaWZlbmQgcXVpcyB2aXZlcnJhIG51bGxhLiBTdXNwZW5kaXNzZSBvZGlvIGRpYW0sIGV1aXNtb2QgZXUgcG9ydHRpdG9yIG1vbGVzdGllLCBzb2xsaWNpdHVkaW4gc2l0IGFtZXQgbnVsbGEuIFNlZCBhbnRlIHVybmEsIGRpY3R1bSBiaWJlbmR1bSByaG9uY3VzIGV0LCBibGFuZGl0IG5lYyBhbnRlLiBTdXNwZW5kaXNzZSB0b3J0b3IgYXVndWUsIGFjY3Vtc2FuIHF1aXMgc3VzY2lwaXQgaWQsIGFjY3Vtc2FuIHNpdCBhbWV0IGVyYXQuIERvbmVjIHBoYXJldHJhIHZhcml1cyBsb2JvcnRpcy4gTWFlY2VuYXMgaXBzdW0gZGlhbSwgZmF1Y2lidXMgZXUgdGVtcHVzIGlkLCBjb252YWxsaXMgbmVjIGVuaW0uIER1aXMgYXJjdSB0dXJwaXMsIGZyaW5naWxsYSBuZWMgZWdlc3RhcyB1dCwgZGlnbmlzc2ltIHRyaXN0aXF1ZSBudWxsYS4gQ3VyYWJpdHVyIHN1c2Npc
      Gl0IGR1aSBub24ganVzdG8gdWx0cmljZXMgcGhhcmV0cmEuIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gTnVsbGEgZmFjaWxpc2kuIFF1aXNxdWUgaWQgZmVsaXMgZXUgc2VtIGFsaXF1YW0gZnJpbmdpbGxhLgoKRXRpYW0gcXVpcyBhdWd1ZSBpbiB0ZWxsdXMgY29uc2VxdWF0IGVsZWlmZW5kLiBBZW5lYW4gZGlnbmlzc2ltIGNvbmd1ZSBmZWxpcyBpZCBlbGVtZW50dW0uIER1aXMgZnJpbmdpbGxhIHZhcml1cyBpcHN1bSwgbmVjIHN1c2NpcGl0IGxlbyBzZW1wZXIgdmVsLiBVdCBzb2xsaWNpdHVkaW4sIG9yY2kgYSB0aW5jaWR1bnQgYWNjdW1zYW4sIGRpYW0gbGVjdHVzIGxhb3JlZXQgbGFjdXMsIHZlbCBmZXJtZW50dW0gcXVhbSBlc3QgdmVsIGVyb3MuIEFsaXF1YW0gZnJpbmdpbGxhIHNhcGllbiBhYyBzYXBpZW4gZmF1Y2lidXMgY29udmFsbGlzLiBBbGlxdWFtIGlkIG51bmMgZXUganVzdG8gY29uc2VxdWF0IHRpbmNpZHVudC4gUXVpc3F1ZSBuZWMgbmlzbCBkdWkuIFBoYXNlbGx1cyBhdWd1ZSBsZWN0dXMsIHZhcml1cyB2aXRhZSBhdWN0b3IgdmVsLCBydXRydW0gYXQgcmlzdXMuIFZpdmFtdXMgbGFjaW5pYSBsZW8gcXVpcyBuZXF1ZSB1bHRyaWNlcyBuZWMgZWxlbWVudHVtIGZlbGlzIGZyaW5naWxsYS4gUHJvaW4gdmVsIHBvcnR0aXRvciBsZWN0dXMuCgpDdXJhYml0dXIgc2FwaWVuIGxvcmVtLCBtb2xsaXMgdXQgYWNjdW1zYW4gbm9uLCB1bHRyaWNpZXMgZXQgbWV0dXMuIEN1cmFiaXR1ciB2ZWwgbG9yZW
      0gcXVpcyBzYXBpZW4gZnJpbmdpbGxhIGxhb3JlZXQuIE1vcmJpIGlkIHVybmEgYWMgb3JjaSBlbGVtZW50dW0gYmxhbmRpdCBlZ2V0IHZvbHV0cGF0IG5lcXVlLiBQZWxsZW50ZXNxdWUgc2VtIG9kaW8sIGlhY3VsaXMgZXUgcGhhcmV0cmEgdml0YWUsIGN1cnN1cyBpbiBxdWFtLiBOdWxsYSBtb2xlc3RpZSBsaWd1bGEgaWQgbWFzc2EgbHVjdHVzIGV0IHB1bHZpbmFyIG5pc2kgcHVsdmluYXIuIE51bmMgZmVybWVudHVtIGF1Z3VlIGEgbGFjdXMgZnJpbmdpbGxhIHJob25jdXMgcG9ydHRpdG9yIGVyYXQgZGljdHVtLiBOdW5jIHNpdCBhbWV0IHRlbGx1cyBldCBkdWkgdml2ZXJyYSBhdWN0b3IgZXVpc21vZCBhdCBuaXNsLiBJbiBzZWQgY29uZ3VlIG1hZ25hLiBQcm9pbiBldCB0b3J0b3IgdXQgYXVndWUgcGxhY2VyYXQgZGlnbmlzc2ltIGEgZXUganVzdG8uIE1vcmJpIHBvcnR0aXRvciBwb3J0YSBsb2JvcnRpcy4gUGVsbGVudGVzcXVlIG5pYmggbGFjdXMsIGFkaXBpc2NpbmcgdXQgdHJpc3RpcXVlIHF1aXMsIGNvbnNlcXVhdCB2aXRhZSB2ZWxpdC4gTWFlY2VuYXMgdXQgbHVjdHVzIGxpYmVyby4gVml2YW11cyBhdWN0b3Igb2RpbyBldCBlcmF0IHNlbXBlciBzYWdpdHRpcy4gVml2YW11cyBpbnRlcmR1bSB2ZWxpdCBpbiByaXN1cyBtYXR0aXMgcXVpcyBkaWN0dW0gYW50ZSByaG9uY3VzLiBJbiBzYWdpdHRpcyBwb3J0dGl0b3IgZXJvcywgYXQgbG9ib3J0aXMgbWV0dXMgdWx0cmljZXMgdmVsLiBDdXJhYml0dXI
      gbm9uIGFsaXF1YW0gbmlzbC4gVmVzdGlidWx1bSBsdWN0dXMgZmV1Z2lhdCBzdXNjaXBpdC4gRXRpYW0gbm9uIGxhY3VzIHZlbCBudWxsYSBlZ2VzdGFzIGlhY3VsaXMgaWQgcXVpcyByaXN1cy4KCkV0aWFtIGluIGF1Y3RvciB1cm5hLiBGdXNjZSB1bHRyaWNpZXMgbW9sZXN0aWUgY29udmFsbGlzLiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4gVmVzdGlidWx1bSBhbnRlIGlwc3VtIHByaW1pcyBpbiBmYXVjaWJ1cyBvcmNpIGx1Y3R1cyBldCB1bHRyaWNlcyBwb3N1ZXJlIGN1YmlsaWEgQ3VyYWU7IE1hdXJpcyBpYWN1bGlzIGxvcmVtIGZhdWNpYnVzIHB1cnVzIGdyYXZpZGEgYXQgY29udmFsbGlzIHR1cnBpcyBzb2xsaWNpdHVkaW4uIFN1c3BlbmRpc3NlIGF0IHZlbGl0IGxvcmVtLCBhIGZlcm1lbnR1bSBpcHN1bS4gRXRpYW0gY29uZGltZW50dW0sIGR1aSB2ZWwgY29uZGltZW50dW0gZWxlbWVudHVtLCBzYXBpZW4gc2VtIGJsYW5kaXQgc2FwaWVuLCBldCBwaGFyZXRyYSBsZW8gbmVxdWUgZXQgbGVjdHVzLiBOdW5jIHZpdmVycmEgdXJuYSBpYWN1bGlzIGF1Z3VlIHVsdHJpY2VzIGFjIHBvcnR0aXRvciBsYWN1cyBkaWduaXNzaW0uIEFsaXF1YW0gdXQgdHVycGlzIGR1aS4gU2VkIGVnZXQgYWxpcXVldCBmZWxpcy4gSW4gYmliZW5kdW0gbmliaCBzaXQgYW1ldCBzYXBpZW4gYWNjdW1zYW4gYWNjdW1zYW4gcGhhcmV0cmEgbWFnbmEgbW9sZXN0aWUuCgpNYXVyaXMgYWxpcXVldCB1cm5hIGVnZXQg
      bGVjdHVzIGFkaXBpc2NpbmcgYXQgY29uZ3VlIHR1cnBpcyBjb25zZXF1YXQuIFZpdmFtdXMgdGluY2lkdW50IGZlcm1lbnR1bSByaXN1cyBldCBmZXVnaWF0LiBOdWxsYSBtb2xlc3RpZSB1bGxhbWNvcnBlciBuaWJoIHNlZCBmYWNpbGlzaXMuIFBoYXNlbGx1cyBldCBjdXJzdXMgcHVydXMuIE5hbSBjdXJzdXMsIGR1aSBkaWN0dW0gdWx0cmljZXMgdml2ZXJyYSwgZXJhdCByaXN1cyB2YXJpdXMgZWxpdCwgZXUgbW9sZXN0aWUgZHVpIGVyb3MgcXVpcyBxdWFtLiBBbGlxdWFtIGV0IGFudGUgbmVxdWUsIGFjIGNvbnNlY3RldHVyIGR1aS4gRG9uZWMgY29uZGltZW50dW0gZXJhdCBpZCBlbGl0IGRpY3R1bSBzZWQgYWNjdW1zYW4gbGVvIHNhZ2l0dGlzLiBQcm9pbiBjb25zZXF1YXQgY29uZ3VlIHJpc3VzLCB2ZWwgdGluY2lkdW50IGxlbyBpbXBlcmRpZXQgZXUuIFZlc3RpYnVsdW0gbWFsZXN1YWRhIHR1cnBpcyBldSBtZXR1cyBpbXBlcmRpZXQgcHJldGl1bS4gQWxpcXVhbSBjb25kaW1lbnR1bSB1bHRyaWNlcyBuaWJoLCBldSBzZW1wZXIgZW5pbSBlbGVpZmVuZCBhLiBFdGlhbSBjb25kaW1lbnR1bSBuaXNsIHF1YW0uCgpQZWxsZW50ZXNxdWUgaWQgbW9sZXN0aWUgbmlzbC4gTWFlY2VuYXMgZXQgbGVjdHVzIGF0IGp1c3RvIG1vbGVzdGllIHZpdmVycmEgc2l0IGFtZXQgc2l0IGFtZXQgbGlndWxhLiBOdWxsYW0gbm9uIHBvcnR0aXRvciBtYWduYS4gUXVpc3F1ZSBlbGVtZW50dW0gYXJjdSBjdXJzdXMgdG9ydG9yI
      HJ1dHJ1bSBsb2JvcnRpcy4gTW9yYmkgc2l0IGFtZXQgbGVjdHVzIHZpdGFlIGVuaW0gZXVpc21vZCBkaWduaXNzaW0gZWdldCBhdCBuZXF1ZS4gVml2YW11cyBjb25zZXF1YXQgdmVoaWN1bGEgZHVpLCB2aXRhZSBhdWN0b3IgYXVndWUgZGlnbmlzc2ltIGluLiBJbiB0ZW1wdXMgc2VtIHF1aXMganVzdG8gdGluY2lkdW50IHNpdCBhbWV0IGF1Y3RvciB0dXJwaXMgbG9ib3J0aXMuIFBlbGxlbnRlc3F1ZSBub24gZXN0IG51bmMuIFZlc3RpYnVsdW0gbW9sbGlzIGZyaW5naWxsYSBpbnRlcmR1bS4gTWFlY2VuYXMgaXBzdW0gZG9sb3IsIHBoYXJldHJhIGlkIHRyaXN0aXF1ZSBtYXR0aXMsIGx1Y3R1cyB2aXRhZSB1cm5hLiBVdCB1bGxhbWNvcnBlciBhcmN1IGVnZXQgZWxpdCBjb252YWxsaXMgbW9sbGlzLiBQZWxsZW50ZXNxdWUgY29uZGltZW50dW0sIG1hc3NhIGFjIGhlbmRyZXJpdCB0ZW1wb3IsIG1hdXJpcyBwdXJ1cyBibGFuZGl0IGp1c3RvLCBldCBwaGFyZXRyYSBsZW8ganVzdG8gYSBlc3QuIER1aXMgYXJjdSBhdWd1ZSwgZmFjaWxpc2lzIHZlbCBkaWduaXNzaW0gc2VkLCBhbGlxdWFtIHF1aXMgbWFnbmEuIFF1aXNxdWUgbm9uIGNvbnNlcXVhdCBkb2xvci4gU3VzcGVuZGlzc2UgYSB1bHRyaWNlcyBsZW8uCgpEb25lYyB2aXRhZSBwcmV0aXVtIG5pYmguIE1hZWNlbmFzIGJpYmVuZHVtIGJpYmVuZHVtIGRpYW0gaW4gcGxhY2VyYXQuIFV0IGFjY3Vtc2FuLCBtaSB2aXRhZSB2ZXN0aWJ1bHVtIGV1aXNtb2QsIG
      51bmMganVzdG8gdnVscHV0YXRlIG5pc2ksIG5vbiBwbGFjZXJhdCBtaSB1cm5hIGV0IGRpYW0uIE1hZWNlbmFzIG1hbGVzdWFkYSBsb3JlbSB1dCBhcmN1IG1hdHRpcyBtb2xsaXMuIE51bGxhIGZhY2lsaXNpLiBEb25lYyBlc3QgbGVvLCBiaWJlbmR1bSBldSBwdWx2aW5hciBpbiwgY3Vyc3VzIHZlbCBtZXR1cy4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LiBOdWxsYW0gZmV1Z2lhdCBwb3J0dGl0b3IgbmVxdWUgaW4gdnVscHV0YXRlLiBRdWlzcXVlIG5lYyBtaSBldSBtYWduYSBjb25zZXF1YXQgY3Vyc3VzIG5vbiBhdCBhcmN1LiBFdGlhbSByaXN1cyBtZXR1cywgc29sbGljaXR1ZGluIGV0IHVsdHJpY2VzIGF0LCB0aW5jaWR1bnQgc2VkIG51bmMuIFNlZCBlZ2V0IHNjZWxlcmlzcXVlIGF1Z3VlLiBVdCBmcmluZ2lsbGEgdmVuZW5hdGlzIHNlbSBub24gZWxlaWZlbmQuIE51bmMgbWF0dGlzLCByaXN1cyBzaXQgYW1ldCB2dWxwdXRhdGUgdmFyaXVzLCByaXN1cyBqdXN0byBlZ2VzdGFzIG1hdXJpcywgaWQgaW50ZXJkdW0gb2RpbyBpcHN1bSBldCBuaXNsLiBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBNb3JiaSBpZCBlcmF0IG9kaW8sIG5lYyBwdWx2aW5hciBlbmltLgoKQ3VyYWJpdHVyIGFjIGZlcm1lbnR1bSBxdWFtLiBNb3JiaSBldSBlcm9zIHNhcGllbiwgdml0YWUgdGVtcHVzIGRvbG9yLiBNYXVyaXMgdmVzdGlidWx1bSBibGFuZGl0IGVuaW0gdXQgdmV
      uZW5hdGlzLiBBbGlxdWFtIGVnZXN0YXMsIGVyb3MgYXQgY29uc2VjdGV0dXIgdGluY2lkdW50LCBsb3JlbSBhdWd1ZSBpYWN1bGlzIGVzdCwgbmVjIG1vbGxpcyBmZWxpcyBhcmN1IGluIG51bmMuIFNlZCBpbiBvZGlvIHNlZCBsaWJlcm8gcGVsbGVudGVzcXVlIHZvbHV0cGF0IHZpdGFlIGEgYW50ZS4gTW9yYmkgY29tbW9kbyB2b2x1dHBhdCB0ZWxsdXMsIHV0IHZpdmVycmEgcHVydXMgcGxhY2VyYXQgZmVybWVudHVtLiBJbnRlZ2VyIGlhY3VsaXMgZmFjaWxpc2lzIGFyY3UsIGF0IGdyYXZpZGEgbG9yZW0gYmliZW5kdW0gYXQuIEFlbmVhbiBpZCBlcm9zIGVnZXQgZXN0IHNhZ2l0dGlzIGNvbnZhbGxpcyBzZWQgZXQgZHVpLiBEb25lYyBldSBwdWx2aW5hciB0ZWxsdXMuIE51bmMgZGlnbmlzc2ltIHJob25jdXMgdGVsbHVzLCBhdCBwZWxsZW50ZXNxdWUgbWV0dXMgbHVjdHVzIGF0LiBTZWQgb3JuYXJlIGFsaXF1YW0gZGlhbSwgYSBwb3J0dGl0b3IgbGVvIHNvbGxpY2l0dWRpbiBzZWQuIE5hbSB2aXRhZSBsZWN0dXMgbGFjdXMuIEludGVnZXIgYWRpcGlzY2luZyBxdWFtIG5lcXVlLCBibGFuZGl0IHBvc3VlcmUgbGliZXJvLiBTZWQgbGliZXJvIG51bmMsIGVnZXN0YXMgc29kYWxlcyB0ZW1wdXMgc2VkLCBjdXJzdXMgYmxhbmRpdCB0ZWxsdXMuIFZlc3RpYnVsdW0gbWkgcHVydXMsIHVsdHJpY2llcyBxdWlzIHBsYWNlcmF0IHZlbCwgbW9sZXN0aWUgYXQgZHVpLgoKTnVsbGEgY29tbW9kbyBvZGlvIGp1c3RvLiBQ
      ZWxsZW50ZXNxdWUgbm9uIG9ybmFyZSBkaWFtLiBJbiBjb25zZWN0ZXR1ciBzYXBpZW4gYWMgbnVuYyBzYWdpdHRpcyBtYWxlc3VhZGEuIE1vcmJpIHVsbGFtY29ycGVyIHRlbXBvciBlcmF0IG5lYyBydXRydW0uIER1aXMgdXQgY29tbW9kbyBqdXN0by4gQ3JhcyBlc3Qgb3JjaSwgY29uc2VjdGV0dXIgc2VkIGludGVyZHVtIHNlZCwgc2NlbGVyaXNxdWUgc2l0IGFtZXQgbnVsbGEuIFZlc3RpYnVsdW0ganVzdG8gbnVsbGEsIHBlbGxlbnRlc3F1ZSBhIHRlbXB1cyBldCwgZGFwaWJ1cyBldCBhcmN1LiBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBNb3JiaSB0cmlzdGlxdWUsIGVyb3MgbmVjIGNvbmd1ZSBhZGlwaXNjaW5nLCBsaWd1bGEgc2VtIHJob25jdXMgZmVsaXMsIGF0IG9ybmFyZSB0ZWxsdXMgbWF1cmlzIGFjIHJpc3VzLiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgUHJvaW4gbWF1cmlzIGR1aSwgdGVtcG9yIGZlcm1lbnR1bSBkaWN0dW0gZXQsIGN1cnN1cyBhIGxlby4gTWFlY2VuYXMgbmVjIG5pc2wgYSB0ZWxsdXMgcGVsbGVudGVzcXVlIHJob25jdXMuIE51bGxhbSB1bHRyaWNlcyBldWlzbW9kIGR1aSBldSBjb25ndWUuCgpJbiBuZWMgdGVtcG9yIHJpc3VzLiBJbiBmYXVjaWJ1cyBuaXNpIGVnZXQgZGlhbSBkaWduaXNzaW0gY29uc2Vxd
      WF0LiBEb25lYyBwdWx2aW5hciBhbnRlIG5lYyBlbmltIG1hdHRpcyBydXRydW0uIFZlc3RpYnVsdW0gbGVvIGF1Z3VlLCBtb2xlc3RpZSBuZWMgZGFwaWJ1cyBpbiwgZGljdHVtIGF0IGVuaW0uIEludGVnZXIgYWxpcXVhbSwgbG9yZW0gZXUgdnVscHV0YXRlIGxhY2luaWEsIG1pIG9yY2kgdGVtcG9yIGVuaW0sIGVnZXQgbWF0dGlzIGxpZ3VsYSBtYWduYSBhIG1hZ25hLiBQcmFlc2VudCBzZWQgZXJhdCB1dCB0b3J0b3IgaW50ZXJkdW0gdml2ZXJyYS4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gTnVsbGEgZmFjaWxpc2kuIE1hZWNlbmFzIHNpdCBhbWV0IGxlY3R1cyBsYWN1cy4gTnVuYyB2aXRhZSBwdXJ1cyBpZCBsaWd1bGEgbGFvcmVldCBjb25kaW1lbnR1bS4gRHVpcyBhdWN0b3IgdG9ydG9yIHZlbCBkdWkgcHVsdmluYXIgYSBmYWNpbGlzaXMgYXJjdSBkaWduaXNzaW0uIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBEb25lYyBzb2xsaWNpdHVkaW4gcGVsbGVudGVzcXVlIGVnZXN0YXMuIFNlZCBzZWQgc2VtIGp1c3RvLiBNYWVjZW5hcyBsYW9yZWV0IGhlbmRyZXJpdCBtYXVyaXMsIHV0IHBvcnR0aXRvciBsb3JlbSBpYWN1bGlzIGFjLiBRdWlzcXVlIG1vbGVzdGllIHNlbSBxdWlzIGxvcmVtIHRlbXBvciBydXRydW0uIFBoYXNlbGx1cyBuaWJoIG1hdXJpcywgcmhvbmN1cyBpbiBjb25zZWN0ZXR1ciBub24sIGFsaXF1ZXQgZXUgbWFzc2
      EuCgpDdXJhYml0dXIgdmVsaXQgYXJjdSwgcHJldGl1bSBwb3J0YSBwbGFjZXJhdCBxdWlzLCB2YXJpdXMgdXQgbWV0dXMuIFZlc3RpYnVsdW0gdnVscHV0YXRlIHRpbmNpZHVudCBqdXN0bywgdml0YWUgcG9ydHRpdG9yIGxlY3R1cyBpbXBlcmRpZXQgc2l0IGFtZXQuIFZpdmFtdXMgZW5pbSBkb2xvciwgc29sbGljaXR1ZGluIHV0IHNlbXBlciBub24sIG9ybmFyZSBvcm5hcmUgZHVpLiBBbGlxdWFtIHRlbXBvciBmZXJtZW50dW0gc2FwaWVuIGVnZXQgY29uZGltZW50dW0uIEN1cmFiaXR1ciBsYW9yZWV0IGJpYmVuZHVtIGFudGUsIGluIGV1aXNtb2QgbGFjdXMgbGFjaW5pYSBldS4gUGVsbGVudGVzcXVlIGhhYml0YW50IG1vcmJpIHRyaXN0aXF1ZSBzZW5lY3R1cyBldCBuZXR1cyBldCBtYWxlc3VhZGEgZmFtZXMgYWMgdHVycGlzIGVnZXN0YXMuIFN1c3BlbmRpc3NlIHBvdGVudGkuIFNlZCBhdCBsaWJlcm8gZXUgdG9ydG9yIHRlbXB1cyBzY2VsZXJpc3F1ZS4gTnVsbGEgZmFjaWxpc2kuIE51bGxhbSB2aXRhZSBuZXF1ZSBpZCBqdXN0byB2aXZlcnJhIHJob25jdXMgcHJldGl1bSBhdCBsaWJlcm8uIEV0aWFtIGVzdCB1cm5hLCBhbGlxdWFtIHZlbCBwdWx2aW5hciBub24sIG9ybmFyZSB2ZWwgcHVydXMuCgpOdWxsYSB2YXJpdXMsIG5pc2kgZWdldCBjb25kaW1lbnR1bSBzZW1wZXIsIG1ldHVzIGVzdCBkaWN0dW0gb2RpbywgdmVsIG1hdHRpcyByaXN1cyBlc3Qgc2VkIHZlbGl0LiBDdW0gc29jaWlzIG5hdG9xdWU
      gcGVuYXRpYnVzIGV0IG1hZ25pcyBkaXMgcGFydHVyaWVudCBtb250ZXMsIG5hc2NldHVyIHJpZGljdWx1cyBtdXMuIE51bmMgbm9uIGVzdCBuZWMgdGVsbHVzIHVsdHJpY2llcyBtYXR0aXMgdXQgZWdldCB2ZWxpdC4gSW50ZWdlciBjb25kaW1lbnR1bSBhbnRlIGlkIGxvcmVtIGJsYW5kaXQgbGFjaW5pYS4gRG9uZWMgdmVsIHRvcnRvciBhdWd1ZSwgaW4gY29uZGltZW50dW0gbmlzaS4gUGVsbGVudGVzcXVlIHBlbGxlbnRlc3F1ZSBudWxsYSB1dCBudWxsYSBwb3J0dGl0b3IgcXVpcyBzb2RhbGVzIGVuaW0gcnV0cnVtLiBTZWQgYXVndWUgcmlzdXMsIGV1aXNtb2QgYSBhbGlxdWV0IGF0LCB2dWxwdXRhdGUgbm9uIGxpYmVyby4gTnVsbGFtIG5pYmggb2RpbywgZGlnbmlzc2ltIGZlcm1lbnR1bSBwdWx2aW5hciBhYywgY29uZ3VlIGV1IG1pLiBEdWlzIHRpbmNpZHVudCwgbmliaCBpZCB2ZW5lbmF0aXMgcGxhY2VyYXQsIGRpYW0gdHVycGlzIGdyYXZpZGEgbGVvLCBzaXQgYW1ldCBtb2xsaXMgbWFzc2EgZG9sb3IgcXVpcyBtYXVyaXMuIFZpdmFtdXMgc2NlbGVyaXNxdWUgc29kYWxlcyBhcmN1IGV0IGRhcGlidXMuIFN1c3BlbmRpc3NlIHBvdGVudGkuIENyYXMgcXVpcyB0ZWxsdXMgYXJjdSwgcXVpcyBsYW9yZWV0IHNlbS4gRnVzY2UgcG9ydHRpdG9yLCBzYXBpZW4gdmVsIHRyaXN0aXF1ZSBzb2RhbGVzLCB2ZWxpdCBsZW8gcG9ydGEgYXJjdSwgcXVpcyBwZWxsZW50ZXNxdWUgbnVuYyBtZXR1cyBub24gb2Rpby4g
      TmFtIGFyY3UgbGliZXJvLCB1bGxhbWNvcnBlciB1dCBwaGFyZXRyYSBub24sIGRpZ25pc3NpbSBldCB2ZWxpdC4gUXVpc3F1ZSBkb2xvciBsb3JlbSwgdmVoaWN1bGEgc2l0IGFtZXQgc2NlbGVyaXNxdWUgaW4sIHZhcml1cyBhdCBudWxsYS4gUGVsbGVudGVzcXVlIHZpdGFlIHNlbSBlZ2V0IHRvcnRvciBpYWN1bGlzIHB1bHZpbmFyLiBTZWQgbnVuYyBqdXN0bywgZXVpc21vZCBncmF2aWRhIHB1bHZpbmFyIGVnZXQsIGdyYXZpZGEgZWdldCB0dXJwaXMuIENyYXMgdmVsIGRpY3R1bSBuaXNpLiBOdWxsYW0gbnVsbGEgbGliZXJvLCBncmF2aWRhIHNpdCBhbWV0IGFsaXF1YW0gcXVpcywgY29tbW9kbyB2aXRhZSBvZGlvLiBDcmFzIHZpdGFlIG5pYmggbmVjIGR1aSBwbGFjZXJhdCBzZW1wZXIuCgpWaXZhbXVzIGF0IGZyaW5naWxsYSBlcm9zLiBWaXZhbXVzIGF0IG5pc2wgaWQgbWFzc2EgY29tbW9kbyBmZXVnaWF0IHF1aXMgbm9uIG1hc3NhLiBNb3JiaSB0ZWxsdXMgdXJuYSwgYXVjdG9yIHNpdCBhbWV0IGVsZW1lbnR1bSBzZWQsIHJ1dHJ1bSBub24gbGVjdHVzLiBOdWxsYSBmZXVnaWF0IGR1aSBpbiBzYXBpZW4gb3JuYXJlIGV0IGltcGVyZGlldCBlc3Qgb3JuYXJlLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gVmVzdGlidWx1bSBzZW1wZXIgcnV0cnVtIHRlbXBvci4gU2VkIGluIGZlbGlzI
      G5pYmgsIHNlZCBhbGlxdWFtIGVuaW0uIEN1cmFiaXR1ciB1dCBxdWFtIHNjZWxlcmlzcXVlIHZlbGl0IHBsYWNlcmF0IGRpY3R1bS4gRG9uZWMgZWxlaWZlbmQgdmVoaWN1bGEgcHVydXMsIGV1IHZlc3RpYnVsdW0gc2FwaWVuIHJ1dHJ1bSBldS4gVml2YW11cyBpbiBvZGlvIHZlbCBlc3QgdnVscHV0YXRlIGlhY3VsaXMuIE51bmMgcnV0cnVtIGZldWdpYXQgcHJldGl1bS4KCk1hZWNlbmFzIGlwc3VtIG5lcXVlLCBhdWN0b3IgcXVpcyBsYWNpbmlhIHZpdGFlLCBldWlzbW9kIGFjIG9yY2kuIERvbmVjIG1vbGVzdGllIG1hc3NhIGNvbnNlcXVhdCBlc3QgcG9ydGEgYWMgcG9ydGEgcHVydXMgdGluY2lkdW50LiBOYW0gYmliZW5kdW0gbGVvIG5lYyBsYWN1cyBtb2xsaXMgbm9uIGNvbmRpbWVudHVtIGRvbG9yIHJob25jdXMuIE51bGxhIGFjIHZvbHV0cGF0IGxvcmVtLiBOdWxsYW0gZXJhdCBwdXJ1cywgY29udmFsbGlzIGVnZXQgY29tbW9kbyBpZCwgdmFyaXVzIHF1aXMgYXVndWUuIE51bGxhbSBhbGlxdWFtIGVnZXN0YXMgbWksIHZlbCBzdXNjaXBpdCBuaXNsIG1hdHRpcyBjb25zZXF1YXQuIFF1aXNxdWUgdmVsIGVnZXN0YXMgc2FwaWVuLiBOdW5jIGxvcmVtIHZlbGl0LCBjb252YWxsaXMgbmVjIGxhb3JlZXQgZXQsIGFsaXF1ZXQgZWdldCBtYXNzYS4gTmFtIGV0IG5pYmggYWMgZHVpIHZlaGljdWxhIGFsaXF1YW0gcXVpcyBldSBhdWd1ZS4gQ3JhcyB2ZWwgbWFnbmEgdXQgZWxpdCByaG9uY3VzIGludGVyZHVtIG
      lhY3VsaXMgdm9sdXRwYXQgbmlzbC4gU3VzcGVuZGlzc2UgYXJjdSBsb3JlbSwgdmFyaXVzIHJob25jdXMgdGVtcG9yIGlkLCBwdWx2aW5hciBzZWQgdG9ydG9yLiBQZWxsZW50ZXNxdWUgdWx0cmljaWVzIGxhb3JlZXQgb2RpbyBhYyBkaWduaXNzaW0uIEFsaXF1YW0gZGlhbSBhcmN1LCBwbGFjZXJhdCBxdWlzIGVnZXN0YXMgZWdldCwgZmFjaWxpc2lzIGV1IG51bmMuIE1hdXJpcyB2dWxwdXRhdGUsIG5pc2wgc2l0IGFtZXQgbW9sbGlzIGludGVyZHVtLCByaXN1cyB0b3J0b3Igb3JuYXJlIG9yY2ksIHNlZCBlZ2VzdGFzIG9yY2kgZXJvcyBub24gZGlhbS4gVmVzdGlidWx1bSBoZW5kcmVyaXQsIG1ldHVzIHF1aXMgcGxhY2VyYXQgcGVsbGVudGVzcXVlLCBlbmltIHB1cnVzIGZhdWNpYnVzIGR1aSwgc2l0IGFtZXQgdWx0cmljaWVzIGxlY3R1cyBpcHN1bSBpZCBsb3JlbS4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBzb2Npb3NxdSBhZCBsaXRvcmEgdG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBwZXIgaW5jZXB0b3MgaGltZW5hZW9zLiBQcmFlc2VudCBlZ2V0IGRpYW0gb2RpbywgZXUgYmliZW5kdW0gZWxpdC4gSW4gdmVzdGlidWx1bSBvcmNpIGV1IGVyYXQgdGluY2lkdW50IHRyaXN0aXF1ZS4KCkNyYXMgY29uc2VjdGV0dXIgYW50ZSBldSB0dXJwaXMgcGxhY2VyYXQgc29sbGljaXR1ZGluLiBNYXVyaXMgZXQgbGFjdXMgdG9ydG9yLCBlZ2V0IHBoYXJldHJhIHZlbGl0LiBEb25lYyBhY2N1bXNhbiB1bHRyaWNlcyB
      0ZW1wb3IuIERvbmVjIGF0IG5pYmggYSBlbGl0IGNvbmRpbWVudHVtIGRhcGlidXMuIEludGVnZXIgc2l0IGFtZXQgdnVscHV0YXRlIGFudGUuIFN1c3BlbmRpc3NlIHBvdGVudGkuIEluIHNvZGFsZXMgbGFvcmVldCBtYXNzYSB2aXRhZSBsYWNpbmlhLiBNb3JiaSB2ZWwgbGFjdXMgZmV1Z2lhdCBhcmN1IHZ1bHB1dGF0ZSBtb2xlc3RpZS4gQWxpcXVhbSBtYXNzYSBtYWduYSwgdWxsYW1jb3JwZXIgYWNjdW1zYW4gZ3JhdmlkYSBxdWlzLCByaG9uY3VzIHB1bHZpbmFyIG51bGxhLiBQcmFlc2VudCBzaXQgYW1ldCBpcHN1bSBkaWFtLCBzaXQgYW1ldCBsYWNpbmlhIG5lcXVlLiBJbiBldCBzYXBpZW4gYXVndWUuIEV0aWFtIGVuaW0gZWxpdCwgdWx0cmljZXMgdmVsIHJ1dHJ1bSBpZCwgc2NlbGVyaXNxdWUgbm9uIGVuaW0uCgpQcm9pbiBldCBlZ2VzdGFzIG5lcXVlLiBQcmFlc2VudCBldCBpcHN1bSBkb2xvci4gTnVuYyBub24gdmFyaXVzIG5pc2wuIEZ1c2NlIGluIHRvcnRvciBuaXNpLiBNYWVjZW5hcyBjb252YWxsaXMgbmVxdWUgaW4gbGlndWxhIGJsYW5kaXQgcXVpcyB2ZWhpY3VsYSBsZW8gbW9sbGlzLiBQZWxsZW50ZXNxdWUgc2FnaXR0aXMgYmxhbmRpdCBsZW8sIGRhcGlidXMgcGVsbGVudGVzcXVlIGxlbyB1bHRyaWNlcyBhYy4gQ3VyYWJpdHVyIGFjIGVnZXN0YXMgbGliZXJvLiBEb25lYyBwcmV0aXVtIHBoYXJldHJhIHByZXRpdW0uIEZ1c2NlIGltcGVyZGlldCwgdHVycGlzIGV1IGFsaXF1YW0gcG9ydGEs
      IGFudGUgZWxpdCBlbGVpZmVuZCByaXN1cywgbHVjdHVzIGF1Y3RvciBhcmN1IGFudGUgdXQgbnVuYy4gVml2YW11cyBpbiBsZW8gZmVsaXMsIHZpdGFlIGVsZWlmZW5kIGxhY3VzLiBEb25lYyB0ZW1wdXMgYWxpcXVhbSBwdXJ1cyBwb3J0dGl0b3IgdHJpc3RpcXVlLiBTdXNwZW5kaXNzZSBkaWFtIG5lcXVlLCBzdXNjaXBpdCBmZXVnaWF0IGZyaW5naWxsYSBub24sIGVsZWlmZW5kIHNpdCBudWxsYW0uCg==
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/lots_of_docs.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/lots_of_docs.js b/test/javascript/tests/lots_of_docs.js
    new file mode 100644
    index 0000000..2fe702b
    --- /dev/null
    +++ b/test/javascript/tests/lots_of_docs.js
    @@ -0,0 +1,55 @@
    +// 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.
    +
    +// test saving a semi-large quanitity of documents and do some view queries.
    +couchTests.lots_of_docs = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // keep number lowish for now to keep tests fasts. Crank up manually to
    + // to really test.
    + var numDocsToCreate = 500;
    +
    + for(var i=0; i < numDocsToCreate; i += 100) {
    + var createNow = Math.min(numDocsToCreate - i, 100);
    + var docs = makeDocs(i, i + createNow);
    + db.bulkSave(docs);
    + }
    +
    + // query all documents, and return the doc.integer member as a key.
    + results = db.query(function(doc){ emit(doc.integer, null) });
    +
    + T(results.total_rows == numDocsToCreate);
    +
    + // validate the keys are ordered ascending
    + for(var i=0; i<numDocsToCreate; i++) {
    + T(results.rows[i].key==i);
    + }
    +
    + // do the query again, but with descending output
    + results = db.query(function(doc){ emit(doc.integer, null) }, null, {
    + descending: true
    + });
    +
    + T(results.total_rows == numDocsToCreate);
    +
    + // validate the keys are ordered descending
    + for(var i=0; i<numDocsToCreate; i++) {
    + T(results.rows[numDocsToCreate-1-i].key==i);
    + }
    +
    + // Check _all_docs with descending=true again (now that there are many docs)
    + var desc = db.allDocs({descending:true});
    + T(desc.total_rows == desc.rows.length);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/method_override.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/method_override.js b/test/javascript/tests/method_override.js
    new file mode 100644
    index 0000000..0bb4c61
    --- /dev/null
    +++ b/test/javascript/tests/method_override.js
    @@ -0,0 +1,40 @@
    +// 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.
    +
    +// Allow broken HTTP clients to fake a full method vocabulary with an X-HTTP-METHOD-OVERRIDE header
    +couchTests.method_override = function(debug) {
    + var result = JSON.parse(CouchDB.request("GET", "/").responseText);
    + T(result.couchdb == "Welcome");
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    +
    + db.createDb();
    +
    + var doc = {bob : "connie"};
    + xhr = CouchDB.request("POST", "/test_suite_db/fnord", {body: JSON.stringify(doc), headers:{"X-HTTP-Method-Override" : "PUT"}});
    + T(xhr.status == 201);
    +
    + doc = db.open("fnord");
    + T(doc.bob == "connie");
    +
    + xhr = CouchDB.request("POST", "/test_suite_db/fnord?rev=" + doc._rev, {headers:{"X-HTTP-Method-Override" : "DELETE"}});
    + T(xhr.status == 200);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/fnord2", {body: JSON.stringify(doc), headers:{"X-HTTP-Method-Override" : "PUT"}});
    + // Method Override is ignored when original Method isn't POST
    + T(xhr.status == 404);
    +
    + doc = db.open("fnord");
    + T(doc == null);
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/multiple_rows.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/multiple_rows.js b/test/javascript/tests/multiple_rows.js
    new file mode 100644
    index 0000000..4f6fcd3
    --- /dev/null
    +++ b/test/javascript/tests/multiple_rows.js
    @@ -0,0 +1,80 @@
    +// 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.
    +
    +couchTests.multiple_rows = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var nc = {_id:"NC", cities:["Charlotte", "Raleigh"]};
    + var ma = {_id:"MA", cities:["Boston", "Lowell", "Worcester", "Cambridge", "Springfield"]};
    + var fl = {_id:"FL", cities:["Miami", "Tampa", "Orlando", "Springfield"]};
    +
    + T(db.save(nc).ok);
    + T(db.save(ma).ok);
    + T(db.save(fl).ok);
    +
    + var generateListOfCitiesAndState = "function(doc) {" +
    + " for (var i = 0; i < doc.cities.length; i++)" +
    + " emit(doc.cities[i] + \", \" + doc._id, null);" +
    + "}";
    +
    + var results = db.query(generateListOfCitiesAndState);
    + var rows = results.rows;
    +
    + T(rows[0].key == "Boston, MA");
    + T(rows[1].key == "Cambridge, MA");
    + T(rows[2].key == "Charlotte, NC");
    + T(rows[3].key == "Lowell, MA");
    + T(rows[4].key == "Miami, FL");
    + T(rows[5].key == "Orlando, FL");
    + T(rows[6].key == "Raleigh, NC");
    + T(rows[7].key == "Springfield, FL");
    + T(rows[8].key == "Springfield, MA");
    + T(rows[9].key == "Tampa, FL");
    + T(rows[10].key == "Worcester, MA");
    +
    + // add another city to NC
    + nc.cities.push("Wilmington");
    + T(db.save(nc).ok);
    +
    + var results = db.query(generateListOfCitiesAndState);
    + var rows = results.rows;
    +
    + T(rows[0].key == "Boston, MA");
    + T(rows[1].key == "Cambridge, MA");
    + T(rows[2].key == "Charlotte, NC");
    + T(rows[3].key == "Lowell, MA");
    + T(rows[4].key == "Miami, FL");
    + T(rows[5].key == "Orlando, FL");
    + T(rows[6].key == "Raleigh, NC");
    + T(rows[7].key == "Springfield, FL");
    + T(rows[8].key == "Springfield, MA");
    + T(rows[9].key == "Tampa, FL");
    + T(rows[10].key == "Wilmington, NC");
    + T(rows[11].key == "Worcester, MA");
    +
    + // now delete MA
    + T(db.deleteDoc(ma).ok);
    +
    + var results = db.query(generateListOfCitiesAndState);
    + var rows = results.rows;
    +
    + T(rows[0].key == "Charlotte, NC");
    + T(rows[1].key == "Miami, FL");
    + T(rows[2].key == "Orlando, FL");
    + T(rows[3].key == "Raleigh, NC");
    + T(rows[4].key == "Springfield, FL");
    + T(rows[5].key == "Tampa, FL");
    + T(rows[6].key == "Wilmington, NC");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/oauth_users_db.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/oauth_users_db.js b/test/javascript/tests/oauth_users_db.js
    new file mode 100644
    index 0000000..b98069e
    --- /dev/null
    +++ b/test/javascript/tests/oauth_users_db.js
    @@ -0,0 +1,161 @@
    +// 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.
    +
    +couchTests.oauth_users_db = function(debug) {
    + // This tests OAuth authentication using the _users DB instead of the ini
    + // configuration for storing OAuth tokens and secrets.
    +
    + if (debug) debugger;
    +
    + var usersDb = new CouchDB("test_suite_users",{"X-Couch-Full-Commit":"false"});
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + var host = CouchDB.host;
    + var authorization_url = "/_oauth/authorize";
    +
    +
    + // Simple secret key generator
    + function generateSecret(length) {
    + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    + var secret = '';
    + for (var i = 0; i < length; i++) {
    + secret += tab.charAt(Math.floor(Math.random() * 64));
    + }
    + return secret;
    + }
    +
    +
    + function oauthRequest(method, path, message, accessor) {
    + message.action = path;
    + message.method = method || 'GET';
    + OAuth.SignatureMethod.sign(message, accessor);
    + var parameters = message.parameters;
    + if (method == "POST" || method == "GET") {
    + if (method == "GET") {
    + return CouchDB.request("GET", OAuth.addToURL(path, parameters));
    + } else {
    + return CouchDB.request("POST", path, {
    + headers: {"Content-Type": "application/x-www-form-urlencoded"},
    + body: OAuth.formEncode(parameters)
    + });
    + }
    + } else {
    + return CouchDB.request(method, path, {
    + headers: {Authorization: OAuth.getAuthorizationHeader('', parameters)}
    + });
    + }
    + }
    +
    +
    + // this function will be called on the modified server
    + var testFun = function () {
    + var fdmanana = CouchDB.prepareUserDoc({
    + name: "fdmanana",
    + roles: ["dev"],
    + oauth: {
    + consumer_keys: {
    + "key_foo": "bar",
    + "key_xpto": "mars"
    + },
    + tokens: {
    + "salut": "ola",
    + "tok1": "123"
    + }
    + }
    + }, "qwerty");
    + TEquals(true, usersDb.save(fdmanana).ok);
    +
    + var signatureMethods = ["PLAINTEXT", "HMAC-SHA1"];
    + var message, xhr, responseMessage, accessor, data;
    +
    + for (var i = 0; i < signatureMethods.length; i++) {
    + message = {
    + parameters: {
    + oauth_signature_method: signatureMethods[i],
    + oauth_consumer_key: "key_foo",
    + oauth_token: "tok1",
    + oauth_version: "1.0"
    + }
    + };
    + accessor = {
    + consumerSecret: "bar",
    + tokenSecret: "123"
    + };
    +
    + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token",
    + message, accessor
    + );
    + TEquals(200, xhr.status);
    +
    + responseMessage = OAuth.decodeForm(xhr.responseText);
    +
    + // Obtaining User Authorization
    + // Only needed for 3-legged OAuth
    + //xhr = CouchDB.request(
    + // "GET", authorization_url + '?oauth_token=' + responseMessage.oauth_token);
    + //TEquals(200, xhr.status);
    +
    + xhr = oauthRequest(
    + "GET", CouchDB.protocol + host + "/_session", message, accessor);
    + TEquals(200, xhr.status);
    + data = JSON.parse(xhr.responseText);
    + TEquals(true, data.ok);
    + TEquals("object", typeof data.userCtx);
    + TEquals("fdmanana", data.userCtx.name);
    + TEquals("dev", data.userCtx.roles[0]);
    + TEquals("oauth", data.info.authenticated);
    +
    + // test invalid token
    + message.parameters.oauth_token = "not a token!";
    + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session",
    + message, accessor
    + );
    + TEquals(400, xhr.status, "Request should be invalid.");
    +
    + // test invalid secret
    + message.parameters.oauth_token = "tok1";
    + accessor.tokenSecret = "badone";
    + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session",
    + message, accessor
    + );
    + data = JSON.parse(xhr.responseText);
    + TEquals(null, data.userCtx.name);
    + TEquals(1, data.userCtx.roles.length);
    + TEquals("_admin", data.userCtx.roles[0]);
    + TEquals(true, data.info.authentication_handlers.indexOf("default") >= 0);
    + TEquals("default", data.info.authenticated);
    + }
    + };
    +
    +
    + usersDb.deleteDb();
    +
    + run_on_modified_server(
    + [
    + {section: "httpd",
    + key: "WWW-Authenticate", value: 'OAuth'},
    + {section: "couch_httpd_auth",
    + key: "secret", value: generateSecret(64)},
    + {section: "couch_httpd_auth",
    + key: "authentication_db", value: usersDb.name},
    + {section: "couch_httpd_oauth",
    + key: "use_users_db", value: "true"},
    + {section: "httpd", key: "authentication_handlers",
    + value: "{couch_httpd_oauth, oauth_authentication_handler}, " +
    + "{couch_httpd_auth, default_authentication_handler}"}
    + ],
    + testFun
    + );
    +
    + // cleanup
    + usersDb.deleteDb();
    + db.deleteDb();
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/proxyauth.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/proxyauth.js b/test/javascript/tests/proxyauth.js
    new file mode 100644
    index 0000000..1677a66
    --- /dev/null
    +++ b/test/javascript/tests/proxyauth.js
    @@ -0,0 +1,130 @@
    +// 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.
    +
    +
    +
    +couchTests.proxyauth = function(debug) {
    + // this test proxy authentification handler
    +
    + var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    +
    + if (debug) debugger;
    +
    + usersDb.deleteDb();
    +
    + // Simple secret key generator
    + function generateSecret(length) {
    + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    + var secret = '';
    + for (var i=0; i<length; i++) {
    + secret += tab.charAt(Math.floor(Math.random() * 64));
    + }
    + return secret;
    + }
    +
    + var secret = generateSecret(64);
    +
    + function TestFun() {
    + db.deleteDb();
    + db.createDb();
    +
    + var benoitcUserDoc = CouchDB.prepareUserDoc({
    + name: "benoitc@apache.org"
    + }, "test");
    + T(usersDb.save(benoitcUserDoc).ok);
    +
    + T(CouchDB.session().userCtx.name == null);
    +
    + // test that you can use basic auth aginst the users db
    + var s = CouchDB.session({
    + headers : {
    + "Authorization" : "Basic YmVub2l0Y0BhcGFjaGUub3JnOnRlc3Q="
    + }
    + });
    + T(s.userCtx.name == "benoitc@apache.org");
    + T(s.info.authenticated == "default");
    +
    + CouchDB.logout();
    +
    + var headers = {
    + "X-Auth-CouchDB-UserName": "benoitc@apache.org",
    + "X-Auth-CouchDB-Roles": "test",
    + "X-Auth-CouchDB-Token": hex_hmac_sha1(secret, "benoitc@apache.org")
    + };
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    +
    + shows: {
    + "welcome": stringFun(function(doc,req) {
    + return "Welcome " + req.userCtx["name"];
    + }),
    + "role": stringFun(function(doc, req) {
    + return req.userCtx['roles'][0];
    + })
    + }
    + };
    +
    + db.save(designDoc);
    +
    + var req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/welcome",
    + {headers: headers});
    + T(req.responseText == "Welcome benoitc@apache.org");
    +
    + req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/role",
    + {headers: headers});
    + T(req.responseText == "test");
    +
    + var xhr = CouchDB.request("PUT", "/_config/couch_httpd_auth/proxy_use_secret",{
    + body : JSON.stringify("true"),
    + headers: {"X-Couch-Persist": "false"}
    + });
    + T(xhr.status == 200);
    +
    + req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/welcome",
    + {headers: headers});
    + T(req.responseText == "Welcome benoitc@apache.org");
    +
    + req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/role",
    + {headers: headers});
    + T(req.responseText == "test");
    +
    + }
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "authentication_handlers",
    + value:"{couch_httpd_auth, proxy_authentification_handler}, {couch_httpd_auth, default_authentication_handler}"},
    + {section: "couch_httpd_auth",
    + key: "authentication_db",
    + value: "test_suite_users"},
    + {section: "couch_httpd_auth",
    + key: "secret",
    + value: secret},
    + {section: "couch_httpd_auth",
    + key: "x_auth_username",
    + value: "X-Auth-CouchDB-UserName"},
    + {section: "couch_httpd_auth",
    + key: "x_auth_roles",
    + value: "X-Auth-CouchDB-Roles"},
    + {section: "couch_httpd_auth",
    + key: "x_auth_token",
    + value: "X-Auth-CouchDB-Token"},
    + {section: "couch_httpd_auth",
    + key: "proxy_use_secret",
    + value: "false"}],
    + TestFun
    + );
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/purge.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/purge.js b/test/javascript/tests/purge.js
    new file mode 100644
    index 0000000..2968913
    --- /dev/null
    +++ b/test/javascript/tests/purge.js
    @@ -0,0 +1,145 @@
    +// 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.
    +
    +couchTests.purge = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + /*
    + purge is not to be confused with a document deletion. It removes the
    + document and all edit history from the local instance of the database.
    + */
    +
    + var numDocs = 10;
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
    + single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"}
    + }
    + };
    +
    + T(db.save(designDoc).ok);
    +
    + db.bulkSave(makeDocs(1, numDocs + 1));
    +
    + // go ahead and validate the views before purging
    + var rows = db.view("test/all_docs_twice").rows;
    + for (var i = 0; i < numDocs; i++) {
    + T(rows[2*i].key == i+1);
    + T(rows[(2*i)+1].key == i+1);
    + }
    + T(db.view("test/single_doc").total_rows == 1);
    +
    + var info = db.info();
    + var doc1 = db.open("1");
    + var doc2 = db.open("2");
    +
    + // purge the documents
    + var xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    + body: JSON.stringify({"1":[doc1._rev], "2":[doc2._rev]})
    + });
    + T(xhr.status == 200);
    +
    + var result = JSON.parse(xhr.responseText);
    + var newInfo = db.info();
    +
    + // purging increments the update sequence
    + T(info.update_seq+1 == newInfo.update_seq);
    + // and it increments the purge_seq
    + T(info.purge_seq+1 == newInfo.purge_seq);
    + T(result.purge_seq == newInfo.purge_seq);
    +
    + T(result.purged["1"][0] == doc1._rev);
    + T(result.purged["2"][0] == doc2._rev);
    +
    + T(db.open("1") == null);
    + T(db.open("2") == null);
    +
    + var rows = db.view("test/all_docs_twice").rows;
    + for (var i = 2; i < numDocs; i++) {
    + T(rows[2*(i-2)].key == i+1);
    + T(rows[(2*(i-2))+1].key == i+1);
    + }
    + T(db.view("test/single_doc").total_rows == 0);
    +
    + // purge sequences are preserved after compaction (COUCHDB-1021)
    + T(db.compact().ok);
    + T(db.last_req.status == 202);
    + // compaction isn't instantaneous, loop until done
    + while (db.info().compact_running) {};
    + var compactInfo = db.info();
    + T(compactInfo.purge_seq == newInfo.purge_seq);
    +
    + // purge documents twice in a row without loading views
    + // (causes full view rebuilds)
    +
    + var doc3 = db.open("3");
    + var doc4 = db.open("4");
    +
    + xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    + body: JSON.stringify({"3":[doc3._rev]})
    + });
    +
    + T(xhr.status == 200);
    +
    + xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    + body: JSON.stringify({"4":[doc4._rev]})
    + });
    +
    + T(xhr.status == 200);
    + result = JSON.parse(xhr.responseText);
    + T(result.purge_seq == db.info().purge_seq);
    +
    + var rows = db.view("test/all_docs_twice").rows;
    + for (var i = 4; i < numDocs; i++) {
    + T(rows[2*(i-4)].key == i+1);
    + T(rows[(2*(i-4))+1].key == i+1);
    + }
    + T(db.view("test/single_doc").total_rows == 0);
    +
    + // COUCHDB-1065
    + var dbA = new CouchDB("test_suite_db_a");
    + var dbB = new CouchDB("test_suite_db_b");
    + dbA.deleteDb();
    + dbA.createDb();
    + dbB.deleteDb();
    + dbB.createDb();
    + var docA = {_id:"test", a:1};
    + var docB = {_id:"test", a:2};
    + dbA.save(docA);
    + dbB.save(docB);
    + CouchDB.replicate(dbA.name, dbB.name);
    + var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", {
    + body: JSON.stringify({"test":[docA._rev]})
    + });
    + TEquals(200, xhr.status, "single rev purge after replication succeeds");
    +
    + var xhr = CouchDB.request("GET", "/" + dbB.name + "/test?rev=" + docA._rev);
    + TEquals(404, xhr.status, "single rev purge removes revision");
    +
    + var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", {
    + body: JSON.stringify({"test":[docB._rev]})
    + });
    + TEquals(200, xhr.status, "single rev purge after replication succeeds");
    + var xhr = CouchDB.request("GET", "/" + dbB.name + "/test?rev=" + docB._rev);
    + TEquals(404, xhr.status, "single rev purge removes revision");
    +
    + var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", {
    + body: JSON.stringify({"test":[docA._rev, docB._rev]})
    + });
    + TEquals(200, xhr.status, "all rev purge after replication succeeds");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/reader_acl.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/reader_acl.js b/test/javascript/tests/reader_acl.js
    new file mode 100644
    index 0000000..ff770c7
    --- /dev/null
    +++ b/test/javascript/tests/reader_acl.js
    @@ -0,0 +1,219 @@
    +// 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.
    +
    +couchTests.reader_acl = function(debug) {
    + // this tests read access control
    +
    + var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    + var secretDb = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + function testFun() {
    + try {
    + usersDb.deleteDb();
    + try {
    + usersDb.createDb();
    + } catch(e) {
    + if(usersDb.last_req.status != 412) {
    + throw e;
    + }
    + }
    + secretDb.deleteDb();
    + secretDb.createDb();
    +
    + // create a user with top-secret-clearance
    + var jchrisUserDoc = CouchDB.prepareUserDoc({
    + name: "jchris@apache.org",
    + roles : ["top-secret"]
    + }, "funnybone");
    + T(usersDb.save(jchrisUserDoc).ok);
    + usersDb.ensureFullCommit();
    +
    + T(CouchDB.session().userCtx.name == null);
    +
    + // set secret db to be read controlled
    + T(secretDb.save({_id:"baz",foo:"bar"}).ok);
    + T(secretDb.open("baz").foo == "bar");
    +
    + T(secretDb.setSecObj({
    + "members" : {
    + roles : ["super-secret-club"],
    + names : ["joe","barb"]
    + }
    + }).ok);
    + } finally {
    + CouchDB.logout();
    + }
    + }
    +
    + // split into 2 funs so we can test restart behavior
    + function testFun2() {
    + try {
    + // can't read it as jchris b/c he's missing the needed role
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + T(CouchDB.session().userCtx.name == "jchris@apache.org");
    +
    + try {
    + secretDb.open("baz");
    + T(false && "can't open a doc from a secret db") ;
    + } catch(e) {
    + T(true)
    + }
    +
    + CouchDB.logout();
    +
    + // make anyone with the top-secret role an admin
    + // db admins are automatically members
    + T(secretDb.setSecObj({
    + "admins" : {
    + roles : ["top-secret"],
    + names : []
    + },
    + "members" : {
    + roles : ["super-secret-club"],
    + names : ["joe","barb"]
    + }
    + }).ok);
    +
    +
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    +
    + // db admin can read
    + T(secretDb.open("baz").foo == "bar");
    +
    + // and run temp views
    + TEquals(secretDb.query(function(doc) {
    + emit(null, null)
    + }).total_rows, 1);
    +
    + CouchDB.logout();
    + T(CouchDB.session().userCtx.roles.indexOf("_admin") != -1);
    +
    + // admin now adds the top-secret role to the db's members
    + // and removes db-admins
    + T(secretDb.setSecObj({
    + "admins" : {
    + roles : [],
    + names : []
    + },
    + "members" : {
    + roles : ["super-secret-club", "top-secret"],
    + names : ["joe","barb"]
    + }
    + }).ok);
    +
    + // server _admin can always read
    + T(secretDb.open("baz").foo == "bar");
    +
    + // and run temp views
    + TEquals(secretDb.query(function(doc) {
    + emit(null, null)
    + }).total_rows, 1);
    +
    + T(secretDb.save({
    + "_id" : "_design/foo",
    + views : {
    + bar : {
    + map : "function(doc){emit(null, null)}"
    + }
    + }
    + }).ok)
    +
    + // now top-secret users can read too
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
    + T(secretDb.open("baz").foo == "bar");
    + // members can query stored views
    + T(secretDb.view("foo/bar").total_rows == 1);
    +
    + // members can't do temp views
    + try {
    + var results = secretDb.query(function(doc) {
    + emit(null, null);
    + });
    + T(false && "temp view should be admin only");
    + } catch (e) {
    + T(true && "temp view is admin only");
    + }
    +
    + CouchDB.logout();
    +
    + // works with readers (backwards compat with 1.0)
    + T(secretDb.setSecObj({
    + "admins" : {
    + roles : [],
    + names : []
    + },
    + "readers" : {
    + roles : ["super-secret-club", "top-secret"],
    + names : ["joe","barb"]
    + }
    + }).ok);
    +
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
    + T(secretDb.open("baz").foo == "bar");
    +
    + // can't set non string reader names or roles
    + try {
    + secretDb.setSecObj({
    + "members" : {
    + roles : ["super-secret-club", {"top-secret":"awesome"}],
    + names : ["joe","barb"]
    + }
    + })
    + T(false && "only string roles");
    + } catch (e) {}
    +
    + try {
    + secretDb.setSecObj({
    + "members" : {
    + roles : ["super-secret-club", {"top-secret":"awesome"}],
    + names : ["joe",22]
    + }
    + });
    + T(false && "only string names");
    + } catch (e) {}
    +
    + try {
    + secretDb.setSecObj({
    + "members" : {
    + roles : ["super-secret-club", {"top-secret":"awesome"}],
    + names : "joe"
    + }
    + });
    + T(false && "only lists of names");
    + } catch (e) {}
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "authentication_handlers",
    + value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
    + {section: "couch_httpd_auth",
    + key: "authentication_db", value: "test_suite_users"}],
    + testFun
    + );
    +
    + // security changes will always commit synchronously
    + restartServer();
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "authentication_handlers",
    + value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
    + {section: "couch_httpd_auth",
    + key: "authentication_db", value: "test_suite_users"}],
    + testFun2
    + );
    +}
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/users_db_security.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/users_db_security.js b/test/javascript/tests/users_db_security.js
    new file mode 100644
    index 0000000..f2ca8bc
    --- /dev/null
    +++ b/test/javascript/tests/users_db_security.js
    @@ -0,0 +1,423 @@
    +// 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.
    +
    +couchTests.users_db_security = function(debug) {
    + var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    + if (debug) debugger;
    +
    + function wait(ms) {
    + var t0 = new Date(), t1;
    + do {
    + CouchDB.request("GET", "/");
    + t1 = new Date();
    + } while ((t1 - t0) <= ms);
    + }
    +
    + var loginUser = function(username) {
    + var pws = {
    + jan: "apple",
    + jchris: "mp3",
    + jchris1: "couch",
    + fdmanana: "foobar",
    + benoitc: "test"
    + };
    + var username1 = username.replace(/[0-9]$/, "");
    + var password = pws[username];
    + T(CouchDB.login(username1, pws[username]).ok);
    + };
    +
    + var open_as = function(db, docId, username) {
    + loginUser(username);
    + try {
    + return db.open(docId, {"anti-cache": Math.round(Math.random() * 100000)});
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + var view_as = function(db, viewname, username) {
    + loginUser(username);
    + try {
    + return db.view(viewname);
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + var save_as = function(db, doc, username)
    + {
    + loginUser(username);
    + try {
    + return db.save(doc);
    + } catch (ex) {
    + return ex;
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + var changes_as = function(db, username)
    + {
    + loginUser(username);
    + try {
    + return db.changes();
    + } catch(ex) {
    + return ex;
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + var testFun = function()
    + {
    +
    + // _users db
    + // a doc with a field 'password' should be hashed to 'derived_key'
    + // with salt and salt stored in 'salt', 'password' is set to null.
    + // Exising 'derived_key' and 'salt' fields are overwritten with new values
    + // when a non-null 'password' field exists.
    + // anonymous should be able to create a user document
    + var userDoc = {
    + _id: "org.couchdb.user:jchris",
    + type: "user",
    + name: "jchris",
    + password: "mp3",
    + roles: []
    + };
    +
    + // jan's gonna be admin as he's the first user
    + TEquals(true, usersDb.save(userDoc).ok, "should save document");
    + userDoc = usersDb.open("org.couchdb.user:jchris");
    + TEquals(undefined, userDoc.password, "password field should be null 1");
    + TEquals(40, userDoc.derived_key.length, "derived_key should exist");
    + TEquals(32, userDoc.salt.length, "salt should exist");
    +
    + // create server admin
    + run_on_modified_server([
    + {
    + section: "couch_httpd_auth",
    + key: "iterations",
    + value: "1"
    + },
    + {
    + section: "admins",
    + key: "jan",
    + value: "apple"
    + }
    + ], function() {
    +
    + // anonymous should not be able to read an existing user's user document
    + var res = usersDb.open("org.couchdb.user:jchris");
    + TEquals(null, res, "anonymous user doc read should be not found");
    +
    + // anonymous should not be able to read /_users/_changes
    + try {
    + var ch = usersDb.changes();
    + T(false, "anonymous can read _changes");
    + } catch(e) {
    + TEquals("unauthorized", e.error, "anoymous can't read _changes");
    + }
    +
    + // user should be able to read their own document
    + var jchrisDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris");
    + TEquals("org.couchdb.user:jchris", jchrisDoc._id);
    +
    + // user should not be able to read /_users/_changes
    + var changes = changes_as(usersDb, "jchris");
    + TEquals("unauthorized", changes.error, "user can't read _changes");
    +
    + // new 'password' fields should trigger new hashing routine
    + jchrisDoc.password = "couch";
    +
    + TEquals(true, save_as(usersDb, jchrisDoc, "jchris").ok);
    + wait(100);
    + var jchrisDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris1");
    +
    + TEquals(undefined, jchrisDoc.password, "password field should be null 2");
    + TEquals(40, jchrisDoc.derived_key.length, "derived_key should exist");
    + TEquals(32, jchrisDoc.salt.length, "salt should exist");
    +
    + TEquals(true, userDoc.salt != jchrisDoc.salt, "should have new salt");
    + TEquals(true, userDoc.derived_key != jchrisDoc.derived_key,
    + "should have new derived_key");
    +
    + // SHA-1 password hashes are upgraded to PBKDF2 on successful
    + // authentication
    + var rnewsonDoc = {
    + _id: "org.couchdb.user:rnewson",
    + type: "user",
    + name: "rnewson",
    + // password: "plaintext_password",
    + password_sha: "e29dc3aeed5abf43185c33e479f8998558c59474",
    + salt: "24f1e0a87c2e374212bda1073107e8ae",
    + roles: []
    + };
    +
    + var password_sha = rnewsonDoc.password_sha,
    + salt = rnewsonDoc.salt,
    + derived_key,
    + iterations;
    +
    + usersDb.save(rnewsonDoc);
    + rnewsonDoc = open_as(usersDb, rnewsonDoc._id, "jan");
    + T(!rnewsonDoc.password_scheme);
    + T(!rnewsonDoc.derived_key);
    + T(!rnewsonDoc.iterations);
    +
    + // check that we don't upgrade when the password is wrong
    + TEquals("unauthorized", CouchDB.login("rnewson", "wrong_password").error);
    + rnewsonDoc = open_as(usersDb, rnewsonDoc._id, "jan");
    + TEquals(salt, rnewsonDoc.salt);
    + TEquals(password_sha, rnewsonDoc.password_sha);
    + T(!rnewsonDoc.password_scheme);
    + T(!rnewsonDoc.derived_key);
    + T(!rnewsonDoc.iterations);
    +
    + TEquals(true, CouchDB.login("rnewson", "plaintext_password").ok);
    + rnewsonDoc = usersDb.open(rnewsonDoc._id);
    + TEquals("pbkdf2", rnewsonDoc.password_scheme);
    + T(rnewsonDoc.salt != salt);
    + T(!rnewsonDoc.password_sha);
    + T(rnewsonDoc.derived_key);
    + T(rnewsonDoc.iterations);
    +
    + salt = rnewsonDoc.salt,
    + derived_key = rnewsonDoc.derived_key,
    + iterations = rnewsonDoc.iterations;
    +
    + // check that authentication is still working
    + // and everything is staying the same now
    + CouchDB.logout();
    + TEquals(true, CouchDB.login("rnewson", "plaintext_password").ok);
    + rnewsonDoc = usersDb.open(rnewsonDoc._id);
    + TEquals("pbkdf2", rnewsonDoc.password_scheme);
    + TEquals(salt, rnewsonDoc.salt);
    + T(!rnewsonDoc.password_sha);
    + TEquals(derived_key, rnewsonDoc.derived_key);
    + TEquals(iterations, rnewsonDoc.iterations);
    +
    + CouchDB.logout();
    +
    + // user should not be able to read another user's user document
    + var fdmananaDoc = {
    + _id: "org.couchdb.user:fdmanana",
    + type: "user",
    + name: "fdmanana",
    + password: "foobar",
    + roles: []
    + };
    +
    + usersDb.save(fdmananaDoc);
    +
    + var fdmananaDocAsReadByjchris =
    + open_as(usersDb, "org.couchdb.user:fdmanana", "jchris1");
    + TEquals(null, fdmananaDocAsReadByjchris,
    + "should not_found opening another user's user doc");
    +
    +
    + // save a db admin
    + var benoitcDoc = {
    + _id: "org.couchdb.user:benoitc",
    + type: "user",
    + name: "benoitc",
    + password: "test",
    + roles: ["user_admin"]
    + };
    + save_as(usersDb, benoitcDoc, "jan");
    +
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + T(usersDb.setSecObj({
    + "admins" : {
    + roles : [],
    + names : ["benoitc"]
    + }
    + }).ok);
    + CouchDB.logout();
    +
    + // user should not be able to read from any view
    + var ddoc = {
    + _id: "_design/user_db_auth",
    + views: {
    + test: {
    + map: "function(doc) { emit(doc._id, null); }"
    + }
    + }
    + };
    +
    + save_as(usersDb, ddoc, "jan");
    +
    + try {
    + usersDb.view("user_db_auth/test");
    + T(false, "user had access to view in admin db");
    + } catch(e) {
    + TEquals("forbidden", e.error,
    + "non-admins should not be able to read a view");
    + }
    +
    + // admin should be able to read from any view
    + var result = view_as(usersDb, "user_db_auth/test", "jan");
    + TEquals(4, result.total_rows, "should allow access and list four users to admin");
    +
    + // db admin should be able to read from any view
    + var result = view_as(usersDb, "user_db_auth/test", "benoitc");
    + TEquals(4, result.total_rows, "should allow access and list four users to db admin");
    +
    +
    + // non-admins can't read design docs
    + try {
    + open_as(usersDb, "_design/user_db_auth", "jchris1");
    + T(false, "non-admin read design doc, should not happen");
    + } catch(e) {
    + TEquals("forbidden", e.error, "non-admins can't read design docs");
    + }
    +
    + // admin should be able to read and edit any user doc
    + fdmananaDoc.password = "mobile";
    + var result = save_as(usersDb, fdmananaDoc, "jan");
    + TEquals(true, result.ok, "admin should be able to update any user doc");
    +
    + // admin should be able to read and edit any user doc
    + fdmananaDoc.password = "mobile1";
    + var result = save_as(usersDb, fdmananaDoc, "benoitc");
    + TEquals(true, result.ok, "db admin by role should be able to update any user doc");
    +
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + T(usersDb.setSecObj({
    + "admins" : {
    + roles : ["user_admin"],
    + names : []
    + }
    + }).ok);
    + CouchDB.logout();
    +
    + // db admin should be able to read and edit any user doc
    + fdmananaDoc.password = "mobile2";
    + var result = save_as(usersDb, fdmananaDoc, "benoitc");
    + TEquals(true, result.ok, "db admin should be able to update any user doc");
    +
    + // ensure creation of old-style docs still works
    + var robertDoc = CouchDB.prepareUserDoc({ name: "robert" }, "anchovy");
    + var result = usersDb.save(robertDoc);
    + TEquals(true, result.ok, "old-style user docs should still be accepted");
    +
    + // log in one last time so run_on_modified_server can clean up the admin account
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + });
    +
    + run_on_modified_server([
    + {
    + section: "couch_httpd_auth",
    + key: "iterations",
    + value: "1"
    + },
    + {
    + section: "couch_httpd_auth",
    + key: "public_fields",
    + value: "name,type"
    + },
    + {
    + section: "couch_httpd_auth",
    + key: "users_db_public",
    + value: "true"
    + },
    + {
    + section: "admins",
    + key: "jan",
    + value: "apple"
    + }
    + ], function() {
    + var res = usersDb.open("org.couchdb.user:jchris");
    + TEquals("jchris", res.name);
    + TEquals("user", res.type);
    + TEquals(undefined, res.roles);
    + TEquals(undefined, res.salt);
    + TEquals(undefined, res.password_scheme);
    + TEquals(undefined, res.derived_key);
    +
    + TEquals(true, CouchDB.login("jchris", "couch").ok);
    +
    + var all = usersDb.allDocs({ include_docs: true });
    + T(all.rows);
    + if (all.rows) {
    + T(all.rows.every(function(row) {
    + if (row.doc) {
    + return Object.keys(row.doc).every(function(key) {
    + return key === 'name' || key === 'type';
    + });
    + } else {
    + if(row.id[0] == "_") {
    + // ignore design docs
    + return true
    + } else {
    + return false;
    + }
    + }
    + }));
    + }
    + // log in one last time so run_on_modified_server can clean up the admin account
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + });
    +
    + run_on_modified_server([
    + {
    + section: "couch_httpd_auth",
    + key: "iterations",
    + value: "1"
    + },
    + {
    + section: "couch_httpd_auth",
    + key: "public_fields",
    + value: "name"
    + },
    + {
    + section: "couch_httpd_auth",
    + key: "users_db_public",
    + value: "false"
    + },
    + {
    + section: "admins",
    + key: "jan",
    + value: "apple"
    + }
    + ], function() {
    + TEquals(true, CouchDB.login("jchris", "couch").ok);
    +
    + try {
    + var all = usersDb.allDocs({ include_docs: true });
    + T(false); // should never hit
    + } catch(e) {
    + TEquals("forbidden", e.error, "should throw");
    + }
    +
    + // COUCHDB-1888 make sure admins always get all fields
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + var all_admin = usersDb.allDocs({ include_docs: "true" });
    + TEquals("user", all_admin.rows[2].doc.type,
    + "should return type");
    +
    +
    + // log in one last time so run_on_modified_server can clean up the admin account
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + });
    + };
    +
    + usersDb.deleteDb();
    + run_on_modified_server(
    + [{section: "couch_httpd_auth",
    + key: "iterations", value: "1"},
    + {section: "couch_httpd_auth",
    + key: "authentication_db", value: usersDb.name}],
    + testFun
    + );
    + usersDb.deleteDb(); // cleanup
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/utf8.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/utf8.js b/test/javascript/tests/utf8.js
    new file mode 100644
    index 0000000..04f6313
    --- /dev/null
    +++ b/test/javascript/tests/utf8.js
    @@ -0,0 +1,42 @@
    +// 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.
    +
    +couchTests.utf8 = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var texts = [];
    +
    + texts[0] = "1. Ascii: hello"
    + texts[1] = "2. Russian: �а берегу пу�тынных волн"
    + texts[2] = "3. Math: ∮ E⋅da = Q, n → ∞, ∑ f(i) = � g(i),"
    + texts[3] = "4. Geek: STARGΛ̊TE SG-1"
    + texts[4] = "5. Braille: ⡌�⠧⠑ ⠼�⠒ �⠜⠇⠑⠹⠰⠎ ⡣⠕⠌"
    + texts[5] = "6. null \u0000 byte"
    +
    + // check that we can save a reload with full fidelity
    + for (var i=0; i<texts.length; i++) {
    + T(db.save({_id:i.toString(), text:texts[i]}).ok);
    + }
    +
    + for (var i=0; i<texts.length; i++) {
    + T(db.open(i.toString()).text == texts[i]);
    + }
    +
    + // check that views and key collation don't blow up
    + var rows = db.query(function(doc) { emit(null, doc.text) }).rows;
    + for (var i=0; i<texts.length; i++) {
    + T(rows[i].value == texts[i]);
    + }
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/uuids.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/uuids.js b/test/javascript/tests/uuids.js
    new file mode 100644
    index 0000000..d304c4e
    --- /dev/null
    +++ b/test/javascript/tests/uuids.js
    @@ -0,0 +1,149 @@
    +// 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.
    +
    +couchTests.uuids = function(debug) {
    + var etags = [];
    + var testHashBustingHeaders = function(xhr) {
    + T(xhr.getResponseHeader("Cache-Control").match(/no-cache/));
    + T(xhr.getResponseHeader("Pragma") == "no-cache");
    +
    + var newetag = xhr.getResponseHeader("ETag");
    + T(etags.indexOf(newetag) < 0);
    + etags[etags.length] = newetag;
    +
    + // Removing the time based tests as they break easily when
    + // running CouchDB on a remote server in regards to the browser
    + // running the Futon test suite.
    + //
    + //var currentTime = new Date();
    + //var expiresHeader = Date.parse(xhr.getResponseHeader("Expires"));
    + //var dateHeader = Date.parse(xhr.getResponseHeader("Date"));
    +
    + //T(expiresHeader < currentTime);
    + //T(currentTime - dateHeader < 3000);
    + };
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // a single UUID without an explicit count
    + var xhr = CouchDB.request("GET", "/_uuids");
    + T(xhr.status == 200);
    + var result = JSON.parse(xhr.responseText);
    + T(result.uuids.length == 1);
    + var first = result.uuids[0];
    + testHashBustingHeaders(xhr);
    +
    + // a single UUID with an explicit count
    + xhr = CouchDB.request("GET", "/_uuids?count=1");
    + T(xhr.status == 200);
    + result = JSON.parse(xhr.responseText);
    + T(result.uuids.length == 1);
    + var second = result.uuids[0];
    + T(first != second);
    +
    + // no collisions with 1,000 UUIDs
    + xhr = CouchDB.request("GET", "/_uuids?count=1000");
    + T(xhr.status == 200);
    + result = JSON.parse(xhr.responseText);
    + T( result.uuids.length == 1000 );
    + var seen = {};
    + for(var i in result.uuids) {
    + var id = result.uuids[i];
    + T(seen[id] === undefined);
    + seen[id] = 1;
    + }
    +
    + // ensure we return a 405 on POST
    + xhr = CouchDB.request("POST", "/_uuids?count=1000");
    + T(xhr.status == 405);
    +
    + // Test sequential uuids
    + var seq_testfun = function() {
    + xhr = CouchDB.request("GET", "/_uuids?count=1000");
    + T(xhr.status == 200);
    + result = JSON.parse(xhr.responseText);
    + for(var i = 1; i < result.uuids.length; i++) {
    + T(result.uuids[i].length == 32);
    + T(result.uuids[i-1] < result.uuids[i], "Sequential uuids are ordered.");
    + }
    + };
    +
    + // test max_uuid_count
    + var xhr = CouchDB.request("GET", "/_uuids?count=1001");
    + TEquals(403, xhr.status, "should error when count > max_count");
    +
    + run_on_modified_server([{
    + "section": "uuids",
    + "key": "algorithm",
    + "value": "sequential",
    + }],
    + seq_testfun
    + );
    +
    + // Test utc_random uuids
    + var utc_testfun = function() {
    + xhr = CouchDB.request("GET", "/_uuids?count=1000");
    + T(xhr.status == 200);
    + result = JSON.parse(xhr.responseText);
    + T(result.uuids[1].length == 32);
    +
    + // no collisions
    + var seen = {};
    + for(var i in result.uuids) {
    + var id = result.uuids[i];
    + T(seen[id] === undefined);
    + seen[id] = 1;
    + }
    +
    + // roughly ordered
    + var u1 = result.uuids[1].substr(0, 13);
    + var u2 = result.uuids[result.uuids.length-1].substr(0, 13);
    + T(u1 < u2, "UTC uuids are only roughly ordered, so this assertion may fail occasionally. Don't sweat it.");
    + };
    +
    + run_on_modified_server([{
    + "section": "uuids",
    + "key": "algorithm",
    + "value": "utc_random"
    + }],
    + utc_testfun
    + );
    +
    + // Test utc_id uuids
    + var utc_id_suffix = "frog";
    + var suffix_testfun = function() {
    + xhr = CouchDB.request("GET", "/_uuids?count=10");
    + T(xhr.status == 200);
    + result = JSON.parse(xhr.responseText);
    + for(var i = 1; i < result.uuids.length; i++) {
    + T(result.uuids[i].length == 14 + utc_id_suffix.length);
    + T(result.uuids[i].substring(14) == utc_id_suffix);
    + T(result.uuids[i-1] < result.uuids[i], "utc_id_suffix uuids are ordered.");
    + }
    + };
    +
    + run_on_modified_server([{
    + "section": "uuids",
    + "key": "algorithm",
    + "value": "utc_id"
    + }, {
    + "section": "uuids",
    + "key": "utc_id_suffix",
    + "value": utc_id_suffix
    + }],
    + suffix_testfun
    + );
    +
    + };

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_collation.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_collation.js b/test/javascript/tests/view_collation.js
    new file mode 100644
    index 0000000..b01a5c5
    --- /dev/null
    +++ b/test/javascript/tests/view_collation.js
    @@ -0,0 +1,116 @@
    +// 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.
    +
    +couchTests.view_collation = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // NOTE, the values are already in their correct sort order. Consider this
    + // a specification of collation of json types.
    +
    + var values = [];
    +
    + // special values sort before all other types
    + values.push(null);
    + values.push(false);
    + values.push(true);
    +
    + // then numbers
    + values.push(1);
    + values.push(2);
    + values.push(3.0);
    + values.push(4);
    +
    + // then text, case sensitive
    + values.push("a");
    + values.push("A");
    + values.push("aa");
    + values.push("b");
    + values.push("B");
    + values.push("ba");
    + values.push("bb");
    +
    + // then arrays. compared element by element until different.
    + // Longer arrays sort after their prefixes
    + values.push(["a"]);
    + values.push(["b"]);
    + values.push(["b","c"]);
    + values.push(["b","c", "a"]);
    + values.push(["b","d"]);
    + values.push(["b","d", "e"]);
    +
    + // then object, compares each key value in the list until different.
    + // larger objects sort after their subset objects.
    + values.push({a:1});
    + values.push({a:2});
    + values.push({b:1});
    + values.push({b:2});
    + values.push({b:2, a:1}); // Member order does matter for collation.
    + // CouchDB preserves member order
    + // but doesn't require that clients will.
    + // (this test might fail if used with a js engine
    + // that doesn't preserve order)
    + values.push({b:2, c:2});
    +
    + for (var i=0; i<values.length; i++) {
    + db.save({_id:(i).toString(), foo:values[i]});
    + }
    +
    + var queryFun = function(doc) { emit(doc.foo, null); };
    + var rows = db.query(queryFun).rows;
    + for (i=0; i<values.length; i++) {
    + T(equals(rows[i].key, values[i]));
    + }
    +
    + // everything has collated correctly. Now to check the descending output
    + rows = db.query(queryFun, null, {descending: true}).rows;
    + for (i=0; i<values.length; i++) {
    + T(equals(rows[i].key, values[values.length - 1 -i]));
    + }
    +
    + // now check the key query args
    + for (i=1; i<values.length; i++) {
    + var queryOptions = {key:values[i]};
    + rows = db.query(queryFun, null, queryOptions).rows;
    + T(rows.length == 1 && equals(rows[0].key, values[i]));
    + }
    +
    + // test inclusive_end=true (the default)
    + // the inclusive_end=true functionality is limited to endkey currently
    + // if you need inclusive_start=false for startkey, please do implement. ;)
    + var rows = db.query(queryFun, null, {endkey : "b", inclusive_end:true}).rows;
    + T(rows[rows.length-1].key == "b");
    + // descending=true
    + var rows = db.query(queryFun, null, {endkey : "b",
    + descending:true, inclusive_end:true}).rows;
    + T(rows[rows.length-1].key == "b");
    +
    + // test inclusive_end=false
    + var rows = db.query(queryFun, null, {endkey : "b", inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "aa");
    + // descending=true
    + var rows = db.query(queryFun, null, {endkey : "b",
    + descending:true, inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "B");
    +
    + var rows = db.query(queryFun, null, {
    + endkey : "b", endkey_docid: "10",
    + inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "aa");
    +
    + var rows = db.query(queryFun, null, {
    + endkey : "b", endkey_docid: "11",
    + inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "b");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_collation_raw.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_collation_raw.js b/test/javascript/tests/view_collation_raw.js
    new file mode 100644
    index 0000000..779f7eb
    --- /dev/null
    +++ b/test/javascript/tests/view_collation_raw.js
    @@ -0,0 +1,130 @@
    +// 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.
    +
    +couchTests.view_collation_raw = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // NOTE, the values are already in their correct sort order. Consider this
    + // a specification of collation of json types.
    +
    + var values = [];
    +
    + // numbers
    + values.push(1);
    + values.push(2);
    + values.push(3);
    + values.push(4);
    +
    + values.push(false);
    + values.push(null);
    + values.push(true);
    +
    + // then object, compares each key value in the list until different.
    + // larger objects sort after their subset objects.
    + values.push({a:1});
    + values.push({a:2});
    + values.push({b:1});
    + values.push({b:2});
    + values.push({b:2, a:1}); // Member order does matter for collation.
    + // CouchDB preserves member order
    + // but doesn't require that clients will.
    + // (this test might fail if used with a js engine
    + // that doesn't preserve order)
    + values.push({b:2, c:2});
    +
    + // then arrays. compared element by element until different.
    + // Longer arrays sort after their prefixes
    + values.push(["a"]);
    + values.push(["b"]);
    + values.push(["b","c"]);
    + values.push(["b","c", "a"]);
    + values.push(["b","d"]);
    + values.push(["b","d", "e"]);
    +
    +
    + // then text, case sensitive
    + values.push("A");
    + values.push("B");
    + values.push("a");
    + values.push("aa");
    + values.push("b");
    + values.push("ba");
    + values.push("bb");
    +
    + for (var i=0; i<values.length; i++) {
    + db.save({_id:(i).toString(), foo:values[i]});
    + }
    +
    + var designDoc = {
    + _id:"_design/test", // turn off couch.js id escaping?
    + language: "javascript",
    + views: {
    + test: {map: "function(doc) { emit(doc.foo, null); }",
    + options: {collation:"raw"}}
    + }
    + }
    + T(db.save(designDoc).ok);
    +
    + // Confirm that everything collates correctly.
    + var rows = db.view("test/test").rows;
    + for (i=0; i<values.length; i++) {
    + T(equals(rows[i].key, values[i]));
    + }
    +
    + // Confirm that couch allows raw semantics in key ranges.
    + rows = db.view("test/test", {startkey:"Z", endkey:"a"}).rows;
    + TEquals(1, rows.length);
    + TEquals("a", rows[0].key);
    +
    + // Check the descending output.
    + rows = db.view("test/test", {descending: true}).rows;
    + for (i=0; i<values.length; i++) {
    + T(equals(rows[i].key, values[values.length - 1 -i]));
    + }
    +
    + // now check the key query args
    + for (i=1; i<values.length; i++) {
    + rows = db.view("test/test", {key:values[i]}).rows;
    + T(rows.length == 1 && equals(rows[0].key, values[i]));
    + }
    +
    + // test inclusive_end=true (the default)
    + // the inclusive_end=true functionality is limited to endkey currently
    + // if you need inclusive_start=false for startkey, please do implement. ;)
    + var rows = db.view("test/test", {endkey : "b", inclusive_end:true}).rows;
    + T(rows[rows.length-1].key == "b");
    + // descending=true
    + var rows = db.view("test/test", {endkey : "b",
    + descending:true, inclusive_end:true}).rows;
    + T(rows[rows.length-1].key == "b");
    +
    + // test inclusive_end=false
    + var rows = db.view("test/test", {endkey : "b", inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "aa");
    + // descending=true
    + var rows = db.view("test/test", {endkey : "b",
    + descending:true, inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "ba");
    +
    + var rows = db.view("test/test", {
    + endkey : "b", endkey_docid: "10",
    + inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "aa");
    +
    + var rows = db.view("test/test", {
    + endkey : "b", endkey_docid: "11",
    + inclusive_end:false}).rows;
    + T(rows[rows.length-1].key == "aa");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_compaction.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_compaction.js b/test/javascript/tests/view_compaction.js
    new file mode 100644
    index 0000000..35d6276
    --- /dev/null
    +++ b/test/javascript/tests/view_compaction.js
    @@ -0,0 +1,110 @@
    +// 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.
    +
    +couchTests.view_compaction = function(debug) {
    +
    + if (debug) debugger;
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit": "true"});
    +
    + db.deleteDb();
    + db.createDb();
    +
    + var ddoc = {
    + _id: "_design/foo",
    + language: "javascript",
    + views: {
    + view1: {
    + map: "function(doc) { emit(doc._id, doc.value) }"
    + },
    + view2: {
    + map: "function(doc) { if (typeof(doc.integer) === 'number') {emit(doc._id, doc.integer);} }",
    + reduce: "function(keys, values, rereduce) { return sum(values); }"
    + }
    + }
    + };
    + T(db.save(ddoc).ok);
    +
    + var docs = makeDocs(0, 10000);
    + db.bulkSave(docs);
    +
    + var resp = db.view('foo/view1', {});
    + TEquals(10000, resp.rows.length);
    +
    + resp = db.view('foo/view2', {});
    + TEquals(1, resp.rows.length);
    +
    + resp = db.designInfo("_design/foo");
    + TEquals(10001, resp.view_index.update_seq);
    +
    +
    + // update docs
    + for (var i = 0; i < docs.length; i++) {
    + docs[i].integer = docs[i].integer + 1;
    + }
    + db.bulkSave(docs);
    +
    +
    + resp = db.view('foo/view1', {});
    + TEquals(10000, resp.rows.length);
    +
    + resp = db.view('foo/view2', {});
    + TEquals(1, resp.rows.length);
    +
    + resp = db.designInfo("_design/foo");
    + TEquals(20001, resp.view_index.update_seq);
    +
    +
    + // update docs again...
    + for (var i = 0; i < docs.length; i++) {
    + docs[i].integer = docs[i].integer + 2;
    + }
    + db.bulkSave(docs);
    +
    +
    + resp = db.view('foo/view1', {});
    + TEquals(10000, resp.rows.length);
    +
    + resp = db.view('foo/view2', {});
    + TEquals(1, resp.rows.length);
    +
    + resp = db.designInfo("_design/foo");
    + TEquals(30001, resp.view_index.update_seq);
    +
    + var disk_size_before_compact = resp.view_index.disk_size;
    + var data_size_before_compact = resp.view_index.data_size;
    +
    + TEquals("number", typeof data_size_before_compact, "data size is a number");
    + T(data_size_before_compact < disk_size_before_compact, "data size < file size");
    +
    + // compact view group
    + var xhr = CouchDB.request("POST", "/" + db.name + "/_design/foo/_compact");
    + T(JSON.parse(xhr.responseText).ok === true);
    +
    + resp = db.designInfo("_design/foo");
    + while (resp.view_index.compact_running === true) {
    + resp = db.designInfo("_design/foo");
    + }
    +
    +
    + resp = db.view('foo/view1', {});
    + TEquals(10000, resp.rows.length);
    +
    + resp = db.view('foo/view2', {});
    + TEquals(1, resp.rows.length);
    +
    + resp = db.designInfo("_design/foo");
    + TEquals(30001, resp.view_index.update_seq);
    + T(resp.view_index.disk_size < disk_size_before_compact);
    + TEquals("number", typeof resp.view_index.data_size, "data size is a number");
    + T(resp.view_index.data_size < resp.view_index.disk_size, "data size < file size");
    +};
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_conflicts.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_conflicts.js b/test/javascript/tests/view_conflicts.js
    new file mode 100644
    index 0000000..96f97d5
    --- /dev/null
    +++ b/test/javascript/tests/view_conflicts.js
    @@ -0,0 +1,49 @@
    +// 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.
    +
    +couchTests.view_conflicts = function(debug) {
    + var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
    + dbA.deleteDb();
    + dbA.createDb();
    + var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
    + dbB.deleteDb();
    + dbB.createDb();
    + if (debug) debugger;
    +
    + var docA = {_id: "foo", bar: 42};
    + T(dbA.save(docA).ok);
    + CouchDB.replicate(dbA.name, dbB.name);
    +
    + var docB = dbB.open("foo");
    + docB.bar = 43;
    + dbB.save(docB);
    + docA.bar = 41;
    + dbA.save(docA);
    + CouchDB.replicate(dbA.name, dbB.name);
    +
    + var doc = dbB.open("foo", {conflicts: true});
    + T(doc._conflicts.length == 1);
    + var conflictRev = doc._conflicts[0];
    + if (doc.bar == 41) { // A won
    + T(conflictRev == docB._rev);
    + } else { // B won
    + T(doc.bar == 43);
    + T(conflictRev == docA._rev);
    + }
    +
    + var results = dbB.query(function(doc) {
    + if (doc._conflicts) {
    + emit(doc._id, doc._conflicts);
    + }
    + });
    + T(results.rows[0].value[0] == conflictRev);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_errors.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_errors.js b/test/javascript/tests/view_errors.js
    new file mode 100644
    index 0000000..e8bd08e
    --- /dev/null
    +++ b/test/javascript/tests/view_errors.js
    @@ -0,0 +1,189 @@
    +// 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.
    +
    +couchTests.view_errors = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + run_on_modified_server(
    + [{section: "couchdb",
    + key: "os_process_timeout",
    + value: "500"}],
    + function() {
    + var doc = {integer: 1, string: "1", array: [1, 2, 3]};
    + T(db.save(doc).ok);
    +
    + // emitting a key value that is undefined should result in that row
    + // being included in the view results as null
    + var results = db.query(function(doc) {
    + emit(doc.undef, null);
    + });
    + T(results.total_rows == 1);
    + T(results.rows[0].key == null);
    +
    + // if a view function throws an exception, its results are not included in
    + // the view index, but the view does not itself raise an error
    + var results = db.query(function(doc) {
    + doc.undef(); // throws an error
    + });
    + T(results.total_rows == 0);
    +
    + // if a view function includes an undefined value in the emitted key or
    + // value, it is treated as null
    + var results = db.query(function(doc) {
    + emit([doc._id, doc.undef], null);
    + });
    + T(results.total_rows == 1);
    + T(results.rows[0].key[1] == null);
    +
    + // querying a view with invalid params should give a resonable error message
    + var xhr = CouchDB.request("POST", "/test_suite_db/_temp_view?startkey=foo", {
    + headers: {"Content-Type": "application/json"},
    + body: JSON.stringify({language: "javascript",
    + map : "function(doc){emit(doc.integer)}"
    + })
    + });
    + T(JSON.parse(xhr.responseText).error == "bad_request");
    +
    + // content type must be json
    + var xhr = CouchDB.request("POST", "/test_suite_db/_temp_view", {
    + headers: {"Content-Type": "application/x-www-form-urlencoded"},
    + body: JSON.stringify({language: "javascript",
    + map : "function(doc){}"
    + })
    + });
    + T(xhr.status == 415);
    +
    + var map = function (doc) {emit(doc.integer, doc.integer);};
    +
    + try {
    + db.query(map, null, {group: true});
    + T(0 == 1);
    + } catch(e) {
    + T(e.error == "query_parse_error");
    + }
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + "no_reduce": {map:"function(doc) {emit(doc._id, null);}"},
    + "with_reduce": {
    + map:"function (doc) {emit(doc.integer, doc.integer)};",
    + reduce:"function (keys, values) { return sum(values); };"}
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var designDoc2 = {
    + _id:"_design/testbig",
    + language: "javascript",
    + views: {
    + "reduce_too_big" : {
    + map:"function (doc) {emit(doc.integer, doc.integer)};",
    + reduce:"function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"}
    + }
    + };
    + T(db.save(designDoc2).ok);
    +
    + try {
    + db.view("test/no_reduce", {group: true});
    + T(0 == 1);
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "query_parse_error");
    + }
    +
    + try {
    + db.view("test/no_reduce", {group_level: 1});
    + T(0 == 1);
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "query_parse_error");
    + }
    +
    + try {
    + db.view("test/no_reduce", {reduce: true});
    + T(0 == 1);
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "query_parse_error");
    + }
    +
    + db.view("test/no_reduce", {reduce: false});
    + TEquals(200, db.last_req.status, "reduce=false for map views (without"
    + + " group or group_level) is allowed");
    +
    + try {
    + db.view("test/with_reduce", {group: true, reduce: false});
    + T(0 == 1);
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "query_parse_error");
    + }
    +
    + try {
    + db.view("test/with_reduce", {group_level: 1, reduce: false});
    + T(0 == 1);
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "query_parse_error");
    + }
    +
    + var designDoc3 = {
    + _id:"_design/infinite",
    + language: "javascript",
    + views: {
    + "infinite_loop" :{map:"function(doc) {while(true){emit(doc,doc);}};"}
    + }
    + };
    + T(db.save(designDoc3).ok);
    +
    + try {
    + db.view("infinite/infinite_loop");
    + T(0 == 1);
    + } catch(e) {
    + T(e.error == "os_process_error");
    + }
    +
    + // Check error responses for invalid multi-get bodies.
    + var path = "/test_suite_db/_design/test/_view/no_reduce";
    + var xhr = CouchDB.request("POST", path, {body: "[]"});
    + T(xhr.status == 400);
    + result = JSON.parse(xhr.responseText);
    + T(result.error == "bad_request");
    + T(result.reason == "Request body must be a JSON object");
    + var data = "{\"keys\": 1}";
    + xhr = CouchDB.request("POST", path, {body:data});
    + T(xhr.status == 400);
    + result = JSON.parse(xhr.responseText);
    + T(result.error == "bad_request");
    + T(result.reason == "`keys` member must be a array.");
    +
    + // if the reduce grows to fast, throw an overflow error
    + var path = "/test_suite_db/_design/testbig/_view/reduce_too_big";
    + xhr = CouchDB.request("GET", path);
    + T(xhr.status == 500);
    + result = JSON.parse(xhr.responseText);
    + T(result.error == "reduce_overflow_error");
    +
    + try {
    + db.query(function() {emit(null, null)}, null, {startkey: 2, endkey:1});
    + T(0 == 1);
    + } catch(e) {
    + T(e.error == "query_parse_error");
    + T(e.reason.match(/no rows can match/i));
    + }
    + });
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_include_docs.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_include_docs.js b/test/javascript/tests/view_include_docs.js
    new file mode 100644
    index 0000000..dab79b8
    --- /dev/null
    +++ b/test/javascript/tests/view_include_docs.js
    @@ -0,0 +1,192 @@
    +// 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.
    +
    +couchTests.view_include_docs = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var docs = makeDocs(0, 100);
    + db.bulkSave(docs);
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + all_docs: {
    + map: "function(doc) { emit(doc.integer, doc.string) }"
    + },
    + with_prev: {
    + map: "function(doc){if(doc.prev) emit(doc._id,{'_rev':doc.prev}); else emit(doc._id,{'_rev':doc._rev});}"
    + },
    + with_id: {
    + map: "function(doc) {if(doc.link_id) { var value = {'_id':doc.link_id}; if (doc.link_rev) {value._rev = doc.link_rev}; emit(doc._id, value);}};"
    + },
    + summate: {
    + map:"function (doc) { if (typeof doc.integer === 'number') {emit(doc.integer, doc.integer)};}",
    + reduce:"function (keys, values) { return sum(values); };"
    + }
    + }
    + }
    + T(db.save(designDoc).ok);
    +
    + var resp = db.view('test/all_docs', {include_docs: true, limit: 2});
    + T(resp.rows.length == 2);
    + T(resp.rows[0].id == "0");
    + T(resp.rows[0].doc._id == "0");
    + T(resp.rows[1].id == "1");
    + T(resp.rows[1].doc._id == "1");
    +
    + resp = db.view('test/all_docs', {include_docs: true}, [29, 74]);
    + T(resp.rows.length == 2);
    + T(resp.rows[0].doc._id == "29");
    + T(resp.rows[1].doc.integer == 74);
    +
    + resp = db.allDocs({limit: 2, skip: 1, include_docs: true});
    + T(resp.rows.length == 2);
    + T(resp.rows[0].doc.integer == 1);
    + T(resp.rows[1].doc.integer == 10);
    +
    + resp = db.allDocs({include_docs: true}, ['not_a_doc']);
    + T(resp.rows.length == 1);
    + T(!resp.rows[0].doc);
    +
    + resp = db.allDocs({include_docs: true}, ["1", "foo"]);
    + T(resp.rows.length == 2);
    + T(resp.rows[0].doc.integer == 1);
    + T(!resp.rows[1].doc);
    +
    + resp = db.allDocs({include_docs: true, limit: 0});
    + T(resp.rows.length == 0);
    +
    + // No reduce support
    + try {
    + resp = db.view('test/summate', {include_docs: true});
    + alert(JSON.stringify(resp));
    + T(0==1);
    + } catch (e) {
    + T(e.error == 'query_parse_error');
    + }
    +
    + // Reduce support when reduce=false
    + resp = db.view('test/summate', {reduce: false, include_docs: true});
    + T(resp.rows.length == 100);
    +
    + // Not an error with include_docs=false&reduce=true
    + resp = db.view('test/summate', {reduce: true, include_docs: false});
    + T(resp.rows.length == 1);
    + T(resp.rows[0].value == 4950);
    +
    + T(db.save({
    + "_id": "link-to-10",
    + "link_id" : "10"
    + }).ok);
    +
    + // you can link to another doc from a value.
    + resp = db.view("test/with_id", {key:"link-to-10"});
    + T(resp.rows[0].key == "link-to-10");
    + T(resp.rows[0].value["_id"] == "10");
    +
    + resp = db.view("test/with_id", {key:"link-to-10",include_docs: true});
    + T(resp.rows[0].key == "link-to-10");
    + T(resp.rows[0].value["_id"] == "10");
    + T(resp.rows[0].doc._id == "10");
    +
    + // Check emitted _rev controls things
    + resp = db.allDocs({include_docs: true}, ["0"]);
    + var before = resp.rows[0].doc;
    +
    + var after = db.open("0");
    + after.integer = 100;
    + after.prev = after._rev;
    + resp = db.save(after)
    + T(resp.ok);
    +
    + var after = db.open("0");
    + TEquals(resp.rev, after._rev, "fails with firebug running");
    + T(after._rev != after.prev, "passes");
    + TEquals(100, after.integer, "fails with firebug running");
    +
    + // should emit the previous revision
    + resp = db.view("test/with_prev", {include_docs: true}, ["0"]);
    + T(resp.rows[0].doc._id == "0");
    + T(resp.rows[0].doc._rev == before._rev);
    + T(!resp.rows[0].doc.prev);
    + T(resp.rows[0].doc.integer == 0);
    +
    + var xhr = CouchDB.request("POST", "/test_suite_db/_compact");
    + T(xhr.status == 202)
    + while (db.info().compact_running) {}
    +
    + resp = db.view("test/with_prev", {include_docs: true}, ["0", "23"]);
    + T(resp.rows.length == 2);
    + T(resp.rows[0].key == "0");
    + T(resp.rows[0].id == "0");
    + T(!resp.rows[0].doc);
    + T(resp.rows[0].doc == null);
    + T(resp.rows[1].doc.integer == 23);
    +
    + // COUCHDB-549 - include_docs=true with conflicts=true
    +
    + var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
    + var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
    +
    + dbA.deleteDb();
    + dbA.createDb();
    + dbB.deleteDb();
    + dbB.createDb();
    +
    + var ddoc = {
    + _id: "_design/mydesign",
    + language : "javascript",
    + views : {
    + myview : {
    + map: (function(doc) {
    + emit(doc.value, 1);
    + }).toString()
    + }
    + }
    + };
    + TEquals(true, dbA.save(ddoc).ok);
    +
    + var doc1a = {_id: "foo", value: 1, str: "1"};
    + TEquals(true, dbA.save(doc1a).ok);
    +
    + var doc1b = {_id: "foo", value: 1, str: "666"};
    + TEquals(true, dbB.save(doc1b).ok);
    +
    + var doc2 = {_id: "bar", value: 2, str: "2"};
    + TEquals(true, dbA.save(doc2).ok);
    +
    + TEquals(true, CouchDB.replicate(dbA.name, dbB.name).ok);
    +
    + doc1b = dbB.open("foo", {conflicts: true});
    + TEquals(true, doc1b._conflicts instanceof Array);
    + TEquals(1, doc1b._conflicts.length);
    + var conflictRev = doc1b._conflicts[0];
    +
    + doc2 = dbB.open("bar", {conflicts: true});
    + TEquals("undefined", typeof doc2._conflicts);
    +
    + resp = dbB.view("mydesign/myview", {include_docs: true, conflicts: true});
    +
    + TEquals(2, resp.rows.length);
    + TEquals(true, resp.rows[0].doc._conflicts instanceof Array);
    + TEquals(1, resp.rows[0].doc._conflicts.length);
    + TEquals(conflictRev, resp.rows[0].doc._conflicts[0]);
    + TEquals("undefined", typeof resp.rows[1].doc._conflicts);
    +
    + // cleanup
    + dbA.deleteDb();
    + dbB.deleteDb();
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_multi_key_all_docs.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_multi_key_all_docs.js b/test/javascript/tests/view_multi_key_all_docs.js
    new file mode 100644
    index 0000000..7c7f6f8
    --- /dev/null
    +++ b/test/javascript/tests/view_multi_key_all_docs.js
    @@ -0,0 +1,95 @@
    +// 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.
    +
    +couchTests.view_multi_key_all_docs = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var docs = makeDocs(0, 100);
    + db.bulkSave(docs);
    +
    + var keys = ["10","15","30","37","50"];
    + var rows = db.allDocs({},keys).rows;
    + T(rows.length == keys.length);
    + for(var i=0; i<rows.length; i++)
    + T(rows[i].id == keys[i]);
    +
    + // keys in GET parameters
    + rows = db.allDocs({keys:keys}, null).rows;
    + T(rows.length == keys.length);
    + for(var i=0; i<rows.length; i++)
    + T(rows[i].id == keys[i]);
    +
    + rows = db.allDocs({limit: 1}, keys).rows;
    + T(rows.length == 1);
    + T(rows[0].id == keys[0]);
    +
    + // keys in GET parameters
    + rows = db.allDocs({limit: 1, keys: keys}, null).rows;
    + T(rows.length == 1);
    + T(rows[0].id == keys[0]);
    +
    + rows = db.allDocs({skip: 2}, keys).rows;
    + T(rows.length == 3);
    + for(var i=0; i<rows.length; i++)
    + T(rows[i].id == keys[i+2]);
    +
    + // keys in GET parameters
    + rows = db.allDocs({skip: 2, keys: keys}, null).rows;
    + T(rows.length == 3);
    + for(var i=0; i<rows.length; i++)
    + T(rows[i].id == keys[i+2]);
    +
    + rows = db.allDocs({descending: "true"}, keys).rows;
    + T(rows.length == keys.length);
    + for(var i=0; i<rows.length; i++)
    + T(rows[i].id == keys[keys.length-i-1]);
    +
    + // keys in GET parameters
    + rows = db.allDocs({descending: "true", keys: keys}, null).rows;
    + T(rows.length == keys.length);
    + for(var i=0; i<rows.length; i++)
    + T(rows[i].id == keys[keys.length-i-1]);
    +
    + rows = db.allDocs({descending: "true", skip: 3, limit:1}, keys).rows;
    + T(rows.length == 1);
    + T(rows[0].id == keys[1]);
    +
    + // keys in GET parameters
    + rows = db.allDocs({descending: "true", skip: 3, limit:1, keys: keys}, null).rows;
    + T(rows.length == 1);
    + T(rows[0].id == keys[1]);
    +
    + // Check we get invalid rows when the key doesn't exist
    + rows = db.allDocs({}, [1, "i_dont_exist", "0"]).rows;
    + T(rows.length == 3);
    + T(rows[0].error == "not_found");
    + T(!rows[0].id);
    + T(rows[1].error == "not_found");
    + T(!rows[1].id);
    + T(rows[2].id == rows[2].key && rows[2].key == "0");
    +
    + // keys in GET parameters
    + rows = db.allDocs({keys: [1, "i_dont_exist", "0"]}, null).rows;
    + T(rows.length == 3);
    + T(rows[0].error == "not_found");
    + T(!rows[0].id);
    + T(rows[1].error == "not_found");
    + T(!rows[1].id);
    + T(rows[2].id == rows[2].key && rows[2].key == "0");
    +
    + // empty keys
    + rows = db.allDocs({keys: []}, null).rows;
    + T(rows.length == 0);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_multi_key_design.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_multi_key_design.js b/test/javascript/tests/view_multi_key_design.js
    new file mode 100644
    index 0000000..a84d07a
    --- /dev/null
    +++ b/test/javascript/tests/view_multi_key_design.js
    @@ -0,0 +1,220 @@
    +// 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.
    +
    +couchTests.view_multi_key_design = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var docs = makeDocs(0, 100);
    + db.bulkSave(docs);
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + all_docs: {
    + map: "function(doc) { emit(doc.integer, doc.string) }"
    + },
    + multi_emit: {
    + map: "function(doc) {for(var i = 0 ; i < 3 ; i++) { emit(i, doc.integer) ; } }"
    + },
    + summate: {
    + map:"function (doc) {emit(doc.integer, doc.integer)};",
    + reduce:"function (keys, values) { return sum(values); };"
    + }
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + // Test that missing keys work too
    + var keys = [101,30,15,37,50];
    + var reduce = db.view("test/summate",{group:true},keys).rows;
    + T(reduce.length == keys.length-1); // 101 is missing
    + for(var i=0; i<reduce.length; i++) {
    + T(keys.indexOf(reduce[i].key) != -1);
    + T(reduce[i].key == reduce[i].value);
    + }
    +
    + // First, the goods:
    + var keys = [10,15,30,37,50];
    + var rows = db.view("test/all_docs",{},keys).rows;
    + for(var i=0; i<rows.length; i++) {
    + T(keys.indexOf(rows[i].key) != -1);
    + T(rows[i].key == rows[i].value);
    + }
    +
    + // with GET keys
    + rows = db.view("test/all_docs",{keys:keys},null).rows;
    + for(var i=0;i<rows.length; i++) {
    + T(keys.indexOf(rows[i].key) != -1);
    + T(rows[i].key == rows[i].value);
    + }
    +
    + // with empty keys
    + rows = db.view("test/all_docs",{keys:[]},null).rows;
    + T(rows.length == 0);
    +
    + var reduce = db.view("test/summate",{group:true},keys).rows;
    + T(reduce.length == keys.length);
    + for(var i=0; i<reduce.length; i++) {
    + T(keys.indexOf(reduce[i].key) != -1);
    + T(reduce[i].key == reduce[i].value);
    + }
    +
    + // with GET keys
    + reduce = db.view("test/summate",{group:true,keys:keys},null).rows;
    + T(reduce.length == keys.length);
    + for(var i=0; i<reduce.length; i++) {
    + T(keys.indexOf(reduce[i].key) != -1);
    + T(reduce[i].key == reduce[i].value);
    + }
    +
    + // Test that invalid parameter combinations get rejected
    + var badargs = [{startkey:0}, {endkey:0}, {key: 0}, {group_level: 2}];
    + var getbadargs = [{startkey:0, keys:keys}, {endkey:0, keys:keys},
    + {key:0, keys:keys}, {group_level: 2, keys:keys}];
    + for(var i in badargs)
    + {
    + try {
    + db.view("test/all_docs",badargs[i],keys);
    + T(0==1);
    + } catch (e) {
    + T(e.error == "query_parse_error");
    + }
    +
    + try {
    + db.view("test/all_docs",getbadargs[i],null);
    + T(0==1);
    + } catch (e) {
    + T(e.error = "query_parse_error");
    + }
    + }
    +
    + try {
    + db.view("test/summate",{},keys);
    + T(0==1);
    + } catch (e) {
    + T(e.error == "query_parse_error");
    + }
    +
    + try {
    + db.view("test/summate",{keys:keys},null);
    + T(0==1);
    + } catch (e) {
    + T(e.error == "query_parse_error");
    + }
    +
    + // Test that a map & reduce containing func support keys when reduce=false
    + var resp = db.view("test/summate", {reduce: false}, keys);
    + T(resp.rows.length == 5);
    +
    + resp = db.view("test/summate", {reduce: false, keys: keys}, null);
    + T(resp.rows.length == 5);
    +
    + // Check that limiting by startkey_docid and endkey_docid get applied
    + // as expected.
    + var curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23}, [0, 2]).rows;
    + var exp_key = [ 0, 0, 0, 2, 2, 2] ;
    + var exp_val = [21, 22, 23, 21, 22, 23] ;
    + T(curr.length == 6);
    + for( var i = 0 ; i < 6 ; i++)
    + {
    + T(curr[i].key == exp_key[i]);
    + T(curr[i].value == exp_val[i]);
    + }
    +
    + curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23, keys: [0, 2]}, null).rows;
    + T(curr.length == 6);
    + for( var i = 0 ; i < 6 ; i++)
    + {
    + T(curr[i].key == exp_key[i]);
    + T(curr[i].value == exp_val[i]);
    + }
    +
    + // Check limit works
    + curr = db.view("test/all_docs", {limit: 1}, keys).rows;
    + T(curr.length == 1);
    + T(curr[0].key == 10);
    +
    + curr = db.view("test/all_docs", {limit: 1, keys: keys}, null).rows;
    + T(curr.length == 1);
    + T(curr[0].key == 10);
    +
    + // Check offset works
    + curr = db.view("test/multi_emit", {skip: 1}, [0]).rows;
    + T(curr.length == 99);
    + T(curr[0].value == 1);
    +
    + curr = db.view("test/multi_emit", {skip: 1, keys: [0]}, null).rows;
    + T(curr.length == 99);
    + T(curr[0].value == 1);
    +
    + // Check that dir works
    + curr = db.view("test/multi_emit", {descending: "true"}, [1]).rows;
    + T(curr.length == 100);
    + T(curr[0].value == 99);
    + T(curr[99].value == 0);
    +
    + curr = db.view("test/multi_emit", {descending: "true", keys: [1]}, null).rows;
    + T(curr.length == 100);
    + T(curr[0].value == 99);
    + T(curr[99].value == 0);
    +
    + // Check a couple combinations
    + curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2}, [2]).rows;
    + T(curr.length, 2);
    + T(curr[0].value == 96);
    + T(curr[1].value == 95);
    +
    + curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2, keys: [2]}, null).rows;
    + T(curr.length, 2);
    + T(curr[0].value == 96);
    + T(curr[1].value == 95);
    +
    + curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13"}, [0]).rows;
    + T(curr.length == 3);
    + T(curr[0].value == 15);
    + T(curr[1].value == 16);
    + T(curr[2].value == 17);
    +
    + curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13", keys: [0]}, null).rows;
    + T(curr.length == 3);
    + T(curr[0].value == 15);
    + T(curr[1].value == 16);
    + T(curr[2].value == 17);
    +
    + curr = db.view("test/multi_emit",
    + {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"}, [1]).rows;
    + T(curr.length == 2);
    + T(curr[0].value == 26);
    + T(curr[1].value == 27);
    +
    + curr = db.view("test/multi_emit",
    + {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27", keys: [1]}, null).rows;
    + T(curr.length == 2);
    + T(curr[0].value == 26);
    + T(curr[1].value == 27);
    +
    + curr = db.view("test/multi_emit",
    + {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true"}, [1]).rows;
    + T(curr.length == 2);
    + T(curr[0].value == 27);
    + T(curr[1].value == 26);
    +
    + curr = db.view("test/multi_emit",
    + {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true", keys: [1]}, null).rows;
    + T(curr.length == 2);
    + T(curr[0].value == 27);
    + T(curr[1].value == 26);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_multi_key_temp.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_multi_key_temp.js b/test/javascript/tests/view_multi_key_temp.js
    new file mode 100644
    index 0000000..3c05409
    --- /dev/null
    +++ b/test/javascript/tests/view_multi_key_temp.js
    @@ -0,0 +1,40 @@
    +// 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.
    +
    +couchTests.view_multi_key_temp = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var docs = makeDocs(0, 100);
    + db.bulkSave(docs);
    +
    + var queryFun = function(doc) { emit(doc.integer, doc.integer) };
    + var reduceFun = function (keys, values) { return sum(values); };
    +
    + var keys = [10,15,30,37,50];
    + var rows = db.query(queryFun, null, {}, keys).rows;
    + for(var i=0; i<rows.length; i++) {
    + T(keys.indexOf(rows[i].key) != -1);
    + T(rows[i].key == rows[i].value);
    + }
    +
    + var reduce = db.query(queryFun, reduceFun, {group:true}, keys).rows;
    + for(var i=0; i<reduce.length; i++) {
    + T(keys.indexOf(reduce[i].key) != -1);
    + T(reduce[i].key == reduce[i].value);
    + }
    +
    + rows = db.query(queryFun, null, {}, []).rows;
    + T(rows.length == 0);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_offsets.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_offsets.js b/test/javascript/tests/view_offsets.js
    new file mode 100644
    index 0000000..464a1ae
    --- /dev/null
    +++ b/test/javascript/tests/view_offsets.js
    @@ -0,0 +1,108 @@
    +// 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.
    +
    +couchTests.view_offsets = function(debug) {
    + if (debug) debugger;
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    +
    + var designDoc = {
    + _id : "_design/test",
    + views : {
    + offset : {
    + map : "function(doc) { emit([doc.letter, doc.number], doc); }",
    + }
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var docs = [
    + {_id : "a1", letter : "a", number : 1, foo: "bar"},
    + {_id : "a2", letter : "a", number : 2, foo: "bar"},
    + {_id : "a3", letter : "a", number : 3, foo: "bar"},
    + {_id : "b1", letter : "b", number : 1, foo: "bar"},
    + {_id : "b2", letter : "b", number : 2, foo: "bar"},
    + {_id : "b3", letter : "b", number : 3, foo: "bar"},
    + {_id : "b4", letter : "b", number : 4, foo: "bar"},
    + {_id : "b5", letter : "b", number : 5, foo: "bar"},
    + {_id : "c1", letter : "c", number : 1, foo: "bar"},
    + {_id : "c2", letter : "c", number : 2, foo: "bar"},
    + ];
    + db.bulkSave(docs);
    +
    + var check = function(startkey, offset) {
    + var opts = {startkey: startkey, descending: true};
    + T(db.view("test/offset", opts).offset == offset);
    + };
    +
    + [
    + [["c", 2], 0],
    + [["c", 1], 1],
    + [["b", 5], 2],
    + [["b", 4], 3],
    + [["b", 3], 4],
    + [["b", 2], 5],
    + [["b", 1], 6],
    + [["a", 3], 7],
    + [["a", 2], 8],
    + [["a", 1], 9]
    + ].forEach(function(row){ check(row[0], row[1]);});
    +
    + var runTest = function () {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    +
    + var designDoc = {
    + _id : "_design/test",
    + views : {
    + offset : {
    + map : "function(doc) { emit([doc.letter, doc.number], doc);}",
    + }
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var docs = [
    + {_id : "a1", letter : "a", number : 1, foo : "bar"},
    + {_id : "a2", letter : "a", number : 2, foo : "bar"},
    + {_id : "a3", letter : "a", number : 3, foo : "bar"},
    + {_id : "b1", letter : "b", number : 1, foo : "bar"},
    + {_id : "b2", letter : "b", number : 2, foo : "bar"},
    + {_id : "b3", letter : "b", number : 3, foo : "bar"},
    + {_id : "b4", letter : "b", number : 4, foo : "bar"},
    + {_id : "b5", letter : "b", number : 5, foo : "bar"},
    + {_id : "c1", letter : "c", number : 1, foo : "bar"},
    + {_id : "c2", letter : "c", number : 2, foo : "bar"}
    + ];
    + db.bulkSave(docs);
    +
    + var res1 = db.view("test/offset", {
    + startkey: ["b",4], startkey_docid: "b4", endkey: ["b"],
    + limit: 2, descending: true, skip: 1
    + })
    +
    + var res2 = db.view("test/offset", {startkey: ["c", 3]});
    + var res3 = db.view("test/offset", {
    + startkey: ["b", 6],
    + endkey: ["b", 7]
    + });
    +
    + return res1.offset == 4 && res2.offset == docs.length && res3.offset == 8;
    +
    + };
    +
    + for(var i = 0; i < 15; i++) T(runTest());
    +}
    +

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_pagination.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_pagination.js b/test/javascript/tests/view_pagination.js
    new file mode 100644
    index 0000000..ed3a7ee
    --- /dev/null
    +++ b/test/javascript/tests/view_pagination.js
    @@ -0,0 +1,147 @@
    +// 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.
    +
    +couchTests.view_pagination = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var docs = makeDocs(0, 100);
    + db.bulkSave(docs);
    +
    + var queryFun = function(doc) { emit(doc.integer, null); };
    + var i;
    +
    + // page through the view ascending
    + for (i = 0; i < docs.length; i += 10) {
    + var queryResults = db.query(queryFun, null, {
    + startkey: i,
    + startkey_docid: i,
    + limit: 10
    + });
    + T(queryResults.rows.length == 10);
    + T(queryResults.total_rows == docs.length);
    + T(queryResults.offset == i);
    + var j;
    + for (j = 0; j < 10;j++) {
    + T(queryResults.rows[j].key == i + j);
    + }
    +
    + // test aliases start_key and start_key_doc_id
    + queryResults = db.query(queryFun, null, {
    + start_key: i,
    + start_key_doc_id: i,
    + limit: 10
    + });
    + T(queryResults.rows.length == 10);
    + T(queryResults.total_rows == docs.length);
    + T(queryResults.offset == i);
    + for (j = 0; j < 10;j++) {
    + T(queryResults.rows[j].key == i + j);
    + }
    + }
    +
    + // page through the view descending
    + for (i = docs.length - 1; i >= 0; i -= 10) {
    + var queryResults = db.query(queryFun, null, {
    + startkey: i,
    + startkey_docid: i,
    + descending: true,
    + limit: 10
    + });
    + T(queryResults.rows.length == 10);
    + T(queryResults.total_rows == docs.length);
    + T(queryResults.offset == docs.length - i - 1);
    + var j;
    + for (j = 0; j < 10; j++) {
    + T(queryResults.rows[j].key == i - j);
    + }
    + }
    +
    + // ignore decending=false. CouchDB should just ignore that.
    + for (i = 0; i < docs.length; i += 10) {
    + var queryResults = db.query(queryFun, null, {
    + startkey: i,
    + startkey_docid: i,
    + descending: false,
    + limit: 10
    + });
    + T(queryResults.rows.length == 10);
    + T(queryResults.total_rows == docs.length);
    + T(queryResults.offset == i);
    + var j;
    + for (j = 0; j < 10;j++) {
    + T(queryResults.rows[j].key == i + j);
    + }
    + }
    +
    + function testEndkeyDocId(queryResults) {
    + T(queryResults.rows.length == 35);
    + T(queryResults.total_rows == docs.length);
    + T(queryResults.offset == 1);
    + T(queryResults.rows[0].id == "1");
    + T(queryResults.rows[1].id == "10");
    + T(queryResults.rows[2].id == "11");
    + T(queryResults.rows[3].id == "12");
    + T(queryResults.rows[4].id == "13");
    + T(queryResults.rows[5].id == "14");
    + T(queryResults.rows[6].id == "15");
    + T(queryResults.rows[7].id == "16");
    + T(queryResults.rows[8].id == "17");
    + T(queryResults.rows[9].id == "18");
    + T(queryResults.rows[10].id == "19");
    + T(queryResults.rows[11].id == "2");
    + T(queryResults.rows[12].id == "20");
    + T(queryResults.rows[13].id == "21");
    + T(queryResults.rows[14].id == "22");
    + T(queryResults.rows[15].id == "23");
    + T(queryResults.rows[16].id == "24");
    + T(queryResults.rows[17].id == "25");
    + T(queryResults.rows[18].id == "26");
    + T(queryResults.rows[19].id == "27");
    + T(queryResults.rows[20].id == "28");
    + T(queryResults.rows[21].id == "29");
    + T(queryResults.rows[22].id == "3");
    + T(queryResults.rows[23].id == "30");
    + T(queryResults.rows[24].id == "31");
    + T(queryResults.rows[25].id == "32");
    + T(queryResults.rows[26].id == "33");
    + T(queryResults.rows[27].id == "34");
    + T(queryResults.rows[28].id == "35");
    + T(queryResults.rows[29].id == "36");
    + T(queryResults.rows[30].id == "37");
    + T(queryResults.rows[31].id == "38");
    + T(queryResults.rows[32].id == "39");
    + T(queryResults.rows[33].id == "4");
    + T(queryResults.rows[34].id == "40");
    + }
    +
    + // test endkey_docid
    + var queryResults = db.query(function(doc) { emit(null, null); }, null, {
    + startkey: null,
    + startkey_docid: 1,
    + endkey: null,
    + endkey_docid: 40
    + });
    + testEndkeyDocId(queryResults);
    +
    + // test aliases end_key_doc_id and end_key
    + queryResults = db.query(function(doc) { emit(null, null); }, null, {
    + start_key: null,
    + start_key_doc_id: 1,
    + end_key: null,
    + end_key_doc_id: 40
    + });
    + testEndkeyDocId(queryResults);
    +
    + };

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_sandboxing.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_sandboxing.js b/test/javascript/tests/view_sandboxing.js
    new file mode 100644
    index 0000000..5c73c5a
    --- /dev/null
    +++ b/test/javascript/tests/view_sandboxing.js
    @@ -0,0 +1,140 @@
    +// 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.
    +
    +couchTests.view_sandboxing = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var doc = {integer: 1, string: "1", array: [1, 2, 3]};
    + T(db.save(doc).ok);
    +/*
    + // make sure that attempting to change the document throws an error
    + var results = db.query(function(doc) {
    + doc.integer = 2;
    + emit(null, doc);
    + });
    + T(results.total_rows == 0);
    +
    + var results = db.query(function(doc) {
    + doc.array[0] = 0;
    + emit(null, doc);
    + });
    + T(results.total_rows == 0);
    +*/
    + // make sure that a view cannot invoke interpreter internals such as the
    + // garbage collector
    + var results = db.query(function(doc) {
    + gc();
    + emit(null, doc);
    + });
    + T(results.total_rows == 0);
    +
    + // make sure that a view cannot access the map_funs array defined used by
    + // the view server
    + var results = db.query(function(doc) { map_funs.push(1); emit(null, doc); });
    + T(results.total_rows == 0);
    +
    + // make sure that a view cannot access the map_results array defined used by
    + // the view server
    + var results = db.query(function(doc) { map_results.push(1); emit(null, doc); });
    + T(results.total_rows == 0);
    +
    + // test for COUCHDB-925
    + // altering 'doc' variable in map function affects other map functions
    + var ddoc = {
    + _id: "_design/foobar",
    + language: "javascript",
    + views: {
    + view1: {
    + map:
    + (function(doc) {
    + if (doc.values) {
    + doc.values = [666];
    + }
    + if (doc.tags) {
    + doc.tags.push("qwerty");
    + }
    + if (doc.tokens) {
    + doc.tokens["c"] = 3;
    + }
    + }).toString()
    + },
    + view2: {
    + map:
    + (function(doc) {
    + if (doc.values) {
    + emit(doc._id, doc.values);
    + }
    + if (doc.tags) {
    + emit(doc._id, doc.tags);
    + }
    + if (doc.tokens) {
    + emit(doc._id, doc.tokens);
    + }
    + }).toString()
    + }
    + }
    + };
    + var doc1 = {
    + _id: "doc1",
    + values: [1, 2, 3]
    + };
    + var doc2 = {
    + _id: "doc2",
    + tags: ["foo", "bar"],
    + tokens: {a: 1, b: 2}
    + };
    +
    + db.deleteDb();
    + db.createDb();
    + T(db.save(ddoc).ok);
    + T(db.save(doc1).ok);
    + T(db.save(doc2).ok);
    +
    + var view1Results = db.view(
    + "foobar/view1", {bypass_cache: Math.round(Math.random() * 1000)});
    + var view2Results = db.view(
    + "foobar/view2", {bypass_cache: Math.round(Math.random() * 1000)});
    +
    + TEquals(0, view1Results.rows.length, "view1 has 0 rows");
    + TEquals(3, view2Results.rows.length, "view2 has 3 rows");
    +
    + TEquals(doc1._id, view2Results.rows[0].key);
    + TEquals(doc2._id, view2Results.rows[1].key);
    + TEquals(doc2._id, view2Results.rows[2].key);
    +
    + // https://bugzilla.mozilla.org/show_bug.cgi?id=449657
    + TEquals(3, view2Results.rows[0].value.length,
    + "Warning: installed SpiderMonkey version doesn't allow sealing of arrays");
    + if (view2Results.rows[0].value.length === 3) {
    + TEquals(1, view2Results.rows[0].value[0]);
    + TEquals(2, view2Results.rows[0].value[1]);
    + TEquals(3, view2Results.rows[0].value[2]);
    + }
    +
    + TEquals(1, view2Results.rows[1].value["a"]);
    + TEquals(2, view2Results.rows[1].value["b"]);
    + TEquals('undefined', typeof view2Results.rows[1].value["c"],
    + "doc2.tokens object was not sealed");
    +
    + TEquals(2, view2Results.rows[2].value.length,
    + "Warning: installed SpiderMonkey version doesn't allow sealing of arrays");
    + if (view2Results.rows[2].value.length === 2) {
    + TEquals("foo", view2Results.rows[2].value[0]);
    + TEquals("bar", view2Results.rows[2].value[1]);
    + }
    +
    + // cleanup
    + db.deleteDb();
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/view_update_seq.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/view_update_seq.js b/test/javascript/tests/view_update_seq.js
    new file mode 100644
    index 0000000..df92b11
    --- /dev/null
    +++ b/test/javascript/tests/view_update_seq.js
    @@ -0,0 +1,106 @@
    +// 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.
    +
    +couchTests.view_update_seq = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + T(db.info().update_seq == 0);
    +
    + var resp = db.allDocs({update_seq:true});
    +
    + T(resp.rows.length == 0);
    + T(resp.update_seq == 0);
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + all_docs: {
    + map: "function(doc) { emit(doc.integer, doc.string) }"
    + },
    + summate: {
    + map:"function (doc) { if (typeof doc.integer === 'number') { emit(doc.integer, doc.integer)}; }",
    + reduce:"function (keys, values) { return sum(values); };"
    + }
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + T(db.info().update_seq == 1);
    +
    + resp = db.allDocs({update_seq:true});
    +
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 1);
    +
    + var docs = makeDocs(0, 100);
    + db.bulkSave(docs);
    +
    + resp = db.allDocs({limit: 1});
    + T(resp.rows.length == 1);
    + T(!resp.update_seq, "all docs");
    +
    + resp = db.allDocs({limit: 1, update_seq:true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 101);
    +
    + resp = db.view('test/all_docs', {limit: 1, update_seq:true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 101);
    +
    + resp = db.view('test/all_docs', {limit: 1, update_seq:false});
    + T(resp.rows.length == 1);
    + T(!resp.update_seq, "view");
    +
    + resp = db.view('test/summate', {update_seq:true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 101);
    +
    + db.save({"id":"0", "integer": 1});
    + resp = db.view('test/all_docs', {limit: 1,stale: "ok", update_seq:true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 101);
    +
    + db.save({"id":"00", "integer": 2});
    + resp = db.view('test/all_docs',
    + {limit: 1, stale: "update_after", update_seq: true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 101);
    +
    + // wait 5 seconds for the next assertions to pass in very slow machines
    + var t0 = new Date(), t1;
    + do {
    + CouchDB.request("GET", "/");
    + t1 = new Date();
    + } while ((t1 - t0) < 5000);
    +
    + resp = db.view('test/all_docs', {limit: 1, stale: "ok", update_seq: true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 103);
    +
    + resp = db.view('test/all_docs', {limit: 1, update_seq:true});
    + T(resp.rows.length == 1);
    + T(resp.update_seq == 103);
    +
    + resp = db.view('test/all_docs',{update_seq:true},["0","1"]);
    + T(resp.update_seq == 103);
    +
    + resp = db.view('test/all_docs',{update_seq:true},["0","1"]);
    + T(resp.update_seq == 103);
    +
    + resp = db.view('test/summate',{group:true, update_seq:true},[0,1]);
    + TEquals(103, resp.update_seq);
    +
    +};
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/script/jquery.form.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/jquery.form.js b/share/www/script/jquery.form.js
    deleted file mode 100644
    index 14e1457..0000000
    --- a/share/www/script/jquery.form.js
    +++ /dev/null
    @@ -1,803 +0,0 @@
    -/*!
    - * jQuery Form Plugin
    - * version: 2.63 (29-JAN-2011)
    - * @requires jQuery v1.3.2 or later
    - *
    - * Examples and documentation at: http://malsup.com/jquery/form/
    - * Dual licensed under the MIT and GPL licenses:
    - * http://www.opensource.org/licenses/mit-license.php
    - * http://www.gnu.org/licenses/gpl.html
    - */
    -;(function($) {
    -
    -/*
    - Usage Note:
    - -----------
    - Do not use both ajaxSubmit and ajaxForm on the same form. These
    - functions are intended to be exclusive. Use ajaxSubmit if you want
    - to bind your own submit handler to the form. For example,
    -
    - $(document).ready(function() {
    - $('#myForm').bind('submit', function(e) {
    - e.preventDefault(); // <-- important
    - $(this).ajaxSubmit({
    - target: '#output'
    - });
    - });
    - });
    -
    - Use ajaxForm when you want the plugin to manage all the event binding
    - for you. For example,
    -
    - $(document).ready(function() {
    - $('#myForm').ajaxForm({
    - target: '#output'
    - });
    - });
    -
    - When using ajaxForm, the ajaxSubmit function will be invoked for you
    - at the appropriate time.
    -*/
    -
    -/**
    - * ajaxSubmit() provides a mechanism for immediately submitting
    - * an HTML form using AJAX.
    - */
    -$.fn.ajaxSubmit = function(options) {
    - // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
    - if (!this.length) {
    - log('ajaxSubmit: skipping submit process - no element selected');
    - return this;
    - }
    -
    - if (typeof options == 'function') {
    - options = { success: options };
    - }
    -
    - var action = this.attr('action');
    - var url = (typeof action === 'string') ? $.trim(action) : '';
    - if (url) {
    - // clean url (don't include hash vaue)
    - url = (url.match(/^([^#]+)/)||[])[1];
    - }
    - url = url || window.location.href || '';
    -
    - options = $.extend(true, {
    - url: url,
    - type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
    - iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
    - }, options);
    -
    - // hook for manipulating the form data before it is extracted;
    - // convenient for use with rich editors like tinyMCE or FCKEditor
    - var veto = {};
    - this.trigger('form-pre-serialize', [this, options, veto]);
    - if (veto.veto) {
    - log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
    - return this;
    - }
    -
    - // provide opportunity to alter form data before it is serialized
    - if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
    - log('ajaxSubmit: submit aborted via beforeSerialize callback');
    - return this;
    - }
    -
    - var n,v,a = this.formToArray(options.semantic);
    - if (options.data) {
    - options.extraData = options.data;
    - for (n in options.data) {
    - if(options.data[n] instanceof Array) {
    - for (var k in options.data[n]) {
    - a.push( { name: n, value: options.data[n][k] } );
    - }
    - }
    - else {
    - v = options.data[n];
    - v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
    - a.push( { name: n, value: v } );
    - }
    - }
    - }
    -
    - // give pre-submit callback an opportunity to abort the submit
    - if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
    - log('ajaxSubmit: submit aborted via beforeSubmit callback');
    - return this;
    - }
    -
    - // fire vetoable 'validate' event
    - this.trigger('form-submit-validate', [a, this, options, veto]);
    - if (veto.veto) {
    - log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
    - return this;
    - }
    -
    - var q = $.param(a);
    -
    - if (options.type.toUpperCase() == 'GET') {
    - options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
    - options.data = null; // data is null for 'get'
    - }
    - else {
    - options.data = q; // data is the query string for 'post'
    - }
    -
    - var $form = this, callbacks = [];
    - if (options.resetForm) {
    - callbacks.push(function() { $form.resetForm(); });
    - }
    - if (options.clearForm) {
    - callbacks.push(function() { $form.clearForm(); });
    - }
    -
    - // perform a load on the target only if dataType is not provided
    - if (!options.dataType && options.target) {
    - var oldSuccess = options.success || function(){};
    - callbacks.push(function(data) {
    - var fn = options.replaceTarget ? 'replaceWith' : 'html';
    - $(options.target)[fn](data).each(oldSuccess, arguments);
    - });
    - }
    - else if (options.success) {
    - callbacks.push(options.success);
    - }
    -
    - options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
    - var context = options.context || options; // jQuery 1.4+ supports scope context
    - for (var i=0, max=callbacks.length; i < max; i++) {
    - callbacks[i].apply(context, [data, status, xhr || $form, $form]);
    - }
    - };
    -
    - // are there files to upload?
    - var fileInputs = $('input:file', this).length > 0;
    - var mp = 'multipart/form-data';
    - var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
    -
    - // options.iframe allows user to force iframe mode
    - // 06-NOV-09: now defaulting to iframe mode if file input is detected
    - if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
    - // hack to fix Safari hang (thanks to Tim Molendijk for this)
    - // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
    - if (options.closeKeepAlive) {
    - $.get(options.closeKeepAlive, fileUpload);
    - }
    - else {
    - fileUpload();
    - }
    - }
    - else {
    - $.ajax(options);
    - }
    -
    - // fire 'notify' event
    - this.trigger('form-submit-notify', [this, options]);
    - return this;
    -
    -
    - // private function for handling file uploads (hat tip to YAHOO!)
    - function fileUpload() {
    - var form = $form[0];
    -
    - if ($(':input[name=submit],:input[id=submit]', form).length) {
    - // if there is an input with a name or id of 'submit' then we won't be
    - // able to invoke the submit fn on the form (at least not x-browser)
    - alert('Error: Form elements must not have name or id of "submit".');
    - return;
    - }
    -
    - var s = $.extend(true, {}, $.ajaxSettings, options);
    - s.context = s.context || s;
    - var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
    - var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />');
    - var io = $io[0];
    -
    - $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
    -
    - var xhr = { // mock object
    - aborted: 0,
    - responseText: null,
    - responseXML: null,
    - status: 0,
    - statusText: 'n/a',
    - getAllResponseHeaders: function() {},
    - getResponseHeader: function() {},
    - setRequestHeader: function() {},
    - abort: function() {
    - this.aborted = 1;
    - $io.attr('src', s.iframeSrc); // abort op in progress
    - }
    - };
    -
    - var g = s.global;
    - // trigger ajax global events so that activity/block indicators work like normal
    - if (g && ! $.active++) {
    - $.event.trigger("ajaxStart");
    - }
    - if (g) {
    - $.event.trigger("ajaxSend", [xhr, s]);
    - }
    -
    - if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
    - if (s.global) {
    - $.active--;
    - }
    - return;
    - }
    - if (xhr.aborted) {
    - return;
    - }
    -
    - var timedOut = 0;
    -
    - // add submitting element to data if we know it
    - var sub = form.clk;
    - if (sub) {
    - var n = sub.name;
    - if (n && !sub.disabled) {
    - s.extraData = s.extraData || {};
    - s.extraData[n] = sub.value;
    - if (sub.type == "image") {
    - s.extraData[n+'.x'] = form.clk_x;
    - s.extraData[n+'.y'] = form.clk_y;
    - }
    - }
    - }
    -
    - // take a breath so that pending repaints get some cpu time before the upload starts
    - function doSubmit() {
    - // make sure form attrs are set
    - var t = $form.attr('target'), a = $form.attr('action');
    -
    - // update form attrs in IE friendly way
    - form.setAttribute('target',id);
    - if (form.getAttribute('method') != 'POST') {
    - form.setAttribute('method', 'POST');
    - }
    - if (form.getAttribute('action') != s.url) {
    - form.setAttribute('action', s.url);
    - }
    -
    - // ie borks in some cases when setting encoding
    - if (! s.skipEncodingOverride) {
    - $form.attr({
    - encoding: 'multipart/form-data',
    - enctype: 'multipart/form-data'
    - });
    - }
    -
    - // support timout
    - if (s.timeout) {
    - setTimeout(function() { timedOut = true; cb(); }, s.timeout);
    - }
    -
    - // add "extra" data to form if provided in options
    - var extraInputs = [];
    - try {
    - if (s.extraData) {
    - for (var n in s.extraData) {
    - extraInputs.push(
    - $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
    - .appendTo(form)[0]);
    - }
    - }
    -
    - // add iframe to doc and submit the form
    - $io.appendTo('body');
    - io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
    - form.submit();
    - }
    - finally {
    - // reset attrs and remove "extra" input elements
    - form.setAttribute('action',a);
    - if(t) {
    - form.setAttribute('target', t);
    - } else {
    - $form.removeAttr('target');
    - }
    - $(extraInputs).remove();
    - }
    - }
    -
    - if (s.forceSync) {
    - doSubmit();
    - }
    - else {
    - setTimeout(doSubmit, 10); // this lets dom updates render
    - }
    -
    - var data, doc, domCheckCount = 50;
    -
    - function cb() {
    - doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
    - if (!doc || doc.location.href == s.iframeSrc) {
    - // response not received yet
    - return;
    - }
    - io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
    -
    - var ok = true;
    - try {
    - if (timedOut) {
    - throw 'timeout';
    - }
    -
    - var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
    - log('isXml='+isXml);
    - if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
    - if (--domCheckCount) {
    - // in some browsers (Opera) the iframe DOM is not always traversable when
    - // the onload callback fires, so we loop a bit to accommodate
    - log('requeing onLoad callback, DOM not available');
    - setTimeout(cb, 250);
    - return;
    - }
    - // let this fall through because server response could be an empty document
    - //log('Could not access iframe DOM after mutiple tries.');
    - //throw 'DOMException: not available';
    - }
    -
    - //log('response detected');
    - xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null;
    - xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
    - xhr.getResponseHeader = function(header){
    - var headers = {'content-type': s.dataType};
    - return headers[header];
    - };
    -
    - var scr = /(json|script)/.test(s.dataType);
    - if (scr || s.textarea) {
    - // see if user embedded response in textarea
    - var ta = doc.getElementsByTagName('textarea')[0];
    - if (ta) {
    - xhr.responseText = ta.value;
    - }
    - else if (scr) {
    - // account for browsers injecting pre around json response
    - var pre = doc.getElementsByTagName('pre')[0];
    - var b = doc.getElementsByTagName('body')[0];
    - if (pre) {
    - xhr.responseText = pre.textContent;
    - }
    - else if (b) {
    - xhr.responseText = b.innerHTML;
    - }
    - }
    - }
    - else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
    - xhr.responseXML = toXml(xhr.responseText);
    - }
    -
    - data = httpData(xhr, s.dataType, s);
    - }
    - catch(e){
    - log('error caught:',e);
    - ok = false;
    - xhr.error = e;
    - s.error.call(s.context, xhr, 'error', e);
    - g && $.event.trigger("ajaxError", [xhr, s, e]);
    - }
    -
    - if (xhr.aborted) {
    - log('upload aborted');
    - ok = false;
    - }
    -
    - // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
    - if (ok) {
    - s.success.call(s.context, data, 'success', xhr);
    - g && $.event.trigger("ajaxSuccess", [xhr, s]);
    - }
    -
    - g && $.event.trigger("ajaxComplete", [xhr, s]);
    -
    - if (g && ! --$.active) {
    - $.event.trigger("ajaxStop");
    - }
    -
    - s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
    -
    - // clean up
    - setTimeout(function() {
    - $io.removeData('form-plugin-onload');
    - $io.remove();
    - xhr.responseXML = null;
    - }, 100);
    - }
    -
    - var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
    - if (window.ActiveXObject) {
    - doc = new ActiveXObject('Microsoft.XMLDOM');
    - doc.async = 'false';
    - doc.loadXML(s);
    - }
    - else {
    - doc = (new DOMParser()).parseFromString(s, 'text/xml');
    - }
    - return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
    - };
    - var parseJSON = $.parseJSON || function(s) {
    - return window['eval']('(' + s + ')');
    - };
    -
    - var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
    - var ct = xhr.getResponseHeader('content-type') || '',
    - xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
    - data = xml ? xhr.responseXML : xhr.responseText;
    -
    - if (xml && data.documentElement.nodeName === 'parsererror') {
    - $.error && $.error('parsererror');
    - }
    - if (s && s.dataFilter) {
    - data = s.dataFilter(data, type);
    - }
    - if (typeof data === 'string') {
    - if (type === 'json' || !type && ct.indexOf('json') >= 0) {
    - data = parseJSON(data);
    - } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
    - $.globalEval(data);
    - }
    - }
    - return data;
    - };
    - }
    -};
    -
    -/**
    - * ajaxForm() provides a mechanism for fully automating form submission.
    - *
    - * The advantages of using this method instead of ajaxSubmit() are:
    - *
    - * 1: This method will include coordinates for <input type="image" /> elements (if the element
    - * is used to submit the form).
    - * 2. This method will include the submit element's name/value data (for the element that was
    - * used to submit the form).
    - * 3. This method binds the submit() method to the form for you.
    - *
    - * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
    - * passes the options argument along after properly binding events for submit elements and
    - * the form itself.
    - */
    -$.fn.ajaxForm = function(options) {
    - // in jQuery 1.3+ we can fix mistakes with the ready state
    - if (this.length === 0) {
    - var o = { s: this.selector, c: this.context };
    - if (!$.isReady && o.s) {
    - log('DOM not ready, queuing ajaxForm');
    - $(function() {
    - $(o.s,o.c).ajaxForm(options);
    - });
    - return this;
    - }
    - // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
    - log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
    - return this;
    - }
    -
    - return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
    - if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
    - e.preventDefault();
    - $(this).ajaxSubmit(options);
    - }
    - }).bind('click.form-plugin', function(e) {
    - var target = e.target;
    - var $el = $(target);
    - if (!($el.is(":submit,input:image"))) {
    - // is this a child element of the submit el? (ex: a span within a button)
    - var t = $el.closest(':submit');
    - if (t.length == 0) {
    - return;
    - }
    - target = t[0];
    - }
    - var form = this;
    - form.clk = target;
    - if (target.type == 'image') {
    - if (e.offsetX != undefined) {
    - form.clk_x = e.offsetX;
    - form.clk_y = e.offsetY;
    - } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
    - var offset = $el.offset();
    - form.clk_x = e.pageX - offset.left;
    - form.clk_y = e.pageY - offset.top;
    - } else {
    - form.clk_x = e.pageX - target.offsetLeft;
    - form.clk_y = e.pageY - target.offsetTop;
    - }
    - }
    - // clear form vars
    - setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
    - });
    -};
    -
    -// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
    -$.fn.ajaxFormUnbind = function() {
    - return this.unbind('submit.form-plugin click.form-plugin');
    -};
    -
    -/**
    - * formToArray() gathers form element data into an array of objects that can
    - * be passed to any of the following ajax functions: $.get, $.post, or load.
    - * Each object in the array has both a 'name' and 'value' property. An example of
    - * an array for a simple login form might be:
    - *
    - * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
    - *
    - * It is this array that is passed to pre-submit callback functions provided to the
    - * ajaxSubmit() and ajaxForm() methods.
    - */
    -$.fn.formToArray = function(semantic) {
    - var a = [];
    - if (this.length === 0) {
    - return a;
    - }
    -
    - var form = this[0];
    - var els = semantic ? form.getElementsByTagName('*') : form.elements;
    - if (!els) {
    - return a;
    - }
    -
    - var i,j,n,v,el,max,jmax;
    - for(i=0, max=els.length; i < max; i++) {
    - el = els[i];
    - n = el.name;
    - if (!n) {
    - continue;
    - }
    -
    - if (semantic && form.clk && el.type == "image") {
    - // handle image inputs on the fly when semantic == true
    - if(!el.disabled && form.clk == el) {
    - a.push({name: n, value: $(el).val()});
    - a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
    - }
    - continue;
    - }
    -
    - v = $.fieldValue(el, true);
    - if (v && v.constructor == Array) {
    - for(j=0, jmax=v.length; j < jmax; j++) {
    - a.push({name: n, value: v[j]});
    - }
    - }
    - else if (v !== null && typeof v != 'undefined') {
    - a.push({name: n, value: v});
    - }
    - }
    -
    - if (!semantic && form.clk) {
    - // input type=='image' are not found in elements array! handle it here
    - var $input = $(form.clk), input = $input[0];
    - n = input.name;
    - if (n && !input.disabled && input.type == 'image') {
    - a.push({name: n, value: $input.val()});
    - a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
    - }
    - }
    - return a;
    -};
    -
    -/**
    - * Serializes form data into a 'submittable' string. This method will return a string
    - * in the format: name1=value1&amp;name2=value2
    - */
    -$.fn.formSerialize = function(semantic) {
    - //hand off to jQuery.param for proper encoding
    - return $.param(this.formToArray(semantic));
    -};
    -
    -/**
    - * Serializes all field elements in the jQuery object into a query string.
    - * This method will return a string in the format: name1=value1&amp;name2=value2
    - */
    -$.fn.fieldSerialize = function(successful) {
    - var a = [];
    - this.each(function() {
    - var n = this.name;
    - if (!n) {
    - return;
    - }
    - var v = $.fieldValue(this, successful);
    - if (v && v.constructor == Array) {
    - for (var i=0,max=v.length; i < max; i++) {
    - a.push({name: n, value: v[i]});
    - }
    - }
    - else if (v !== null && typeof v != 'undefined') {
    - a.push({name: this.name, value: v});
    - }
    - });
    - //hand off to jQuery.param for proper encoding
    - return $.param(a);
    -};
    -
    -/**
    - * Returns the value(s) of the element in the matched set. For example, consider the following form:
    - *
    - * <form><fieldset>
    - * <input name="A" type="text" />
    - * <input name="A" type="text" />
    - * <input name="B" type="checkbox" value="B1" />
    - * <input name="B" type="checkbox" value="B2"/>
    - * <input name="C" type="radio" value="C1" />
    - * <input name="C" type="radio" value="C2" />
    - * </fieldset></form>
    - *
    - * var v = $(':text').fieldValue();
    - * // if no values are entered into the text inputs
    - * v == ['','']
    - * // if values entered into the text inputs are 'foo' and 'bar'
    - * v == ['foo','bar']
    - *
    - * var v = $(':checkbox').fieldValue();
    - * // if neither checkbox is checked
    - * v === undefined
    - * // if both checkboxes are checked
    - * v == ['B1', 'B2']
    - *
    - * var v = $(':radio').fieldValue();
    - * // if neither radio is checked
    - * v === undefined
    - * // if first radio is checked
    - * v == ['C1']
    - *
    - * The successful argument controls whether or not the field element must be 'successful'
    - * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
    - * The default value of the successful argument is true. If this value is false the value(s)
    - * for each element is returned.
    - *
    - * Note: This method *always* returns an array. If no valid value can be determined the
    - * array will be empty, otherwise it will contain one or more values.
    - */
    -$.fn.fieldValue = function(successful) {
    - for (var val=[], i=0, max=this.length; i < max; i++) {
    - var el = this[i];
    - var v = $.fieldValue(el, successful);
    - if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
    - continue;
    - }
    - v.constructor == Array ? $.merge(val, v) : val.push(v);
    - }
    - return val;
    -};
    -
    -/**
    - * Returns the value of the field element.
    - */
    -$.fieldValue = function(el, successful) {
    - var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
    - if (successful === undefined) {
    - successful = true;
    - }
    -
    - if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
    - (t == 'checkbox' || t == 'radio') && !el.checked ||
    - (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
    - tag == 'select' && el.selectedIndex == -1)) {
    - return null;
    - }
    -
    - if (tag == 'select') {
    - var index = el.selectedIndex;
    - if (index < 0) {
    - return null;
    - }
    - var a = [], ops = el.options;
    - var one = (t == 'select-one');
    - var max = (one ? index+1 : ops.length);
    - for(var i=(one ? index : 0); i < max; i++) {
    - var op = ops[i];
    - if (op.selected) {
    - var v = op.value;
    - if (!v) { // extra pain for IE...
    - v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
    - }
    - if (one) {
    - return v;
    - }
    - a.push(v);
    - }
    - }
    - return a;
    - }
    - return $(el).val();
    -};
    -
    -/**
    - * Clears the form data. Takes the following actions on the form's input fields:
    - * - input text fields will have their 'value' property set to the empty string
    - * - select elements will have their 'selectedIndex' property set to -1
    - * - checkbox and radio inputs will have their 'checked' property set to false
    - * - inputs of type submit, button, reset, and hidden will *not* be effected
    - * - button elements will *not* be effected
    - */
    -$.fn.clearForm = function() {
    - return this.each(function() {
    - $('input,select,textarea', this).clearFields();
    - });
    -};
    -
    -/**
    - * Clears the selected form elements.
    - */
    -$.fn.clearFields = $.fn.clearInputs = function() {
    - return this.each(function() {
    - var t = this.type, tag = this.tagName.toLowerCase();
    - if (t == 'text' || t == 'password' || tag == 'textarea') {
    - this.value = '';
    - }
    - else if (t == 'checkbox' || t == 'radio') {
    - this.checked = false;
    - }
    - else if (tag == 'select') {
    - this.selectedIndex = -1;
    - }
    - });
    -};
    -
    -/**
    - * Resets the form data. Causes all form elements to be reset to their original value.
    - */
    -$.fn.resetForm = function() {
    - return this.each(function() {
    - // guard against an input with the name of 'reset'
    - // note that IE reports the reset function as an 'object'
    - if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
    - this.reset();
    - }
    - });
    -};
    -
    -/**
    - * Enables or disables any matching elements.
    - */
    -$.fn.enable = function(b) {
    - if (b === undefined) {
    - b = true;
    - }
    - return this.each(function() {
    - this.disabled = !b;
    - });
    -};
    -
    -/**
    - * Checks/unchecks any matching checkboxes or radio buttons and
    - * selects/deselects and matching option elements.
    - */
    -$.fn.selected = function(select) {
    - if (select === undefined) {
    - select = true;
    - }
    - return this.each(function() {
    - var t = this.type;
    - if (t == 'checkbox' || t == 'radio') {
    - this.checked = select;
    - }
    - else if (this.tagName.toLowerCase() == 'option') {
    - var $sel = $(this).parent('select');
    - if (select && $sel[0] && $sel[0].type == 'select-one') {
    - // deselect all other options
    - $sel.find('option').selected(false);
    - }
    - this.selected = select;
    - }
    - });
    -};
    -
    -// helper fn for console logging
    -// set $.fn.ajaxSubmit.debug to true to enable debug logging
    -function log() {
    - if ($.fn.ajaxSubmit.debug) {
    - var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
    - if (window.console && window.console.log) {
    - window.console.log(msg);
    - }
    - else if (window.opera && window.opera.postError) {
    - window.opera.postError(msg);
    - }
    - }
    -};
    -
    -})(jQuery);
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/lorem_b64.txt
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/lorem_b64.txt b/share/www/script/test/lorem_b64.txt
    deleted file mode 100644
    index 8a21d79..0000000
    --- a/share/www/script/test/lorem_b64.txt
    +++ /dev/null
    @@ -1 +0,0 @@
    -TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUGhhc2VsbHVzIG51bmMgc2FwaWVuLCBwb3J0YSBpZCBwZWxsZW50ZXNxdWUgYXQsIGVsZW1lbnR1bSBldCBmZWxpcy4gQ3VyYWJpdHVyIGNvbmRpbWVudHVtIGFudGUgaW4gbWV0dXMgaWFjdWxpcyBxdWlzIGNvbmd1ZSBkaWFtIGNvbW1vZG8uIERvbmVjIGVsZWlmZW5kIGFudGUgc2VkIG51bGxhIGRhcGlidXMgY29udmFsbGlzLiBVdCBjdXJzdXMgYWxpcXVhbSBuZXF1ZSwgdmVsIHBvcnR0aXRvciB0ZWxsdXMgaW50ZXJkdW0gdXQuIFNlZCBwaGFyZXRyYSBsYWNpbmlhIGFkaXBpc2NpbmcuIEluIHRyaXN0aXF1ZSB0cmlzdGlxdWUgZmVsaXMgbm9uIHRpbmNpZHVudC4gTnVsbGEgYXVjdG9yIG1hdXJpcyBhIHZlbGl0IGN1cnN1cyB1bHRyaWNpZXMuIEluIGF0IGxpYmVybyBxdWlzIGp1c3RvIGNvbnNlY3RldHVyIGxhb3JlZXQuIE51bGxhbSBpZCB1bHRyaWNlcyBudW5jLiBEb25lYyBub24gdHVycGlzIG51bGxhLCBldSBsYWNpbmlhIGFudGUuIE51bmMgZXUgb3JjaSBldCB0dXJwaXMgcHJldGl1bSB2ZW5lbmF0aXMuIE5hbSBtb2xlc3RpZSwgbGFjdXMgYXQgZGlnbmlzc2ltIGVsZW1lbnR1bSwgYW50ZSBsaWJlcm8gY29uc2VjdGV0dXIgbGliZXJvLCB1dCBsYWNpbmlhIGxhY3VzIHVybmEgZXQgcHVydXMuIE51bGxhbSBsb3JlbSBpcHN1bSwgZGFwaWJ1cyB2ZWwgdWxsYW1jb3JwZXIgYSwgbWFsZXN1YWRhIGEgb
      WV0dXMuIFNlZCBwb3J0YSBhZGlwaXNjaW5nIG1hZ25hLCBxdWlzIHB1bHZpbmFyIHB1cnVzIG1hdHRpcyBmcmluZ2lsbGEuIEludGVnZXIgcGVsbGVudGVzcXVlIHNhcGllbiBpbiBuZXF1ZSB0cmlzdGlxdWUgYWMgaWFjdWxpcyBsaWJlcm8gdWx0cmljaWVzLiBVdCBlZ2V0IHBoYXJldHJhIHB1cnVzLgoKTnVsbGEgaW4gY29udmFsbGlzIHRlbGx1cy4gUHJvaW4gdGluY2lkdW50IHN1c2NpcGl0IHZ1bHB1dGF0ZS4gU3VzcGVuZGlzc2UgcG90ZW50aS4gTnVsbGFtIHRyaXN0aXF1ZSBqdXN0byBtaSwgYSB0cmlzdGlxdWUgbGlndWxhLiBEdWlzIGNvbnZhbGxpcyBhbGlxdWFtIGlhY3VsaXMuIE51bGxhIGRpY3R1bSBmcmluZ2lsbGEgY29uZ3VlLiBTdXNwZW5kaXNzZSBhYyBsZW8gbGVjdHVzLCBhYyBhbGlxdWFtIGp1c3RvLiBVdCBwb3J0dGl0b3IgY29tbW9kbyBtaSBzZWQgbHVjdHVzLiBOdWxsYSBhdCBlbmltIGxvcmVtLiBOdW5jIGV1IGp1c3RvIHNhcGllbiwgYSBibGFuZGl0IG9kaW8uIEN1cmFiaXR1ciBmYXVjaWJ1cyBzb2xsaWNpdHVkaW4gZG9sb3IsIGlkIGxhY2luaWEgc2VtIGF1Y3RvciBpbi4gRG9uZWMgdmFyaXVzIG51bmMgYXQgbGVjdHVzIHNhZ2l0dGlzIG5lYyBsdWN0dXMgYXJjdSBwaGFyZXRyYS4gTnVuYyBzZWQgbWV0dXMganVzdG8uIENyYXMgdmVsIG1hdXJpcyBkaWFtLiBVdCBmZXVnaWF0IGZlbGlzIGVnZXQgbmVxdWUgcGhhcmV0cmEgdmVzdGlidWx1bSBjb25zZWN0ZXR1ciBtYXNzYSBmYW
      NpbGlzaXMuIFF1aXNxdWUgY29uc2VjdGV0dXIgbHVjdHVzIG5pc2kgcXVpcyB0aW5jaWR1bnQuIFZpdmFtdXMgY3Vyc3VzIGN1cnN1cyBxdWFtIG5vbiBibGFuZGl0LiBQZWxsZW50ZXNxdWUgZXQgdmVsaXQgbGFjdXMuIFBlbGxlbnRlc3F1ZSBoYWJpdGFudCBtb3JiaSB0cmlzdGlxdWUgc2VuZWN0dXMgZXQgbmV0dXMgZXQgbWFsZXN1YWRhIGZhbWVzIGFjIHR1cnBpcyBlZ2VzdGFzLgoKSW4gZXQgZG9sb3Igdml0YWUgb3JjaSBhZGlwaXNjaW5nIGNvbmd1ZS4gQWxpcXVhbSBncmF2aWRhIG5pYmggYXQgbmlzbCBncmF2aWRhIG1vbGVzdGllLiBDdXJhYml0dXIgYSBiaWJlbmR1bSBzYXBpZW4uIEFsaXF1YW0gdGluY2lkdW50LCBudWxsYSBuZWMgcHJldGl1bSBsb2JvcnRpcywgb2RpbyBhdWd1ZSB0aW5jaWR1bnQgYXJjdSwgYSBsb2JvcnRpcyBvZGlvIHNlbSB1dCBwdXJ1cy4gRG9uZWMgYWNjdW1zYW4gbWF0dGlzIG51bmMgdml0YWUgbGFjaW5pYS4gU3VzcGVuZGlzc2UgcG90ZW50aS4gSW50ZWdlciBjb21tb2RvIG5pc2wgcXVpcyBuaWJoIGludGVyZHVtIG5vbiBmcmluZ2lsbGEgZHVpIHNvZGFsZXMuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIEV0aWFtIHVsbGFtY29ycGVyLCBtaSBpZCBmZXVnaWF0IGJpYmVuZHVtLCBwdXJ1cyB
      uZXF1ZSBjdXJzdXMgbWF1cmlzLCBpZCBzb2RhbGVzIHF1YW0gbmlzaSBpZCB2ZWxpdC4gU2VkIGxlY3R1cyBsZW8sIHRpbmNpZHVudCB2ZWwgcmhvbmN1cyBpbXBlcmRpZXQsIGJsYW5kaXQgaW4gbGVvLiBJbnRlZ2VyIHF1aXMgbWFnbmEgbnVsbGEuIERvbmVjIHZlbCBuaXNsIG1hZ25hLCB1dCByaG9uY3VzIGR1aS4gQWxpcXVhbSBncmF2aWRhLCBudWxsYSBuZWMgZWxlaWZlbmQgbHVjdHVzLCBuZXF1ZSBuaWJoIHBoYXJldHJhIGFudGUsIHF1aXMgZWdlc3RhcyBlbGl0IG1ldHVzIGEgbWkuIE51bmMgbmVjIGF1Z3VlIHF1YW0uIE1vcmJpIHRpbmNpZHVudCB0cmlzdGlxdWUgdmFyaXVzLiBTdXNwZW5kaXNzZSBpYWN1bGlzIGVsaXQgZmV1Z2lhdCBtYWduYSBwZWxsZW50ZXNxdWUgdWx0cmljaWVzLiBWZXN0aWJ1bHVtIGFsaXF1YW0gdG9ydG9yIG5vbiBhbnRlIHVsbGFtY29ycGVyIGZyaW5naWxsYS4gRG9uZWMgaWFjdWxpcyBtaSBxdWlzIG1hdXJpcyBvcm5hcmUgdmVzdGlidWx1bS4KCkluIGEgbWFnbmEgbmlzaSwgYSB1bHRyaWNpZXMgbWFzc2EuIERvbmVjIGVsaXQgbmVxdWUsIHZpdmVycmEgbm9uIHRlbXBvciBxdWlzLCBmcmluZ2lsbGEgaW4gbWV0dXMuIEludGVnZXIgb2RpbyBvZGlvLCBldWlzbW9kIHZpdGFlIG1vbGxpcyBzZWQsIHNvZGFsZXMgZWdldCBsaWJlcm8uIERvbmVjIG5lYyBtYXNzYSBpbiBmZWxpcyBvcm5hcmUgcGhhcmV0cmEgYXQgbmVjIHRlbGx1cy4gTnVuYyBsb3JlbSBkb2xvciwgcHJl
      dGl1bSB2ZWwgYXVjdG9yIGluLCB2b2x1dHBhdCB2aXRhZSBmZWxpcy4gTWFlY2VuYXMgcmhvbmN1cywgb3JjaSB2ZWwgYmxhbmRpdCBldWlzbW9kLCB0dXJwaXMgZXJhdCB0aW5jaWR1bnQgYW50ZSwgZWxlbWVudHVtIGFkaXBpc2NpbmcgbmlzbCB1cm5hIGluIG5pc2kuIFBoYXNlbGx1cyBzYWdpdHRpcywgZW5pbSBzZWQgYWNjdW1zYW4gY29uc2VxdWF0LCB1cm5hIGF1Z3VlIGxvYm9ydGlzIGVyYXQsIG5vbiBtYWxlc3VhZGEgcXVhbSBtZXR1cyBzb2xsaWNpdHVkaW4gYW50ZS4gSW4gbGVvIHB1cnVzLCBkaWduaXNzaW0gcXVpcyB2YXJpdXMgdmVsLCBwZWxsZW50ZXNxdWUgZXQgbmliaC4gSW4gc2VkIHRvcnRvciBpYWN1bGlzIGxpYmVybyBtb2xsaXMgcGVsbGVudGVzcXVlIGlkIHZpdGFlIGxlY3R1cy4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIFBoYXNlbGx1cyBtYXVyaXMgZW5pbSwgcG9zdWVyZSBlZ2V0IGx1Y3R1cyBhYywgaWFjdWxpcyBldCBxdWFtLiBWaXZhbXVzIGV0IG5pYmggZGlhbSwgZWxlbWVudHVtIGVnZXN0YXMgdGVsbHVzLiBBZW5lYW4gdnVscHV0YXRlIG1hbGVzdWFkYSBlc3QuIFNlZCBwb3N1ZXJlIHBvcnRhIGRpYW0gYSBzb2RhbGVzLiBQcm9pbiBldSBzZW0gbm9uIHZlbGl0IGZhY2lsaXNpcyB2ZW5lbmF0aXMgc2VkIGEgdHVycGlzLgoKUGVsbGVudGVzcXVlIHNlZCByaXN1cyBhIGFudGUgdnVscHV0YXRlIGxvYm9ydGlzIHNpdCBhbWV0IGV1IG5pc2wuIFN1c3BlbmRpc
      3NlIHV0IGVyb3MgbWksIGEgcmhvbmN1cyBsYWN1cy4gQ3VyYWJpdHVyIGZlcm1lbnR1bSB2ZWhpY3VsYSB0ZWxsdXMsIGEgb3JuYXJlIG1pIGNvbmRpbWVudHVtIHZlbC4gSW50ZWdlciBtb2xlc3RpZSB2b2x1dHBhdCB2aXZlcnJhLiBJbnRlZ2VyIHBvc3VlcmUgZXVpc21vZCB2ZW5lbmF0aXMuIFByb2luIGFjIG1hdXJpcyBzZWQgbnVsbGEgcGhhcmV0cmEgcG9ydHRpdG9yLiBEdWlzIHZlbCBkdWkgaW4gcmlzdXMgc29kYWxlcyBhdWN0b3Igc2l0IGFtZXQgbm9uIGVuaW0uIE1hZWNlbmFzIG1vbGxpcyBsYWN1cyBhdCBsaWd1bGEgZmF1Y2lidXMgc29kYWxlcy4gQ3JhcyB2ZWwgbmVxdWUgYXJjdS4gU2VkIHRpbmNpZHVudCB0b3J0b3IgcHJldGl1bSBuaXNpIGludGVyZHVtIHF1aXMgZGljdHVtIGFyY3UgbGFvcmVldC4gTW9yYmkgcHJldGl1bSB1bHRyaWNlcyBmZXVnaWF0LiBNYWVjZW5hcyBjb252YWxsaXMgYXVndWUgbmVjIGZlbGlzIG1hbGVzdWFkYSBtYWxlc3VhZGEgc2NlbGVyaXNxdWUgbWF1cmlzIHBsYWNlcmF0LiBTZWQgYXQgbWFnbmEgZW5pbSwgYXQgZnJpbmdpbGxhIGRvbG9yLiBRdWlzcXVlIHV0IG1hdHRpcyBkdWkuIFByYWVzZW50IGNvbnNlY3RldHVyIGFudGUgdml2ZXJyYSBuaXNpIGJsYW5kaXQgcGhhcmV0cmEuIFF1aXNxdWUgbWV0dXMgZWxpdCwgZGlnbmlzc2ltIHZpdGFlIGZlcm1lbnR1bSBzaXQgYW1ldCwgZnJpbmdpbGxhIGltcGVyZGlldCBvZGlvLiBDcmFzIGVnZXQgcHVydXMgZWdldC
      B0ZWxsdXMgZmV1Z2lhdCBsdWN0dXMgYSBhYyBwdXJ1cy4gQ3JhcyB2aXRhZSBuaXNsIHZlbCBhdWd1ZSByaG9uY3VzIHBvcnR0aXRvciBzaXQgYW1ldCBxdWlzIGxvcmVtLiBEb25lYyBpbnRlcmR1bSBwZWxsZW50ZXNxdWUgYWRpcGlzY2luZy4gUGhhc2VsbHVzIG5lcXVlIGxpYmVybywgYWxpcXVhbSBpbiBtYXR0aXMgdml0YWUsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgbmliaC4KCkRvbmVjIG5lYyBudWxsYSB1cm5hLCBhYyBzYWdpdHRpcyBsZWN0dXMuIFN1c3BlbmRpc3NlIG5vbiBlbGl0IHNlZCBtaSBhdWN0b3IgZmFjaWxpc2lzIHZpdGFlIGV0IGxlY3R1cy4gRnVzY2UgYWMgdnVscHV0YXRlIG1hdXJpcy4gTW9yYmkgY29uZGltZW50dW0gdWx0cmljZXMgbWV0dXMsIGV0IGFjY3Vtc2FuIHB1cnVzIG1hbGVzdWFkYSBhdC4gTWFlY2VuYXMgbG9ib3J0aXMgYW50ZSBzZWQgbWFzc2EgZGljdHVtIHZpdGFlIHZlbmVuYXRpcyBlbGl0IGNvbW1vZG8uIFByb2luIHRlbGx1cyBlcm9zLCBhZGlwaXNjaW5nIHNlZCBkaWduaXNzaW0gdml0YWUsIHRlbXBvciBlZ2V0IGFudGUuIEFlbmVhbiBpZCB0ZWxsdXMgbmVjIG1hZ25hIGN1cnN1cyBwaGFyZXRyYSB2aXRhZSB2ZWwgZW5pbS4gTW9yYmkgdmVzdGlidWx1bSBwaGFyZXRyYSBlc3QgaW4gdnVscHV0YXRlLiBBbGlxdWFtIHZpdGFlIG1ldHVzIGFyY3UsIGlkIGFsaXF1ZXQgbnVsbGEuIFBoYXNlbGx1cyBsaWd1bGEgZXN0LCBoZW5kcmVyaXQgbmVjIGlhY3VsaXMgdXQ
      sIHZvbHV0cGF0IHZlbCBlcm9zLiBTdXNwZW5kaXNzZSB2aXRhZSB1cm5hIHR1cnBpcywgcGxhY2VyYXQgYWRpcGlzY2luZyBkaWFtLiBQaGFzZWxsdXMgZmV1Z2lhdCB2ZXN0aWJ1bHVtIG5lcXVlIGV1IGRhcGlidXMuIE51bGxhIGZhY2lsaXNpLiBEdWlzIHRvcnRvciBmZWxpcywgZXVpc21vZCBzaXQgYW1ldCBhbGlxdWV0IGluLCB2b2x1dHBhdCBuZWMgdHVycGlzLiBNYXVyaXMgcmhvbmN1cyBpcHN1bSB1dCBwdXJ1cyBlbGVpZmVuZCB1dCBsb2JvcnRpcyBsZWN0dXMgZGFwaWJ1cy4gUXVpc3F1ZSBub24gZXJhdCBsb3JlbS4gVml2YW11cyBwb3N1ZXJlIGltcGVyZGlldCBpYWN1bGlzLiBVdCBsaWd1bGEgbGFjdXMsIGVsZWlmZW5kIGF0IHRlbXBvciBpZCwgYXVjdG9yIGV1IGxlby4KCkRvbmVjIG1pIGVuaW0sIGxhb3JlZXQgcHVsdmluYXIgbW9sbGlzIGV1LCBtYWxlc3VhZGEgdml2ZXJyYSBudW5jLiBJbiB2aXRhZSBtZXR1cyB2aXRhZSBuZXF1ZSB0ZW1wb3IgZGFwaWJ1cy4gTWFlY2VuYXMgdGluY2lkdW50IHB1cnVzIGEgZmVsaXMgYWxpcXVhbSBwbGFjZXJhdC4gTnVsbGEgZmFjaWxpc2kuIFN1c3BlbmRpc3NlIHBsYWNlcmF0IHBoYXJldHJhIG1hdHRpcy4gSW50ZWdlciB0ZW1wb3IgbWFsZXN1YWRhIGp1c3RvIGF0IHRlbXB1cy4gTWFlY2VuYXMgdmVoaWN1bGEgbG9yZW0gYSBzYXBpZW4gYmliZW5kdW0gdmVsIGlhY3VsaXMgcmlzdXMgZmV1Z2lhdC4gUGVsbGVudGVzcXVlIGRpYW0gZXJhdCwgZGFwaWJ1
      cyBldCBwZWxsZW50ZXNxdWUgcXVpcywgbW9sZXN0aWUgdXQgbWFzc2EuIFZpdmFtdXMgaWFjdWxpcyBpbnRlcmR1bSBtYXNzYSBpZCBiaWJlbmR1bS4gUXVpc3F1ZSB1dCBtYXVyaXMgZHVpLCBzaXQgYW1ldCB2YXJpdXMgZWxpdC4gVmVzdGlidWx1bSBlbGl0IGxvcmVtLCBydXRydW0gbm9uIGNvbnNlY3RldHVyIHV0LCBsYW9yZWV0IG5lYyBudW5jLiBEb25lYyBuZWMgbWF1cmlzIGFudGUuIEN1cmFiaXR1ciB1dCBlc3Qgc2VkIG9kaW8gcGhhcmV0cmEgbGFvcmVldC4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3VyYWJpdHVyIHB1cnVzIHJpc3VzLCBsYW9yZWV0IHNlZCBwb3J0YSBpZCwgc2FnaXR0aXMgdmVsIGlwc3VtLiBNYWVjZW5hcyBuaWJoIGRpYW0sIGN1cnN1cyBldCB2YXJpdXMgc2l0IGFtZXQsIGZyaW5naWxsYSBzZWQgbWFnbmEuIE51bGxhbSBpZCBuZXF1ZSBldSBsZW8gZmF1Y2lidXMgbW9sbGlzLiBEdWlzIG5lYyBhZGlwaXNjaW5nIG1hdXJpcy4gU3VzcGVuZGlzc2Ugc29sbGljaXR1ZGluLCBlbmltIGV1IHB1bHZpbmFyIGNvbW1vZG8sIGVyYXQgYXVndWUgdWx0cmljZXMgbWksIGEgdHJpc3RpcXVlIG1hZ25hIHNlbSBub24gbGliZXJvLgoKU2VkIGluIG1ldHVzIG51bGxhLiBQcmFlc2VudCBuZWMgYWRpcGlzY2luZyBzYXBpZW4uIERvbmVjIGxhb3JlZXQsIHZlbGl0IG5vbiBydXRydW0gdmVzdGlidWx1bSwgbGlndWxhIG5lcXVlIGFka
      XBpc2NpbmcgdHVycGlzLCBhdCBhdWN0b3Igc2FwaWVuIGVsaXQgdXQgbWFzc2EuIE51bGxhbSBhbGlxdWFtLCBlbmltIHZlbCBwb3N1ZXJlIHJ1dHJ1bSwganVzdG8gZXJhdCBsYW9yZWV0IGVzdCwgdmVsIGZyaW5naWxsYSBsYWN1cyBuaXNpIG5vbiBsZWN0dXMuIEV0aWFtIGxlY3R1cyBudW5jLCBsYW9yZWV0IGV0IHBsYWNlcmF0IGF0LCB2ZW5lbmF0aXMgcXVpcyBsaWJlcm8uIFByYWVzZW50IGluIHBsYWNlcmF0IGVsaXQuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gUGVsbGVudGVzcXVlIGZyaW5naWxsYSBhdWd1ZSBldSBuaWJoIHBsYWNlcmF0IGRpY3R1bS4gTnVuYyBwb3J0dGl0b3IgdHJpc3RpcXVlIGRpYW0sIGV1IGFsaXF1YW0gZW5pbSBhbGlxdWV0IHZlbC4gQWxpcXVhbSBsYWNpbmlhIGludGVyZHVtIGlwc3VtLCBpbiBwb3N1ZXJlIG1ldHVzIGx1Y3R1cyB2ZWwuIFZpdmFtdXMgZXQgbmlzbCBhIGVyb3Mgc2VtcGVyIGVsZW1lbnR1bS4gRG9uZWMgdmVuZW5hdGlzIG9yY2kgYXQgZGlhbSB0cmlzdGlxdWUgc29sbGljaXR1ZGluLiBJbiBldSBlcm9zIHNlZCBvZGlvIHJ1dHJ1bSBsdWN0dXMgbm9uIG5lYyB0ZWxsdXMuCgpOdWxsYSBuZWMgZmVsaXMgZWxpdC4gTnVsbGFtIGluIGlwc3VtIGluIGlwc3VtIGNvbnNlcXVhdCBmcmluZ2lsbGEgcXVpcyB2ZWwgdG9ydG9yLiBQaGFzZWxsdX
      Mgbm9uIG1hc3NhIG5pc2ksIHNpdCBhbWV0IGFsaXF1YW0gdXJuYS4gU2VkIGZlcm1lbnR1bSBuaWJoIHZpdGFlIGxhY3VzIHRpbmNpZHVudCBuZWMgdGluY2lkdW50IG1hc3NhIGJpYmVuZHVtLiBFdGlhbSBlbGl0IGR1aSwgZmFjaWxpc2lzIHNpdCBhbWV0IHZlaGljdWxhIG5lYywgaWFjdWxpcyBhdCBzYXBpZW4uIFV0IGF0IG1hc3NhIGlkIGR1aSB1bHRyaWNlcyB2b2x1dHBhdCB1dCBhYyBsaWJlcm8uIEZ1c2NlIGlwc3VtIG1pLCBiaWJlbmR1bSBhIGxhY2luaWEgZXQsIHB1bHZpbmFyIGVnZXQgbWF1cmlzLiBQcm9pbiBmYXVjaWJ1cyB1cm5hIHV0IGxvcmVtIGVsZW1lbnR1bSB2dWxwdXRhdGUuIER1aXMgcXVhbSBsZW8sIG1hbGVzdWFkYSBub24gZXVpc21vZCB1dCwgYmxhbmRpdCBmYWNpbGlzaXMgbWF1cmlzLiBTdXNwZW5kaXNzZSBzaXQgYW1ldCBtYWduYSBpZCB2ZWxpdCB0aW5jaWR1bnQgYWxpcXVldCBuZWMgZXUgZG9sb3IuIEN1cmFiaXR1ciBiaWJlbmR1bSBsb3JlbSB2ZWwgZmVsaXMgdGVtcHVzIGRhcGlidXMuIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gQWVuZWFuIGN1cnN1cyB0b3J0b3IgbmVjIGR1aSBhbGlxdWV0IHBvcnRhLiBBZW5lYW4gY29tbW9kbyBpYWN1bGlzIHN1c2NpcGl0LiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgUXVpc3F1ZSBzaXQgYW1ldCBvcm5hcmUgZWxpdC4gTmF
      tIGxpZ3VsYSByaXN1cywgdmVzdGlidWx1bSBuZWMgbWF0dGlzIGluLCBjb25kaW1lbnR1bSBhYyBhbnRlLiBEb25lYyBmcmluZ2lsbGEsIGp1c3RvIGV0IHVsdHJpY2VzIGZhdWNpYnVzLCB0ZWxsdXMgZXN0IHZvbHV0cGF0IG1hc3NhLCB2aXRhZSBjb21tb2RvIHNhcGllbiBkaWFtIG5vbiByaXN1cy4gVml2YW11cyBhdCBhcmN1IGdyYXZpZGEgcHVydXMgbW9sbGlzIGZldWdpYXQuCgpOdWxsYSBhIHR1cnBpcyBxdWlzIHNhcGllbiBjb21tb2RvIGRpZ25pc3NpbSBldSBxdWlzIGp1c3RvLiBNYWVjZW5hcyBldSBsb3JlbSBvZGlvLCB1dCBoZW5kcmVyaXQgdmVsaXQuIEN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0dXIgcmlkaWN1bHVzIG11cy4gUHJvaW4gZmFjaWxpc2lzIHBvcnR0aXRvciB1bGxhbWNvcnBlci4gUHJhZXNlbnQgbW9sbGlzIGRpZ25pc3NpbSBtYXNzYSwgbGFvcmVldCBhbGlxdWV0IHZlbGl0IHBlbGxlbnRlc3F1ZSBub24uIE51bmMgZmFjaWxpc2lzIGNvbnZhbGxpcyB0cmlzdGlxdWUuIE1hdXJpcyBwb3J0dGl0b3IgYW50ZSBhdCB0ZWxsdXMgY29udmFsbGlzIHBsYWNlcmF0LiBNb3JiaSBhbGlxdWV0IG5pc2kgYWMgbmlzbCBwdWx2aW5hciBpZCBkaWN0dW0gbmlzbCBtb2xsaXMuIFNlZCBvcm5hcmUgc2VtIGV0IHJpc3VzIHBsYWNlcmF0IGxvYm9ydGlzIGlkIGVnZXQgZWxpdC4gSW50ZWdlciBjb25zZXF1YXQsIG1hZ25h
      IGlkIHN1c2NpcGl0IHBoYXJldHJhLCBudWxsYSB2ZWxpdCBzdXNjaXBpdCBvcmNpLCB1dCBpbnRlcmR1bSBhdWd1ZSBhdWd1ZSBxdWlzIHF1YW0uIEZ1c2NlIHByZXRpdW0gYWxpcXVldCB2dWxwdXRhdGUuIE1hdXJpcyBibGFuZGl0IGRpY3R1bSBtb2xlc3RpZS4gUHJvaW4gbnVsbGEgbmliaCwgYmliZW5kdW0gZXUgcGxhY2VyYXQgYXQsIHRpbmNpZHVudCBhYyBuaXNsLiBOdWxsYW0gdnVscHV0YXRlIG1ldHVzIHV0IGxpYmVybyBydXRydW0gdWx0cmljaWVzLiBOdW5jIHNpdCBhbWV0IGR1aSBtYXVyaXMuIFN1c3BlbmRpc3NlIGFkaXBpc2NpbmcgbGFjdXMgaW4gYXVndWUgZWxlaWZlbmQgbW9sbGlzLgoKRHVpcyBwcmV0aXVtIHVsdHJpY2VzIG1hdHRpcy4gTmFtIGV1aXNtb2QgcmlzdXMgYSBlcmF0IGxhY2luaWEgYmliZW5kdW0uIE1vcmJpIG1hc3NhIHRvcnRvciwgY29uc2VjdGV0dXIgaWQgZWxlaWZlbmQgaWQsIHBlbGxlbnRlc3F1ZSB2ZWwgdG9ydG9yLiBQcmFlc2VudCB1cm5hIGxvcmVtLCBwb3J0dGl0b3IgYXQgY29uZGltZW50dW0gdml0YWUsIGx1Y3R1cyBlZ2V0IGVsaXQuIE1hZWNlbmFzIGZyaW5naWxsYSBxdWFtIGNvbnZhbGxpcyBlc3QgaGVuZHJlcml0IHZpdmVycmEuIEV0aWFtIHZlaGljdWxhLCBzYXBpZW4gbm9uIHB1bHZpbmFyIGFkaXBpc2NpbmcsIG5pc2kgbWFzc2EgdmVzdGlidWx1bSBlc3QsIGlkIGludGVyZHVtIG1hdXJpcyB2ZWxpdCBldSBlc3QuIFZlc3RpYnVsdW0gZXN0IGFyY3UsI
      GZhY2lsaXNpcyBhdCB1bHRyaWNpZXMgbm9uLCB2dWxwdXRhdGUgaWQgc2FwaWVuLiBWZXN0aWJ1bHVtIGlwc3VtIG1ldHVzLCBwaGFyZXRyYSBuZWMgcGVsbGVudGVzcXVlIGlkLCBmYWNpbGlzaXMgaWQgc2FwaWVuLiBEb25lYyBydXRydW0gb2RpbyBldCBsYWN1cyB1bHRyaWNpZXMgdWxsYW1jb3JwZXIuIEludGVnZXIgc2VkIGVzdCB1dCBtaSBwb3N1ZXJlIHRpbmNpZHVudCBxdWlzIG5vbiBsZW8uIE1vcmJpIHRlbGx1cyBqdXN0bywgdWx0cmljaWVzIHNpdCBhbWV0IHVsdHJpY2VzIHF1aXMsIGZhY2lsaXNpcyB2aXRhZSBtYWduYS4gRG9uZWMgbGlndWxhIG1ldHVzLCBwZWxsZW50ZXNxdWUgbm9uIHRyaXN0aXF1ZSBhYywgdmVzdGlidWx1bSBzZWQgZXJhdC4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LgoKTmFtIGRpZ25pc3NpbSwgbmlzbCBlZ2V0IGNvbnNlcXVhdCBldWlzbW9kLCBzZW0gbGVjdHVzIGF1Y3RvciBvcmNpLCB1dCBwb3J0dGl0b3IgbGFjdXMgZHVpIGFjIG5lcXVlLiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4gRnVzY2UgZWdlc3RhcyBwb3J0YSBmYWNpbGlzaXMuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBNYXVyaXMgY3Vyc3VzIHJob25jdXMgcmlzdXMgYWMgZXVpc21vZC4gUXVpc3F1ZSB2aXRhZSByaXN1cyBhIHRlbGx1cyB2ZW5lbmF0aXMgY29udmFsbGlzLiBDdXJhYml0dXIgbGFvcmVldCBzYXBpZW4gZXUgcXVhbSBsdWN0dXMgbG9ib3J0aXMuIFZpdmFtdX
      Mgc29sbGljaXR1ZGluIHNvZGFsZXMgZG9sb3Igdml0YWUgc29kYWxlcy4gU3VzcGVuZGlzc2UgcGhhcmV0cmEgbGFvcmVldCBhbGlxdWV0LiBNYWVjZW5hcyB1bGxhbWNvcnBlciBvcmNpIHZlbCB0b3J0b3IgbHVjdHVzIGlhY3VsaXMgdXQgdml0YWUgbWV0dXMuIFZlc3RpYnVsdW0gdXQgYXJjdSBhYyB0ZWxsdXMgbWF0dGlzIGVsZWlmZW5kIGVnZXQgdmVoaWN1bGEgZWxpdC4KCkluIHNlZCBmZXVnaWF0IGVyb3MuIERvbmVjIGJpYmVuZHVtIHVsbGFtY29ycGVyIGRpYW0sIGV1IGZhdWNpYnVzIG1hdXJpcyBkaWN0dW0gc2VkLiBEdWlzIHRpbmNpZHVudCBqdXN0byBpbiBuZXF1ZSBhY2N1bXNhbiBkaWN0dW0uIE1hZWNlbmFzIGluIHJ1dHJ1bSBzYXBpZW4uIFV0IGlkIGZldWdpYXQgbGFjdXMuIE51bGxhIGZhY2lsaXNpLiBOdW5jIGFjIGxvcmVtIGlkIHF1YW0gdmFyaXVzIGN1cnN1cyBhIGV0IGVsaXQuIEFlbmVhbiBwb3N1ZXJlIGxpYmVybyBldSB0b3J0b3IgdmVoaWN1bGEgdXQgdWxsYW1jb3JwZXIgb2RpbyBjb25zZXF1YXQuIFNlZCBpbiBkaWduaXNzaW0gZHVpLiBDdXJhYml0dXIgaWFjdWxpcyB0ZW1wb3IgcXVhbSBuZWMgcGxhY2VyYXQuIEFsaXF1YW0gdmVuZW5hdGlzIG5pYmggZXQganVzdG8gaWFjdWxpcyBsYWNpbmlhLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gTG9yZW0gaXB
      zdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUGVsbGVudGVzcXVlIHRlbXB1cyBtYWduYSBzZWQgbWkgYWxpcXVldCBlZ2V0IHZhcml1cyBvZGlvIGNvbmd1ZS4KCkludGVnZXIgc2VtIHNlbSwgc2VtcGVyIGluIHZlc3RpYnVsdW0gdml0YWUsIGxvYm9ydGlzIHF1aXMgZXJhdC4gRHVpcyBhbnRlIGxlY3R1cywgZmVybWVudHVtIHNlZCB0ZW1wb3Igc2l0IGFtZXQsIHBsYWNlcmF0IHNpdCBhbWV0IHNlbS4gTWF1cmlzIGNvbmd1ZSB0aW5jaWR1bnQgaXBzdW0uIFV0IHZpdmVycmEsIGxhY3VzIHZlbCB2YXJpdXMgcGhhcmV0cmEsIHB1cnVzIGVuaW0gcHVsdmluYXIgaXBzdW0sIG5vbiBwZWxsZW50ZXNxdWUgZW5pbSBqdXN0byBub24gZXJhdC4gRnVzY2UgaXBzdW0gb3JjaSwgdWx0cmljZXMgc2VkIHBlbGxlbnRlc3F1ZSBhdCwgaGVuZHJlcml0IGxhb3JlZXQgZW5pbS4gTnVuYyBibGFuZGl0IG1vbGxpcyBwcmV0aXVtLiBVdCBtb2xsaXMsIG51bGxhIGFsaXF1YW0gc29kYWxlcyB2ZXN0aWJ1bHVtLCBsaWJlcm8gbG9yZW0gdGVtcHVzIHRvcnRvciwgYSBwZWxsZW50ZXNxdWUgbmliaCBlbGl0IGEgaXBzdW0uIFBoYXNlbGx1cyBmZXJtZW50dW0gbGlndWxhIGF0IG5lcXVlIGFkaXBpc2Npbmcgc29sbGljaXR1ZGluLiBTdXNwZW5kaXNzZSBpZCBpcHN1bSBhcmN1LiBTZWQgdGluY2lkdW50IHBsYWNlcmF0IHZpdmVycmEuIERvbmVjIGxpYmVybyBhdWd1ZSwgcG9ydHRpdG9yIHNp
      dCBhbWV0IHZhcml1cyBlZ2V0LCBydXRydW0gbmVjIGxhY3VzLiBQcm9pbiBibGFuZGl0IG9yY2kgc2l0IGFtZXQgZGlhbSBkaWN0dW0gaWQgcG9ydHRpdG9yIHJpc3VzIGlhY3VsaXMuIEludGVnZXIgbGFjaW5pYSBmZXVnaWF0IGxlbywgdml0YWUgYXVjdG9yIHR1cnBpcyBlbGVpZmVuZCB2ZWwuIFN1c3BlbmRpc3NlIGxvcmVtIHF1YW0sIHByZXRpdW0gaWQgYmliZW5kdW0gc2VkLCB2aXZlcnJhIHZpdGFlIHRvcnRvci4gTnVsbGFtIHVsdHJpY2llcyBsaWJlcm8gZXUgcmlzdXMgY29udmFsbGlzIGVnZXQgdWxsYW1jb3JwZXIgbmlzaSBlbGVtZW50dW0uIE1hdXJpcyBudWxsYSBlbGl0LCBiaWJlbmR1bSBpZCB2dWxwdXRhdGUgdml0YWUsIGltcGVyZGlldCBydXRydW0gbG9yZW0uIEN1cmFiaXR1ciBlZ2V0IGRpZ25pc3NpbSBvcmNpLiBTZWQgc2VtcGVyIHRlbGx1cyBpcHN1bSwgYXQgYmxhbmRpdCBkdWkuIEludGVnZXIgZGFwaWJ1cyBmYWNpbGlzaXMgc29kYWxlcy4gVml2YW11cyBzb2xsaWNpdHVkaW4gdmFyaXVzIGVzdCwgcXVpcyBvcm5hcmUganVzdG8gY3Vyc3VzIGlkLgoKTnVuYyB2ZWwgdWxsYW1jb3JwZXIgbWkuIFN1c3BlbmRpc3NlIHBvdGVudGkuIE51bmMgZXQgdXJuYSBhIGF1Z3VlIHNjZWxlcmlzcXVlIHVsdHJpY2VzIG5vbiBxdWlzIG1pLiBJbiBxdWlzIHBvcnR0aXRvciBlbGl0LiBBZW5lYW4gcXVpcyBlcmF0IG51bGxhLCBhIHZlbmVuYXRpcyB0ZWxsdXMuIEZ1c2NlIHZlc3RpYnVsdW0gbmlza
      SBzZWQgbGVvIGFkaXBpc2NpbmcgZGlnbmlzc2ltLiBOdW5jIGludGVyZHVtLCBsb3JlbSBldCBsYWNpbmlhIHZlc3RpYnVsdW0sIHF1YW0gZXN0IG1hdHRpcyBtYWduYSwgc2l0IGFtZXQgdm9sdXRwYXQgZWxpdCBhdWd1ZSBhdCBsaWJlcm8uIENyYXMgZ3JhdmlkYSBkdWkgcXVpcyB2ZWxpdCBsb2JvcnRpcyBjb25kaW1lbnR1bSBldCBlbGVpZmVuZCBsaWd1bGEuIFBoYXNlbGx1cyBhYyBtZXR1cyBxdWFtLCBpZCB2ZW5lbmF0aXMgbWkuIEFsaXF1YW0gdXQgdHVycGlzIGFjIHRlbGx1cyBkYXBpYnVzIGRhcGlidXMgZXUgaW4gbWkuIFF1aXNxdWUgZWdldCBuaWJoIGVyb3MuIEZ1c2NlIGNvbnNlY3RldHVyIGxlbyB2ZWxpdC4KClZlc3RpYnVsdW0gc2VtcGVyIGVnZXN0YXMgbWF1cmlzLiBNb3JiaSB2ZXN0aWJ1bHVtIHNlbSBzZW0uIEFsaXF1YW0gdmVuZW5hdGlzLCBmZWxpcyBzZWQgZWxlaWZlbmQgcG9ydGEsIG1hdXJpcyBkaWFtIHNlbXBlciBhcmN1LCBzaXQgYW1ldCB1bHRyaWNpZXMgZXN0IHNhcGllbiBzaXQgYW1ldCBsaWJlcm8uIFZlc3RpYnVsdW0gZHVpIG9yY2ksIG9ybmFyZSBjb25kaW1lbnR1bSBtb2xsaXMgbmVjLCBtb2xlc3RpZSBhYyBlcm9zLiBQcm9pbiB2aXRhZSBtb2xsaXMgdmVsaXQuIFByYWVzZW50IGVnZXQgZmVsaXMgbWkuIE1hZWNlbmFzIGV1IHZ1bHB1dGF0ZSBuaXNpLiBWZXN0aWJ1bHVtIHZhcml1cywgYXJjdSBpbiB1bHRyaWNpZXMgdmVzdGlidWx1bSwgbmliaCBsZW8gc2FnaXR0aX
      Mgb2RpbywgdXQgYmliZW5kdW0gbmlzbCBtaSBuZWMgZGlhbS4gSW50ZWdlciBhdCBlbmltIGZldWdpYXQgbnVsbGEgc2VtcGVyIGJpYmVuZHVtIHV0IGEgdmVsaXQuIFByb2luIGF0IG5pc2kgdXQgbG9yZW0gYWxpcXVhbSB2YXJpdXMgZWdldCBxdWlzIGVsaXQuIE51bGxhbSBuZWMgb2RpbyB2ZWwgbGVjdHVzIGNvbmd1ZSBjb25zZXF1YXQgYWRpcGlzY2luZyBhYyBtaS4gRnVzY2Ugdml0YWUgbGFvcmVldCBsaWJlcm8uIEN1cmFiaXR1ciBzaXQgYW1ldCBzZW0gbmVxdWUsIG5lYyBwb3N1ZXJlIGVuaW0uIEN1cmFiaXR1ciBhdCBtYXNzYSBhIHNlbSBncmF2aWRhIGlhY3VsaXMgbmVjIGV0IG5pYmguIFNlZCB2aXRhZSBkdWkgdml0YWUgbGVvIHRpbmNpZHVudCBwcmV0aXVtIGEgYWxpcXVhbSBlcmF0LiBTdXNwZW5kaXNzZSB1bHRyaWNpZXMgb2RpbyBhdCBtZXR1cyB0ZW1wb3IgaW4gcGVsbGVudGVzcXVlIGFyY3UgdWx0cmljaWVzLgoKU2VkIGFsaXF1YW0gbWF0dGlzIHF1YW0sIGluIHZ1bHB1dGF0ZSBzYXBpZW4gdWx0cmljZXMgaW4uIFBlbGxlbnRlc3F1ZSBxdWlzIHZlbGl0IHNlZCBkdWkgaGVuZHJlcml0IGN1cnN1cy4gUGVsbGVudGVzcXVlIG5vbiBudW5jIGxhY3VzLCBhIHNlbXBlciBtZXR1cy4gRnVzY2UgZXVpc21vZCB2ZWxpdCBxdWlzIGRpYW0gc3VzY2lwaXQgY29uc2VxdWF0LiBQcmFlc2VudCBjb21tb2RvIGFjY3Vtc2FuIG5lcXVlLiBQcm9pbiB2aXZlcnJhLCBpcHN1bSBub24gdHJpc3RpcXVlIHV
      sdHJpY2VzLCB2ZWxpdCB2ZWxpdCBmYWNpbGlzaXMgbG9yZW0sIHZlbCBydXRydW0gbmVxdWUgZXJvcyBhYyBuaXNpLiBTdXNwZW5kaXNzZSBmZWxpcyBtYXNzYSwgZmF1Y2lidXMgaW4gdm9sdXRwYXQgYWMsIGRhcGlidXMgZXQgb2Rpby4gUGVsbGVudGVzcXVlIGlkIHRlbGx1cyBzaXQgYW1ldCByaXN1cyB1bHRyaWNpZXMgdWxsYW1jb3JwZXIgbm9uIG5lYyBzYXBpZW4uIE5hbSBwbGFjZXJhdCB2aXZlcnJhIHVsbGFtY29ycGVyLiBOYW0gcGxhY2VyYXQgcG9ydHRpdG9yIHNhcGllbiBuZWMgcHVsdmluYXIuIEN1cmFiaXR1ciB2ZWwgb2RpbyBzaXQgYW1ldCBvZGlvIGFjY3Vtc2FuIGFsaXF1ZXQgdml0YWUgYSBsZWN0dXMuIFBlbGxlbnRlc3F1ZSBsb2JvcnRpcyB2aXZlcnJhIGNvbnNlcXVhdC4gTWF1cmlzIGVsZW1lbnR1bSBjdXJzdXMgbnVsbGEsIHNpdCBhbWV0IGhlbmRyZXJpdCBqdXN0byBkaWN0dW0gc2VkLiBNYWVjZW5hcyBkaWFtIG9kaW8sIGZyaW5naWxsYSBhYyBjb25ndWUgcXVpcywgYWRpcGlzY2luZyB1dCBlbGl0LgoKQWxpcXVhbSBsb3JlbSBlcm9zLCBwaGFyZXRyYSBuZWMgZWdlc3RhcyB2aXRhZSwgbWF0dGlzIG5lYyByaXN1cy4gTWF1cmlzIGFyY3UgbWFzc2EsIHNvZGFsZXMgZWdldCBncmF2aWRhIHNlZCwgdml2ZXJyYSB2aXRhZSB0dXJwaXMuIFV0IGxpZ3VsYSB1cm5hLCBldWlzbW9kIGFjIHRpbmNpZHVudCBldSwgZmF1Y2lidXMgc2VkIGZlbGlzLiBQcmFlc2VudCBtb2xsaXMsIGlwc3Vt
      IHF1aXMgcmhvbmN1cyBkaWduaXNzaW0sIG9kaW8gc2VtIHZlbmVuYXRpcyBudWxsYSwgYXQgY29uc2VxdWF0IGZlbGlzIGF1Z3VlIHZlbCBlcmF0LiBOYW0gZmVybWVudHVtIGZldWdpYXQgdm9sdXRwYXQuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gRXRpYW0gdml0YWUgZHVpIGluIG5pc2kgYWRpcGlzY2luZyB1bHRyaWNpZXMgbm9uIGV1IGp1c3RvLiBEb25lYyB0cmlzdGlxdWUgdWx0cmljaWVzIGFkaXBpc2NpbmcuIE51bGxhIHNvZGFsZXMsIG51bmMgYSB0cmlzdGlxdWUgZWxlbWVudHVtLCBlcmF0IG5lcXVlIGVnZXN0YXMgbmlzbCwgYXQgaGVuZHJlcml0IG9yY2kgc2FwaWVuIHNlZCBsaWJlcm8uIFZpdmFtdXMgYSBtYXVyaXMgdHVycGlzLCBxdWlzIGxhb3JlZXQgaXBzdW0uIE51bmMgbmVjIG1pIGV0IG5pc2wgcGVsbGVudGVzcXVlIHNjZWxlcmlzcXVlLiBWaXZhbXVzIHZvbHV0cGF0LCBqdXN0byB0cmlzdGlxdWUgbGFjaW5pYSBjb25kaW1lbnR1bSwgZXJhdCBqdXN0byB1bHRyaWNlcyB1cm5hLCBlbGVtZW50dW0gdml2ZXJyYSBlcm9zIGF1Z3VlIG5vbiBsaWJlcm8uIFNlZCBtb2xsaXMgbW9sbGlzIGFyY3UsIGF0IGZlcm1lbnR1bSBkaWFtIHN1c2NpcGl0IHF1aXMuCgpFdGlhbSBzaXQgYW1ldCBuaWJoIGp1c3RvLCBwb3N1ZXJlIHZvbHV0cGF0IG51bmMuIE1vcmJpIHBlbGxlbnRlc
      3F1ZSBuZXF1ZSBpbiBvcmNpIHZvbHV0cGF0IGV1IHNjZWxlcmlzcXVlIGxvcmVtIGRpY3R1bS4gTWF1cmlzIG1vbGxpcyBpYWN1bGlzIGVzdCwgbmVjIHNhZ2l0dGlzIHNhcGllbiBjb25zZXF1YXQgaWQuIE51bmMgbmVjIG1hbGVzdWFkYSBvZGlvLiBEdWlzIHF1aXMgc3VzY2lwaXQgb2Rpby4gTWF1cmlzIHB1cnVzIGR1aSwgc29kYWxlcyBpZCBtYXR0aXMgc2l0IGFtZXQsIHBvc3VlcmUgaW4gYXJjdS4gUGhhc2VsbHVzIHBvcnRhIGVsZW1lbnR1bSBjb252YWxsaXMuIE1hZWNlbmFzIGF0IG9yY2kgZXQgbWkgdnVscHV0YXRlIHNvbGxpY2l0dWRpbiBpbiBpbiB0dXJwaXMuIFBlbGxlbnRlc3F1ZSBjdXJzdXMgYWRpcGlzY2luZyBuZXF1ZSBzaXQgYW1ldCBjb21tb2RvLiBGdXNjZSB1dCBtaSBldSBsZWN0dXMgcG9ydHRpdG9yIHZvbHV0cGF0IGV0IG5lYyBmZWxpcy4KCkN1cmFiaXR1ciBzY2VsZXJpc3F1ZSBlcm9zIHF1aXMgbmlzbCB2aXZlcnJhIHZlbCB1bHRyaWNlcyB2ZWxpdCB2ZXN0aWJ1bHVtLiBTZWQgbG9ib3J0aXMgcHVsdmluYXIgc2FwaWVuIGFjIHZlbmVuYXRpcy4gU2VkIGFudGUgbmliaCwgcmhvbmN1cyBlZ2V0IGRpY3R1bSBpbiwgbW9sbGlzIHV0IG5pc2kuIFBoYXNlbGx1cyBmYWNpbGlzaXMgbWkgbm9uIGxvcmVtIHRyaXN0aXF1ZSBub24gZWxlaWZlbmQgc2VtIGZyaW5naWxsYS4gSW50ZWdlciB1dCBhdWd1ZSBlc3QuIEluIHZlbmVuYXRpcyB0aW5jaWR1bnQgc2NlbGVyaXNxdWUuIEV0aWFtIG
      FudGUgZHVpLCBwb3N1ZXJlIHF1aXMgbWFsZXN1YWRhIHZpdGFlLCBtYWxlc3VhZGEgYSBhcmN1LiBBZW5lYW4gZmF1Y2lidXMgdmVuZW5hdGlzIHNhcGllbiwgdXQgZmFjaWxpc2lzIG5pc2kgYmxhbmRpdCB2ZWwuIEFlbmVhbiBhYyBsb3JlbSBldSBzZW0gZmVybWVudHVtIHBsYWNlcmF0LiBQcm9pbiBuZXF1ZSBwdXJ1cywgYWxpcXVldCB1dCB0aW5jaWR1bnQgdXQsIGNvbnZhbGxpcyBzaXQgYW1ldCBlcm9zLiBQaGFzZWxsdXMgdmVoaWN1bGEgdWxsYW1jb3JwZXIgZW5pbSBub24gdmVoaWN1bGEuIEV0aWFtIHBvcnRhIG9kaW8gdXQgaXBzdW0gYWRpcGlzY2luZyBlZ2VzdGFzIGlkIGEgb2Rpby4gUGVsbGVudGVzcXVlIGJsYW5kaXQsIHNhcGllbiB1dCBwdWx2aW5hciBpbnRlcmR1bSwgbWkgbnVsbGEgaGVuZHJlcml0IGVsaXQsIGluIHRlbXBvciBkaWFtIGVuaW0gYSB1cm5hLiBJbiB0ZWxsdXMgb2Rpbywgb3JuYXJlIHNlZCBjb25kaW1lbnR1bSBhLCBtYXR0aXMgZXUgYXVndWUuCgpGdXNjZSBoZW5kcmVyaXQgcG9ydHRpdG9yIGV1aXNtb2QuIERvbmVjIG1hbGVzdWFkYSBlZ2VzdGFzIHR1cnBpcywgZXQgdWx0cmljaWVzIGZlbGlzIGVsZW1lbnR1bSB2aXRhZS4gTnVsbGFtIGluIHNlbSBuaWJoLiBOdWxsYW0gdWx0cmljaWVzIGhlbmRyZXJpdCBqdXN0byBzaXQgYW1ldCBsb2JvcnRpcy4gU2VkIHRpbmNpZHVudCwgbWF1cmlzIGF0IG9ybmFyZSBsYW9yZWV0LCBzYXBpZW4gcHVydXMgZWxlbWVudHVtIGVsaXQ
      sIG5lYyBwb3J0dGl0b3IgbmlzbCBwdXJ1cyBldCBlcmF0LiBEb25lYyBmZWxpcyBuaXNpLCBydXRydW0gdWxsYW1jb3JwZXIgZ3JhdmlkYSBhYywgdGluY2lkdW50IHNpdCBhbWV0IHVybmEuIFByb2luIHZlbCBqdXN0byB2aXRhZSBlcm9zIHNhZ2l0dGlzIGJpYmVuZHVtIGEgdXQgbmliaC4gUGhhc2VsbHVzIHNvZGFsZXMgbGFvcmVldCB0aW5jaWR1bnQuIE1hZWNlbmFzIG9kaW8gbWFzc2EsIGNvbmRpbWVudHVtIGlkIGFsaXF1ZXQgdXQsIHJob25jdXMgdmVsIGxlY3R1cy4gRHVpcyBwaGFyZXRyYSBjb25zZWN0ZXR1ciBzYXBpZW4uIFBoYXNlbGx1cyBwb3N1ZXJlIHVsdHJpY2llcyBtYXNzYSwgbm9uIHJob25jdXMgcmlzdXMgYWxpcXVhbSB0ZW1wdXMuCgpQcmFlc2VudCB2ZW5lbmF0aXMgbWFnbmEgaWQgc2VtIGRpY3R1bSBldSB2ZWhpY3VsYSBpcHN1bSB2dWxwdXRhdGUuIFNlZCBhIGNvbnZhbGxpcyBzYXBpZW4uIFNlZCBqdXN0byBkb2xvciwgcmhvbmN1cyB2ZWwgcnV0cnVtIG1hdHRpcywgc29sbGljaXR1ZGluIHV0IHJpc3VzLiBOdWxsYW0gc2l0IGFtZXQgY29udmFsbGlzIGVzdC4gRXRpYW0gbm9uIHRpbmNpZHVudCBsaWd1bGEuIEZ1c2NlIHN1c2NpcGl0IHByZXRpdW0gZWxpdCBhdCB1bGxhbWNvcnBlci4gUXVpc3F1ZSBzb2xsaWNpdHVkaW4sIGRpYW0gaWQgaW50ZXJkdW0gcG9ydGEsIG1ldHVzIGlwc3VtIHZvbHV0cGF0IGxpYmVybywgaWQgdmVuZW5hdGlzIGZlbGlzIG9yY2kgbm9uIHZlbGl0LiBT
      dXNwZW5kaXNzZSBwb3RlbnRpLiBNYXVyaXMgcnV0cnVtLCB0b3J0b3Igc2l0IGFtZXQgcGVsbGVudGVzcXVlIHRpbmNpZHVudCwgZXJhdCBxdWFtIHVsdHJpY2llcyBvZGlvLCBpZCBhbGlxdWFtIGVsaXQgbGVvIG5lYyBsZW8uIFBlbGxlbnRlc3F1ZSBqdXN0byBlcm9zLCBydXRydW0gYXQgZmV1Z2lhdCBuZWMsIHBvcnRhIGV0IHRlbGx1cy4gQWVuZWFuIGVnZXQgbWV0dXMgbGVjdHVzLgoKUHJhZXNlbnQgZXVpc21vZCwgdHVycGlzIHF1aXMgbGFvcmVldCBjb25zZXF1YXQsIG5lcXVlIGFudGUgaW1wZXJkaWV0IHF1YW0sIGFjIHNlbXBlciB0b3J0b3IgbmliaCBpbiBudWxsYS4gSW50ZWdlciBzY2VsZXJpc3F1ZSBlcm9zIHZlaGljdWxhIHVybmEgbGFjaW5pYSBhYyBmYWNpbGlzaXMgbWF1cmlzIGFjY3Vtc2FuLiBQaGFzZWxsdXMgYXQgbWF1cmlzIG5pYmguIEN1cmFiaXR1ciBlbmltIGFudGUsIHJ1dHJ1bSBzZWQgYWRpcGlzY2luZyBoZW5kcmVyaXQsIHBlbGxlbnRlc3F1ZSBub24gYXVndWUuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBOYW0gdGVtcHVzIGV1aXNtb2QgbWFzc2EgYSBkaWN0dW0uIERvbmVjIHNpdCBhbWV0IGp1c3RvIGFjIGRpYW0gdWx0cmljaWVzIHVsdHJpY2llcy4gU2VkIHRpbmNpZHVudCBlcmF0IHF1aXMgcXVhbSB0ZW1wdXMgdmVsIGludGVyZHVtIGVyYXQgcmhvbmN1cy4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIFZlc3RpYnVsdW0gdmVoaWN1bGEgd
      mFyaXVzIHNlbSBlZ2V0IGludGVyZHVtLiBDcmFzIGJpYmVuZHVtIGxlbyBuZWMgZmVsaXMgdmVuZW5hdGlzIHNlZCBwaGFyZXRyYSBzZW0gZmV1Z2lhdC4gQ3VtIHNvY2lpcyBuYXRvcXVlIHBlbmF0aWJ1cyBldCBtYWduaXMgZGlzIHBhcnR1cmllbnQgbW9udGVzLCBuYXNjZXR1ciByaWRpY3VsdXMgbXVzLiBTZWQgcXVhbSBvcmNpLCBtb2xsaXMgZWdldCBzYWdpdHRpcyBhY2N1bXNhbiwgdnVscHV0YXRlIHNpdCBhbWV0IGR1aS4gUHJhZXNlbnQgZXUgZWxlbWVudHVtIGFyY3UuCgpMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBWZXN0aWJ1bHVtIG5pc2wgbWV0dXMsIGhlbmRyZXJpdCB1dCBsYW9yZWV0IHNlZCwgY29uc2VjdGV0dXIgYXQgcHVydXMuIER1aXMgaW50ZXJkdW0gY29uZ3VlIGxvYm9ydGlzLiBOdWxsYW0gc2VkIG1hc3NhIHBvcnRhIGZlbGlzIGVsZWlmZW5kIGNvbnNlcXVhdCBzaXQgYW1ldCBuZWMgbWV0dXMuIEFsaXF1YW0gcGxhY2VyYXQgZGljdHVtIGVyYXQgYXQgZWxlaWZlbmQuIFZlc3RpYnVsdW0gbGliZXJvIGFudGUsIHVsbGFtY29ycGVyIGEgcG9ydHRpdG9yIHN1c2NpcGl0LCBhY2N1bXNhbiB2ZWwgbmlzaS4gRG9uZWMgZXQgbWFnbmEgbmVxdWUuIE5hbSBlbGVtZW50dW0gdWx0cmljZXMganVzdG8sIGVnZXQgc29sbGljaXR1ZGluIHNhcGllbiBpbXBlcmRpZXQgZWdldC4gTnVsbGFtIGF1Y3RvciBkaWN0dW0gbnVuYywgYXQgZmV1Z2
      lhdCBvZGlvIHZlc3RpYnVsdW0gYS4gU2VkIGVyYXQgbnVsbGEsIHZpdmVycmEgaGVuZHJlcml0IGNvbW1vZG8gaWQsIHVsbGFtY29ycGVyIGFjIG9yY2kuIFBoYXNlbGx1cyBwZWxsZW50ZXNxdWUgZmV1Z2lhdCBzdXNjaXBpdC4gRXRpYW0gZWdlc3RhcyBmZXJtZW50dW0gZW5pbS4gRXRpYW0gZ3JhdmlkYSBpbnRlcmR1bSB0ZWxsdXMgYWMgbGFvcmVldC4gTW9yYmkgbWF0dGlzIGFsaXF1ZXQgZXJvcywgbm9uIHRlbXBvciBlcmF0IHVsbGFtY29ycGVyIGluLiBFdGlhbSBwdWx2aW5hciBpbnRlcmR1bSB0dXJwaXMgYWMgdmVoaWN1bGEuIFNlZCBxdWFtIGp1c3RvLCBhY2N1bXNhbiBpZCBjb25zZWN0ZXR1ciBhLCBhbGlxdWV0IHNlZCBsZW8uIEFlbmVhbiB2aXRhZSBibGFuZGl0IG1hdXJpcy4KCkluIHNlZCBlcm9zIGF1Z3VlLCBub24gcnV0cnVtIG9kaW8uIEV0aWFtIHZpdGFlIGR1aSBuZXF1ZSwgaW4gdHJpc3RpcXVlIG1hc3NhLiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgTWFlY2VuYXMgZGljdHVtIGVsaXQgYXQgbGVjdHVzIHRlbXBvciBub24gcGhhcmV0cmEgbmlzbCBoZW5kcmVyaXQuIFNlZCBzZWQgcXVhbSBldSBsZWN0dXMgdWx0cmljZXMgbWFsZXN1YWRhIHRpbmNpZHVudCBhIGVzdC4gTmFtIHZlbCBlcm9zIHJpc3VzLiBNYWVjZW5hcyBlcm9zIGVsaXQsIGJsYW5kaXQgZmVybWVudHVtIHR
      lbXBvciBlZ2V0LCBsb2JvcnRpcyBpZCBkaWFtLiBWZXN0aWJ1bHVtIGxhY2luaWEgbGFjdXMgdml0YWUgbWFnbmEgdm9sdXRwYXQgZXUgZGlnbmlzc2ltIGVyb3MgY29udmFsbGlzLiBWaXZhbXVzIGFjIHZlbGl0IHRlbGx1cywgYSBjb25ndWUgbmVxdWUuIEludGVnZXIgbWkgbnVsbGEsIHZhcml1cyBub24gbHVjdHVzIGluLCBkaWN0dW0gc2l0IGFtZXQgc2VtLiBVdCBsYW9yZWV0LCBzYXBpZW4gc2l0IGFtZXQgc2NlbGVyaXNxdWUgcG9ydGEsIHB1cnVzIHNhcGllbiB2ZXN0aWJ1bHVtIG5pYmgsIHNlZCBsdWN0dXMgbGliZXJvIG1hc3NhIGFjIGVsaXQuIERvbmVjIGlhY3VsaXMgb2RpbyBlZ2V0IG9kaW8gc2FnaXR0aXMgbmVjIHZlbmVuYXRpcyBsb3JlbSBibGFuZGl0LgoKQWxpcXVhbSBpbXBlcmRpZXQgdGVsbHVzIHBvc3VlcmUganVzdG8gdmVoaWN1bGEgc2VkIHZlc3RpYnVsdW0gYW50ZSB0cmlzdGlxdWUuIEZ1c2NlIGZldWdpYXQgZmF1Y2lidXMgcHVydXMgbmVjIG1vbGVzdGllLiBOdWxsYSB0ZW1wb3IgbmVxdWUgaWQgbWFnbmEgaWFjdWxpcyBxdWlzIHNvbGxpY2l0dWRpbiBlcm9zIHNlbXBlci4gUHJhZXNlbnQgdml2ZXJyYSBzYWdpdHRpcyBsdWN0dXMuIE1vcmJpIHNpdCBhbWV0IG1hZ25hIHNlZCBvZGlvIGdyYXZpZGEgdmFyaXVzLiBVdCBuaXNpIGxpYmVybywgdnVscHV0YXRlIGZldWdpYXQgcHJldGl1bSB0ZW1wdXMsIGVnZXN0YXMgc2l0IGFtZXQganVzdG8uIFBlbGxlbnRlc3F1ZSBjb25zZXF1
      YXQgdGVtcG9yIG5pc2kgaW4gbG9ib3J0aXMuIFNlZCBmZXJtZW50dW0gY29udmFsbGlzIGR1aSBhYyBzb2xsaWNpdHVkaW4uIEludGVnZXIgYXVjdG9yIGF1Z3VlIGVnZXQgdGVsbHVzIHRlbXB1cyBmcmluZ2lsbGEuIFByb2luIG5lYyBkb2xvciBzYXBpZW4sIG5lYyB0cmlzdGlxdWUgbmliaC4gQWxpcXVhbSBhIHZlbGl0IGF0IG1pIG1hdHRpcyBhbGlxdWV0LgoKUGVsbGVudGVzcXVlIGhhYml0YW50IG1vcmJpIHRyaXN0aXF1ZSBzZW5lY3R1cyBldCBuZXR1cyBldCBtYWxlc3VhZGEgZmFtZXMgYWMgdHVycGlzIGVnZXN0YXMuIEFsaXF1YW0gdWx0cmljZXMgZXJhdCBub24gdHVycGlzIGF1Y3RvciBpZCBvcm5hcmUgbWF1cmlzIHNhZ2l0dGlzLiBRdWlzcXVlIHBvcnR0aXRvciwgdGVsbHVzIHV0IGNvbnZhbGxpcyBzYWdpdHRpcywgbWkgbGliZXJvIGZldWdpYXQgdGVsbHVzLCByaG9uY3VzIHBsYWNlcmF0IGlwc3VtIHRvcnRvciBpZCByaXN1cy4gRG9uZWMgdGluY2lkdW50IGZldWdpYXQgbGVvLiBDcmFzIGlkIG1pIG5lcXVlLCBldSBtYWxlc3VhZGEgZXJvcy4gVXQgbW9sZXN0aWUgbWFnbmEgcXVpcyBsaWJlcm8gcGxhY2VyYXQgbWFsZXN1YWRhLiBBbGlxdWFtIGVyYXQgdm9sdXRwYXQuIEFsaXF1YW0gbm9uIG1hdXJpcyBsb3JlbSwgaW4gYWRpcGlzY2luZyBtZXR1cy4gRG9uZWMgZWdldCBpcHN1bSBpbiBlbGl0IGNvbW1vZG8gb3JuYXJlIGJpYmVuZHVtIGEgbmliaC4gVml2YW11cyBvZGlvIGVyYXQsIHBsY
      WNlcmF0IGFjIHZlc3RpYnVsdW0gZWdldCwgbWFsZXN1YWRhIHV0IG5pc2kuIEV0aWFtIHN1c2NpcGl0IHNvbGxpY2l0dWRpbiBsZW8gc2VtcGVyIHNvbGxpY2l0dWRpbi4gU2VkIHJob25jdXMgcmlzdXMgc2l0IGFtZXQgc2VtIGVsZWlmZW5kIGRpY3R1bSBwcmV0aXVtIHNhcGllbiBlZ2VzdGFzLiBOdWxsYSBhdCB1cm5hIG51bmMsIHZlbCBhbGlxdWV0IGxlby4gUHJhZXNlbnQgdWx0cmljaWVzLCBtaSBldSBwcmV0aXVtIGxvYm9ydGlzLCBlcmF0IG5pYmggZXVpc21vZCBsZW8sIHNpdCBhbWV0IGdyYXZpZGEgc2FwaWVuIGVyb3MgZXQgdHVycGlzLiBEb25lYyBsYWNpbmlhIHZlbmVuYXRpcyBsZWN0dXMsIG5vbiBsYWNpbmlhIG1pIGhlbmRyZXJpdCBzaXQgYW1ldC4gSW50ZWdlciBzZWQgZmVsaXMgdmVsIG9yY2kgYWxpcXVhbSBwdWx2aW5hci4gUGhhc2VsbHVzIGV0IHJpc3VzIGlkIGVyYXQgZXVpc21vZCB0aW5jaWR1bnQuIFNlZCBsdWN0dXMgdGVtcG9yIG5pc2ksIG5lYyB0ZW1wb3IgaXBzdW0gZWxlbWVudHVtIGVnZXQuIEludGVnZXIgbmlzbCB0b3J0b3IsIHZpdmVycmEgaW4gZGFwaWJ1cyBhdCwgbWF0dGlzIGFjIGVyYXQuIEN1cmFiaXR1ciBuZWMgZHVpIGxlY3R1cy4KClBoYXNlbGx1cyBzdXNjaXBpdCwgdG9ydG9yIGV1IHZhcml1cyBmcmluZ2lsbGEsIHNhcGllbiBtYWduYSBlZ2VzdGFzIHJpc3VzLCB1dCBzdXNjaXBpdCBkdWkgbWF1cmlzIHF1aXMgdmVsaXQuIENyYXMgYSBzYXBpZW4gcXVpcyBzYX
      BpZW4gaGVuZHJlcml0IHRyaXN0aXF1ZSBhIHNpdCBhbWV0IGVsaXQuIFBlbGxlbnRlc3F1ZSBkdWkgYXJjdSwgbWFsZXN1YWRhIGV0IHNvZGFsZXMgc2l0IGFtZXQsIGRhcGlidXMgdmVsIHF1YW0uIFNlZCBub24gYWRpcGlzY2luZyBsaWd1bGEuIFV0IHZ1bHB1dGF0ZSBwdXJ1cyBhdCBuaXNsIHBvc3VlcmUgc29kYWxlcy4gTWFlY2VuYXMgZGlhbSB2ZWxpdCwgdGluY2lkdW50IGlkIG1hdHRpcyBldSwgYWxpcXVhbSBhYyBuaXNpLiBNYWVjZW5hcyBwcmV0aXVtLCBhdWd1ZSBhIHNhZ2l0dGlzIHN1c2NpcGl0LCBsZW8gbGlndWxhIGVsZWlmZW5kIGRvbG9yLCBtb2xsaXMgZmV1Z2lhdCBvZGlvIGF1Z3VlIG5vbiBlcm9zLiBQZWxsZW50ZXNxdWUgc2NlbGVyaXNxdWUgb3JjaSBwcmV0aXVtIHF1YW0gbW9sbGlzIGF0IGxvYm9ydGlzIGR1aSBmYWNpbGlzaXMuIE1vcmJpIGNvbmd1ZSBtZXR1cyBpZCB0b3J0b3IgcG9ydGEgZnJpbmdpbGxhLiBTZWQgbG9yZW0gbWksIG1vbGVzdGllIGZlcm1lbnR1bSBzYWdpdHRpcyBhdCwgZ3JhdmlkYSBhIG5pc2kuIERvbmVjIGV1IHZlc3RpYnVsdW0gdmVsaXQuIEluIHZpdmVycmEsIGVuaW0gZXUgZWxlbWVudHVtIHNvZGFsZXMsIGVuaW0gb2RpbyBkYXBpYnVzIHVybmEsIGVnZXQgY29tbW9kbyBuaXNsIG1hdXJpcyB1dCBvZGlvLiBDdXJhYml0dXIgbmVjIGVuaW0gbnVsbGEuIEluIG5lYyBlbGl0IGlwc3VtLiBOdW5jIGluIG1hc3NhIHN1c2NpcGl0IG1hZ25hIGVsZW1lbnR1bSB
      mYXVjaWJ1cyBpbiBuZWMgaXBzdW0uIE51bGxhbSBzdXNjaXBpdCBtYWxlc3VhZGEgZWxlbWVudHVtLiBFdGlhbSBzZWQgbWkgaW4gbmliaCB1bHRyaWNpZXMgdmVuZW5hdGlzIG5lYyBwaGFyZXRyYSBtYWduYS4gSW4gcHVydXMgYW50ZSwgcmhvbmN1cyB2ZWwgcGxhY2VyYXQgc2VkLCBmZXJtZW50dW0gc2l0IGFtZXQgZHVpLiBTZWQgYXQgc29kYWxlcyB2ZWxpdC4KCkR1aXMgc3VzY2lwaXQgcGVsbGVudGVzcXVlIHBlbGxlbnRlc3F1ZS4gUHJhZXNlbnQgcG9ydGEgbG9ib3J0aXMgY3Vyc3VzLiBRdWlzcXVlIHNhZ2l0dGlzIHZlbGl0IG5vbiB0ZWxsdXMgYmliZW5kdW0gYXQgc29sbGljaXR1ZGluIGxhY3VzIGFsaXF1ZXQuIFNlZCBuaWJoIHJpc3VzLCBibGFuZGl0IGEgYWxpcXVldCBlZ2V0LCB2ZWhpY3VsYSBldCBlc3QuIFN1c3BlbmRpc3NlIGZhY2lsaXNpcyBiaWJlbmR1bSBhbGlxdWFtLiBGdXNjZSBjb25zZWN0ZXR1ciBjb252YWxsaXMgZXJhdCwgZWdldCBtb2xsaXMgZGlhbSBmZXJtZW50dW0gc29sbGljaXR1ZGluLiBRdWlzcXVlIHRpbmNpZHVudCBwb3J0dGl0b3IgcHJldGl1bS4gTnVsbGFtIGlkIG5pc2wgZXQgdXJuYSB2dWxwdXRhdGUgZGFwaWJ1cy4gRG9uZWMgcXVpcyBsb3JlbSB1cm5hLiBRdWlzcXVlIGlkIGp1c3RvIG5lYyBudW5jIGJsYW5kaXQgY29udmFsbGlzLiBOdW5jIHZvbHV0cGF0LCBtYXNzYSBzb2xsaWNpdHVkaW4gYWRpcGlzY2luZyB2ZXN0aWJ1bHVtLCBtYXNzYSB1cm5hIGNvbmd1
      ZSBsZWN0dXMsIHNpdCBhbWV0IHVsdHJpY2llcyBhdWd1ZSBvcmNpIGNvbnZhbGxpcyB0dXJwaXMuIE51bGxhIGF0IGxvcmVtIGVsaXQuIE51bmMgdHJpc3RpcXVlLCBxdWFtIGZhY2lsaXNpcyBjb21tb2RvIHBvcnR0aXRvciwgbGFjdXMgbGlndWxhIGFjY3Vtc2FuIG5pc2ksIGV0IGxhb3JlZXQganVzdG8gYW50ZSB2aXRhZSBlcm9zLiBDdXJhYml0dXIgc2VkIGF1Z3VlIGFyY3UuIFBoYXNlbGx1cyBwb3J0dGl0b3IgdmVzdGlidWx1bSBmZWxpcywgdXQgY29uc2VjdGV0dXIgYXJjdSB0ZW1wb3Igbm9uLiBJbiBqdXN0byByaXN1cywgc2VtcGVyIGV0IHN1c2NpcGl0IGlkLCB1bGxhbWNvcnBlciBhdCB1cm5hLiBRdWlzcXVlIHRpbmNpZHVudCwgdXJuYSBuZWMgYWxpcXVhbSB0cmlzdGlxdWUsIG5pYmggb2RpbyBmYXVjaWJ1cyBhdWd1ZSwgaW4gb3JuYXJlIGVuaW0gdHVycGlzIGFjY3Vtc2FuIGRvbG9yLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gU3VzcGVuZGlzc2Ugc29kYWxlcyB2YXJpdXMgdHVycGlzIGV1IGZlcm1lbnR1bS4KCk1vcmJpIHVsdHJpY2llcyBkaWFtIGVnZXQgbWFzc2EgcG9zdWVyZSBsb2JvcnRpcy4gQWxpcXVhbSB2b2x1dHBhdCBwZWxsZW50ZXNxdWUgZW5pbSBldSBwb3J0dGl0b3IuIERvbmVjIGxhY3VzIGZlbGlzLCBjb25zZWN0ZXR1ciBhIHByZXRpdW0gdml0YWUsI
      GJpYmVuZHVtIG5vbiBlbmltLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gRXRpYW0gdXQgbmliaCBhIHF1YW0gcGVsbGVudGVzcXVlIGF1Y3RvciB1dCBpZCB2ZWxpdC4gRHVpcyBsYWNpbmlhIGp1c3RvIGVnZXQgbWkgcGxhY2VyYXQgYmliZW5kdW0uIEN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0dXIgcmlkaWN1bHVzIG11cy4gRG9uZWMgdmVsaXQgdG9ydG9yLCB0ZW1wdXMgbmVjIHRyaXN0aXF1ZSBpZCwgYWxpcXVldCBzaXQgYW1ldCB0dXJwaXMuIFByYWVzZW50IGV0IG5lcXVlIG5lYyBtYWduYSBwb3J0YSBmcmluZ2lsbGEuIE1vcmJpIGlkIGVnZXN0YXMgZXJvcy4gRG9uZWMgc2VtcGVyIHRpbmNpZHVudCB1bGxhbWNvcnBlci4gUGhhc2VsbHVzIHRlbXB1cyBsYWNpbmlhIGhlbmRyZXJpdC4gUXVpc3F1ZSBmYXVjaWJ1cyBwcmV0aXVtIG5lcXVlIG5vbiBjb252YWxsaXMuIE51bmMgbWFsZXN1YWRhIGFjY3Vtc2FuIHJob25jdXMuIENyYXMgbG9ib3J0aXMsIHNlbSBzZWQgZnJpbmdpbGxhIGNvbnZhbGxpcywgYXVndWUgdmVsaXQgc2VtcGVyIG5pc2wsIGNvbW1vZG8gdmFyaXVzIG5pc2kgZGlhbSBhYyBsZW8uCgpRdWlzcXVlIGludGVyZHVtIHRlbGx1cyBhYyBhbnRlIHBvc3VlcmUgdXQgY3Vyc3VzIGxvcmVtIG
      VnZXN0YXMuIE51bGxhIGZhY2lsaXNpLiBBZW5lYW4gc2VkIG1hc3NhIG5lYyBuaXNpIHNjZWxlcmlzcXVlIHZ1bHB1dGF0ZS4gRXRpYW0gY29udmFsbGlzIGNvbnNlY3RldHVyIGlhY3VsaXMuIE1hZWNlbmFzIGFjIHB1cnVzIHV0IGFudGUgZGlnbmlzc2ltIGF1Y3RvciBhYyBxdWlzIGxvcmVtLiBQZWxsZW50ZXNxdWUgc3VzY2lwaXQgdGluY2lkdW50IG9yY2kuIEZ1c2NlIGFsaXF1YW0gZGFwaWJ1cyBvcmNpLCBhdCBiaWJlbmR1bSBpcHN1bSBhZGlwaXNjaW5nIGVnZXQuIE1vcmJpIHBlbGxlbnRlc3F1ZSBoZW5kcmVyaXQgcXVhbSwgbmVjIHBsYWNlcmF0IHVybmEgdnVscHV0YXRlIHNlZC4gUXVpc3F1ZSB2ZWwgZGlhbSBsb3JlbS4gUHJhZXNlbnQgaWQgZGlhbSBxdWlzIGVuaW0gZWxlbWVudHVtIHJob25jdXMgc2FnaXR0aXMgZWdldCBwdXJ1cy4gUXVpc3F1ZSBmcmluZ2lsbGEgYmliZW5kdW0gbGVvIGluIGxhb3JlZXQuIFZlc3RpYnVsdW0gaWQgbmliaCByaXN1cywgbm9uIGVsZW1lbnR1bSBtZXR1cy4gVXQgYSBmZWxpcyBkaWFtLCBub24gbW9sbGlzIG5pc2wuIENyYXMgZWxpdCBhbnRlLCB1bGxhbWNvcnBlciBxdWlzIGlhY3VsaXMgZXUsIHNvZGFsZXMgdmVsIGVzdC4gQ3VyYWJpdHVyIHF1aXMgbG9ib3J0aXMgZG9sb3IuIEFsaXF1YW0gbWF0dGlzIGdyYXZpZGEgbWV0dXMgcGVsbGVudGVzcXVlIHZ1bHB1dGF0ZS4KClV0IGlkIGF1Z3VlIGlkIGRvbG9yIGx1Y3R1cyBldWlzbW9kIGV0IHF1aXMgdmVsaXQ
      uIE1hZWNlbmFzIGVuaW0gZG9sb3IsIHRlbXB1cyBzaXQgYW1ldCBoZW5kcmVyaXQgZXUsIGZhdWNpYnVzIHZpdGFlIG5lcXVlLiBQcm9pbiBzaXQgYW1ldCB2YXJpdXMgZWxpdC4gUHJvaW4gdmFyaXVzIGZlbGlzIHVsbGFtY29ycGVyIHB1cnVzIGRpZ25pc3NpbSBjb25zZXF1YXQuIENyYXMgY3Vyc3VzIHRlbXB1cyBlcm9zLiBOdW5jIHVsdHJpY2VzIHZlbmVuYXRpcyB1bGxhbWNvcnBlci4gQWxpcXVhbSBldCBmZXVnaWF0IHRlbGx1cy4gUGhhc2VsbHVzIHNpdCBhbWV0IHZlc3RpYnVsdW0gZWxpdC4gUGhhc2VsbHVzIGFjIHB1cnVzIGxhY3VzLCBldCBhY2N1bXNhbiBlcm9zLiBNb3JiaSB1bHRyaWNlcywgcHVydXMgYSBwb3J0YSBzb2RhbGVzLCBvZGlvIG1ldHVzIHBvc3VlcmUgbmVxdWUsIG5lYyBlbGVtZW50dW0gcmlzdXMgdHVycGlzIHNpdCBhbWV0IG1hZ25hLiBTZWQgZXN0IHF1YW0sIHVsdHJpY2llcyBhdCBjb25ndWUgYWRpcGlzY2luZywgbG9ib3J0aXMgaW4ganVzdG8uIFByb2luIGlhY3VsaXMgZGljdHVtIG51bmMsIGV1IGxhb3JlZXQgcXVhbSB2YXJpdXMgdml0YWUuIERvbmVjIHNpdCBhbWV0IGZldWdpYXQgdHVycGlzLiBNYXVyaXMgc2l0IGFtZXQgbWFnbmEgcXVhbSwgYWMgY29uc2VjdGV0dXIgZHVpLiBDdXJhYml0dXIgZWdldCBtYWduYSB0ZWxsdXMsIGV1IHBoYXJldHJhIGZlbGlzLiBEb25lYyBzaXQgYW1ldCB0b3J0b3IgbmlzbC4gQWxpcXVhbSBldCB0b3J0b3IgZmFjaWxpc2lzIGxhY3Vz
      IHRpbmNpZHVudCBjb21tb2RvLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gQ3VyYWJpdHVyIG51bmMgbWFnbmEsIHVsdHJpY2llcyBpZCBjb252YWxsaXMgYXQsIHVsbGFtY29ycGVyIHZpdGFlIG1hc3NhLgoKUGhhc2VsbHVzIHZpdmVycmEgaWFjdWxpcyBwbGFjZXJhdC4gTnVsbGEgY29uc2VxdWF0IGRvbG9yIHNpdCBhbWV0IGVyYXQgZGlnbmlzc2ltIHBvc3VlcmUuIE51bGxhIGxhY2luaWEgYXVndWUgdml0YWUgbWkgdGVtcG9yIGdyYXZpZGEuIFBoYXNlbGx1cyBub24gdGVtcG9yIHRlbGx1cy4gUXVpc3F1ZSBub24gZW5pbSBzZW1wZXIgdG9ydG9yIHNhZ2l0dGlzIGZhY2lsaXNpcy4gQWxpcXVhbSB1cm5hIGZlbGlzLCBlZ2VzdGFzIGF0IHBvc3VlcmUgbmVjLCBhbGlxdWV0IGV1IG5pYmguIFByYWVzZW50IHNlZCB2ZXN0aWJ1bHVtIGVuaW0uIE1hdXJpcyBpYWN1bGlzIHZlbGl0IGR1aSwgZXQgZnJpbmdpbGxhIGVuaW0uIE51bGxhIG5lYyBuaXNpIG9yY2kuIFNlZCB2b2x1dHBhdCwganVzdG8gZWdldCBmcmluZ2lsbGEgYWRpcGlzY2luZywgbmlzbCBudWxsYSBjb25kaW1lbnR1bSBsaWJlcm8sIHNlZCBzb2RhbGVzIGVzdCBlc3QgZXQgb2Rpby4gQ3JhcyBpcHN1bSBkdWksIHZhcml1cyBldSBlbGVtZW50dW0gY29uc2VxdWF0LCBmYXVjaWJ1cyBpbiBsZW8uIFBlbGxlbnRlc3F1ZSBoY
      WJpdGFudCBtb3JiaSB0cmlzdGlxdWUgc2VuZWN0dXMgZXQgbmV0dXMgZXQgbWFsZXN1YWRhIGZhbWVzIGFjIHR1cnBpcyBlZ2VzdGFzLgoKVXQgbWFsZXN1YWRhIG1vbGVzdGllIGVsZWlmZW5kLiBDdXJhYml0dXIgaWQgZW5pbSBkdWksIGV1IHRpbmNpZHVudCBuaWJoLiBNYXVyaXMgc2l0IGFtZXQgYW50ZSBsZW8uIER1aXMgdHVycGlzIGlwc3VtLCBiaWJlbmR1bSBzZWQgbWF0dGlzIHNpdCBhbWV0LCBhY2N1bXNhbiBxdWlzIGRvbG9yLiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgQWVuZWFuIGEgaW1wZXJkaWV0IG1ldHVzLiBRdWlzcXVlIHNvbGxpY2l0dWRpbiBmZWxpcyBpZCBuZXF1ZSB0ZW1wb3Igc2NlbGVyaXNxdWUuIERvbmVjIGF0IG9yY2kgZmVsaXMuIFZpdmFtdXMgdGVtcHVzIGNvbnZhbGxpcyBhdWN0b3IuIERvbmVjIGludGVyZHVtIGV1aXNtb2QgbG9ib3J0aXMuIFNlZCBhdCBsYWN1cyBuZWMgb2RpbyBkaWduaXNzaW0gbW9sbGlzLiBTZWQgc2FwaWVuIG9yY2ksIHBvcnR0aXRvciB0ZW1wdXMgYWNjdW1zYW4gdmVsLCB0aW5jaWR1bnQgbmVjIGFudGUuIE51bmMgcmhvbmN1cyBlZ2VzdGFzIGRhcGlidXMuIFN1c3BlbmRpc3NlIGZlcm1lbnR1bSBkaWN0dW0gZnJpbmdpbGxhLiBOdWxsYW0gbmlzaSBqdXN0bywgZWxlaWZlbmQgYSBjb25zZWN0ZXR1ciBjb252YWxsaXMsIHBvcnR0aXRvci
      BldCB0b3J0b3IuIFByb2luIHZpdGFlIGxvcmVtIG5vbiBkb2xvciBzdXNjaXBpdCBsYWNpbmlhIGV1IGVnZXQgbnVsbGEuCgpTdXNwZW5kaXNzZSBlZ2VzdGFzLCBzYXBpZW4gc2l0IGFtZXQgYmxhbmRpdCBzY2VsZXJpc3F1ZSwgbnVsbGEgYXJjdSB0cmlzdGlxdWUgZHVpLCBhIHBvcnRhIGp1c3RvIHF1YW0gdml0YWUgYXJjdS4gSW4gbWV0dXMgbGliZXJvLCBiaWJlbmR1bSBub24gdm9sdXRwYXQgdXQsIGxhb3JlZXQgdmVsIHR1cnBpcy4gTnVuYyBmYXVjaWJ1cyB2ZWxpdCBldSBpcHN1bSBjb21tb2RvIG5lYyBpYWN1bGlzIGVyb3Mgdm9sdXRwYXQuIFZpdmFtdXMgY29uZ3VlIGF1Y3RvciBlbGl0IHNlZCBzdXNjaXBpdC4gRHVpcyBjb21tb2RvLCBsaWJlcm8gZXUgdmVzdGlidWx1bSBmZXVnaWF0LCBsZW8gbWkgZGFwaWJ1cyB0ZWxsdXMsIGluIHBsYWNlcmF0IG5pc2wgZHVpIGF0IGVzdC4gVmVzdGlidWx1bSB2aXZlcnJhIHRyaXN0aXF1ZSBsb3JlbSwgb3JuYXJlIGVnZXN0YXMgZXJhdCBydXRydW0gYS4gTnVsbGFtIGF0IGF1Z3VlIG1hc3NhLCB1dCBjb25zZWN0ZXR1ciBpcHN1bS4gUGVsbGVudGVzcXVlIG1hbGVzdWFkYSwgdmVsaXQgdXQgbG9ib3J0aXMgc2FnaXR0aXMsIG5pc2kgbWFzc2Egc2VtcGVyIG9kaW8sIG1hbGVzdWFkYSBzZW1wZXIgcHVydXMgbmlzbCB2ZWwgbGVjdHVzLiBOdW5jIGR1aSBzZW0sIG1hdHRpcyB2aXRhZSBsYW9yZWV0IHZpdGFlLCBzb2xsaWNpdHVkaW4gYWMgbGVvLiBOdWxsYSB
      2ZWwgZmVybWVudHVtIGVzdC4KClZpdmFtdXMgaW4gb2RpbyBhIG5pc2kgZGlnbmlzc2ltIHJob25jdXMgaW4gaW4gbGFjdXMuIERvbmVjIGV0IG5pc2wgdG9ydG9yLiBEb25lYyBzYWdpdHRpcyBjb25zZXF1YXQgbWksIHZlbCBwbGFjZXJhdCB0ZWxsdXMgY29udmFsbGlzIGlkLiBBbGlxdWFtIGZhY2lsaXNpcyBydXRydW0gbmlzbCBzZWQgcHJldGl1bS4gRG9uZWMgZXQgbGFjaW5pYSBuaXNsLiBBbGlxdWFtIGVyYXQgdm9sdXRwYXQuIEN1cmFiaXR1ciBhYyBwdWx2aW5hciB0ZWxsdXMuIE51bGxhbSB2YXJpdXMgbG9ib3J0aXMgcG9ydGEuIENyYXMgZGFwaWJ1cywgbGlndWxhIHV0IHBvcnRhIHVsdHJpY2llcywgbGVvIGxhY3VzIHZpdmVycmEgcHVydXMsIHF1aXMgbW9sbGlzIHVybmEgcmlzdXMgZXUgbGVvLiBOdW5jIG1hbGVzdWFkYSBjb25zZWN0ZXR1ciBwdXJ1cywgdmVsIGF1Y3RvciBsZWN0dXMgc2NlbGVyaXNxdWUgcG9zdWVyZS4gTWFlY2VuYXMgZHVpIG1hc3NhLCB2ZXN0aWJ1bHVtIGJpYmVuZHVtIGJsYW5kaXQgbm9uLCBpbnRlcmR1bSBlZ2V0IG1hdXJpcy4gUGhhc2VsbHVzIGVzdCBhbnRlLCBwdWx2aW5hciBhdCBpbXBlcmRpZXQgcXVpcywgaW1wZXJkaWV0IHZlbCB1cm5hLiBRdWlzcXVlIGVnZXQgdm9sdXRwYXQgb3JjaS4gUXVpc3F1ZSBldCBhcmN1IHB1cnVzLCB1dCBmYXVjaWJ1cyB2ZWxpdC4KClByYWVzZW50IHNlZCBpcHN1bSB1cm5hLiBQcmFlc2VudCBzYWdpdHRpcyB2YXJpdXMgbWFnbmEs
      IGlkIGNvbW1vZG8gZG9sb3IgbWFsZXN1YWRhIGFjLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gUXVpc3F1ZSBzaXQgYW1ldCBudW5jIGV1IHNlbSBvcm5hcmUgdGVtcG9yLiBNYXVyaXMgaWQgZG9sb3IgbmVjIGVyYXQgY29udmFsbGlzIHBvcnRhIGluIGxvYm9ydGlzIG5pc2kuIEN1cmFiaXR1ciBoZW5kcmVyaXQgcmhvbmN1cyB0b3J0b3IgZXUgaGVuZHJlcml0LiBQZWxsZW50ZXNxdWUgZXUgYW50ZSB2ZWwgZWxpdCBsdWN0dXMgZWxlaWZlbmQgcXVpcyB2aXZlcnJhIG51bGxhLiBTdXNwZW5kaXNzZSBvZGlvIGRpYW0sIGV1aXNtb2QgZXUgcG9ydHRpdG9yIG1vbGVzdGllLCBzb2xsaWNpdHVkaW4gc2l0IGFtZXQgbnVsbGEuIFNlZCBhbnRlIHVybmEsIGRpY3R1bSBiaWJlbmR1bSByaG9uY3VzIGV0LCBibGFuZGl0IG5lYyBhbnRlLiBTdXNwZW5kaXNzZSB0b3J0b3IgYXVndWUsIGFjY3Vtc2FuIHF1aXMgc3VzY2lwaXQgaWQsIGFjY3Vtc2FuIHNpdCBhbWV0IGVyYXQuIERvbmVjIHBoYXJldHJhIHZhcml1cyBsb2JvcnRpcy4gTWFlY2VuYXMgaXBzdW0gZGlhbSwgZmF1Y2lidXMgZXUgdGVtcHVzIGlkLCBjb252YWxsaXMgbmVjIGVuaW0uIER1aXMgYXJjdSB0dXJwaXMsIGZyaW5naWxsYSBuZWMgZWdlc3RhcyB1dCwgZGlnbmlzc2ltIHRyaXN0aXF1ZSBudWxsYS4gQ3VyYWJpdHVyIHN1c2Npc
      Gl0IGR1aSBub24ganVzdG8gdWx0cmljZXMgcGhhcmV0cmEuIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gTnVsbGEgZmFjaWxpc2kuIFF1aXNxdWUgaWQgZmVsaXMgZXUgc2VtIGFsaXF1YW0gZnJpbmdpbGxhLgoKRXRpYW0gcXVpcyBhdWd1ZSBpbiB0ZWxsdXMgY29uc2VxdWF0IGVsZWlmZW5kLiBBZW5lYW4gZGlnbmlzc2ltIGNvbmd1ZSBmZWxpcyBpZCBlbGVtZW50dW0uIER1aXMgZnJpbmdpbGxhIHZhcml1cyBpcHN1bSwgbmVjIHN1c2NpcGl0IGxlbyBzZW1wZXIgdmVsLiBVdCBzb2xsaWNpdHVkaW4sIG9yY2kgYSB0aW5jaWR1bnQgYWNjdW1zYW4sIGRpYW0gbGVjdHVzIGxhb3JlZXQgbGFjdXMsIHZlbCBmZXJtZW50dW0gcXVhbSBlc3QgdmVsIGVyb3MuIEFsaXF1YW0gZnJpbmdpbGxhIHNhcGllbiBhYyBzYXBpZW4gZmF1Y2lidXMgY29udmFsbGlzLiBBbGlxdWFtIGlkIG51bmMgZXUganVzdG8gY29uc2VxdWF0IHRpbmNpZHVudC4gUXVpc3F1ZSBuZWMgbmlzbCBkdWkuIFBoYXNlbGx1cyBhdWd1ZSBsZWN0dXMsIHZhcml1cyB2aXRhZSBhdWN0b3IgdmVsLCBydXRydW0gYXQgcmlzdXMuIFZpdmFtdXMgbGFjaW5pYSBsZW8gcXVpcyBuZXF1ZSB1bHRyaWNlcyBuZWMgZWxlbWVudHVtIGZlbGlzIGZyaW5naWxsYS4gUHJvaW4gdmVsIHBvcnR0aXRvciBsZWN0dXMuCgpDdXJhYml0dXIgc2FwaWVuIGxvcmVtLCBtb2xsaXMgdXQgYWNjdW1zYW4gbm9uLCB1bHRyaWNpZXMgZXQgbWV0dXMuIEN1cmFiaXR1ciB2ZWwgbG9yZW
      0gcXVpcyBzYXBpZW4gZnJpbmdpbGxhIGxhb3JlZXQuIE1vcmJpIGlkIHVybmEgYWMgb3JjaSBlbGVtZW50dW0gYmxhbmRpdCBlZ2V0IHZvbHV0cGF0IG5lcXVlLiBQZWxsZW50ZXNxdWUgc2VtIG9kaW8sIGlhY3VsaXMgZXUgcGhhcmV0cmEgdml0YWUsIGN1cnN1cyBpbiBxdWFtLiBOdWxsYSBtb2xlc3RpZSBsaWd1bGEgaWQgbWFzc2EgbHVjdHVzIGV0IHB1bHZpbmFyIG5pc2kgcHVsdmluYXIuIE51bmMgZmVybWVudHVtIGF1Z3VlIGEgbGFjdXMgZnJpbmdpbGxhIHJob25jdXMgcG9ydHRpdG9yIGVyYXQgZGljdHVtLiBOdW5jIHNpdCBhbWV0IHRlbGx1cyBldCBkdWkgdml2ZXJyYSBhdWN0b3IgZXVpc21vZCBhdCBuaXNsLiBJbiBzZWQgY29uZ3VlIG1hZ25hLiBQcm9pbiBldCB0b3J0b3IgdXQgYXVndWUgcGxhY2VyYXQgZGlnbmlzc2ltIGEgZXUganVzdG8uIE1vcmJpIHBvcnR0aXRvciBwb3J0YSBsb2JvcnRpcy4gUGVsbGVudGVzcXVlIG5pYmggbGFjdXMsIGFkaXBpc2NpbmcgdXQgdHJpc3RpcXVlIHF1aXMsIGNvbnNlcXVhdCB2aXRhZSB2ZWxpdC4gTWFlY2VuYXMgdXQgbHVjdHVzIGxpYmVyby4gVml2YW11cyBhdWN0b3Igb2RpbyBldCBlcmF0IHNlbXBlciBzYWdpdHRpcy4gVml2YW11cyBpbnRlcmR1bSB2ZWxpdCBpbiByaXN1cyBtYXR0aXMgcXVpcyBkaWN0dW0gYW50ZSByaG9uY3VzLiBJbiBzYWdpdHRpcyBwb3J0dGl0b3IgZXJvcywgYXQgbG9ib3J0aXMgbWV0dXMgdWx0cmljZXMgdmVsLiBDdXJhYml0dXI
      gbm9uIGFsaXF1YW0gbmlzbC4gVmVzdGlidWx1bSBsdWN0dXMgZmV1Z2lhdCBzdXNjaXBpdC4gRXRpYW0gbm9uIGxhY3VzIHZlbCBudWxsYSBlZ2VzdGFzIGlhY3VsaXMgaWQgcXVpcyByaXN1cy4KCkV0aWFtIGluIGF1Y3RvciB1cm5hLiBGdXNjZSB1bHRyaWNpZXMgbW9sZXN0aWUgY29udmFsbGlzLiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4gVmVzdGlidWx1bSBhbnRlIGlwc3VtIHByaW1pcyBpbiBmYXVjaWJ1cyBvcmNpIGx1Y3R1cyBldCB1bHRyaWNlcyBwb3N1ZXJlIGN1YmlsaWEgQ3VyYWU7IE1hdXJpcyBpYWN1bGlzIGxvcmVtIGZhdWNpYnVzIHB1cnVzIGdyYXZpZGEgYXQgY29udmFsbGlzIHR1cnBpcyBzb2xsaWNpdHVkaW4uIFN1c3BlbmRpc3NlIGF0IHZlbGl0IGxvcmVtLCBhIGZlcm1lbnR1bSBpcHN1bS4gRXRpYW0gY29uZGltZW50dW0sIGR1aSB2ZWwgY29uZGltZW50dW0gZWxlbWVudHVtLCBzYXBpZW4gc2VtIGJsYW5kaXQgc2FwaWVuLCBldCBwaGFyZXRyYSBsZW8gbmVxdWUgZXQgbGVjdHVzLiBOdW5jIHZpdmVycmEgdXJuYSBpYWN1bGlzIGF1Z3VlIHVsdHJpY2VzIGFjIHBvcnR0aXRvciBsYWN1cyBkaWduaXNzaW0uIEFsaXF1YW0gdXQgdHVycGlzIGR1aS4gU2VkIGVnZXQgYWxpcXVldCBmZWxpcy4gSW4gYmliZW5kdW0gbmliaCBzaXQgYW1ldCBzYXBpZW4gYWNjdW1zYW4gYWNjdW1zYW4gcGhhcmV0cmEgbWFnbmEgbW9sZXN0aWUuCgpNYXVyaXMgYWxpcXVldCB1cm5hIGVnZXQg
      bGVjdHVzIGFkaXBpc2NpbmcgYXQgY29uZ3VlIHR1cnBpcyBjb25zZXF1YXQuIFZpdmFtdXMgdGluY2lkdW50IGZlcm1lbnR1bSByaXN1cyBldCBmZXVnaWF0LiBOdWxsYSBtb2xlc3RpZSB1bGxhbWNvcnBlciBuaWJoIHNlZCBmYWNpbGlzaXMuIFBoYXNlbGx1cyBldCBjdXJzdXMgcHVydXMuIE5hbSBjdXJzdXMsIGR1aSBkaWN0dW0gdWx0cmljZXMgdml2ZXJyYSwgZXJhdCByaXN1cyB2YXJpdXMgZWxpdCwgZXUgbW9sZXN0aWUgZHVpIGVyb3MgcXVpcyBxdWFtLiBBbGlxdWFtIGV0IGFudGUgbmVxdWUsIGFjIGNvbnNlY3RldHVyIGR1aS4gRG9uZWMgY29uZGltZW50dW0gZXJhdCBpZCBlbGl0IGRpY3R1bSBzZWQgYWNjdW1zYW4gbGVvIHNhZ2l0dGlzLiBQcm9pbiBjb25zZXF1YXQgY29uZ3VlIHJpc3VzLCB2ZWwgdGluY2lkdW50IGxlbyBpbXBlcmRpZXQgZXUuIFZlc3RpYnVsdW0gbWFsZXN1YWRhIHR1cnBpcyBldSBtZXR1cyBpbXBlcmRpZXQgcHJldGl1bS4gQWxpcXVhbSBjb25kaW1lbnR1bSB1bHRyaWNlcyBuaWJoLCBldSBzZW1wZXIgZW5pbSBlbGVpZmVuZCBhLiBFdGlhbSBjb25kaW1lbnR1bSBuaXNsIHF1YW0uCgpQZWxsZW50ZXNxdWUgaWQgbW9sZXN0aWUgbmlzbC4gTWFlY2VuYXMgZXQgbGVjdHVzIGF0IGp1c3RvIG1vbGVzdGllIHZpdmVycmEgc2l0IGFtZXQgc2l0IGFtZXQgbGlndWxhLiBOdWxsYW0gbm9uIHBvcnR0aXRvciBtYWduYS4gUXVpc3F1ZSBlbGVtZW50dW0gYXJjdSBjdXJzdXMgdG9ydG9yI
      HJ1dHJ1bSBsb2JvcnRpcy4gTW9yYmkgc2l0IGFtZXQgbGVjdHVzIHZpdGFlIGVuaW0gZXVpc21vZCBkaWduaXNzaW0gZWdldCBhdCBuZXF1ZS4gVml2YW11cyBjb25zZXF1YXQgdmVoaWN1bGEgZHVpLCB2aXRhZSBhdWN0b3IgYXVndWUgZGlnbmlzc2ltIGluLiBJbiB0ZW1wdXMgc2VtIHF1aXMganVzdG8gdGluY2lkdW50IHNpdCBhbWV0IGF1Y3RvciB0dXJwaXMgbG9ib3J0aXMuIFBlbGxlbnRlc3F1ZSBub24gZXN0IG51bmMuIFZlc3RpYnVsdW0gbW9sbGlzIGZyaW5naWxsYSBpbnRlcmR1bS4gTWFlY2VuYXMgaXBzdW0gZG9sb3IsIHBoYXJldHJhIGlkIHRyaXN0aXF1ZSBtYXR0aXMsIGx1Y3R1cyB2aXRhZSB1cm5hLiBVdCB1bGxhbWNvcnBlciBhcmN1IGVnZXQgZWxpdCBjb252YWxsaXMgbW9sbGlzLiBQZWxsZW50ZXNxdWUgY29uZGltZW50dW0sIG1hc3NhIGFjIGhlbmRyZXJpdCB0ZW1wb3IsIG1hdXJpcyBwdXJ1cyBibGFuZGl0IGp1c3RvLCBldCBwaGFyZXRyYSBsZW8ganVzdG8gYSBlc3QuIER1aXMgYXJjdSBhdWd1ZSwgZmFjaWxpc2lzIHZlbCBkaWduaXNzaW0gc2VkLCBhbGlxdWFtIHF1aXMgbWFnbmEuIFF1aXNxdWUgbm9uIGNvbnNlcXVhdCBkb2xvci4gU3VzcGVuZGlzc2UgYSB1bHRyaWNlcyBsZW8uCgpEb25lYyB2aXRhZSBwcmV0aXVtIG5pYmguIE1hZWNlbmFzIGJpYmVuZHVtIGJpYmVuZHVtIGRpYW0gaW4gcGxhY2VyYXQuIFV0IGFjY3Vtc2FuLCBtaSB2aXRhZSB2ZXN0aWJ1bHVtIGV1aXNtb2QsIG
      51bmMganVzdG8gdnVscHV0YXRlIG5pc2ksIG5vbiBwbGFjZXJhdCBtaSB1cm5hIGV0IGRpYW0uIE1hZWNlbmFzIG1hbGVzdWFkYSBsb3JlbSB1dCBhcmN1IG1hdHRpcyBtb2xsaXMuIE51bGxhIGZhY2lsaXNpLiBEb25lYyBlc3QgbGVvLCBiaWJlbmR1bSBldSBwdWx2aW5hciBpbiwgY3Vyc3VzIHZlbCBtZXR1cy4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LiBOdWxsYW0gZmV1Z2lhdCBwb3J0dGl0b3IgbmVxdWUgaW4gdnVscHV0YXRlLiBRdWlzcXVlIG5lYyBtaSBldSBtYWduYSBjb25zZXF1YXQgY3Vyc3VzIG5vbiBhdCBhcmN1LiBFdGlhbSByaXN1cyBtZXR1cywgc29sbGljaXR1ZGluIGV0IHVsdHJpY2VzIGF0LCB0aW5jaWR1bnQgc2VkIG51bmMuIFNlZCBlZ2V0IHNjZWxlcmlzcXVlIGF1Z3VlLiBVdCBmcmluZ2lsbGEgdmVuZW5hdGlzIHNlbSBub24gZWxlaWZlbmQuIE51bmMgbWF0dGlzLCByaXN1cyBzaXQgYW1ldCB2dWxwdXRhdGUgdmFyaXVzLCByaXN1cyBqdXN0byBlZ2VzdGFzIG1hdXJpcywgaWQgaW50ZXJkdW0gb2RpbyBpcHN1bSBldCBuaXNsLiBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBNb3JiaSBpZCBlcmF0IG9kaW8sIG5lYyBwdWx2aW5hciBlbmltLgoKQ3VyYWJpdHVyIGFjIGZlcm1lbnR1bSBxdWFtLiBNb3JiaSBldSBlcm9zIHNhcGllbiwgdml0YWUgdGVtcHVzIGRvbG9yLiBNYXVyaXMgdmVzdGlidWx1bSBibGFuZGl0IGVuaW0gdXQgdmV
      uZW5hdGlzLiBBbGlxdWFtIGVnZXN0YXMsIGVyb3MgYXQgY29uc2VjdGV0dXIgdGluY2lkdW50LCBsb3JlbSBhdWd1ZSBpYWN1bGlzIGVzdCwgbmVjIG1vbGxpcyBmZWxpcyBhcmN1IGluIG51bmMuIFNlZCBpbiBvZGlvIHNlZCBsaWJlcm8gcGVsbGVudGVzcXVlIHZvbHV0cGF0IHZpdGFlIGEgYW50ZS4gTW9yYmkgY29tbW9kbyB2b2x1dHBhdCB0ZWxsdXMsIHV0IHZpdmVycmEgcHVydXMgcGxhY2VyYXQgZmVybWVudHVtLiBJbnRlZ2VyIGlhY3VsaXMgZmFjaWxpc2lzIGFyY3UsIGF0IGdyYXZpZGEgbG9yZW0gYmliZW5kdW0gYXQuIEFlbmVhbiBpZCBlcm9zIGVnZXQgZXN0IHNhZ2l0dGlzIGNvbnZhbGxpcyBzZWQgZXQgZHVpLiBEb25lYyBldSBwdWx2aW5hciB0ZWxsdXMuIE51bmMgZGlnbmlzc2ltIHJob25jdXMgdGVsbHVzLCBhdCBwZWxsZW50ZXNxdWUgbWV0dXMgbHVjdHVzIGF0LiBTZWQgb3JuYXJlIGFsaXF1YW0gZGlhbSwgYSBwb3J0dGl0b3IgbGVvIHNvbGxpY2l0dWRpbiBzZWQuIE5hbSB2aXRhZSBsZWN0dXMgbGFjdXMuIEludGVnZXIgYWRpcGlzY2luZyBxdWFtIG5lcXVlLCBibGFuZGl0IHBvc3VlcmUgbGliZXJvLiBTZWQgbGliZXJvIG51bmMsIGVnZXN0YXMgc29kYWxlcyB0ZW1wdXMgc2VkLCBjdXJzdXMgYmxhbmRpdCB0ZWxsdXMuIFZlc3RpYnVsdW0gbWkgcHVydXMsIHVsdHJpY2llcyBxdWlzIHBsYWNlcmF0IHZlbCwgbW9sZXN0aWUgYXQgZHVpLgoKTnVsbGEgY29tbW9kbyBvZGlvIGp1c3RvLiBQ
      ZWxsZW50ZXNxdWUgbm9uIG9ybmFyZSBkaWFtLiBJbiBjb25zZWN0ZXR1ciBzYXBpZW4gYWMgbnVuYyBzYWdpdHRpcyBtYWxlc3VhZGEuIE1vcmJpIHVsbGFtY29ycGVyIHRlbXBvciBlcmF0IG5lYyBydXRydW0uIER1aXMgdXQgY29tbW9kbyBqdXN0by4gQ3JhcyBlc3Qgb3JjaSwgY29uc2VjdGV0dXIgc2VkIGludGVyZHVtIHNlZCwgc2NlbGVyaXNxdWUgc2l0IGFtZXQgbnVsbGEuIFZlc3RpYnVsdW0ganVzdG8gbnVsbGEsIHBlbGxlbnRlc3F1ZSBhIHRlbXB1cyBldCwgZGFwaWJ1cyBldCBhcmN1LiBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBNb3JiaSB0cmlzdGlxdWUsIGVyb3MgbmVjIGNvbmd1ZSBhZGlwaXNjaW5nLCBsaWd1bGEgc2VtIHJob25jdXMgZmVsaXMsIGF0IG9ybmFyZSB0ZWxsdXMgbWF1cmlzIGFjIHJpc3VzLiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kgbHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgUHJvaW4gbWF1cmlzIGR1aSwgdGVtcG9yIGZlcm1lbnR1bSBkaWN0dW0gZXQsIGN1cnN1cyBhIGxlby4gTWFlY2VuYXMgbmVjIG5pc2wgYSB0ZWxsdXMgcGVsbGVudGVzcXVlIHJob25jdXMuIE51bGxhbSB1bHRyaWNlcyBldWlzbW9kIGR1aSBldSBjb25ndWUuCgpJbiBuZWMgdGVtcG9yIHJpc3VzLiBJbiBmYXVjaWJ1cyBuaXNpIGVnZXQgZGlhbSBkaWduaXNzaW0gY29uc2Vxd
      WF0LiBEb25lYyBwdWx2aW5hciBhbnRlIG5lYyBlbmltIG1hdHRpcyBydXRydW0uIFZlc3RpYnVsdW0gbGVvIGF1Z3VlLCBtb2xlc3RpZSBuZWMgZGFwaWJ1cyBpbiwgZGljdHVtIGF0IGVuaW0uIEludGVnZXIgYWxpcXVhbSwgbG9yZW0gZXUgdnVscHV0YXRlIGxhY2luaWEsIG1pIG9yY2kgdGVtcG9yIGVuaW0sIGVnZXQgbWF0dGlzIGxpZ3VsYSBtYWduYSBhIG1hZ25hLiBQcmFlc2VudCBzZWQgZXJhdCB1dCB0b3J0b3IgaW50ZXJkdW0gdml2ZXJyYS4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gTnVsbGEgZmFjaWxpc2kuIE1hZWNlbmFzIHNpdCBhbWV0IGxlY3R1cyBsYWN1cy4gTnVuYyB2aXRhZSBwdXJ1cyBpZCBsaWd1bGEgbGFvcmVldCBjb25kaW1lbnR1bS4gRHVpcyBhdWN0b3IgdG9ydG9yIHZlbCBkdWkgcHVsdmluYXIgYSBmYWNpbGlzaXMgYXJjdSBkaWduaXNzaW0uIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBEb25lYyBzb2xsaWNpdHVkaW4gcGVsbGVudGVzcXVlIGVnZXN0YXMuIFNlZCBzZWQgc2VtIGp1c3RvLiBNYWVjZW5hcyBsYW9yZWV0IGhlbmRyZXJpdCBtYXVyaXMsIHV0IHBvcnR0aXRvciBsb3JlbSBpYWN1bGlzIGFjLiBRdWlzcXVlIG1vbGVzdGllIHNlbSBxdWlzIGxvcmVtIHRlbXBvciBydXRydW0uIFBoYXNlbGx1cyBuaWJoIG1hdXJpcywgcmhvbmN1cyBpbiBjb25zZWN0ZXR1ciBub24sIGFsaXF1ZXQgZXUgbWFzc2
      EuCgpDdXJhYml0dXIgdmVsaXQgYXJjdSwgcHJldGl1bSBwb3J0YSBwbGFjZXJhdCBxdWlzLCB2YXJpdXMgdXQgbWV0dXMuIFZlc3RpYnVsdW0gdnVscHV0YXRlIHRpbmNpZHVudCBqdXN0bywgdml0YWUgcG9ydHRpdG9yIGxlY3R1cyBpbXBlcmRpZXQgc2l0IGFtZXQuIFZpdmFtdXMgZW5pbSBkb2xvciwgc29sbGljaXR1ZGluIHV0IHNlbXBlciBub24sIG9ybmFyZSBvcm5hcmUgZHVpLiBBbGlxdWFtIHRlbXBvciBmZXJtZW50dW0gc2FwaWVuIGVnZXQgY29uZGltZW50dW0uIEN1cmFiaXR1ciBsYW9yZWV0IGJpYmVuZHVtIGFudGUsIGluIGV1aXNtb2QgbGFjdXMgbGFjaW5pYSBldS4gUGVsbGVudGVzcXVlIGhhYml0YW50IG1vcmJpIHRyaXN0aXF1ZSBzZW5lY3R1cyBldCBuZXR1cyBldCBtYWxlc3VhZGEgZmFtZXMgYWMgdHVycGlzIGVnZXN0YXMuIFN1c3BlbmRpc3NlIHBvdGVudGkuIFNlZCBhdCBsaWJlcm8gZXUgdG9ydG9yIHRlbXB1cyBzY2VsZXJpc3F1ZS4gTnVsbGEgZmFjaWxpc2kuIE51bGxhbSB2aXRhZSBuZXF1ZSBpZCBqdXN0byB2aXZlcnJhIHJob25jdXMgcHJldGl1bSBhdCBsaWJlcm8uIEV0aWFtIGVzdCB1cm5hLCBhbGlxdWFtIHZlbCBwdWx2aW5hciBub24sIG9ybmFyZSB2ZWwgcHVydXMuCgpOdWxsYSB2YXJpdXMsIG5pc2kgZWdldCBjb25kaW1lbnR1bSBzZW1wZXIsIG1ldHVzIGVzdCBkaWN0dW0gb2RpbywgdmVsIG1hdHRpcyByaXN1cyBlc3Qgc2VkIHZlbGl0LiBDdW0gc29jaWlzIG5hdG9xdWU
      gcGVuYXRpYnVzIGV0IG1hZ25pcyBkaXMgcGFydHVyaWVudCBtb250ZXMsIG5hc2NldHVyIHJpZGljdWx1cyBtdXMuIE51bmMgbm9uIGVzdCBuZWMgdGVsbHVzIHVsdHJpY2llcyBtYXR0aXMgdXQgZWdldCB2ZWxpdC4gSW50ZWdlciBjb25kaW1lbnR1bSBhbnRlIGlkIGxvcmVtIGJsYW5kaXQgbGFjaW5pYS4gRG9uZWMgdmVsIHRvcnRvciBhdWd1ZSwgaW4gY29uZGltZW50dW0gbmlzaS4gUGVsbGVudGVzcXVlIHBlbGxlbnRlc3F1ZSBudWxsYSB1dCBudWxsYSBwb3J0dGl0b3IgcXVpcyBzb2RhbGVzIGVuaW0gcnV0cnVtLiBTZWQgYXVndWUgcmlzdXMsIGV1aXNtb2QgYSBhbGlxdWV0IGF0LCB2dWxwdXRhdGUgbm9uIGxpYmVyby4gTnVsbGFtIG5pYmggb2RpbywgZGlnbmlzc2ltIGZlcm1lbnR1bSBwdWx2aW5hciBhYywgY29uZ3VlIGV1IG1pLiBEdWlzIHRpbmNpZHVudCwgbmliaCBpZCB2ZW5lbmF0aXMgcGxhY2VyYXQsIGRpYW0gdHVycGlzIGdyYXZpZGEgbGVvLCBzaXQgYW1ldCBtb2xsaXMgbWFzc2EgZG9sb3IgcXVpcyBtYXVyaXMuIFZpdmFtdXMgc2NlbGVyaXNxdWUgc29kYWxlcyBhcmN1IGV0IGRhcGlidXMuIFN1c3BlbmRpc3NlIHBvdGVudGkuIENyYXMgcXVpcyB0ZWxsdXMgYXJjdSwgcXVpcyBsYW9yZWV0IHNlbS4gRnVzY2UgcG9ydHRpdG9yLCBzYXBpZW4gdmVsIHRyaXN0aXF1ZSBzb2RhbGVzLCB2ZWxpdCBsZW8gcG9ydGEgYXJjdSwgcXVpcyBwZWxsZW50ZXNxdWUgbnVuYyBtZXR1cyBub24gb2Rpby4g
      TmFtIGFyY3UgbGliZXJvLCB1bGxhbWNvcnBlciB1dCBwaGFyZXRyYSBub24sIGRpZ25pc3NpbSBldCB2ZWxpdC4gUXVpc3F1ZSBkb2xvciBsb3JlbSwgdmVoaWN1bGEgc2l0IGFtZXQgc2NlbGVyaXNxdWUgaW4sIHZhcml1cyBhdCBudWxsYS4gUGVsbGVudGVzcXVlIHZpdGFlIHNlbSBlZ2V0IHRvcnRvciBpYWN1bGlzIHB1bHZpbmFyLiBTZWQgbnVuYyBqdXN0bywgZXVpc21vZCBncmF2aWRhIHB1bHZpbmFyIGVnZXQsIGdyYXZpZGEgZWdldCB0dXJwaXMuIENyYXMgdmVsIGRpY3R1bSBuaXNpLiBOdWxsYW0gbnVsbGEgbGliZXJvLCBncmF2aWRhIHNpdCBhbWV0IGFsaXF1YW0gcXVpcywgY29tbW9kbyB2aXRhZSBvZGlvLiBDcmFzIHZpdGFlIG5pYmggbmVjIGR1aSBwbGFjZXJhdCBzZW1wZXIuCgpWaXZhbXVzIGF0IGZyaW5naWxsYSBlcm9zLiBWaXZhbXVzIGF0IG5pc2wgaWQgbWFzc2EgY29tbW9kbyBmZXVnaWF0IHF1aXMgbm9uIG1hc3NhLiBNb3JiaSB0ZWxsdXMgdXJuYSwgYXVjdG9yIHNpdCBhbWV0IGVsZW1lbnR1bSBzZWQsIHJ1dHJ1bSBub24gbGVjdHVzLiBOdWxsYSBmZXVnaWF0IGR1aSBpbiBzYXBpZW4gb3JuYXJlIGV0IGltcGVyZGlldCBlc3Qgb3JuYXJlLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZWdlc3Rhcy4gVmVzdGlidWx1bSBzZW1wZXIgcnV0cnVtIHRlbXBvci4gU2VkIGluIGZlbGlzI
      G5pYmgsIHNlZCBhbGlxdWFtIGVuaW0uIEN1cmFiaXR1ciB1dCBxdWFtIHNjZWxlcmlzcXVlIHZlbGl0IHBsYWNlcmF0IGRpY3R1bS4gRG9uZWMgZWxlaWZlbmQgdmVoaWN1bGEgcHVydXMsIGV1IHZlc3RpYnVsdW0gc2FwaWVuIHJ1dHJ1bSBldS4gVml2YW11cyBpbiBvZGlvIHZlbCBlc3QgdnVscHV0YXRlIGlhY3VsaXMuIE51bmMgcnV0cnVtIGZldWdpYXQgcHJldGl1bS4KCk1hZWNlbmFzIGlwc3VtIG5lcXVlLCBhdWN0b3IgcXVpcyBsYWNpbmlhIHZpdGFlLCBldWlzbW9kIGFjIG9yY2kuIERvbmVjIG1vbGVzdGllIG1hc3NhIGNvbnNlcXVhdCBlc3QgcG9ydGEgYWMgcG9ydGEgcHVydXMgdGluY2lkdW50LiBOYW0gYmliZW5kdW0gbGVvIG5lYyBsYWN1cyBtb2xsaXMgbm9uIGNvbmRpbWVudHVtIGRvbG9yIHJob25jdXMuIE51bGxhIGFjIHZvbHV0cGF0IGxvcmVtLiBOdWxsYW0gZXJhdCBwdXJ1cywgY29udmFsbGlzIGVnZXQgY29tbW9kbyBpZCwgdmFyaXVzIHF1aXMgYXVndWUuIE51bGxhbSBhbGlxdWFtIGVnZXN0YXMgbWksIHZlbCBzdXNjaXBpdCBuaXNsIG1hdHRpcyBjb25zZXF1YXQuIFF1aXNxdWUgdmVsIGVnZXN0YXMgc2FwaWVuLiBOdW5jIGxvcmVtIHZlbGl0LCBjb252YWxsaXMgbmVjIGxhb3JlZXQgZXQsIGFsaXF1ZXQgZWdldCBtYXNzYS4gTmFtIGV0IG5pYmggYWMgZHVpIHZlaGljdWxhIGFsaXF1YW0gcXVpcyBldSBhdWd1ZS4gQ3JhcyB2ZWwgbWFnbmEgdXQgZWxpdCByaG9uY3VzIGludGVyZHVtIG
      lhY3VsaXMgdm9sdXRwYXQgbmlzbC4gU3VzcGVuZGlzc2UgYXJjdSBsb3JlbSwgdmFyaXVzIHJob25jdXMgdGVtcG9yIGlkLCBwdWx2aW5hciBzZWQgdG9ydG9yLiBQZWxsZW50ZXNxdWUgdWx0cmljaWVzIGxhb3JlZXQgb2RpbyBhYyBkaWduaXNzaW0uIEFsaXF1YW0gZGlhbSBhcmN1LCBwbGFjZXJhdCBxdWlzIGVnZXN0YXMgZWdldCwgZmFjaWxpc2lzIGV1IG51bmMuIE1hdXJpcyB2dWxwdXRhdGUsIG5pc2wgc2l0IGFtZXQgbW9sbGlzIGludGVyZHVtLCByaXN1cyB0b3J0b3Igb3JuYXJlIG9yY2ksIHNlZCBlZ2VzdGFzIG9yY2kgZXJvcyBub24gZGlhbS4gVmVzdGlidWx1bSBoZW5kcmVyaXQsIG1ldHVzIHF1aXMgcGxhY2VyYXQgcGVsbGVudGVzcXVlLCBlbmltIHB1cnVzIGZhdWNpYnVzIGR1aSwgc2l0IGFtZXQgdWx0cmljaWVzIGxlY3R1cyBpcHN1bSBpZCBsb3JlbS4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBzb2Npb3NxdSBhZCBsaXRvcmEgdG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBwZXIgaW5jZXB0b3MgaGltZW5hZW9zLiBQcmFlc2VudCBlZ2V0IGRpYW0gb2RpbywgZXUgYmliZW5kdW0gZWxpdC4gSW4gdmVzdGlidWx1bSBvcmNpIGV1IGVyYXQgdGluY2lkdW50IHRyaXN0aXF1ZS4KCkNyYXMgY29uc2VjdGV0dXIgYW50ZSBldSB0dXJwaXMgcGxhY2VyYXQgc29sbGljaXR1ZGluLiBNYXVyaXMgZXQgbGFjdXMgdG9ydG9yLCBlZ2V0IHBoYXJldHJhIHZlbGl0LiBEb25lYyBhY2N1bXNhbiB1bHRyaWNlcyB
      0ZW1wb3IuIERvbmVjIGF0IG5pYmggYSBlbGl0IGNvbmRpbWVudHVtIGRhcGlidXMuIEludGVnZXIgc2l0IGFtZXQgdnVscHV0YXRlIGFudGUuIFN1c3BlbmRpc3NlIHBvdGVudGkuIEluIHNvZGFsZXMgbGFvcmVldCBtYXNzYSB2aXRhZSBsYWNpbmlhLiBNb3JiaSB2ZWwgbGFjdXMgZmV1Z2lhdCBhcmN1IHZ1bHB1dGF0ZSBtb2xlc3RpZS4gQWxpcXVhbSBtYXNzYSBtYWduYSwgdWxsYW1jb3JwZXIgYWNjdW1zYW4gZ3JhdmlkYSBxdWlzLCByaG9uY3VzIHB1bHZpbmFyIG51bGxhLiBQcmFlc2VudCBzaXQgYW1ldCBpcHN1bSBkaWFtLCBzaXQgYW1ldCBsYWNpbmlhIG5lcXVlLiBJbiBldCBzYXBpZW4gYXVndWUuIEV0aWFtIGVuaW0gZWxpdCwgdWx0cmljZXMgdmVsIHJ1dHJ1bSBpZCwgc2NlbGVyaXNxdWUgbm9uIGVuaW0uCgpQcm9pbiBldCBlZ2VzdGFzIG5lcXVlLiBQcmFlc2VudCBldCBpcHN1bSBkb2xvci4gTnVuYyBub24gdmFyaXVzIG5pc2wuIEZ1c2NlIGluIHRvcnRvciBuaXNpLiBNYWVjZW5hcyBjb252YWxsaXMgbmVxdWUgaW4gbGlndWxhIGJsYW5kaXQgcXVpcyB2ZWhpY3VsYSBsZW8gbW9sbGlzLiBQZWxsZW50ZXNxdWUgc2FnaXR0aXMgYmxhbmRpdCBsZW8sIGRhcGlidXMgcGVsbGVudGVzcXVlIGxlbyB1bHRyaWNlcyBhYy4gQ3VyYWJpdHVyIGFjIGVnZXN0YXMgbGliZXJvLiBEb25lYyBwcmV0aXVtIHBoYXJldHJhIHByZXRpdW0uIEZ1c2NlIGltcGVyZGlldCwgdHVycGlzIGV1IGFsaXF1YW0gcG9ydGEs
      IGFudGUgZWxpdCBlbGVpZmVuZCByaXN1cywgbHVjdHVzIGF1Y3RvciBhcmN1IGFudGUgdXQgbnVuYy4gVml2YW11cyBpbiBsZW8gZmVsaXMsIHZpdGFlIGVsZWlmZW5kIGxhY3VzLiBEb25lYyB0ZW1wdXMgYWxpcXVhbSBwdXJ1cyBwb3J0dGl0b3IgdHJpc3RpcXVlLiBTdXNwZW5kaXNzZSBkaWFtIG5lcXVlLCBzdXNjaXBpdCBmZXVnaWF0IGZyaW5naWxsYSBub24sIGVsZWlmZW5kIHNpdCBudWxsYW0uCg==
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/lots_of_docs.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/lots_of_docs.js b/share/www/script/test/lots_of_docs.js
    deleted file mode 100644
    index 2fe702b..0000000
    --- a/share/www/script/test/lots_of_docs.js
    +++ /dev/null
    @@ -1,55 +0,0 @@
    -// 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.
    -
    -// test saving a semi-large quanitity of documents and do some view queries.
    -couchTests.lots_of_docs = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - // keep number lowish for now to keep tests fasts. Crank up manually to
    - // to really test.
    - var numDocsToCreate = 500;
    -
    - for(var i=0; i < numDocsToCreate; i += 100) {
    - var createNow = Math.min(numDocsToCreate - i, 100);
    - var docs = makeDocs(i, i + createNow);
    - db.bulkSave(docs);
    - }
    -
    - // query all documents, and return the doc.integer member as a key.
    - results = db.query(function(doc){ emit(doc.integer, null) });
    -
    - T(results.total_rows == numDocsToCreate);
    -
    - // validate the keys are ordered ascending
    - for(var i=0; i<numDocsToCreate; i++) {
    - T(results.rows[i].key==i);
    - }
    -
    - // do the query again, but with descending output
    - results = db.query(function(doc){ emit(doc.integer, null) }, null, {
    - descending: true
    - });
    -
    - T(results.total_rows == numDocsToCreate);
    -
    - // validate the keys are ordered descending
    - for(var i=0; i<numDocsToCreate; i++) {
    - T(results.rows[numDocsToCreate-1-i].key==i);
    - }
    -
    - // Check _all_docs with descending=true again (now that there are many docs)
    - var desc = db.allDocs({descending:true});
    - T(desc.total_rows == desc.rows.length);
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/method_override.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/method_override.js b/share/www/script/test/method_override.js
    deleted file mode 100644
    index 0bb4c61..0000000
    --- a/share/www/script/test/method_override.js
    +++ /dev/null
    @@ -1,40 +0,0 @@
    -// 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.
    -
    -// Allow broken HTTP clients to fake a full method vocabulary with an X-HTTP-METHOD-OVERRIDE header
    -couchTests.method_override = function(debug) {
    - var result = JSON.parse(CouchDB.request("GET", "/").responseText);
    - T(result.couchdb == "Welcome");
    -
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    -
    - db.createDb();
    -
    - var doc = {bob : "connie"};
    - xhr = CouchDB.request("POST", "/test_suite_db/fnord", {body: JSON.stringify(doc), headers:{"X-HTTP-Method-Override" : "PUT"}});
    - T(xhr.status == 201);
    -
    - doc = db.open("fnord");
    - T(doc.bob == "connie");
    -
    - xhr = CouchDB.request("POST", "/test_suite_db/fnord?rev=" + doc._rev, {headers:{"X-HTTP-Method-Override" : "DELETE"}});
    - T(xhr.status == 200);
    -
    - xhr = CouchDB.request("GET", "/test_suite_db/fnord2", {body: JSON.stringify(doc), headers:{"X-HTTP-Method-Override" : "PUT"}});
    - // Method Override is ignored when original Method isn't POST
    - T(xhr.status == 404);
    -
    - doc = db.open("fnord");
    - T(doc == null);
    -
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/multiple_rows.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/multiple_rows.js b/share/www/script/test/multiple_rows.js
    deleted file mode 100644
    index 4f6fcd3..0000000
    --- a/share/www/script/test/multiple_rows.js
    +++ /dev/null
    @@ -1,80 +0,0 @@
    -// 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.
    -
    -couchTests.multiple_rows = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - var nc = {_id:"NC", cities:["Charlotte", "Raleigh"]};
    - var ma = {_id:"MA", cities:["Boston", "Lowell", "Worcester", "Cambridge", "Springfield"]};
    - var fl = {_id:"FL", cities:["Miami", "Tampa", "Orlando", "Springfield"]};
    -
    - T(db.save(nc).ok);
    - T(db.save(ma).ok);
    - T(db.save(fl).ok);
    -
    - var generateListOfCitiesAndState = "function(doc) {" +
    - " for (var i = 0; i < doc.cities.length; i++)" +
    - " emit(doc.cities[i] + \", \" + doc._id, null);" +
    - "}";
    -
    - var results = db.query(generateListOfCitiesAndState);
    - var rows = results.rows;
    -
    - T(rows[0].key == "Boston, MA");
    - T(rows[1].key == "Cambridge, MA");
    - T(rows[2].key == "Charlotte, NC");
    - T(rows[3].key == "Lowell, MA");
    - T(rows[4].key == "Miami, FL");
    - T(rows[5].key == "Orlando, FL");
    - T(rows[6].key == "Raleigh, NC");
    - T(rows[7].key == "Springfield, FL");
    - T(rows[8].key == "Springfield, MA");
    - T(rows[9].key == "Tampa, FL");
    - T(rows[10].key == "Worcester, MA");
    -
    - // add another city to NC
    - nc.cities.push("Wilmington");
    - T(db.save(nc).ok);
    -
    - var results = db.query(generateListOfCitiesAndState);
    - var rows = results.rows;
    -
    - T(rows[0].key == "Boston, MA");
    - T(rows[1].key == "Cambridge, MA");
    - T(rows[2].key == "Charlotte, NC");
    - T(rows[3].key == "Lowell, MA");
    - T(rows[4].key == "Miami, FL");
    - T(rows[5].key == "Orlando, FL");
    - T(rows[6].key == "Raleigh, NC");
    - T(rows[7].key == "Springfield, FL");
    - T(rows[8].key == "Springfield, MA");
    - T(rows[9].key == "Tampa, FL");
    - T(rows[10].key == "Wilmington, NC");
    - T(rows[11].key == "Worcester, MA");
    -
    - // now delete MA
    - T(db.deleteDoc(ma).ok);
    -
    - var results = db.query(generateListOfCitiesAndState);
    - var rows = results.rows;
    -
    - T(rows[0].key == "Charlotte, NC");
    - T(rows[1].key == "Miami, FL");
    - T(rows[2].key == "Orlando, FL");
    - T(rows[3].key == "Raleigh, NC");
    - T(rows[4].key == "Springfield, FL");
    - T(rows[5].key == "Tampa, FL");
    - T(rows[6].key == "Wilmington, NC");
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/oauth.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/oauth.js b/share/www/script/test/oauth.js
    deleted file mode 100644
    index 8b4e694..0000000
    --- a/share/www/script/test/oauth.js
    +++ /dev/null
    @@ -1,294 +0,0 @@
    -// 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.
    -
    -couchTests.oauth = function(debug) {
    - // This tests OAuth authentication.
    -
    - var authorization_url = "/_oauth/authorize";
    -
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
    - var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
    - var dbC = new CouchDB("test_suite_db_c", {"X-Couch-Full-Commit":"false"});
    - var dbD = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    - dbA.deleteDb();
    - dbA.createDb();
    - dbB.deleteDb();
    - dbB.createDb();
    - dbC.deleteDb();
    - dbC.createDb();
    - dbD.deleteDb();
    -
    - // Simple secret key generator
    - function generateSecret(length) {
    - var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    - var secret = '';
    - for (var i=0; i<length; i++) {
    - secret += tab.charAt(Math.floor(Math.random() * 64));
    - }
    - return secret;
    - }
    -
    - function oauthRequest(method, path, message, accessor) {
    - message.action = path;
    - message.method = method || 'GET';
    - OAuth.SignatureMethod.sign(message, accessor);
    - var parameters = message.parameters;
    - if (method == "POST" || method == "GET") {
    - if (method == "GET") {
    - return CouchDB.request("GET", OAuth.addToURL(path, parameters));
    - } else {
    - return CouchDB.request("POST", path, {
    - headers: {"Content-Type": "application/x-www-form-urlencoded"},
    - body: OAuth.formEncode(parameters)
    - });
    - }
    - } else {
    - return CouchDB.request(method, path, {
    - headers: {Authorization: OAuth.getAuthorizationHeader('', parameters)}
    - });
    - }
    - }
    -
    - var consumerSecret = generateSecret(64);
    - var tokenSecret = generateSecret(64);
    - var admintokenSecret = generateSecret(64);
    - var testadminPassword = "ohsosecret";
    -
    - var adminBasicAuthHeaderValue = function() {
    - var retval = 'Basic ' + binb2b64(str2binb("testadmin:" + testadminPassword));
    - return retval;
    - }
    -
    - var host = CouchDB.host;
    - var dbPair = {
    - source: {
    - url: CouchDB.protocol + host + "/test_suite_db_a",
    - auth: {
    - oauth: {
    - consumer_key: "key",
    - consumer_secret: consumerSecret,
    - token_secret: tokenSecret,
    - token: "foo"
    - }
    - }
    - },
    - target: {
    - url: CouchDB.protocol + host + "/test_suite_db_b",
    - headers: {"Authorization": adminBasicAuthHeaderValue()}
    - }
    - };
    -
    - // this function will be called on the modified server
    - var testFun = function () {
    - try {
    - CouchDB.request("PUT", CouchDB.protocol + host + "/_config/admins/testadmin", {
    - headers: {"X-Couch-Persist": "false"},
    - body: JSON.stringify(testadminPassword)
    - });
    - var i = 0;
    - waitForSuccess(function() {
    - //loop until the couch server has processed the password
    - i += 1;
    - var xhr = CouchDB.request("GET", CouchDB.protocol + host + "/_config/admins/testadmin?foo="+i,{
    - headers: {
    - "Authorization": adminBasicAuthHeaderValue()
    - }});
    - if (xhr.responseText.indexOf("\"-pbkdf2-") != 0) {
    - throw("still waiting");
    - }
    - return true;
    - }, "wait-for-admin");
    -
    - CouchDB.newUuids(2); // so we have one to make the salt
    -
    - CouchDB.request("PUT", CouchDB.protocol + host + "/_config/couch_httpd_auth/require_valid_user", {
    - headers: {
    - "X-Couch-Persist": "false",
    - "Authorization": adminBasicAuthHeaderValue()
    - },
    - body: JSON.stringify("true")
    - });
    -
    - var usersDb = new CouchDB("test_suite_users", {
    - "X-Couch-Full-Commit":"false",
    - "Authorization": adminBasicAuthHeaderValue()
    - });
    -
    - // Create a user
    - var jasonUserDoc = CouchDB.prepareUserDoc({
    - name: "jason",
    - roles: ["test"]
    - }, "testpassword");
    - T(usersDb.save(jasonUserDoc).ok);
    -
    -
    - var accessor = {
    - consumerSecret: consumerSecret,
    - tokenSecret: tokenSecret
    - };
    - var adminAccessor = {
    - consumerSecret: consumerSecret,
    - tokenSecret: admintokenSecret
    - };
    -
    - var signatureMethods = ["PLAINTEXT", "HMAC-SHA1"];
    - var consumerKeys = {key: 200, nonexistent_key: 400};
    - for (var i=0; i<signatureMethods.length; i++) {
    - for (var consumerKey in consumerKeys) {
    - var expectedCode = consumerKeys[consumerKey];
    - var message = {
    - parameters: {
    - oauth_signature_method: signatureMethods[i],
    - oauth_consumer_key: consumerKey,
    - oauth_token: "foo",
    - oauth_token_secret: tokenSecret,
    - oauth_version: "1.0"
    - }
    - };
    -
    - // Get request token via Authorization header
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token", message, accessor);
    - T(xhr.status == expectedCode);
    -
    - // GET request token via query parameters
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token", message, accessor);
    - T(xhr.status == expectedCode);
    -
    - responseMessage = OAuth.decodeForm(xhr.responseText);
    -
    - // Obtaining User Authorization
    - //Only needed for 3-legged OAuth
    - //xhr = CouchDB.request("GET", authorization_url + '?oauth_token=' + responseMessage.oauth_token);
    - //T(xhr.status == expectedCode);
    -
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session", message, accessor);
    - T(xhr.status == expectedCode);
    - if (xhr.status == expectedCode == 200) {
    - data = JSON.parse(xhr.responseText);
    - T(data.name == "jason");
    - T(data.roles[0] == "test");
    - }
    -
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session?foo=bar", message, accessor);
    - T(xhr.status == expectedCode);
    -
    - // Test HEAD method
    - xhr = oauthRequest("HEAD", CouchDB.protocol + host + "/_session?foo=bar", message, accessor);
    - T(xhr.status == expectedCode);
    -
    - // Replication
    - var dbA = new CouchDB("test_suite_db_a", {
    - "X-Couch-Full-Commit":"false",
    - "Authorization": adminBasicAuthHeaderValue()
    - });
    - T(dbA.save({_id:"_design/"+i+consumerKey}).ok);
    - var result = CouchDB.replicate(dbPair.source, dbPair.target, {
    - headers: {"Authorization": adminBasicAuthHeaderValue()}
    - });
    - T(result.ok);
    -
    - // Test if rewriting doesn't break OAuth (c.f. COUCHDB-1321)
    - var dbC = new CouchDB("test_suite_db_c", {
    - "X-Couch-Full-Commit":"false",
    - "Authorization": adminBasicAuthHeaderValue()
    - });
    - var ddocId = "_design/"+ i + consumerKey;
    - var ddoc = {
    - _id: ddocId,
    - language: "javascript",
    - _attachments:{
    - "bar": {
    - content_type:"text/plain",
    - data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    - }
    - },
    - rewrites: [{"from": "foo/:a", "to": ":a"}]
    - };
    - T(dbC.save(ddoc).ok);
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/test_suite_db_c/" + ddocId + "/_rewrite/foo/bar", message, accessor);
    - T(xhr.status == expectedCode);
    -
    - // Test auth via admin user defined in .ini
    - var message = {
    - parameters: {
    - oauth_signature_method: signatureMethods[i],
    - oauth_consumer_key: consumerKey,
    - oauth_token: "bar",
    - oauth_token_secret: admintokenSecret,
    - oauth_version: "1.0"
    - }
    - };
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session?foo=bar", message, adminAccessor);
    - if (xhr.status == expectedCode == 200) {
    - data = JSON.parse(xhr.responseText);
    - T(data.name == "testadmin");
    - T(data.roles[0] == "_admin");
    - }
    -
    - // Test when the user's token doesn't exist.
    - message.parameters.oauth_token = "not a token!";
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session?foo=bar",
    - message, adminAccessor);
    - T(xhr.status == 400, "Request should be invalid.");
    - }
    - }
    - } finally {
    - var xhr = CouchDB.request("PUT", CouchDB.protocol + host + "/_config/couch_httpd_auth/require_valid_user", {
    - headers: {
    - "Authorization": adminBasicAuthHeaderValue(),
    - "X-Couch-Persist": "false"
    - },
    - body: JSON.stringify("false")
    - });
    - T(xhr.status == 200);
    -
    - var xhr = CouchDB.request("DELETE", CouchDB.protocol + host + "/_config/admins/testadmin", {
    - headers: {
    - "Authorization": adminBasicAuthHeaderValue(),
    - "X-Couch-Persist": "false"
    - }
    - });
    - T(xhr.status == 200);
    - }
    - };
    -
    - run_on_modified_server(
    - [
    - {section: "httpd",
    - key: "WWW-Authenticate", value: 'OAuth'},
    - {section: "couch_httpd_auth",
    - key: "secret", value: generateSecret(64)},
    - {section: "couch_httpd_auth",
    - key: "authentication_db", value: "test_suite_users"},
    - {section: "oauth_consumer_secrets",
    - key: "key", value: consumerSecret},
    - {section: "oauth_token_users",
    - key: "foo", value: "jason"},
    - {section: "oauth_token_users",
    - key: "bar", value: "testadmin"},
    - {section: "oauth_token_secrets",
    - key: "foo", value: tokenSecret},
    - {section: "oauth_token_secrets",
    - key: "bar", value: admintokenSecret},
    - {section: "couch_httpd_oauth",
    - key: "authorization_url", value: authorization_url},
    - {section: "couch_httpd_oauth",
    - key: "use_users_db", value: "false"}
    - ],
    - testFun
    - );
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/oauth_users_db.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/oauth_users_db.js b/share/www/script/test/oauth_users_db.js
    deleted file mode 100644
    index b98069e..0000000
    --- a/share/www/script/test/oauth_users_db.js
    +++ /dev/null
    @@ -1,161 +0,0 @@
    -// 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.
    -
    -couchTests.oauth_users_db = function(debug) {
    - // This tests OAuth authentication using the _users DB instead of the ini
    - // configuration for storing OAuth tokens and secrets.
    -
    - if (debug) debugger;
    -
    - var usersDb = new CouchDB("test_suite_users",{"X-Couch-Full-Commit":"false"});
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - var host = CouchDB.host;
    - var authorization_url = "/_oauth/authorize";
    -
    -
    - // Simple secret key generator
    - function generateSecret(length) {
    - var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    - var secret = '';
    - for (var i = 0; i < length; i++) {
    - secret += tab.charAt(Math.floor(Math.random() * 64));
    - }
    - return secret;
    - }
    -
    -
    - function oauthRequest(method, path, message, accessor) {
    - message.action = path;
    - message.method = method || 'GET';
    - OAuth.SignatureMethod.sign(message, accessor);
    - var parameters = message.parameters;
    - if (method == "POST" || method == "GET") {
    - if (method == "GET") {
    - return CouchDB.request("GET", OAuth.addToURL(path, parameters));
    - } else {
    - return CouchDB.request("POST", path, {
    - headers: {"Content-Type": "application/x-www-form-urlencoded"},
    - body: OAuth.formEncode(parameters)
    - });
    - }
    - } else {
    - return CouchDB.request(method, path, {
    - headers: {Authorization: OAuth.getAuthorizationHeader('', parameters)}
    - });
    - }
    - }
    -
    -
    - // this function will be called on the modified server
    - var testFun = function () {
    - var fdmanana = CouchDB.prepareUserDoc({
    - name: "fdmanana",
    - roles: ["dev"],
    - oauth: {
    - consumer_keys: {
    - "key_foo": "bar",
    - "key_xpto": "mars"
    - },
    - tokens: {
    - "salut": "ola",
    - "tok1": "123"
    - }
    - }
    - }, "qwerty");
    - TEquals(true, usersDb.save(fdmanana).ok);
    -
    - var signatureMethods = ["PLAINTEXT", "HMAC-SHA1"];
    - var message, xhr, responseMessage, accessor, data;
    -
    - for (var i = 0; i < signatureMethods.length; i++) {
    - message = {
    - parameters: {
    - oauth_signature_method: signatureMethods[i],
    - oauth_consumer_key: "key_foo",
    - oauth_token: "tok1",
    - oauth_version: "1.0"
    - }
    - };
    - accessor = {
    - consumerSecret: "bar",
    - tokenSecret: "123"
    - };
    -
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token",
    - message, accessor
    - );
    - TEquals(200, xhr.status);
    -
    - responseMessage = OAuth.decodeForm(xhr.responseText);
    -
    - // Obtaining User Authorization
    - // Only needed for 3-legged OAuth
    - //xhr = CouchDB.request(
    - // "GET", authorization_url + '?oauth_token=' + responseMessage.oauth_token);
    - //TEquals(200, xhr.status);
    -
    - xhr = oauthRequest(
    - "GET", CouchDB.protocol + host + "/_session", message, accessor);
    - TEquals(200, xhr.status);
    - data = JSON.parse(xhr.responseText);
    - TEquals(true, data.ok);
    - TEquals("object", typeof data.userCtx);
    - TEquals("fdmanana", data.userCtx.name);
    - TEquals("dev", data.userCtx.roles[0]);
    - TEquals("oauth", data.info.authenticated);
    -
    - // test invalid token
    - message.parameters.oauth_token = "not a token!";
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session",
    - message, accessor
    - );
    - TEquals(400, xhr.status, "Request should be invalid.");
    -
    - // test invalid secret
    - message.parameters.oauth_token = "tok1";
    - accessor.tokenSecret = "badone";
    - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session",
    - message, accessor
    - );
    - data = JSON.parse(xhr.responseText);
    - TEquals(null, data.userCtx.name);
    - TEquals(1, data.userCtx.roles.length);
    - TEquals("_admin", data.userCtx.roles[0]);
    - TEquals(true, data.info.authentication_handlers.indexOf("default") >= 0);
    - TEquals("default", data.info.authenticated);
    - }
    - };
    -
    -
    - usersDb.deleteDb();
    -
    - run_on_modified_server(
    - [
    - {section: "httpd",
    - key: "WWW-Authenticate", value: 'OAuth'},
    - {section: "couch_httpd_auth",
    - key: "secret", value: generateSecret(64)},
    - {section: "couch_httpd_auth",
    - key: "authentication_db", value: usersDb.name},
    - {section: "couch_httpd_oauth",
    - key: "use_users_db", value: "true"},
    - {section: "httpd", key: "authentication_handlers",
    - value: "{couch_httpd_oauth, oauth_authentication_handler}, " +
    - "{couch_httpd_auth, default_authentication_handler}"}
    - ],
    - testFun
    - );
    -
    - // cleanup
    - usersDb.deleteDb();
    - db.deleteDb();
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/proxyauth.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/proxyauth.js b/share/www/script/test/proxyauth.js
    deleted file mode 100644
    index 1677a66..0000000
    --- a/share/www/script/test/proxyauth.js
    +++ /dev/null
    @@ -1,130 +0,0 @@
    -// 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.
    -
    -
    -
    -couchTests.proxyauth = function(debug) {
    - // this test proxy authentification handler
    -
    - var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    -
    - if (debug) debugger;
    -
    - usersDb.deleteDb();
    -
    - // Simple secret key generator
    - function generateSecret(length) {
    - var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    - var secret = '';
    - for (var i=0; i<length; i++) {
    - secret += tab.charAt(Math.floor(Math.random() * 64));
    - }
    - return secret;
    - }
    -
    - var secret = generateSecret(64);
    -
    - function TestFun() {
    - db.deleteDb();
    - db.createDb();
    -
    - var benoitcUserDoc = CouchDB.prepareUserDoc({
    - name: "benoitc@apache.org"
    - }, "test");
    - T(usersDb.save(benoitcUserDoc).ok);
    -
    - T(CouchDB.session().userCtx.name == null);
    -
    - // test that you can use basic auth aginst the users db
    - var s = CouchDB.session({
    - headers : {
    - "Authorization" : "Basic YmVub2l0Y0BhcGFjaGUub3JnOnRlc3Q="
    - }
    - });
    - T(s.userCtx.name == "benoitc@apache.org");
    - T(s.info.authenticated == "default");
    -
    - CouchDB.logout();
    -
    - var headers = {
    - "X-Auth-CouchDB-UserName": "benoitc@apache.org",
    - "X-Auth-CouchDB-Roles": "test",
    - "X-Auth-CouchDB-Token": hex_hmac_sha1(secret, "benoitc@apache.org")
    - };
    -
    - var designDoc = {
    - _id:"_design/test",
    - language: "javascript",
    -
    - shows: {
    - "welcome": stringFun(function(doc,req) {
    - return "Welcome " + req.userCtx["name"];
    - }),
    - "role": stringFun(function(doc, req) {
    - return req.userCtx['roles'][0];
    - })
    - }
    - };
    -
    - db.save(designDoc);
    -
    - var req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/welcome",
    - {headers: headers});
    - T(req.responseText == "Welcome benoitc@apache.org");
    -
    - req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/role",
    - {headers: headers});
    - T(req.responseText == "test");
    -
    - var xhr = CouchDB.request("PUT", "/_config/couch_httpd_auth/proxy_use_secret",{
    - body : JSON.stringify("true"),
    - headers: {"X-Couch-Persist": "false"}
    - });
    - T(xhr.status == 200);
    -
    - req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/welcome",
    - {headers: headers});
    - T(req.responseText == "Welcome benoitc@apache.org");
    -
    - req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/role",
    - {headers: headers});
    - T(req.responseText == "test");
    -
    - }
    -
    - run_on_modified_server(
    - [{section: "httpd",
    - key: "authentication_handlers",
    - value:"{couch_httpd_auth, proxy_authentification_handler}, {couch_httpd_auth, default_authentication_handler}"},
    - {section: "couch_httpd_auth",
    - key: "authentication_db",
    - value: "test_suite_users"},
    - {section: "couch_httpd_auth",
    - key: "secret",
    - value: secret},
    - {section: "couch_httpd_auth",
    - key: "x_auth_username",
    - value: "X-Auth-CouchDB-UserName"},
    - {section: "couch_httpd_auth",
    - key: "x_auth_roles",
    - value: "X-Auth-CouchDB-Roles"},
    - {section: "couch_httpd_auth",
    - key: "x_auth_token",
    - value: "X-Auth-CouchDB-Token"},
    - {section: "couch_httpd_auth",
    - key: "proxy_use_secret",
    - value: "false"}],
    - TestFun
    - );
    -
    -};
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/spec/jquery_couch_js_class_methods_spec.js
    ----------------------------------------------------------------------
    diff --git a/share/www/spec/jquery_couch_js_class_methods_spec.js b/share/www/spec/jquery_couch_js_class_methods_spec.js
    deleted file mode 100644
    index f2df81b..0000000
    --- a/share/www/spec/jquery_couch_js_class_methods_spec.js
    +++ /dev/null
    @@ -1,523 +0,0 @@
    -// 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.
    -
    -// Specs for jquery_couch.js lines 48-156 and 415-448
    -
    -describe 'jQuery couchdb'
    - before
    - stubAlert();
    - end
    -
    - after
    - destubAlert();
    - end
    -
    - describe 'activeTasks'
    - before_each
    - db = $.couch.db("spec_db");
    - db.create();
    - end
    -
    - after_each
    - db.drop();
    - end
    -
    - it 'should return an empty array when there are no active tasks'
    - $.couch.activeTasks({
    - success: function(resp){
    - resp.should.eql []
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return an active task'
    - // doing a bit of stuff here so compaction has something to do and takes a while
    - var battlestar, civillian;
    - db.saveDoc({"type":"Battlestar", "name":"Galactica"}, {
    - success: function(resp){
    - db.openDoc(resp.id, {
    - success: function(resp2){
    - battlestar = resp2;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - battlestar.name = "Pegasus";
    - db.saveDoc(battlestar);
    -
    - db.saveDoc({"type":"Civillian", "name":"Cloud 9"}, {
    - success: function(resp){
    - db.openDoc(resp.id, {
    - success: function(resp2){
    - civillian = resp2;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - civillian.name = "Olympic Carrier";
    - db.saveDoc(civillian);
    - db.removeDoc(civillian);
    -
    - db.compact({
    - ajaxStart: function(resp){
    - $.couch.activeTasks({
    - success: function(resp2){
    - resp2[0].type.should.eql "Database Compaction"
    - resp2[0].task.should.eql "spec_db"
    - resp2[0].should.have_prop "status"
    - resp2[0].should.include "pid"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - }
    - });
    - end
    - end
    -
    - describe 'allDbs'
    - it 'should return an array that includes a created database'
    - temp_db = new CouchDB("temp_spec_db", {"X-Couch-Full-Commit":"false"});
    - temp_db.createDb();
    - $.couch.allDbs({
    - success: function(resp){
    - resp.should.include "temp_spec_db"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - temp_db.deleteDb();
    - end
    -
    - it 'should return an array that does not include a database that does not exist'
    - $.couch.allDbs({
    - success: function(resp){
    - resp.should.not.include("not_existing_temp_spec_db");
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'config'
    - it 'should get the config settings'
    - $.couch.config({
    - success: function(resp){
    - resp.httpd.port.should.eql window.location.port
    - resp.stats.samples.should.match /\[.*\]/
    - resp.native_query_servers.should.have_prop "erlang"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should get a specific config setting'
    - $.couch.config({
    - success: function(resp){
    - parseInt(resp.max_document_size).should.be_a Number
    - resp.delayed_commits.should.be_a String
    - resp.database_dir.should.be_a String
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, "couchdb");
    - end
    -
    - it 'should update a config setting'
    - $.couch.config({
    - success: function(resp){
    - resp.should.eql ""
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, "test", "colony", "Caprica");
    -
    - $.couch.config({
    - success: function(resp){
    - resp.colony.should.eql "Caprica"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, "test");
    -
    - $.couch.config({}, "test", "colony", null);
    - end
    -
    - it 'should delete a config setting'
    - $.couch.config({}, "test", "colony", "Caprica");
    -
    - $.couch.config({
    - success: function(resp){
    - resp.should.eql "Caprica"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, "test", "colony", null);
    -
    - $.couch.config({
    - success: function(resp){
    - resp.should.eql {}
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, "test");
    - end
    -
    - it 'should alert with an error message prefix'
    - $.couch.config("asdf", "asdf", "asdf");
    - alert_msg.should.match /An error occurred retrieving\/updating the server configuration/
    - end
    - end
    -
    - describe 'session'
    - it 'should return information about the session'
    - $.couch.session({
    - success: function(resp){
    - resp.info.should.have_prop 'authentication_db'
    - resp.userCtx.should.include 'name'
    - resp.userCtx.roles.should.be_an Array
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'userDb'
    - it 'should return the userDb'
    - var authentication_db;
    - $.couch.session({
    - success: function(resp){
    - authentication_db = resp.info.authentication_db;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    -
    - $.couch.userDb(function(resp){
    - resp.name.should.eql authentication_db
    - });
    - end
    -
    - it 'should return a db instance'
    - $.couch.userDb(function(resp){
    - resp.should.respond_to 'allDocs'
    - resp.should.respond_to 'bulkSave'
    - });
    - end
    - end
    -
    - describe 'user_db stuff'
    - before
    - useTestUserDb();
    - end
    -
    - after
    - useOldUserDb();
    - end
    -
    - describe 'signup'
    - it 'should return a saved user'
    - $.couch.signup(
    - {name: "Tom Zarek"}, "secretpass", {
    - success: function(resp){
    - resp.id.should.eql "org.couchdb.user:Tom Zarek"
    - resp.rev.length.should.be_at_least 30
    - resp.ok.should.be_true
    - users_db.deleteDoc({_id : resp.id, _rev : resp.rev})
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should create a userDoc in the user db'
    - $.couch.signup(
    - {name: "Tom Zarek"}, "secretpass", {
    - success: function(resp){
    - var user = users_db.open(resp.id);
    - user.name.should.eql "Tom Zarek"
    - user._id.should.eql "org.couchdb.user:Tom Zarek"
    - user.roles.should.eql []
    - user.password_sha.length.should.be_at_least 30
    - user.password_sha.should.be_a String
    - users_db.deleteDoc({_id : resp.id, _rev : resp.rev})
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should create a userDoc with roles when specified'
    - $.couch.signup(
    - {name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", {
    - success: function(resp){
    - var user = users_db.open(resp.id);
    - user.roles.should.eql ["vice_president", "activist"]
    - users_db.deleteDoc({_id : resp.id, _rev : resp.rev})
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'login'
    - before_each
    - user = {};
    - $.couch.signup({name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", {
    - success: function(resp){
    - user.id = resp.id;
    - user.rev = resp.rev;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - after_each
    - users_db.deleteDoc({_id : user.id, _rev : user.rev})
    - end
    -
    - it 'should return the logged in user'
    - $.couch.login({
    - name: "Tom Zarek",
    - password: "secretpass",
    - success: function(resp){
    - resp.name.should.eql "Tom Zarek"
    - resp.ok.should.be_true
    - resp.roles.should.eql ["vice_president", "activist"]
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in a session for the logged in user'
    - $.couch.login({
    - name: "Tom Zarek",
    - password: "secretpass"
    - });
    - $.couch.session({
    - success: function(resp){
    - resp.info.authentication_db.should.eql "spec_users_db"
    - resp.userCtx.name.should.eql "Tom Zarek"
    - resp.userCtx.roles.should.eql ["vice_president", "activist"]
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return a 404 when password is wrong'
    - $.couch.login({
    - name: "Tom Zarek",
    - password: "wrongpass",
    - error: function(status, error, reason){
    - status.should.eql 401
    - error.should.eql "unauthorized"
    - reason.should.eql "Name or password is incorrect."
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should return a 404 when the user doesnt exist in the users db'
    - $.couch.login({
    - name: "Number Three",
    - password: "secretpass",
    - error: function(status, error, reason){
    - status.should.eql 401
    - error.should.eql "unauthorized"
    - reason.should.eql "Name or password is incorrect."
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - $.couch.login("asdf");
    - alert_msg.should.match /An error occurred logging in/
    - end
    - end
    -
    - describe 'logout'
    - before_each
    - user = {};
    - $.couch.signup({name: "Tom Zarek", roles: ["vice_president", "activist"]}, "secretpass", {
    - success: function(resp){
    - user.id = resp.id;
    - user.rev = resp.rev;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - $.couch.login({name: "Tom Zarek", password: "secretpass"});
    - end
    -
    - after_each
    - users_db.deleteDoc({_id : user.id, _rev : user.rev})
    - end
    -
    - it 'should return ok true'
    - $.couch.logout({
    - success: function(resp){
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in an empty session'
    - $.couch.logout();
    - $.couch.session({
    - success: function(resp){
    - resp.userCtx.name.should.be_null
    - resp.userCtx.roles.should.not.include ["vice_president"]
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    - end
    -
    - describe 'encodeDocId'
    - it 'should return the encoded docID when it is not a design document'
    - $.couch.encodeDocId("viper").should.eql(encodeURIComponent("viper"))
    - end
    -
    - it 'should encode only the name of the design document'
    - $.couch.encodeDocId("_design/raptor").should.eql("_design/" + encodeURIComponent("raptor"))
    - end
    -
    - it 'should also work when the name of the des'
    - $.couch.encodeDocId("_design/battlestar/_view/crew").should.eql("_design/" + encodeURIComponent("battlestar/_view/crew"))
    - end
    - end
    -
    - describe 'info'
    - it 'should return the CouchDB version'
    - $.couch.info({
    - success: function(resp){
    - resp.couchdb.should.eql "Welcome"
    - resp.version.should_match /^\d\d?\.\d\d?\.\d\d?.*/
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'replicate'
    - before_each
    - db = $.couch.db("spec_db");
    - db.create();
    - db2 = $.couch.db("spec_db_2");
    - db2.create();
    - host = window.location.protocol + "//" + window.location.host ;
    - end
    -
    - after_each
    - db.drop();
    - db2.drop();
    - end
    -
    - it 'should return no_changes true when there are no changes between the dbs'
    - $.couch.replicate(host + db.uri, host + db2.uri, {
    - success: function(resp){
    - resp.ok.should.be_true
    - resp.no_changes.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return the session ID'
    - db.saveDoc({'type':'battlestar', 'name':'galactica'});
    - $.couch.replicate(host + db.uri, host + db2.uri, {
    - success: function(resp){
    - resp.session_id.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return source_last_seq'
    - db.saveDoc({'type':'battlestar', 'name':'galactica'});
    - db.saveDoc({'type':'battlestar', 'name':'pegasus'});
    -
    - $.couch.replicate(host + db.uri, host + db2.uri, {
    - success: function(resp){
    - resp.source_last_seq.should.eql 2
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return the replication history'
    - db.saveDoc({'type':'battlestar', 'name':'galactica'});
    - db.saveDoc({'type':'battlestar', 'name':'pegasus'});
    -
    - $.couch.replicate(host + db.uri, host + db2.uri, {
    - success: function(resp){
    - resp.history[0].docs_written.should.eql 2
    - resp.history[0].start_last_seq.should.eql 0
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through replication options'
    - db.saveDoc({'type':'battlestar', 'name':'galactica'});
    - db2.drop();
    - $.couch.replicate(host + db.uri, host + db2.uri, {
    - error: function(status, error, reason){
    - status.should.eql 500
    - reason.should.match /db_not_found/
    - },
    - success: function(resp){successCallback(resp)}
    - });
    -
    - $.couch.replicate(host + db.uri, host + db2.uri, {
    - success: function(resp){
    - resp.ok.should.eql true
    - resp.history[0].docs_written.should.eql 1
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, {
    - "create_target":true
    - });
    -
    - db2.info({
    - success: function(resp){
    - resp.db_name.should.eql "spec_db_2"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - $.couch.replicate("asdf");
    - alert_msg.should.match /Replication failed/
    - end
    - end
    -
    - describe 'newUUID'
    - it 'should return a new UUID'
    - var new_uuid = $.couch.newUUID(1);
    - new_uuid.should.be_a String
    - new_uuid.should.have_length 32
    - end
    -
    - it 'should fill the uuidCache with the specified number minus 1'
    - // we can't reach the uuidCache from here, so we mock the next request
    - // to test that the next uuid is not coming from the request, but from the cache.
    - $.couch.newUUID(2);
    - mock_request().and_return({'uuids':['a_sample_uuid']})
    - $.couch.newUUID(1).should.not.eql 'a_sample_uuid'
    - $.couch.newUUID(1).should.eql 'a_sample_uuid'
    - end
    -
    - it 'should alert with an error message prefix'
    - $.couch.newUUID("asdf");
    - alert_msg.should.match /Failed to retrieve UUID batch/
    - end
    - end
    -end
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/spec/jquery_couch_js_instance_methods_1_spec.js
    ----------------------------------------------------------------------
    diff --git a/share/www/spec/jquery_couch_js_instance_methods_1_spec.js b/share/www/spec/jquery_couch_js_instance_methods_1_spec.js
    deleted file mode 100644
    index 8538c85..0000000
    --- a/share/www/spec/jquery_couch_js_instance_methods_1_spec.js
    +++ /dev/null
    @@ -1,202 +0,0 @@
    -// 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.
    -
    -// Specs for jquery_couch.js lines 163-209
    -
    -describe 'jQuery couchdb db'
    - before
    - stubAlert();
    - end
    -
    - after
    - destubAlert();
    - end
    -
    - before_each
    - db = $.couch.db('spec_db');
    - end
    -
    - describe 'constructor'
    - it 'should set the name'
    - db.name.should.eql 'spec_db'
    - end
    -
    - it 'should set the uri'
    - db.uri.should.eql '/spec_db/'
    - end
    - end
    -
    - describe 'triggering db functions'
    - before_each
    - db.create();
    - end
    -
    - after_each
    - db.drop();
    - end
    -
    - describe 'compact'
    - it 'should return ok true'
    - db.compact({
    - success: function(resp) {
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should trigger _compact'
    - db.compact({
    - success: function(resp, obj) {
    - obj.url.should.eql "/spec_db/_compact"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'viewCleanup'
    - it 'should return ok true'
    - db.viewCleanup({
    - success: function(resp) {
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should trigger _view_cleanup'
    - db.viewCleanup({
    - success: function(resp, obj) {
    - obj.url.should.eql "/spec_db/_view_cleanup"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'compactView'
    - before_each
    - var designDoc = {
    - "views" : {
    - "people" : {
    - "map" : "function(doc) { emit(doc._id, doc); }"
    - }
    - },
    - "_id" : "_design/myview"
    - };
    - db.saveDoc(designDoc);
    - db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
    - end
    -
    - it 'should return ok true'
    - db.compactView("myview", {
    - success: function(resp) {
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should trigger _compact_view with the groupname'
    - db.compactView("myview", {
    - success: function(resp, obj) {
    - obj.url.should.eql "/spec_db/_compact/myview"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return raise a 404 error when the design name doesnt exist'
    - db.compactView("non_existing_design_name", {
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "missing"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.compactView("asdf");
    - alert_msg.should.match /The view could not be compacted/
    - end
    - end
    - end
    -
    - describe 'create'
    - after_each
    - db.drop();
    - end
    -
    - it 'should return ok true'
    - db.create({
    - success: function(resp) {
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in a created db'
    - db.create();
    - db.create({
    - error: function(status, error, reason){
    - status.should.eql 412
    - error.should.eql "file_exists"
    - reason.should.eql "The database could not be created, the file already exists."
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.create();
    - db.create();
    - alert_msg.should.match /The database could not be created/
    - end
    - end
    -
    - describe 'drop'
    - before_each
    - db.create();
    - end
    -
    - it 'should return ok true'
    - db.drop({
    - success: function(resp) {
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in a deleted db'
    - db.drop();
    - db.drop({
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "missing"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.drop();
    - db.drop();
    - alert_msg.should.match /The database could not be deleted/
    - end
    - end
    -end
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/spec/jquery_couch_js_instance_methods_2_spec.js
    ----------------------------------------------------------------------
    diff --git a/share/www/spec/jquery_couch_js_instance_methods_2_spec.js b/share/www/spec/jquery_couch_js_instance_methods_2_spec.js
    deleted file mode 100644
    index 8f35aff..0000000
    --- a/share/www/spec/jquery_couch_js_instance_methods_2_spec.js
    +++ /dev/null
    @@ -1,433 +0,0 @@
    -// 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.
    -
    -// Specs for jquery_couch.js lines 210-299
    -
    -describe 'jQuery couchdb db'
    - before
    - stubAlert();
    - end
    -
    - after
    - destubAlert();
    - end
    -
    - before_each
    - db = $.couch.db('spec_db');
    - db.create();
    - end
    -
    - after_each
    - db.drop();
    - end
    -
    - describe 'info'
    - before_each
    - result = {};
    - db.info({
    - success: function(resp) { result = resp; },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return the name of the database'
    - result.db_name.should.eql "spec_db"
    - end
    -
    - it 'should return the number of documents'
    - result.doc_count.should.eql 0
    - end
    -
    - it 'should return the start time of the db instance'
    - result.instance_start_time.should.have_length 16
    - end
    - end
    -
    - describe 'allDocs'
    - it 'should return no docs when there arent any'
    - db.allDocs({
    - success: function(resp) {
    - resp.total_rows.should.eql 0
    - resp.rows.should.eql []
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - describe 'with docs'
    - before_each
    - db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
    - db.saveDoc({"Name" : "Samuel T. Anders", "_id" : "456"});
    - end
    -
    - it 'should return all docs'
    - db.allDocs({
    - success: function(resp) {
    - resp.total_rows.should.eql 2
    - resp.rows.should.have_length 2
    - resp.rows[0].id.should.eql "123"
    - resp.rows[0].key.should.eql "123"
    - resp.rows[0].value.rev.length.should.be_at_least 30
    - resp.rows[1].id.should.eql "456"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options'
    - db.allDocs({
    - "startkey": "123",
    - "limit": "1",
    - success: function(resp) {
    - resp.rows.should.have_length 1
    - resp.rows[0].id.should.eql "123"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    - end
    -
    - describe 'allDesignDocs'
    - it 'should return nothing when there arent any design docs'
    - db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
    - db.allDesignDocs({
    - success: function(resp) {
    - resp.rows.should.eql []
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return all design docs'
    - var designDoc = {
    - "views" : {
    - "people" : {
    - "map" : "function(doc) { emit(doc._id, doc); }"
    - }
    - },
    - "_id" : "_design/spec_db"
    - };
    - db.saveDoc(designDoc);
    - db.saveDoc({"Name" : "Felix Gaeta", "_id" : "123"});
    -
    - db.allDesignDocs({
    - success: function(resp) {
    - resp.total_rows.should.eql 2
    - resp.rows.should.have_length 1
    - resp.rows[0].id.should.eql "_design/spec_db"
    - resp.rows[0].key.should.eql "_design/spec_db"
    - resp.rows[0].value.rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    - end
    -
    - describe 'allApps'
    - it 'should provide a custom function with appName, appPath and design document when there is an attachment with index.html'
    - var designDoc = {"_id" : "_design/with_attachments"};
    -
    - designDoc._attachments = {
    - "index.html" : {
    - "content_type": "text\/html",
    - // this is "<html><p>Hi, here is index!</p></html>", base64 encoded
    - "data": "PGh0bWw+PHA+SGksIGhlcmUgaXMgaW5kZXghPC9wPjwvaHRtbD4="
    - }
    - };
    - db.saveDoc(designDoc);
    -
    - db.allApps({
    - eachApp: function(appName, appPath, ddoc) {
    - appName.should.eql "with_attachments"
    - appPath.should.eql "/spec_db/_design/with_attachments/index.html"
    - ddoc._id.should.eql "_design/with_attachments"
    - ddoc._attachments["index.html"].content_type.should.eql "text/html"
    - ddoc._attachments["index.html"].length.should.eql "<html><p>Hi, here is index!</p></html>".length
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should provide a custom function with appName, appPath and design document when there is a couchapp with index file'
    - var designDoc = {"_id" : "_design/with_index"};
    - designDoc.couchapp = {
    - "index" : "cylon"
    - };
    - db.saveDoc(designDoc);
    -
    - db.allApps({
    - eachApp: function(appName, appPath, ddoc) {
    - appName.should.eql "with_index"
    - appPath.should.eql "/spec_db/_design/with_index/cylon"
    - ddoc._id.should.eql "_design/with_index"
    - ddoc.couchapp.index.should.eql "cylon"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should not call the eachApp function when there is neither index.html in _attachments nor a couchapp index file'
    - var designDoc = {"_id" : "_design/nothing"};
    - db.saveDoc(designDoc);
    -
    - var eachApp_called = false;
    - db.allApps({
    - eachApp: function(appName, appPath, ddoc) {
    - eachApp_called = true;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    -
    - eachApp_called.should.be_false
    - end
    -
    - it 'should alert with an error message prefix'
    - db.allApps();
    - alert_msg.should.match /Please provide an eachApp function for allApps()/
    - end
    - end
    -
    - describe 'openDoc'
    - before_each
    - doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "123"};
    - db.saveDoc(doc);
    - end
    -
    - it 'should open the document'
    - db.openDoc("123", {
    - success: function(resp){
    - resp.should.eql doc
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should raise a 404 error when there is no document with the given ID'
    - db.openDoc("non_existing", {
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "missing"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should pass through the options'
    - doc.Name = "Sasha";
    - db.saveDoc(doc);
    - db.openDoc("123", {
    - revs: true,
    - success: function(resp){
    - resp._revisions.start.should.eql 2
    - resp._revisions.ids.should.have_length 2
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.openDoc("asdf");
    - alert_msg.should.match /The document could not be retrieved/
    - end
    - end
    -
    - describe 'saveDoc'
    - before_each
    - doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"};
    - end
    -
    - it 'should save the document'
    - db.saveDoc(doc, {
    - success: function(resp, status){
    - status.should.eql 201
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return ok true'
    - db.saveDoc(doc, {
    - success: function(resp, status){
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return ID and revision of the document'
    - db.saveDoc(doc, {
    - success: function(resp, status){
    - resp.id.should.be_a String
    - resp.id.should.have_length 32
    - resp.rev.should.be_a String
    - resp.rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in a saved document with generated ID'
    - db.saveDoc(doc, {
    - success: function(resp, status){
    - db.openDoc(resp.id, {
    - success: function(resp2){
    - resp2.Name.should.eql "Kara Thrace"
    - resp2.Callsign.should.eql "Starbuck"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should save the document with the specified ID'
    - doc._id = "123";
    - db.saveDoc(doc, {
    - success: function(resp, status){
    - resp.id.should.eql "123"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options'
    - db.saveDoc(doc, {
    - "batch" : "ok",
    - success: function(resp, status){
    - // when using batch ok, couch sends a 202 status immediately
    - status.should.eql 202
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.saveDoc("asdf");
    - alert_msg.should.match /The document could not be saved/
    - end
    - end
    -
    - describe 'bulkSave'
    - before_each
    - doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck"};
    - doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo"};
    - doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer"};
    - docs = [doc, doc2, doc3];
    - end
    -
    - it 'should save all documents'
    - db.bulkSave({"docs": docs});
    - db.allDocs({
    - success: function(resp) {
    - resp.total_rows.should.eql 3
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in saved documents'
    - doc3._id = "789";
    - db.bulkSave({"docs": [doc3]});
    -
    - db.openDoc("789", {
    - success: function(resp){
    - resp.Name.should.eql "Sharon Valerii"
    - resp.Callsign.should.eql "Boomer"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return ID and revision of the documents'
    - db.bulkSave({"docs": docs},{
    - success: function(resp){
    - resp[0].id.should.be_a String
    - resp[0].id.should.have_length 32
    - resp[0].rev.should.be_a String
    - resp[0].rev.length.should.be_at_least 30
    - resp[1].id.should.be_a String
    - resp[1].id.should.have_length 32
    - resp[1].rev.should.be_a String
    - resp[1].rev.length.should.be_at_least 30
    - resp[2].id.should.be_a String
    - resp[2].id.should.have_length 32
    - resp[2].rev.should.be_a String
    - resp[2].rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should save the document with specified IDs'
    - doc._id = "123";
    - doc2._id = "456";
    - docs = [doc, doc2, doc3];
    -
    - db.bulkSave({"docs": docs},{
    - success: function(resp){
    - resp[0].id.should.eql "123"
    - resp[1].id.should.eql "456"
    - resp[2].id.should.have_length 32
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options'
    - // a lengthy way to test that a conflict can't be created with the
    - // all_or_nothing option set to false, but can be when it's true.
    -
    - var old_doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "123"};
    - db.saveDoc(old_doc, {
    - success: function(resp){
    - old_doc._rev = resp.rev;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    -
    - var new_doc = {"Name" : "Sasha", "Callsign" : "Kat", "_id" : "123"};
    -
    - db.bulkSave({"docs": [new_doc], "all_or_nothing": false}, {
    - success: function(resp){
    - resp[0].id.should.eql "123"
    - resp[0].error.should.eql "conflict"
    - resp[0].reason.should.eql "Document update conflict."
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    -
    - db.bulkSave({"docs": [new_doc], "all_or_nothing": true}, {
    - success: function(resp){
    - resp[0].id.should.eql "123"
    - resp[0].rev.should.not.eql old_doc._rev
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    -
    - db.openDoc("123", {
    - "conflicts": true,
    - success: function(resp){
    - resp._conflicts[0].should.eql old_doc._rev
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.bulkSave("asdf");
    - alert_msg.should.match /The documents could not be saved/
    - end
    - end
    -end
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/spec/jquery_couch_js_instance_methods_3_spec.js
    ----------------------------------------------------------------------
    diff --git a/share/www/spec/jquery_couch_js_instance_methods_3_spec.js b/share/www/spec/jquery_couch_js_instance_methods_3_spec.js
    deleted file mode 100644
    index 5d27d81..0000000
    --- a/share/www/spec/jquery_couch_js_instance_methods_3_spec.js
    +++ /dev/null
    @@ -1,540 +0,0 @@
    -// 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.
    -
    -// Specs for jquery_couch.js lines 300-411
    -
    -describe 'jQuery couchdb db'
    - before
    - stubAlert();
    - end
    -
    - after
    - destubAlert();
    - end
    -
    - before_each
    - db = $.couch.db('spec_db');
    - db.create();
    - end
    -
    - after_each
    - db.drop();
    - end
    -
    - describe 'removeDoc'
    - before_each
    - doc = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "345"};
    - saved_doc = {};
    - db.saveDoc(doc, {
    - success: function(resp){
    - saved_doc = resp;
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in a deleted document'
    - db.removeDoc({_id : "345", _rev : saved_doc.rev}, {
    - success: function(resp){
    - db.openDoc("345", {
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "deleted"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return ok true, the ID and the revision of the deleted document'
    - db.removeDoc({_id : "345", _rev : saved_doc.rev}, {
    - success: function(resp){
    - resp.ok.should.be_true
    - resp.id.should.eql "345"
    - resp.rev.should.be_a String
    - resp.rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should record the revision in the deleted document'
    - db.removeDoc({_id : "345", _rev : saved_doc.rev}, {
    - success: function(resp){
    - db.openDoc("345", {
    - rev: resp.rev,
    - success: function(resp2){
    - resp2._rev.should.eql resp.rev
    - resp2._id.should.eql resp.id
    - resp2._deleted.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.removeDoc({_id: "asdf"});
    - alert_msg.should.match /The document could not be deleted/
    - end
    - end
    -
    - describe 'bulkRemove'
    - before_each
    - doc = {"Name" : "Kara Thrace", "Callsign" : "Starbuck", "_id" : "123"};
    - doc2 = {"Name" : "Karl C. Agathon", "Callsign" : "Helo", "_id" : "456"};
    - doc3 = {"Name" : "Sharon Valerii", "Callsign" : "Boomer", "_id" : "789"};
    - docs = [doc, doc2, doc3];
    -
    - db.bulkSave({"docs": docs}, {
    - success: function(resp){
    - for (var i = 0; i < docs.length; i++) {
    - docs[i]._rev = resp[i].rev;
    - }
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should remove all documents specified'
    - db.bulkRemove({"docs": docs});
    - db.allDocs({
    - success: function(resp) {
    - resp.total_rows.should.eql 0
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should not remove documents that should not have been deleted'
    - db.bulkRemove({"docs": [doc3]});
    - db.allDocs({
    - success: function(resp) {
    - resp.total_rows.should.eql 2
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should result in deleted documents'
    - db.bulkRemove({"docs": docs}, {
    - success: function(resp){
    - db.openDoc("123", {
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "deleted"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should return the ID and the revision of the deleted documents'
    - db.bulkRemove({"docs": docs}, {
    - success: function(resp){
    - resp[0].id.should.eql "123"
    - resp[0].rev.should.be_a String
    - resp[0].rev.length.should.be_at_least 30
    - resp[1].id.should.eql "456"
    - resp[1].rev.should.be_a String
    - resp[1].rev.length.should.be_at_least 30
    - resp[2].id.should.eql "789"
    - resp[2].rev.should.be_a String
    - resp[2].rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should record the revision in the deleted documents'
    - db.bulkRemove({"docs": docs}, {
    - success: function(resp){
    - db.openDoc("123", {
    - rev: resp[0].rev,
    - success: function(resp2){
    - resp2._rev.should.eql resp[0].rev
    - resp2._id.should.eql resp[0].id
    - resp2._deleted.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.bulkRemove({docs: ["asdf"]});
    - alert_msg.should.match /The documents could not be deleted/
    - end
    - end
    -
    - describe 'copyDoc'
    - before_each
    - doc = {"Name" : "Sharon Agathon", "Callsign" : "Athena", "_id" : "123"};
    - db.saveDoc(doc);
    - end
    -
    - it 'should result in another document with same data and new id'
    - db.copyDoc("123", {
    - success: function(resp){
    - resp.id.should.eql "456"
    - resp.rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, {
    - headers: {"Destination":"456"}
    - });
    -
    - db.openDoc("456", {
    - success: function(resp){
    - resp.Name.should.eql "Sharon Agathon"
    - resp.Callsign.should.eql "Athena"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should throw an error when trying to overwrite a document without providing a revision'
    - doc2 = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "456"};
    - db.saveDoc(doc2);
    -
    - db.copyDoc("123", {
    - error: function(status, error, reason){
    - status.should.eql 409
    - error.should.eql "conflict"
    - reason.should.eql "Document update conflict."
    - },
    - success: function(resp){successCallback(resp)}
    - }, {
    - headers: {"Destination":"456"}
    - });
    - end
    -
    - it 'should overwrite a document with the correct revision'
    - doc2 = {"Name" : "Louanne Katraine", "Callsign" : "Kat", "_id" : "456"};
    - var doc2_rev;
    - db.saveDoc(doc2, {
    - success: function(resp){
    - doc2_rev = resp.rev;
    - }
    - });
    -
    - db.copyDoc("123", {
    - success: function(resp){
    - resp.id.should.eql "456"
    - resp.rev.length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - }, {
    - headers: {"Destination":"456?rev=" + doc2_rev}
    - });
    -
    - db.openDoc("456", {
    - success: function(resp){
    - resp.Name.should.eql "Sharon Agathon"
    - resp.Callsign.should.eql "Athena"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.copyDoc("asdf", {}, {});
    - alert_msg.should.match /The document could not be copied/
    - end
    - end
    -
    - describe 'query'
    - before_each
    - db.saveDoc({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"});
    - db.saveDoc({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"});
    - db.saveDoc({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"});
    - map_function = "function(doc) { emit(doc._id, 1); }";
    - reduce_function = "function(key, values, rereduce) { return sum(values); }";
    - end
    -
    - it 'should apply the map function'
    - db.query(map_function, null, null, {
    - success: function(resp){
    - resp.rows.should.have_length 3
    - resp.rows[0].id.should.eql "123"
    - resp.rows[0].key.should.eql "123"
    - resp.rows[0].value.should.eql 1
    - resp.rows[1].id.should.eql "456"
    - resp.rows[1].key.should.eql "456"
    - resp.rows[1].value.should.eql 1
    - resp.rows[2].id.should.eql "789"
    - resp.rows[2].key.should.eql "789"
    - resp.rows[2].value.should.eql 1
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should apply the reduce function'
    - db.query(map_function, reduce_function, null, {
    - success: function(resp){
    - resp.rows.should.have_length 1
    - resp.rows[0].key.should.be_null
    - resp.rows[0].value.should_eql 3
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options'
    - db.query(map_function, null, null, {
    - "startkey": "456",
    - success: function(resp){
    - resp.rows.should.have_length 2
    - resp.rows[0].id.should.eql "456"
    - resp.rows[0].key.should.eql "456"
    - resp.rows[0].value.should.eql 1
    - resp.rows[1].id.should.eql "789"
    - resp.rows[1].key.should.eql "789"
    - resp.rows[1].value.should.eql 1
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the keys'
    - //shouldn't this better work? TODO: implement in jquery.couch.js
    - console.log("shouldn't this better work? TODO: implement in jquery.couch.js")
    - db.query(map_function, null, null, {
    - "keys": ["456", "123"],
    - success: function(resp){
    - resp.rows.should.have_length 2
    - resp.rows[0].id.should.eql "456"
    - resp.rows[0].key.should.eql "456"
    - resp.rows[0].value.should.eql 1
    - resp.rows[1].id.should.eql "123"
    - resp.rows[1].key.should.eql "123"
    - resp.rows[1].value.should.eql 1
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options and the keys'
    - //shouldn't this better work? TODO: implement in jquery.couch.js
    - console.log("shouldn't this better work? TODO: implement in jquery.couch.js")
    - db.query(map_function, null, null, {
    - "include_docs":"true",
    - "keys": ["456"],
    - success: function(resp){
    - resp.rows.should.have_length 1
    - resp.rows[0].id.should.eql "456"
    - resp.rows[0].key.should.eql "456"
    - resp.rows[0].value.should.eql 1
    - resp.rows[0].doc["job"].should.eql "pilot"
    - resp.rows[0].doc["_rev"].length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should apply a query in erlang also'
    - // when this test fails, read this: http://wiki.apache.org/couchdb/EnableErlangViews
    - var erlang_map = 'fun({Doc}) -> ' +
    - 'ID = proplists:get_value(<<"_id">>, Doc, null), ' +
    - 'Emit(ID, 1) ' +
    - 'end.';
    - db.query(erlang_map, null, "erlang", {
    - success: function(resp){
    - resp.rows.should.have_length 3
    - resp.rows[0].id.should.eql "123"
    - resp.rows[0].key.should.eql "123"
    - resp.rows[0].value.should.eql 1
    - resp.rows[1].id.should.eql "456"
    - resp.rows[1].key.should.eql "456"
    - resp.rows[1].value.should.eql 1
    - resp.rows[2].id.should.eql "789"
    - resp.rows[2].key.should.eql "789"
    - resp.rows[2].value.should.eql 1
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.query("asdf");
    - alert_msg.should.match /An error occurred querying the database/
    - end
    - end
    -
    - describe 'view'
    - before_each
    - db.saveDoc({"Name" : "Cally Tyrol", "job" : "deckhand", "_id" : "789"});
    - db.saveDoc({"Name" : "Felix Gaeta", "job" : "officer", "_id" : "123"});
    - db.saveDoc({"Name" : "Samuel T. Anders", "job" : "pilot", "_id" : "456"});
    - view = {
    - "views" : {
    - "people" : {
    - "map" : "function(doc) { emit(doc._id, doc.Name); }"
    - }
    - },
    - "_id" : "_design/spec_db"
    - };
    - db.saveDoc(view);
    - end
    -
    - it 'should apply the view'
    - db.view('spec_db/people', {
    - success: function(resp){
    - resp.rows.should.have_length 3
    - resp.rows[0].id.should.eql "123"
    - resp.rows[0].key.should.eql "123"
    - resp.rows[0].value.should.eql "Felix Gaeta"
    - resp.rows[1].id.should.eql "456"
    - resp.rows[1].key.should.eql "456"
    - resp.rows[1].value.should.eql "Samuel T. Anders"
    - resp.rows[2].id.should.eql "789"
    - resp.rows[2].key.should.eql "789"
    - resp.rows[2].value.should.eql "Cally Tyrol"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options'
    - db.view('spec_db/people', {
    - "skip":"2",
    - success: function(resp){
    - resp.rows.should.have_length 1
    - resp.rows[0].id.should.eql "789"
    - resp.rows[0].key.should.eql "789"
    - resp.rows[0].value.should.eql "Cally Tyrol"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the keys'
    - db.view('spec_db/people', {
    - "keys":["456", "123"],
    - success: function(resp){
    - resp.rows.should.have_length 2
    - resp.rows[0].id.should.eql "456"
    - resp.rows[0].key.should.eql "456"
    - resp.rows[0].value.should.eql "Samuel T. Anders"
    - resp.rows[1].id.should.eql "123"
    - resp.rows[1].key.should.eql "123"
    - resp.rows[1].value.should.eql "Felix Gaeta"
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should pass through the options and the keys'
    - db.view('spec_db/people', {
    - "include_docs":"true",
    - "keys":["456"],
    - success: function(resp){
    - resp.rows.should.have_length 1
    - resp.rows[0].id.should.eql "456"
    - resp.rows[0].key.should.eql "456"
    - resp.rows[0].value.should.eql "Samuel T. Anders"
    - resp.rows[0].doc["job"].should.eql "pilot"
    - resp.rows[0].doc["_rev"].length.should.be_at_least 30
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should throw a 404 when the view doesnt exist'
    - db.view('spec_db/non_existing_view', {
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "missing_named_view"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.view("asdf");
    - alert_msg.should.match /An error occurred accessing the view/
    - end
    - end
    -
    - describe 'setDbProperty'
    - it 'should return ok true'
    - db.setDbProperty("_revs_limit", 1500, {
    - success: function(resp){
    - resp.ok.should.be_true
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should set a db property'
    - db.setDbProperty("_revs_limit", 1500);
    - db.getDbProperty("_revs_limit", {
    - success: function(resp){
    - resp.should.eql 1500
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - db.setDbProperty("_revs_limit", 1200);
    - db.getDbProperty("_revs_limit", {
    - success: function(resp){
    - resp.should.eql 1200
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.setDbProperty("asdf");
    - alert_msg.should.match /The property could not be updated/
    - end
    - end
    -
    - describe 'getDbProperty'
    - it 'should get a db property'
    - db.setDbProperty("_revs_limit", 1200);
    - db.getDbProperty("_revs_limit", {
    - success: function(resp){
    - resp.should.eql 1200
    - },
    - error: function(status, error, reason){errorCallback(status, error, reason)}
    - });
    - end
    -
    - it 'should throw a 404 when the property doesnt exist'
    - db.getDbProperty("_doesnt_exist", {
    - error: function(status, error, reason){
    - status.should.eql 404
    - error.should.eql "not_found"
    - reason.should.eql "missing"
    - },
    - success: function(resp){successCallback(resp)}
    - });
    - end
    -
    - it 'should alert with an error message prefix'
    - db.getDbProperty("asdf");
    - alert_msg.should.match /The property could not be retrieved/
    - end
    - end
    -end
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/spec/run.html
    ----------------------------------------------------------------------
    diff --git a/share/www/spec/run.html b/share/www/spec/run.html
    deleted file mode 100644
    index 9a248cb..0000000
    --- a/share/www/spec/run.html
    +++ /dev/null
    @@ -1,47 +0,0 @@
    -<!--
    -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.
    --->
    -<html>
    - <head>
    - <link type="text/css" rel="stylesheet" href="../script/jspec/jspec.css" />
    - <script src="../script/jquery.js"></script>
    - <script src="../script/sha1.js"></script>
    - <script src="../script/jspec/jspec.js"></script>
    - <script src="../script/jspec/jspec.jquery.js"></script>
    - <script src="../script/jspec/jspec.xhr.js"></script>
    - <script src="./custom_helpers.js"></script>
    - <script src="../script/couch.js"></script>
    - <script src="../script/jquery.couch.js"></script>
    - <script src="../script/couch_test_runner.js"></script>
    - <script>
    - function runSuites() {
    - JSpec
    - .exec('couch_js_class_methods_spec.js')
    - .exec('couch_js_instance_methods_1_spec.js')
    - .exec('couch_js_instance_methods_2_spec.js')
    - .exec('couch_js_instance_methods_3_spec.js')
    - .exec('jquery_couch_js_class_methods_spec.js')
    - .exec('jquery_couch_js_instance_methods_1_spec.js')
    - .exec('jquery_couch_js_instance_methods_2_spec.js')
    - .exec('jquery_couch_js_instance_methods_3_spec.js')
    - .run({failuresOnly: true})
    - .report()
    - }
    - </script>
    - </head>
    - <body class="jspec" onLoad="runSuites();">
    - <div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
    - <div id="jspec"></div>
    - <div id="jspec-bottom"></div>
    - </body>
    -</html>

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/status.html
    ----------------------------------------------------------------------
    diff --git a/share/www/status.html b/share/www/status.html
    deleted file mode 100644
    index df35b61..0000000
    --- a/share/www/status.html
    +++ /dev/null
    @@ -1,152 +0,0 @@
    -<!DOCTYPE html>
    -<!--
    -
    -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.
    -
    --->
    -<html lang="en">
    - <head>
    - <title>Status</title>
    - <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    - <link rel="stylesheet" href="style/layout.css?0.11.0" type="text/css">
    - <script src="script/json2.js"></script>
    - <script src="script/sha1.js"></script>
    - <script src="script/jquery.js"></script>
    - <script src="script/jquery.couch.js"></script>
    - <script src="script/jquery.dialog.js"></script>
    - <script src="script/futon.js"></script>
    - </head>
    - <body><div id="wrap">
    - <h1>
    - <a href="index.html">Overview</a>
    - <strong>Status</strong>
    - </h1>
    - <div id="content">
    - <div id="interval">
    - <label>Poll interval:
    - <input type="range" min="1" max="30" value="5" size="3">
    - <span class="secs">5</span> second(s)
    - </label>
    - </div>
    - <table id="status" class="listing" cellspacing="0">
    - <caption>Active Tasks</caption>
    - <thead><tr>
    - <th>Type</th>
    - <th>Object</th>
    - <th>Started on</th>
    - <th>Last updated on</th>
    - <th>PID</th>
    - <th>Status</th>
    - </tr></thead>
    - <tbody class="content"></tbody>
    - </table>
    -
    - </div>
    - </div></body>
    - <script>
    - function toTaskDate(timestamp) {
    - var d = new Date(timestamp * 1000);
    - var hours = d.getHours(), min = d.getMinutes(), secs = d.getSeconds();
    - var year = d.getFullYear(), month = d.getMonth() + 1, day = d.getDate();
    -
    - return String(year) + "-" + (month < 10 ? "0" + month : month) + "-" +
    - day + " " + (hours < 10 ? "0" + hours : hours) + ":" +
    - (min < 10 ? "0" + min : min) + ":" + (secs < 10 ? "0" + secs : secs);
    - }
    - var refreshTimeout = null;
    -
    - $.futon.storage.declare("poll_interval", {defaultValue: 5});
    -
    - function refresh() {
    - $.couch.activeTasks({
    - success: function(tasks) {
    - clearTimeout(refreshTimeout);
    - $("#status tbody.content").empty();
    - if (!tasks.length) {
    - $("<tr class='none'><th colspan='6'>No tasks running</th></tr>")
    - .appendTo("#status tbody.content");
    - } else {
    - $.each(tasks, function(idx, task) {
    - var status, type, object;
    -
    - switch (task.type) {
    - case "database_compaction":
    - type = "Database compaction";
    - object = task.database + (task.retry ? " retry" : "");
    - status = "Copied " + task.changes_done + " of " +
    - task.total_changes + " changes (" + task.progress + "%)";
    - break;
    - case "view_compaction":
    - type = "View compaction";
    - object = task.database + ", " + task.design_document;
    - status = "Progress " + task.progress + "%";
    - break;
    - case "indexer":
    - type = "Indexer";
    - object = task.database + ", " + task.design_document;
    - status = "Processed " + task.changes_done + " of " +
    - task.total_changes + " changes (" + task.progress + "%)";
    - break;
    - case "replication":
    - type = "Replication";
    - object = task.source + " to " + task.target;
    - status = "Checkpointed source sequence " +
    - task.checkpointed_source_seq + ", current source sequence " +
    - task.source_seq + ", progress " + task.progress + "%";
    - }
    -
    - $("<tr><th></th><td class='object'></td><td class='started'>" +
    - "</td><td class='updated'></td><td class='pid'></td>" +
    - "<td class='status'></td></tr>")
    - .find("th").text(type).end()
    - .find("td.object").text(object).end()
    - .find("td.started").text(toTaskDate(task.started_on)).end()
    - .find("td.updated").text(toTaskDate(task.updated_on)).end()
    - .find("td.pid").text(task.pid).end()
    - .find("td.status").text(status).end()
    - .appendTo("#status tbody.content");
    - });
    - }
    - refreshTimeout = setTimeout(refresh,
    - parseInt($("#interval input").val(), 10) * 1000);
    - }
    - });
    - }
    -
    - function updateInterval(value) {
    - if (isNaN(value)) {
    - value = 5;
    - $("#interval input").val(value);
    - }
    - $("#interval .secs").text(value);
    - refresh();
    - $.futon.storage.set("poll_interval", value);
    - }
    -
    - $(function() {
    - var slider = $("#interval input");
    - slider.val(parseInt($.futon.storage.get("poll_interval")));
    - if (slider[0].type == "range") {
    - slider.bind("input", function() {
    - updateInterval(this.value);
    - });
    - $("#interval .secs").text($("#interval input").val());
    - } else {
    - slider.bind("change", function() {
    - updateInterval(this.value);
    - });
    - $("#interval .secs").hide();
    - }
    - refresh();
    - });
    - </script>
    -</html>

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/78f367cf/share/www/style/jquery-ui-1.8.11.custom.css
    ----------------------------------------------------------------------
    diff --git a/share/www/style/jquery-ui-1.8.11.custom.css b/share/www/style/jquery-ui-1.8.11.custom.css
    deleted file mode 100644
    index a6b2f74..0000000
    --- a/share/www/style/jquery-ui-1.8.11.custom.css
    +++ /dev/null
    @@ -1,347 +0,0 @@
    -/*
    - * jQuery UI CSS Framework 1.8.11
    - *
    - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
    - * Dual licensed under the MIT or GPL Version 2 licenses.
    - * http://jquery.org/license
    - *
    - * http://docs.jquery.com/UI/Theming/API
    - */
    -
    -/* Layout helpers
    -----------------------------------*/
    -.ui-helper-hidden { display: none; }
    -.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
    -.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
    -.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
    -.ui-helper-clearfix { display: inline-block; }
    -/* required comment for clearfix to work in Opera \*/
    -* html .ui-helper-clearfix { height:1%; }
    -.ui-helper-clearfix { display:block; }
    -/* end clearfix */
    -.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
    -
    -
    -/* Interaction Cues
    -----------------------------------*/
    -.ui-state-disabled { cursor: default !important; }
    -
    -
    -/* Icons
    -----------------------------------*/
    -
    -/* states and images */
    -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
    -
    -
    -/* Misc visuals
    -----------------------------------*/
    -
    -/* Overlays */
    -.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
    -
    -
    -/*
    - * jQuery UI CSS Framework 1.8.11
    - *
    - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
    - * Dual licensed under the MIT or GPL Version 2 licenses.
    - * http://jquery.org/license
    - *
    - * http://docs.jquery.com/UI/Theming/API
    - *
    - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHig
      hlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
    - */
    -
    -
    -/* Component containers
    -----------------------------------*/
    -.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
    -.ui-widget .ui-widget { font-size: 1em; }
    -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
    -.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee 50% top repeat-x; color: #333333; }
    -.ui-widget-content a { color: #333333; }
    -.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
    -.ui-widget-header a { color: #ffffff; }
    -
    -/* Interaction states
    -----------------------------------*/
    -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
    -.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
    -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce 50% 50% repeat-x; font-weight: bold; color: #c77405; }
    -.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
    -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
    -.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
    -.ui-widget :active { outline: none; }
    -
    -/* Interaction Cues
    -----------------------------------*/
    -.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c 50% top repeat-x; color: #363636; }
    -.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
    -.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 50% 50% repeat; color: #ffffff; }
    -.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
    -.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
    -.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
    -.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
    -.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
    -
    -/* Icons
    -----------------------------------*/
    -
    -/* states and images */
    -.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
    -.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
    -.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
    -.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
    -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
    -.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
    -.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
    -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
    -
    -/* positioning */
    -.ui-icon-carat-1-n { background-position: 0 0; }
    -.ui-icon-carat-1-ne { background-position: -16px 0; }
    -.ui-icon-carat-1-e { background-position: -32px 0; }
    -.ui-icon-carat-1-se { background-position: -48px 0; }
    -.ui-icon-carat-1-s { background-position: -64px 0; }
    -.ui-icon-carat-1-sw { background-position: -80px 0; }
    -.ui-icon-carat-1-w { background-position: -96px 0; }
    -.ui-icon-carat-1-nw { background-position: -112px 0; }
    -.ui-icon-carat-2-n-s { background-position: -128px 0; }
    -.ui-icon-carat-2-e-w { background-position: -144px 0; }
    -.ui-icon-triangle-1-n { background-position: 0 -16px; }
    -.ui-icon-triangle-1-ne { background-position: -16px -16px; }
    -.ui-icon-triangle-1-e { background-position: -32px -16px; }
    -.ui-icon-triangle-1-se { background-position: -48px -16px; }
    -.ui-icon-triangle-1-s { background-position: -64px -16px; }
    -.ui-icon-triangle-1-sw { background-position: -80px -16px; }
    -.ui-icon-triangle-1-w { background-position: -96px -16px; }
    -.ui-icon-triangle-1-nw { background-position: -112px -16px; }
    -.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
    -.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
    -.ui-icon-arrow-1-n { background-position: 0 -32px; }
    -.ui-icon-arrow-1-ne { background-position: -16px -32px; }
    -.ui-icon-arrow-1-e { background-position: -32px -32px; }
    -.ui-icon-arrow-1-se { background-position: -48px -32px; }
    -.ui-icon-arrow-1-s { background-position: -64px -32px; }
    -.ui-icon-arrow-1-sw { background-position: -80px -32px; }
    -.ui-icon-arrow-1-w { background-position: -96px -32px; }
    -.ui-icon-arrow-1-nw { background-position: -112px -32px; }
    -.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
    -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
    -.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
    -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
    -.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
    -.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
    -.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
    -.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
    -.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
    -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
    -.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
    -.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
    -.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
    -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
    -.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
    -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
    -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
    -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
    -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
    -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
    -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
    -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
    -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
    -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
    -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
    -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
    -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
    -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
    -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
    -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
    -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
    -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
    -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
    -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
    -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
    -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
    -.ui-icon-arrow-4 { background-position: 0 -80px; }
    -.ui-icon-arrow-4-diag { background-position: -16px -80px; }
    -.ui-icon-extlink { background-position: -32px -80px; }
    -.ui-icon-newwin { background-position: -48px -80px; }
    -.ui-icon-refresh { background-position: -64px -80px; }
    -.ui-icon-shuffle { background-position: -80px -80px; }
    -.ui-icon-transfer-e-w { background-position: -96px -80px; }
    -.ui-icon-transferthick-e-w { background-position: -112px -80px; }
    -.ui-icon-folder-collapsed { background-position: 0 -96px; }
    -.ui-icon-folder-open { background-position: -16px -96px; }
    -.ui-icon-document { background-position: -32px -96px; }
    -.ui-icon-document-b { background-position: -48px -96px; }
    -.ui-icon-note { background-position: -64px -96px; }
    -.ui-icon-mail-closed { background-position: -80px -96px; }
    -.ui-icon-mail-open { background-position: -96px -96px; }
    -.ui-icon-suitcase { background-position: -112px -96px; }
    -.ui-icon-comment { background-position: -128px -96px; }
    -.ui-icon-person { background-position: -144px -96px; }
    -.ui-icon-print { background-position: -160px -96px; }
    -.ui-icon-trash { background-position: -176px -96px; }
    -.ui-icon-locked { background-position: -192px -96px; }
    -.ui-icon-unlocked { background-position: -208px -96px; }
    -.ui-icon-bookmark { background-position: -224px -96px; }
    -.ui-icon-tag { background-position: -240px -96px; }
    -.ui-icon-home { background-position: 0 -112px; }
    -.ui-icon-flag { background-position: -16px -112px; }
    -.ui-icon-calendar { background-position: -32px -112px; }
    -.ui-icon-cart { background-position: -48px -112px; }
    -.ui-icon-pencil { background-position: -64px -112px; }
    -.ui-icon-clock { background-position: -80px -112px; }
    -.ui-icon-disk { background-position: -96px -112px; }
    -.ui-icon-calculator { background-position: -112px -112px; }
    -.ui-icon-zoomin { background-position: -128px -112px; }
    -.ui-icon-zoomout { background-position: -144px -112px; }
    -.ui-icon-search { background-position: -160px -112px; }
    -.ui-icon-wrench { background-position: -176px -112px; }
    -.ui-icon-gear { background-position: -192px -112px; }
    -.ui-icon-heart { background-position: -208px -112px; }
    -.ui-icon-star { background-position: -224px -112px; }
    -.ui-icon-link { background-position: -240px -112px; }
    -.ui-icon-cancel { background-position: 0 -128px; }
    -.ui-icon-plus { background-position: -16px -128px; }
    -.ui-icon-plusthick { background-position: -32px -128px; }
    -.ui-icon-minus { background-position: -48px -128px; }
    -.ui-icon-minusthick { background-position: -64px -128px; }
    -.ui-icon-close { background-position: -80px -128px; }
    -.ui-icon-closethick { background-position: -96px -128px; }
    -.ui-icon-key { background-position: -112px -128px; }
    -.ui-icon-lightbulb { background-position: -128px -128px; }
    -.ui-icon-scissors { background-position: -144px -128px; }
    -.ui-icon-clipboard { background-position: -160px -128px; }
    -.ui-icon-copy { background-position: -176px -128px; }
    -.ui-icon-contact { background-position: -192px -128px; }
    -.ui-icon-image { background-position: -208px -128px; }
    -.ui-icon-video { background-position: -224px -128px; }
    -.ui-icon-script { background-position: -240px -128px; }
    -.ui-icon-alert { background-position: 0 -144px; }
    -.ui-icon-info { background-position: -16px -144px; }
    -.ui-icon-notice { background-position: -32px -144px; }
    -.ui-icon-help { background-position: -48px -144px; }
    -.ui-icon-check { background-position: -64px -144px; }
    -.ui-icon-bullet { background-position: -80px -144px; }
    -.ui-icon-radio-off { background-position: -96px -144px; }
    -.ui-icon-radio-on { background-position: -112px -144px; }
    -.ui-icon-pin-w { background-position: -128px -144px; }
    -.ui-icon-pin-s { background-position: -144px -144px; }
    -.ui-icon-play { background-position: 0 -160px; }
    -.ui-icon-pause { background-position: -16px -160px; }
    -.ui-icon-seek-next { background-position: -32px -160px; }
    -.ui-icon-seek-prev { background-position: -48px -160px; }
    -.ui-icon-seek-end { background-position: -64px -160px; }
    -.ui-icon-seek-start { background-position: -80px -160px; }
    -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
    -.ui-icon-seek-first { background-position: -80px -160px; }
    -.ui-icon-stop { background-position: -96px -160px; }
    -.ui-icon-eject { background-position: -112px -160px; }
    -.ui-icon-volume-off { background-position: -128px -160px; }
    -.ui-icon-volume-on { background-position: -144px -160px; }
    -.ui-icon-power { background-position: 0 -176px; }
    -.ui-icon-signal-diag { background-position: -16px -176px; }
    -.ui-icon-signal { background-position: -32px -176px; }
    -.ui-icon-battery-0 { background-position: -48px -176px; }
    -.ui-icon-battery-1 { background-position: -64px -176px; }
    -.ui-icon-battery-2 { background-position: -80px -176px; }
    -.ui-icon-battery-3 { background-position: -96px -176px; }
    -.ui-icon-circle-plus { background-position: 0 -192px; }
    -.ui-icon-circle-minus { background-position: -16px -192px; }
    -.ui-icon-circle-close { background-position: -32px -192px; }
    -.ui-icon-circle-triangle-e { background-position: -48px -192px; }
    -.ui-icon-circle-triangle-s { background-position: -64px -192px; }
    -.ui-icon-circle-triangle-w { background-position: -80px -192px; }
    -.ui-icon-circle-triangle-n { background-position: -96px -192px; }
    -.ui-icon-circle-arrow-e { background-position: -112px -192px; }
    -.ui-icon-circle-arrow-s { background-position: -128px -192px; }
    -.ui-icon-circle-arrow-w { background-position: -144px -192px; }
    -.ui-icon-circle-arrow-n { background-position: -160px -192px; }
    -.ui-icon-circle-zoomin { background-position: -176px -192px; }
    -.ui-icon-circle-zoomout { background-position: -192px -192px; }
    -.ui-icon-circle-check { background-position: -208px -192px; }
    -.ui-icon-circlesmall-plus { background-position: 0 -208px; }
    -.ui-icon-circlesmall-minus { background-position: -16px -208px; }
    -.ui-icon-circlesmall-close { background-position: -32px -208px; }
    -.ui-icon-squaresmall-plus { background-position: -48px -208px; }
    -.ui-icon-squaresmall-minus { background-position: -64px -208px; }
    -.ui-icon-squaresmall-close { background-position: -80px -208px; }
    -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
    -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
    -.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
    -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
    -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
    -.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
    -
    -
    -/* Misc visuals
    -----------------------------------*/
    -
    -/* Corner radius */
    -.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
    -.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
    -.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
    -.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
    -.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
    -.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
    -.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
    -.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
    -.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
    -
    -/* Overlays */
    -.ui-widget-overlay { background: #666666 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
    -.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*
    - * jQuery UI Autocomplete 1.8.11
    - *
    - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
    - * Dual licensed under the MIT or GPL Version 2 licenses.
    - * http://jquery.org/license
    - *
    - * http://docs.jquery.com/UI/Autocomplete#theming
    - */
    -.ui-autocomplete { position: absolute; cursor: default; }
    -
    -/* workarounds */
    -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
    -
    -/*
    - * jQuery UI Menu 1.8.11
    - *
    - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
    - * Dual licensed under the MIT or GPL Version 2 licenses.
    - * http://jquery.org/license
    - *
    - * http://docs.jquery.com/UI/Menu#theming
    - */
    -.ui-menu {
    - list-style:none;
    - padding: 2px;
    - margin: 0;
    - display:block;
    - float: left;
    -}
    -.ui-menu .ui-menu {
    - margin-top: -3px;
    -}
    -.ui-menu .ui-menu-item {
    - margin:0;
    - padding: 0;
    - zoom: 1;
    - float: left;
    - clear: left;
    - width: 100%;
    -}
    -.ui-menu .ui-menu-item a {
    - text-decoration:none;
    - display:block;
    - padding:.2em .4em;
    - line-height:1.5;
    - zoom:1;
    -}
    -.ui-menu .ui-menu-item a.ui-state-hover,
    -.ui-menu .ui-menu-item a.ui-state-active {
    - font-weight: normal;
    - margin: -1px;
    -}
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/cookie_auth.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/cookie_auth.js b/test/javascript/tests/cookie_auth.js
    new file mode 100644
    index 0000000..9b4bd64
    --- /dev/null
    +++ b/test/javascript/tests/cookie_auth.js
    @@ -0,0 +1,288 @@
    +// 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.
    +
    +couchTests.cookie_auth = function(debug) {
    + // This tests cookie-based authentication.
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var password = "3.141592653589";
    +
    + var loginUser = function(username) {
    + var pws = {
    + jan: "apple",
    + "Jason Davies": password,
    + jchris: "funnybone"
    + };
    + var username1 = username.replace(/[0-9]$/, "");
    + var password = pws[username];
    + //console.log("Logging in '" + username1 + "' with password '" + password + "'");
    + T(CouchDB.login(username1, pws[username]).ok);
    + };
    +
    + var open_as = function(db, docId, username) {
    + loginUser(username);
    + try {
    + return db.open(docId, {"anti-cache": Math.round(Math.random() * 100000)});
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + var save_as = function(db, doc, username)
    + {
    + loginUser(username);
    + try {
    + return db.save(doc);
    + } catch (ex) {
    + return ex;
    + } finally {
    + CouchDB.logout();
    + }
    + };
    +
    + // Simple secret key generator
    + function generateSecret(length) {
    + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    + var secret = '';
    + for (var i=0; i<length; i++) {
    + secret += tab.charAt(Math.floor(Math.random() * 64));
    + }
    + return secret;
    + }
    +
    + // this function will be called on the modified server
    + var testFun = function () {
    + try {
    +
    + // test that the users db is born with the auth ddoc
    + var ddoc = open_as(usersDb, "_design/_auth", "jan");
    + T(ddoc.validate_doc_update);
    +
    + // TODO test that changing the config so an existing db becomes the users db installs the ddoc also
    +
    + // Create a user
    + var jasonUserDoc = CouchDB.prepareUserDoc({
    + name: "Jason Davies"
    + }, password);
    + T(usersDb.save(jasonUserDoc).ok);
    +
    + var checkDoc = open_as(usersDb, jasonUserDoc._id, "jan");
    + TEquals("Jason Davies", checkDoc.name);
    +
    + var jchrisUserDoc = CouchDB.prepareUserDoc({
    + name: "jchris@apache.org"
    + }, "funnybone");
    + T(usersDb.save(jchrisUserDoc).ok);
    +
    + // make sure we cant create duplicate users
    + var duplicateJchrisDoc = CouchDB.prepareUserDoc({
    + name: "jchris@apache.org"
    + }, "eh, Boo-Boo?");
    +
    + try {
    + usersDb.save(duplicateJchrisDoc);
    + T(false && "Can't create duplicate user names. Should have thrown an error.");
    + } catch (e) {
    + TEquals("conflict", e.error);
    + TEquals(409, usersDb.last_req.status);
    + }
    +
    + // we can't create _names
    + var underscoreUserDoc = CouchDB.prepareUserDoc({
    + name: "_why"
    + }, "copperfield");
    +
    + try {
    + usersDb.save(underscoreUserDoc);
    + T(false && "Can't create underscore user names. Should have thrown an error.");
    + } catch (e) {
    + TEquals("forbidden", e.error);
    + TEquals(403, usersDb.last_req.status);
    + }
    +
    + // we can't create docs with malformed ids
    + var badIdDoc = CouchDB.prepareUserDoc({
    + name: "w00x"
    + }, "bar");
    +
    + badIdDoc._id = "org.apache.couchdb:w00x";
    +
    + try {
    + usersDb.save(badIdDoc);
    + T(false && "Can't create malformed docids. Should have thrown an error.");
    + } catch (e) {
    + TEquals("forbidden", e.error);
    + TEquals(403, usersDb.last_req.status);
    + }
    +
    + // login works
    + T(CouchDB.login('Jason Davies', password).ok);
    + TEquals('Jason Davies', CouchDB.session().userCtx.name);
    +
    + // JSON login works
    + var xhr = CouchDB.request("POST", "/_session", {
    + headers: {"Content-Type": "application/json"},
    + body: JSON.stringify({
    + name: 'Jason Davies',
    + password: password
    + })
    + });
    +
    + T(JSON.parse(xhr.responseText).ok);
    + TEquals('Jason Davies', CouchDB.session().userCtx.name);
    +
    + // update one's own credentials document
    + jasonUserDoc.foo=2;
    + T(usersDb.save(jasonUserDoc).ok);
    + T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
    + // can't delete another users doc unless you are admin
    + try {
    + usersDb.deleteDoc(jchrisUserDoc);
    + T(false && "Can't delete other users docs. Should have thrown an error.");
    + } catch (e) {
    + TEquals("not_found", e.error);
    + TEquals(404, usersDb.last_req.status);
    + }
    +
    + // TODO should login() throw an exception here?
    + T(!CouchDB.login('Jason Davies', "2.71828").ok);
    + T(!CouchDB.login('Robert Allen Zimmerman', 'd00d').ok);
    +
    + // a failed login attempt should log you out
    + T(CouchDB.session().userCtx.name != 'Jason Davies');
    +
    + // test redirect on success
    + xhr = CouchDB.request("POST", "/_session?next=/", {
    + headers: {"Content-Type": "application/x-www-form-urlencoded"},
    + body: "name=Jason%20Davies&password="+encodeURIComponent(password)
    + });
    + // the browser should transparently follow the redirect and GET the server root (/)
    + // see http://dev.w3.org/2006/webapi/XMLHttpRequest/#infrastructure-for-the-send-method
    + if (xhr.status == 200) {
    + T(/Welcome/.test(xhr.responseText))
    + }
    +
    + // test redirect on fail
    + xhr = CouchDB.request("POST", "/_session?fail=/", {
    + headers: {"Content-Type": "application/x-www-form-urlencoded"},
    + body: "name=Jason%20Davies&password=foobar"
    + });
    + if (xhr.status == 200) {
    + T(/Welcome/.test(xhr.responseText));
    + }
    +
    + // test users db validations
    + //
    + // test that you can't update docs unless you are logged in as the user (or are admin)
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + T(CouchDB.session().userCtx.name == "jchris@apache.org");
    + T(CouchDB.session().userCtx.roles.length == 0);
    +
    + jasonUserDoc.foo=3;
    +
    + try {
    + usersDb.save(jasonUserDoc);
    + T(false && "Can't update someone else's user doc. Should have thrown an error.");
    + } catch (e) {
    + T(e.error == "not_found");
    + T(usersDb.last_req.status == 404);
    + }
    +
    + // test that you can't edit roles unless you are admin
    + jchrisUserDoc.roles = ["foo"];
    +
    + try {
    + usersDb.save(jchrisUserDoc);
    + T(false && "Can't set roles unless you are admin. Should have thrown an error.");
    + } catch (e) {
    + T(e.error == "forbidden");
    + T(usersDb.last_req.status == 403);
    + }
    +
    + T(CouchDB.logout().ok);
    +
    + jchrisUserDoc.foo = ["foo"];
    + T(save_as(usersDb, jchrisUserDoc, "jan"));
    +
    + // test that you can't save system (underscore) roles even if you are admin
    + jchrisUserDoc.roles = ["_bar"];
    +
    + var res = save_as(usersDb, jchrisUserDoc, "jan");
    + T(res.error == "forbidden");
    + T(usersDb.last_req.status == 403);
    +
    + // make sure the foo role has been applied
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + T(CouchDB.session().userCtx.name == "jchris@apache.org");
    + T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
    + T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
    +
    + // now let's make jchris a server admin
    + T(CouchDB.logout().ok);
    +
    + // set the -hashed- password so the salt matches
    + // todo ask on the ML about this
    +
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + run_on_modified_server([{section: "admins",
    + key: "jchris@apache.org", value: "funnybone"}], function() {
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + T(CouchDB.session().userCtx.name == "jchris@apache.org");
    + T(CouchDB.session().userCtx.roles.indexOf("_admin") != -1);
    + // test that jchris still has the foo role
    + T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
    +
    + // should work even when user doc has no password
    + jchrisUserDoc = usersDb.open(jchrisUserDoc._id);
    + delete jchrisUserDoc.salt;
    + delete jchrisUserDoc.password_sha;
    + T(usersDb.save(jchrisUserDoc).ok);
    + T(CouchDB.logout().ok);
    + T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    + var s = CouchDB.session();
    + T(s.userCtx.name == "jchris@apache.org");
    + T(s.userCtx.roles.indexOf("_admin") != -1);
    + // test session info
    + T(s.info.authenticated == "cookie");
    + T(s.info.authentication_db == "test_suite_users");
    + // test that jchris still has the foo role
    + T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
    + });
    +
    + } finally {
    + // Make sure we erase any auth cookies so we don't affect other tests
    + T(CouchDB.logout().ok);
    + }
    + // log in one last time so run_on_modified_server can clean up the admin account
    + TEquals(true, CouchDB.login("jan", "apple").ok);
    + };
    +
    + var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    + usersDb.deleteDb();
    +
    + run_on_modified_server(
    + [
    + {section: "couch_httpd_auth",
    + key: "authentication_db", value: "test_suite_users"},
    + {section: "couch_httpd_auth",
    + key: "iterations", value: "1"},
    + {section: "admins",
    + key: "jan", value: "apple"}
    + ],
    + testFun
    + );
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/copy_doc.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/copy_doc.js b/test/javascript/tests/copy_doc.js
    new file mode 100644
    index 0000000..d595761
    --- /dev/null
    +++ b/test/javascript/tests/copy_doc.js
    @@ -0,0 +1,65 @@
    +// 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.
    +
    +couchTests.copy_doc = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // copy a doc
    + var ok = db.save({_id:"doc_to_be_copied",v:1}).ok;
    + TEquals(true, ok, "Should return ok:true");
    + var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied", {
    + headers: {"Destination":"doc_that_was_copied"}
    + });
    +
    + TEquals(true, JSON.parse(xhr.responseText).ok, "Should return ok:true");
    +
    + TEquals(201, xhr.status, "Should return 201 status");
    + TEquals(1, db.open("doc_that_was_copied").v, "Should have value 1");
    +
    + // COPY with existing target
    + var ok = db.save({_id:"doc_to_be_copied2",v:1}).ok;
    + TEquals(true, ok, "Should return ok:true");
    + var doc = db.save({_id:"doc_to_be_overwritten",v:2});
    + TEquals(true, doc.ok, "Should return ok:true");
    +
    + // error condition
    + var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2", {
    + headers: {"Destination":"doc_to_be_overwritten"}
    + });
    + TEquals(409, xhr.status, "Should return 409 status"); // conflict
    +
    + var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2");
    + TEquals(400, xhr.status, "Should return 400 status");
    + TEquals("Destination header is mandatory for COPY.", JSON.parse(xhr.responseText).reason,
    + "Should report missing destination header");
    +
    + var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2", {
    + headers: {
    + "Destination": "http://localhost:5984/test_suite_db/doc_to_be_written"
    + }});
    + TEquals(400, xhr.status, "Should return 400 status");
    + TEquals("Destination URL must be relative.", JSON.parse(xhr.responseText).reason,
    + "Should report invalid destination header");
    +
    + var rev = db.open("doc_to_be_overwritten")._rev;
    + var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2", {
    + headers: {"Destination":"doc_to_be_overwritten?rev=" + rev}
    + });
    + TEquals(201, xhr.status, "Should return 201 status");
    +
    + var over = db.open("doc_to_be_overwritten");
    + T(rev != over._rev);
    + TEquals(1, over.v, "Should be value 1");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/delayed_commits.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/delayed_commits.js b/test/javascript/tests/delayed_commits.js
    new file mode 100644
    index 0000000..dbb072f
    --- /dev/null
    +++ b/test/javascript/tests/delayed_commits.js
    @@ -0,0 +1,154 @@
    +// 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.
    +
    +couchTests.delayed_commits = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + run_on_modified_server(
    + [{section: "couchdb",
    + key: "delayed_commits",
    + value: "true"}],
    +
    + function () {
    + // By default, couchdb doesn't fully commit documents to disk right away,
    + // it waits about a second to batch the full commit flush along with any
    + // other updates. If it crashes or is restarted you may lose the most
    + // recent commits.
    +
    + T(db.save({_id:"1",a:2,b:4}).ok);
    + T(db.open("1") != null);
    +
    + restartServer();
    +
    + T(db.open("1") == null); // lost the update.
    + // note if we waited > 1 sec before the restart, the doc would likely
    + // commit.
    +
    +
    + // Retry the same thing but with full commits on.
    +
    + var db2 = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
    +
    + T(db2.save({_id:"1",a:2,b:4}).ok);
    + T(db2.open("1") != null);
    +
    + restartServer();
    +
    + T(db2.open("1") != null);
    +
    + // You can update but without committing immediately, and then ensure
    + // everything is commited in the last step.
    +
    + T(db.save({_id:"2",a:2,b:4}).ok);
    + T(db.open("2") != null);
    + T(db.ensureFullCommit().ok);
    + restartServer();
    +
    + T(db.open("2") != null);
    +
    + // However, it's possible even when flushed, that the server crashed between
    + // the update and the commit, and you don't want to check to make sure
    + // every doc you updated actually made it to disk. So record the instance
    + // start time of the database before the updates and then check it again
    + // after the flush (the instance start time is returned by the flush
    + // operation). if they are the same, we know everything was updated
    + // safely.
    +
    + // First try it with a crash.
    +
    + var instanceStartTime = db.info().instance_start_time;
    +
    + T(db.save({_id:"3",a:2,b:4}).ok);
    + T(db.open("3") != null);
    +
    + restartServer();
    +
    + var commitResult = db.ensureFullCommit();
    + T(commitResult.ok && commitResult.instance_start_time != instanceStartTime);
    + // start times don't match, meaning the server lost our change
    +
    + T(db.open("3") == null); // yup lost it
    +
    + // retry with no server restart
    +
    + var instanceStartTime = db.info().instance_start_time;
    +
    + T(db.save({_id:"4",a:2,b:4}).ok);
    + T(db.open("4") != null);
    +
    + var commitResult = db.ensureFullCommit();
    + T(commitResult.ok && commitResult.instance_start_time == instanceStartTime);
    + // Successful commit, start times match!
    +
    + restartServer();
    +
    + T(db.open("4") != null);
    + });
    +
    + // Now test that when we exceed the max_dbs_open, pending commits are safely
    + // written.
    + T(db.save({_id:"5",foo:"bar"}).ok);
    + var max = 2;
    + run_on_modified_server(
    + [{section: "couchdb",
    + key: "delayed_commits",
    + value: "true"},
    + {section: "couchdb",
    + key: "max_dbs_open",
    + value: max.toString()}],
    +
    + function () {
    + for(var i=0; i<max; i++) {
    + var dbi = new CouchDB("test_suite_db" + i);
    + dbi.deleteDb();
    + dbi.createDb();
    + }
    + T(db.open("5").foo=="bar");
    + for(var i=0; i<max+1; i++) {
    + var dbi = new CouchDB("test_suite_db" + i);
    + dbi.deleteDb();
    + }
    + });
    +
    +
    + // Test that a conflict can't cause delayed commits to fail
    + run_on_modified_server(
    + [{section: "couchdb",
    + key: "delayed_commits",
    + value: "true"}],
    +
    + function() {
    + //First save a document and commit it
    + T(db.save({_id:"6",a:2,b:4}).ok);
    + T(db.ensureFullCommit().ok);
    + //Generate a conflict
    + try {
    + db.save({_id:"6",a:2,b:4});
    + } catch( e) {
    + T(e.error == "conflict");
    + }
    + //Wait for the delayed commit interval to pass
    + var time = new Date();
    + while(new Date() - time < 2000);
    + //Save a new doc
    + T(db.save({_id:"7",a:2,b:4}).ok);
    + //Wait for the delayed commit interval to pass
    + var time = new Date();
    + while(new Date() - time < 2000);
    + //Crash the server and make sure the last doc was written
    + restartServer();
    + T(db.open("7") != null);
    + });
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/design_docs.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/design_docs.js b/test/javascript/tests/design_docs.js
    new file mode 100644
    index 0000000..dd38858
    --- /dev/null
    +++ b/test/javascript/tests/design_docs.js
    @@ -0,0 +1,466 @@
    +// 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.
    +
    +couchTests.design_docs = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + var db2 = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
    +
    + if (debug) debugger;
    +
    + db.deleteDb();
    + db.createDb();
    + db2.deleteDb();
    + db2.createDb();
    +
    + var server_config = [
    + {
    + section: "query_server_config",
    + key: "reduce_limit",
    + value: "false"
    + }
    + ];
    +
    + var testFun = function() {
    + var numDocs = 500;
    +
    + function makebigstring(power) {
    + var str = "a";
    + while(power-- > 0) {
    + str = str + str;
    + }
    + return str;
    + }
    +
    + var designDoc = {
    + _id: "_design/test",
    + language: "javascript",
    + whatever : {
    + stringzone : "exports.string = 'plankton';",
    + commonjs : {
    + whynot : "exports.test = require('../stringzone'); " +
    + "exports.foo = require('whatever/stringzone');",
    + upper : "exports.testing = require('./whynot').test.string.toUpperCase()+" +
    + "module.id+require('./whynot').foo.string",
    + circular_one: "require('./circular_two'); exports.name = 'One';",
    + circular_two: "require('./circular_one'); exports.name = 'Two';"
    + },
    + // paths relative to parent
    + idtest1: {
    + a: {
    + b: {d: "module.exports = require('../c/e').id;"},
    + c: {e: "exports.id = module.id;"}
    + }
    + },
    + // multiple paths relative to parent
    + idtest2: {
    + a: {
    + b: {d: "module.exports = require('../../a/c/e').id;"},
    + c: {e: "exports.id = module.id;"}
    + }
    + },
    + // paths relative to module
    + idtest3: {
    + a: {
    + b: "module.exports = require('./c/d').id;",
    + c: {
    + d: "module.exports = require('./e');",
    + e: "exports.id = module.id;"
    + }
    + }
    + },
    + // paths relative to module and parent
    + idtest4: {
    + a: {
    + b: "module.exports = require('../a/./c/d').id;",
    + c: {
    + d: "module.exports = require('./e');",
    + e: "exports.id = module.id;"
    + }
    + }
    + },
    + // paths relative to root
    + idtest5: {
    + a: "module.exports = require('whatever/idtest5/b').id;",
    + b: "exports.id = module.id;"
    + }
    + },
    + views: {
    + all_docs_twice: {
    + map:
    + (function(doc) {
    + emit(doc.integer, null);
    + emit(doc.integer, null);
    + }).toString()
    + },
    + no_docs: {
    + map:
    + (function(doc) {
    + }).toString()
    + },
    + single_doc: {
    + map:
    + (function(doc) {
    + if (doc._id === "1") {
    + emit(1, null);
    + }
    + }).toString()
    + },
    + summate: {
    + map:
    + (function(doc) {
    + emit(doc.integer, doc.integer);
    + }).toString(),
    + reduce:
    + (function(keys, values) {
    + return sum(values);
    + }).toString()
    + },
    + summate2: {
    + map:
    + (function(doc) {
    + emit(doc.integer, doc.integer);
    + }).toString(),
    + reduce:
    + (function(keys, values) {
    + return sum(values);
    + }).toString()
    + },
    + huge_src_and_results: {
    + map:
    + (function(doc) {
    + if (doc._id === "1") {
    + emit(makebigstring(16), null);
    + }
    + }).toString(),
    + reduce:
    + (function(keys, values) {
    + return makebigstring(16);
    + }).toString()
    + },
    + lib : {
    + baz : "exports.baz = 'bam';",
    + foo : {
    + foo : "exports.foo = 'bar';",
    + boom : "exports.boom = 'ok';",
    + zoom : "exports.zoom = 'yeah';"
    + }
    + },
    + commonjs : {
    + map :
    + (function(doc) {
    + emit(null, require('views/lib/foo/boom').boom);
    + }).toString()
    + }
    + },
    + shows: {
    + simple:
    + (function() {
    + return 'ok';
    + }).toString(),
    + requirey:
    + (function() {
    + var lib = require('whatever/commonjs/upper');
    + return lib.testing;
    + }).toString(),
    + circular:
    + (function() {
    + var lib = require('whatever/commonjs/upper');
    + return JSON.stringify(this);
    + }).toString(),
    + circular_require:
    + (function() {
    + return require('whatever/commonjs/circular_one').name;
    + }).toString(),
    + idtest1: (function() {
    + return require('whatever/idtest1/a/b/d');
    + }).toString(),
    + idtest2: (function() {
    + return require('whatever/idtest2/a/b/d');
    + }).toString(),
    + idtest3: (function() {
    + return require('whatever/idtest3/a/b');
    + }).toString(),
    + idtest4: (function() {
    + return require('whatever/idtest4/a/b');
    + }).toString(),
    + idtest5: (function() {
    + return require('whatever/idtest5/a');
    + }).toString()
    + }
    + }; // designDoc
    +
    + var xhr = CouchDB.request(
    + "PUT", "/test_suite_db_a/_design/test", {body: JSON.stringify(designDoc)}
    + );
    + var resp = JSON.parse(xhr.responseText);
    +
    + TEquals(resp.rev, db.save(designDoc).rev);
    +
    + // test that editing a show fun on the ddoc results in a change in output
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/simple");
    + T(xhr.status == 200);
    + TEquals(xhr.responseText, "ok");
    +
    + designDoc.shows.simple = (function() {
    + return 'ko';
    + }).toString();
    + T(db.save(designDoc).ok);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/simple");
    + T(xhr.status == 200);
    + TEquals(xhr.responseText, "ko");
    +
    + xhr = CouchDB.request(
    + "GET", "/test_suite_db_a/_design/test/_show/simple?cache=buster"
    + );
    + T(xhr.status == 200);
    + TEquals("ok", xhr.responseText, 'query server used wrong ddoc');
    +
    + // test commonjs require
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/requirey");
    + T(xhr.status == 200);
    + TEquals("PLANKTONwhatever/commonjs/upperplankton", xhr.responseText);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/circular");
    + T(xhr.status == 200);
    + TEquals("javascript", JSON.parse(xhr.responseText).language);
    +
    + // test circular commonjs dependencies
    + xhr = CouchDB.request(
    + "GET",
    + "/test_suite_db/_design/test/_show/circular_require"
    + );
    + TEquals(200, xhr.status);
    + TEquals("One", xhr.responseText);
    +
    + // Test that changes to the design doc properly invalidate cached modules:
    +
    + // update the designDoc and replace
    + designDoc.whatever.commonjs.circular_one = "exports.name = 'Updated';"
    + T(db.save(designDoc).ok);
    +
    + // request circular_require show function again and check the response has
    + // changed
    + xhr = CouchDB.request(
    + "GET",
    + "/test_suite_db/_design/test/_show/circular_require"
    + );
    + TEquals(200, xhr.status);
    + TEquals("Updated", xhr.responseText);
    +
    +
    + // test module id values are as expected:
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest1");
    + TEquals(200, xhr.status);
    + TEquals("whatever/idtest1/a/c/e", xhr.responseText);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest2");
    + TEquals(200, xhr.status);
    + TEquals("whatever/idtest2/a/c/e", xhr.responseText);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest3");
    + TEquals(200, xhr.status);
    + TEquals("whatever/idtest3/a/c/e", xhr.responseText);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest4");
    + TEquals(200, xhr.status);
    + TEquals("whatever/idtest4/a/c/e", xhr.responseText);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest5");
    + TEquals(200, xhr.status);
    + TEquals("whatever/idtest5/b", xhr.responseText);
    +
    +
    + var prev_view_sig = db.designInfo("_design/test").view_index.signature;
    + var prev_view_size = db.designInfo("_design/test").view_index.disk_size;
    +
    + db.bulkSave(makeDocs(1, numDocs + 1));
    + T(db.ensureFullCommit().ok);
    +
    + // test that we get correct design doc info back,
    + // and also that GET /db/_design/test/_info
    + // hasn't triggered an update of the views
    + db.view("test/summate", {stale: "ok"}); // make sure view group's open
    + for (var i = 0; i < 2; i++) {
    + var dinfo = db.designInfo("_design/test");
    + TEquals("test", dinfo.name);
    + var vinfo = dinfo.view_index;
    + TEquals(prev_view_size, vinfo.disk_size, "view group disk size didn't change");
    + TEquals(false, vinfo.compact_running);
    + TEquals(prev_view_sig, vinfo.signature, 'ddoc sig');
    + // wait some time (there were issues where an update
    + // of the views had been triggered in the background)
    + var start = new Date().getTime();
    + while (new Date().getTime() < start + 2000);
    + TEquals(0, db.view("test/all_docs_twice", {stale: "ok"}).total_rows, 'view info');
    + TEquals(0, db.view("test/single_doc", {stale: "ok"}).total_rows, 'view info');
    + TEquals(0, db.view("test/summate", {stale: "ok"}).rows.length, 'view info');
    + T(db.ensureFullCommit().ok);
    + restartServer();
    + };
    +
    + db.bulkSave(makeDocs(numDocs + 1, numDocs * 2 + 1));
    + T(db.ensureFullCommit().ok);
    +
    + // open view group
    + db.view("test/summate", {stale: "ok"});
    + // wait so the views can get initialized
    + var start = new Date().getTime();
    + while (new Date().getTime() < start + 2000);
    +
    + // test that POST /db/_view_cleanup
    + // doesn't trigger an update of the views
    + var len1 = db.view("test/all_docs_twice", {stale: "ok"}).total_rows;
    + var len2 = db.view("test/single_doc", {stale: "ok"}).total_rows;
    + var len3 = db.view("test/summate", {stale: "ok"}).rows.length;
    + for (i = 0; i < 2; i++) {
    + T(db.viewCleanup().ok);
    + // wait some time (there were issues where an update
    + // of the views had been triggered in the background)
    + start = new Date().getTime();
    + while (new Date().getTime() < start + 2000);
    + TEquals(len1, db.view("test/all_docs_twice", {stale: "ok"}).total_rows, 'view cleanup');
    + TEquals(len2, db.view("test/single_doc", {stale: "ok"}).total_rows, 'view cleanup');
    + TEquals(len3, db.view("test/summate", {stale: "ok"}).rows.length, 'view cleanup');
    + T(db.ensureFullCommit().ok);
    + restartServer();
    + // we'll test whether the view group stays closed
    + // and the views stay uninitialized (they should!)
    + len1 = len2 = len3 = 0;
    + };
    +
    + // test commonjs in map functions
    + resp = db.view("test/commonjs", {limit:1});
    + T(resp.rows[0].value == 'ok');
    +
    + // test that the _all_docs view returns correctly with keys
    + var results = db.allDocs({startkey:"_design", endkey:"_design0"});
    + T(results.rows.length == 1);
    +
    + for (i = 0; i < 2; i++) {
    + var rows = db.view("test/all_docs_twice").rows;
    + for (var j = 0; j < numDocs; j++) {
    + T(rows[2 * j].key == (j + 1));
    + T(rows[(2 * j) + 1].key == (j + 1));
    + };
    + T(db.view("test/no_docs").total_rows == 0);
    + T(db.view("test/single_doc").total_rows == 1);
    + T(db.ensureFullCommit().ok);
    + restartServer();
    + };
    +
    + // test when language not specified, Javascript is implied
    + var designDoc2 = {
    + _id: "_design/test2",
    + // language: "javascript",
    + views: {
    + single_doc: {
    + map:
    + (function(doc) {
    + if (doc._id === "1") {
    + emit(1, null);
    + }
    + }).toString()
    + }
    + }
    + };
    +
    + T(db.save(designDoc2).ok);
    + T(db.view("test2/single_doc").total_rows == 1);
    +
    + var summate = function(N) {
    + return (N + 1) * (N / 2);
    + };
    + var result = db.view("test/summate");
    + T(result.rows[0].value == summate(numDocs * 2));
    +
    + result = db.view("test/summate", {startkey: 4, endkey: 4});
    + T(result.rows[0].value == 4);
    +
    + result = db.view("test/summate", {startkey: 4, endkey: 5});
    + T(result.rows[0].value == 9);
    +
    + result = db.view("test/summate", {startkey: 4, endkey: 6});
    + T(result.rows[0].value == 15);
    +
    + // test start_key and end_key aliases
    + result = db.view("test/summate", {start_key: 4, end_key: 6});
    + T(result.rows[0].value == 15);
    +
    + // Verify that a shared index (view def is an exact copy of "summate")
    + // does not confuse the reduce stage
    + result = db.view("test/summate2", {startkey: 4, endkey: 6});
    + T(result.rows[0].value == 15);
    +
    + for(i = 1; i < (numDocs / 2); i += 30) {
    + result = db.view("test/summate", {startkey: i, endkey: (numDocs - i)});
    + T(result.rows[0].value == summate(numDocs - i) - summate(i - 1));
    + }
    +
    + T(db.deleteDoc(designDoc).ok);
    + T(db.open(designDoc._id) == null);
    + T(db.view("test/no_docs") == null);
    +
    + T(db.ensureFullCommit().ok);
    + restartServer();
    + T(db.open(designDoc._id) == null);
    + T(db.view("test/no_docs") == null);
    +
    + // trigger ddoc cleanup
    + T(db.viewCleanup().ok);
    + }; // enf of testFun
    +
    + run_on_modified_server(server_config, testFun);
    +
    + // COUCHDB-1227 - if a design document is deleted, by adding a "_deleted"
    + // field with the boolean value true, its validate_doc_update functions
    + // should no longer have effect.
    + db.deleteDb();
    + db.createDb();
    + var ddoc = {
    + _id: "_design/test",
    + language: "javascript",
    + validate_doc_update: (function(newDoc, oldDoc, userCtx, secObj) {
    + if (newDoc.value % 2 == 0) {
    + throw({forbidden: "dont like even numbers"});
    + }
    + return true;
    + }).toString()
    + };
    +
    + TEquals(true, db.save(ddoc).ok);
    + try {
    + db.save({_id: "doc1", value: 4});
    + T(false, "doc insertion should have failed");
    + } catch (x) {
    + TEquals("forbidden", x.error);
    + }
    +
    + var doc = db.open("doc1");
    + TEquals(null, doc);
    + ddoc._deleted = true;
    + TEquals(true, db.save(ddoc).ok);
    +
    + try {
    + TEquals(true, db.save({_id: "doc1", value: 4}).ok);
    + } catch (x) {
    + T(false, "doc insertion should have succeeded");
    + }
    +
    + doc = db.open("doc1");
    + TEquals(true, doc !== null, "doc was not persisted");
    + TEquals(4, doc.value);
    +
    + // cleanup
    + db.deleteDb();
    + db2.deleteDb();
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/design_options.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/design_options.js b/test/javascript/tests/design_options.js
    new file mode 100644
    index 0000000..05764e2
    --- /dev/null
    +++ b/test/javascript/tests/design_options.js
    @@ -0,0 +1,74 @@
    +// 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.
    +
    +couchTests.design_options = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + //// test the includes_design option
    + var map = "function (doc) {emit(null, doc._id);}";
    + var withseq = "function(doc) {emit(doc._local_seq, null)}"
    +
    + // we need a design doc even to test temp views with it
    + var designDoc = {
    + _id:"_design/fu",
    + language: "javascript",
    + options: {
    + include_design: true,
    + local_seq: true
    + },
    + views: {
    + data: {"map": map},
    + with_seq : {"map" : withseq}
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + // should work for temp views
    + var rows = db.query(map, null, {options:{include_design: true}}).rows;
    + T(rows.length == 1);
    + T(rows[0].value == "_design/fu");
    +
    + rows = db.query(map).rows;
    + T(rows.length == 0);
    +
    + // when true, should include design docs in views
    + rows = db.view("fu/data").rows;
    + T(rows.length == 1);
    + T(rows[0].value == "_design/fu");
    +
    + // when false, should not
    + designDoc.options.include_design = false;
    + delete designDoc._rev;
    + designDoc._id = "_design/bingo";
    + T(db.save(designDoc).ok);
    + rows = db.view("bingo/data").rows;
    + T(rows.length == 0);
    +
    + // should default to false
    + delete designDoc.options;
    + delete designDoc._rev;
    + designDoc._id = "_design/bango";
    + T(db.save(designDoc).ok);
    + rows = db.view("bango/data").rows;
    + T(rows.length == 0);
    +
    + // should also have local_seq in the view
    + var resp = db.save({});
    + rows = db.view("fu/with_seq").rows;
    + T(rows[0].key == 1)
    + T(rows[1].key == 2)
    + var doc = db.open(resp.id);
    + db.deleteDoc(doc);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/design_paths.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/design_paths.js b/test/javascript/tests/design_paths.js
    new file mode 100644
    index 0000000..426a252
    --- /dev/null
    +++ b/test/javascript/tests/design_paths.js
    @@ -0,0 +1,72 @@
    +// 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.
    +
    +couchTests.design_paths = function(debug) {
    + if (debug) debugger;
    + var dbNames = ["test_suite_db", "test_suite_db/with_slashes"];
    + for (var i=0; i < dbNames.length; i++) {
    + var db = new CouchDB(dbNames[i]);
    + var dbName = encodeURIComponent(dbNames[i]);
    + db.deleteDb();
    + db.createDb();
    +
    + // create a ddoc w bulk_docs
    + db.bulkSave([{
    + _id : "_design/test",
    + views : {
    + "testing" : {
    + "map" : "function(){emit(1,1)}"
    + }
    + }
    + }]);
    +
    + // ddoc is getable
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test");
    + var resp = JSON.parse(xhr.responseText);
    + T(resp._id == "_design/test");
    +
    + // it's at 2 urls...
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest");
    + var resp = JSON.parse(xhr.responseText);
    + T(resp._id == "_design/test");
    +
    + // ensure that views are addressable
    + resp = db.view("test/testing")
    + T(resp.total_rows == 0)
    +
    + // create a ddoc by putting to url with raw slash
    + var xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test2",{
    + body : JSON.stringify({
    + _id : "_design/test2",
    + views : {
    + "testing" : {
    + "map" : "function(){emit(1,1)}"
    + }
    + }
    + })
    + });
    +
    + // ddoc is getable
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test2");
    + var resp = JSON.parse(xhr.responseText);
    + T(resp._id == "_design/test2");
    +
    + // it's at 2 urls...
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest2");
    + var resp = JSON.parse(xhr.responseText);
    + T(resp._id == "_design/test2");
    +
    + // ensure that views are addressable
    + resp = db.view("test2/testing");
    + T(resp.total_rows == 0);
    + };
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/erlang_views.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/erlang_views.js b/test/javascript/tests/erlang_views.js
    new file mode 100644
    index 0000000..c6bc5d7
    --- /dev/null
    +++ b/test/javascript/tests/erlang_views.js
    @@ -0,0 +1,136 @@
    +// 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.
    +
    +couchTests.erlang_views = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    +
    +
    + run_on_modified_server(
    + [{section: "native_query_servers",
    + key: "erlang",
    + value: "{couch_native_process, start_link, []}"}],
    + function() {
    + // Note we just do some basic 'smoke tests' here - the
    + // test/query_server_spec.rb tests have more comprehensive tests
    + var doc = {_id: "1", integer: 1, string: "str1", array: [1, 2, 3]};
    + T(db.save(doc).ok);
    +
    + var mfun = 'fun({Doc}) -> ' +
    + ' K = couch_util:get_value(<<"integer">>, Doc, null), ' +
    + ' V = couch_util:get_value(<<"string">>, Doc, null), ' +
    + ' Emit(K, V) ' +
    + 'end.';
    +
    + // emitting a key value that is undefined should result in that row not
    + // being included in the view results
    + var results = db.query(mfun, null, null, null, "erlang");
    + T(results.total_rows == 1);
    + T(results.rows[0].key == 1);
    + T(results.rows[0].value == "str1");
    +
    + // check simple reduction - another doc with same key.
    + var doc = {_id: "2", integer: 1, string: "str2"};
    + T(db.save(doc).ok);
    + rfun = 'fun' +
    + ' (_, Values, false) -> length(Values); ' +
    + ' (_, Values, true) -> lists:sum(Values) ' +
    + ' end.';
    + results = db.query(mfun, rfun, null, null, "erlang");
    + T(results.rows[0].value == 2);
    +
    + // simple 'list' tests
    + var designDoc = {
    + _id:"_design/erlview",
    + language: "erlang",
    + shows: {
    + simple:
    + 'fun(Doc, {Req}) -> ' +
    + ' {Info} = couch_util:get_value(<<"info">>, Req, {[]}), ' +
    + ' Purged = couch_util:get_value(<<"purge_seq">>, Info, -1), ' +
    + ' Verb = couch_util:get_value(<<"method">>, Req, <<"not_get">>), ' +
    + ' R = list_to_binary(io_lib:format("~b - ~s", [Purged, Verb])), ' +
    + ' {[{<<"code">>, 200}, {<<"headers">>, {[]}}, {<<"body">>, R}]} ' +
    + 'end.'
    + },
    + lists: {
    + simple_list :
    + 'fun(Head, {Req}) -> ' +
    + ' Send(<<"head">>), ' +
    + ' Fun = fun({Row}, _) -> ' +
    + ' Val = couch_util:get_value(<<"value">>, Row, -1), ' +
    + ' Send(list_to_binary(integer_to_list(Val))), ' +
    + ' {ok, nil} ' +
    + ' end, ' +
    + ' {ok, _} = FoldRows(Fun, nil), ' +
    + ' <<"tail">> ' +
    + 'end. '
    + },
    + views: {
    + simple_view : {
    + map: mfun,
    + reduce: rfun
    + }
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var url = "/test_suite_db/_design/erlview/_show/simple/1";
    + var xhr = CouchDB.request("GET", url);
    + T(xhr.status == 200, "standard get should be 200");
    + T(xhr.responseText == "0 - GET");
    +
    + var url = "/test_suite_db/_design/erlview/_list/simple_list/simple_view";
    + var xhr = CouchDB.request("GET", url);
    + T(xhr.status == 200, "standard get should be 200");
    + T(xhr.responseText == "head2tail");
    +
    + // Larger dataset
    +
    + db.deleteDb();
    + db.createDb();
    + var words = "foo bar abc def baz xxyz".split(/\s+/);
    +
    + var docs = [];
    + for(var i = 0; i < 250; i++) {
    + var body = [];
    + for(var j = 0; j < 100; j++) {
    + body.push({
    + word: words[j%words.length],
    + count: j
    + });
    + }
    + docs.push({
    + "_id": "test-" + i,
    + "words": body
    + });
    + }
    + T(db.bulkSave(docs).length, 250, "Saved big doc set.");
    +
    + var mfun = 'fun({Doc}) -> ' +
    + 'Words = couch_util:get_value(<<"words">>, Doc), ' +
    + 'lists:foreach(fun({Word}) -> ' +
    + 'WordString = couch_util:get_value(<<"word">>, Word), ' +
    + 'Count = couch_util:get_value(<<"count">>, Word), ' +
    + 'Emit(WordString , Count) ' +
    + 'end, Words) ' +
    + 'end.';
    +
    + var rfun = 'fun(Keys, Values, RR) -> length(Values) end.';
    + var results = db.query(mfun, rfun, null, null, "erlang");
    + T(results.rows[0].key === null, "Returned a reduced value.");
    + T(results.rows[0].value > 0, "Reduce value exists.");
    + });
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/etags_head.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/etags_head.js b/test/javascript/tests/etags_head.js
    new file mode 100644
    index 0000000..63e2999
    --- /dev/null
    +++ b/test/javascript/tests/etags_head.js
    @@ -0,0 +1,78 @@
    +// 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.
    +
    +couchTests.etags_head = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var xhr;
    +
    + // create a new doc
    + xhr = CouchDB.request("PUT", "/test_suite_db/1", {
    + body: "{}"
    + });
    + T(xhr.status == 201);
    +
    + // extract the ETag header values
    + var etag = xhr.getResponseHeader("etag");
    +
    + // get the doc and verify the headers match
    + xhr = CouchDB.request("GET", "/test_suite_db/1");
    + T(etag == xhr.getResponseHeader("etag"));
    +
    + // 'head' the doc and verify the headers match
    + xhr = CouchDB.request("HEAD", "/test_suite_db/1", {
    + headers: {"if-none-match": "s"}
    + });
    + T(etag == xhr.getResponseHeader("etag"));
    +
    + // replace a doc
    + xhr = CouchDB.request("PUT", "/test_suite_db/1", {
    + body: "{}",
    + headers: {"if-match": etag}
    + });
    + T(xhr.status == 201);
    +
    + // extract the new ETag value
    + var etagOld= etag;
    + etag = xhr.getResponseHeader("etag");
    +
    + // fail to replace a doc
    + xhr = CouchDB.request("PUT", "/test_suite_db/1", {
    + body: "{}"
    + });
    + T(xhr.status == 409);
    +
    + // verify get w/Etag
    + xhr = CouchDB.request("GET", "/test_suite_db/1", {
    + headers: {"if-none-match": etagOld}
    + });
    + T(xhr.status == 200);
    + xhr = CouchDB.request("GET", "/test_suite_db/1", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // fail to delete a doc
    + xhr = CouchDB.request("DELETE", "/test_suite_db/1", {
    + headers: {"if-match": etagOld}
    + });
    + T(xhr.status == 409);
    +
    + //now do it for real
    + xhr = CouchDB.request("DELETE", "/test_suite_db/1", {
    + headers: {"if-match": etag}
    + });
    + T(xhr.status == 200);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/etags_views.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/etags_views.js b/test/javascript/tests/etags_views.js
    new file mode 100644
    index 0000000..6d8e97b
    --- /dev/null
    +++ b/test/javascript/tests/etags_views.js
    @@ -0,0 +1,220 @@
    +// 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.
    +
    +couchTests.etags_views = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var designDoc = {
    + _id: "_design/etags",
    + language: "javascript",
    + views : {
    + fooView: {
    + map: stringFun(function(doc) {
    + if (doc.foo) {
    + emit("bar", 1);
    + }
    + }),
    + },
    + basicView : {
    + map : stringFun(function(doc) {
    + if(doc.integer && doc.string) {
    + emit(doc.integer, doc.string);
    + }
    + })
    + },
    + withReduce : {
    + map : stringFun(function(doc) {
    + if(doc.integer && doc.string) {
    + emit(doc.integer, doc.string);
    + }
    + }),
    + reduce : stringFun(function(keys, values, rereduce) {
    + if (rereduce) {
    + return sum(values);
    + } else {
    + return values.length;
    + }
    + })
    + }
    + }
    + };
    + T(db.save(designDoc).ok);
    + db.bulkSave(makeDocs(0, 10));
    +
    + var xhr;
    +
    + // verify get w/Etag on map view
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
    + T(xhr.status == 200);
    + var etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // verify ETag doesn't change when an update
    + // doesn't change the view group's index
    + T(db.save({"_id":"doc1", "foo":"bar"}).ok);
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 == etag);
    +
    + // verify ETag always changes for include_docs=true on update
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView?include_docs=true");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(db.save({"_id":"doc2", "foo":"bar"}).ok);
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView?include_docs=true");
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2);
    +
    + // Verify that purges affect etags
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
    + var foo_etag = xhr.getResponseHeader("etag");
    + var doc1 = db.open("doc1");
    + xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    + body: JSON.stringify({"doc1":[doc1._rev]})
    + });
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 != foo_etag);
    +
    + // Test that _purge didn't affect the other view etags.
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 == etag);
    +
    + // verify different views in the same view group may have different ETags
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
    + var etag1 = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2);
    +
    + // verify ETag changes when an update changes the view group's index.
    + db.bulkSave(makeDocs(10, 20));
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 != etag);
    +
    + // verify ETag is the same after a restart
    + restartServer();
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 == etag2);
    +
    + // reduce view
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
    + T(xhr.status == 200);
    + var etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce",{
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // verify ETag doesn't change when an update
    + // doesn't change the view group's index
    + T(db.save({"_id":"doc3", "foo":"bar"}).ok);
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 == etag);
    + // purge
    + var doc3 = db.open("doc3");
    + xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    + body: JSON.stringify({"doc3":[doc3._rev]})
    + });
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 == etag);
    +
    + // verify different views in the same view group may have different ETags
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
    + var etag1 = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2);
    +
    + // verify ETag changes when an update changes the view group's index
    + db.bulkSave(makeDocs(20, 30));
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
    + var etag1 = xhr.getResponseHeader("etag");
    + T(etag1 != etag);
    +
    + // verify ETag is the same after a restart
    + restartServer();
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 == etag2);
    +
    + // confirm ETag changes with different POST bodies
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/etags/_view/basicView",
    + {body: JSON.stringify({keys:[1]})}
    + );
    + var etag1 = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/etags/_view/basicView",
    + {body: JSON.stringify({keys:[2]})}
    + );
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2, "POST to map view generates key-depdendent ETags");
    +
    + xhr = CouchDB.request("POST",
    + "/test_suite_db/_design/etags/_view/withReduce?group=true",
    + {body: JSON.stringify({keys:[1]})}
    + );
    + etag1 = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("POST",
    + "/test_suite_db/_design/etags/_view/withReduce?group=true",
    + {body: JSON.stringify({keys:[2]})}
    + );
    + etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2, "POST to reduce view generates key-depdendent ETags");
    +
    + // all docs
    + xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
    + T(xhr.status == 200);
    + var etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_all_docs", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // _changes
    + xhr = CouchDB.request("GET", "/test_suite_db/_changes");
    + T(xhr.status == 200);
    + var etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_changes", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // list etag
    + // in the list test for now
    +
    + // A new database should have unique _all_docs etags.
    + db.deleteDb();
    + db.createDb();
    + db.save({a: 1});
    + xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
    + var etag = xhr.getResponseHeader("etag");
    + db.deleteDb();
    + db.createDb();
    + db.save({a: 2});
    + xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
    + var new_etag = xhr.getResponseHeader("etag");
    + T(etag != new_etag);
    + // but still be cacheable
    + xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
    + T(new_etag == xhr.getResponseHeader("etag"));
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/form_submit.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/form_submit.js b/test/javascript/tests/form_submit.js
    new file mode 100644
    index 0000000..710bf47
    --- /dev/null
    +++ b/test/javascript/tests/form_submit.js
    @@ -0,0 +1,25 @@
    +// 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.
    +
    +// Do some basic tests.
    +couchTests.form_submit = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    +
    + var json = "{}";
    + var xhr = CouchDB.request("POST", "/test_suite_db/baz", {body: json});
    + T(xhr.status == 415);
    + result = JSON.parse(xhr.responseText);
    + T(result.error, "bad_content_type");
    + T(result.reason, "Invalid Content-Type header for form upload");
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/http.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/http.js b/test/javascript/tests/http.js
    new file mode 100644
    index 0000000..21c42a0
    --- /dev/null
    +++ b/test/javascript/tests/http.js
    @@ -0,0 +1,79 @@
    +// 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.
    +
    +couchTests.http = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    +
    + // bug COUCHDB-100: DELETE on non-existent DB returns 500 instead of 404
    + db.deleteDb();
    +
    + db.createDb();
    +
    + // PUT on existing DB should return 412 instead of 500
    + if (debug) debugger;
    +
    + var xhr = CouchDB.request("PUT", "/test_suite_db/test", {body: "{}"});
    + var host = CouchDB.host;
    +
    + TEquals(CouchDB.protocol + host + "/test_suite_db/test",
    + xhr.getResponseHeader("Location"),
    + "should include ip address");
    +
    + xhr = CouchDB.request("PUT", "/test_suite_db/test2", {
    + body: "{}",
    + headers: {"X-Forwarded-Host": "mysite.com"}
    + });
    +
    + TEquals(CouchDB.protocol + "mysite.com/test_suite_db/test2",
    + xhr.getResponseHeader("Location"),
    + "should include X-Forwarded-Host");
    +
    + run_on_modified_server([{
    + section:"httpd",
    + key:"x_forwarded_host",
    + value:"X-Host"}],
    + function() {
    + xhr = CouchDB.request("PUT", "/test_suite_db/test3", {
    + body: "{}",
    + headers: {"X-Host": "mysite2.com"}
    + });
    + TEquals(CouchDB.protocol + "mysite2.com/test_suite_db/test3",
    + xhr.getResponseHeader("Location"),
    + "should include X-Host");
    + });
    +
    + // COUCHDB-708: newlines document names
    + xhr = CouchDB.request("PUT", "/test_suite_db/docid%0A/attachment.txt", {
    + headers: {"Content-Type": "text/plain;charset=utf-8"},
    + body: ""
    + });
    + TEquals(CouchDB.protocol + host + "/test_suite_db/docid%0A/attachment.txt",
    + xhr.getResponseHeader("Location"),
    + "should work with newlines in document names for attachments");
    +
    + xhr = CouchDB.request("PUT", "/test_suite_db/docidtest%0A", {
    + body: JSON.stringify({"foo": "bar"}),
    + headers: {"Content-Type": "application/json"}
    + });
    + TEquals(CouchDB.protocol + host + "/test_suite_db/docidtest%0A",
    + xhr.getResponseHeader("Location"),
    + "should work with newlines in document names");
    +
    + xhr = CouchDB.request("POST", "/test_suite_db/", {
    + body: JSON.stringify({"_id": "docidtestpost%0A"}),
    + headers: {"Content-Type": "application/json"}
    + });
    + TEquals(CouchDB.protocol + host + "/test_suite_db/docidtestpost%250A",
    + xhr.getResponseHeader("Location"),
    + "should work with newlines in document names");
    +}

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/invalid_docids.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/invalid_docids.js b/test/javascript/tests/invalid_docids.js
    new file mode 100644
    index 0000000..d0195b0
    --- /dev/null
    +++ b/test/javascript/tests/invalid_docids.js
    @@ -0,0 +1,77 @@
    +// 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.
    +
    +couchTests.invalid_docids = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // Test _local explicitly first.
    + T(db.save({"_id": "_local/foo"}).ok);
    + T(db.open("_local/foo")._id == "_local/foo");
    +
    + var urls = [
    + "/test_suite_db/_local",
    + "/test_suite_db/_local/",
    + "/test_suite_db/_local%2F",
    + "/test_suite_db/_local/foo/bar",
    + ];
    +
    + urls.forEach(function(u) {
    + var res = db.request("PUT", u, {"body": "{}"});
    + T(res.status == 400);
    + T(JSON.parse(res.responseText).error == "bad_request");
    + });
    +
    + //Test non-string
    + try {
    + db.save({"_id": 1});
    + T(1 == 0, "doc id must be string");
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "bad_request");
    + }
    +
    + // Via PUT with _id not in body.
    + var res = res = db.request("PUT", "/test_suite_db/_other", {"body": "{}"});
    + T(res.status == 400);
    + T(JSON.parse(res.responseText).error == "bad_request");
    +
    + // Accidental POST to form handling code.
    + res = db.request("POST", "/test_suite_db/_tmp_view", {"body": "{}"});
    + T(res.status == 400);
    + T(JSON.parse(res.responseText).error == "bad_request");
    +
    + // Test invalid _prefix
    + try {
    + db.save({"_id": "_invalid"});
    + T(1 == 0, "doc id may not start with underscore");
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "bad_request");
    + }
    +
    + // Test _bulk_docs explicitly.
    + var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}];
    + db.bulkSave(docs);
    + docs.forEach(function(d) {T(db.open(d._id)._id == d._id);});
    +
    + docs = [{"_id": "_invalid"}];
    + try {
    + db.bulkSave(docs);
    + T(1 == 0, "doc id may not start with underscore, even in bulk docs");
    + } catch(e) {
    + T(db.last_req.status == 400);
    + T(e.error == "bad_request");
    + }
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/jsonp.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/jsonp.js b/test/javascript/tests/jsonp.js
    new file mode 100644
    index 0000000..e4f4490
    --- /dev/null
    +++ b/test/javascript/tests/jsonp.js
    @@ -0,0 +1,84 @@
    +// 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.
    +
    +// Verify callbacks ran
    +var jsonp_flag = 0;
    +
    +// Callbacks
    +function jsonp_no_chunk(doc) {
    + T(jsonp_flag == 0);
    + T(doc._id == "0");
    + jsonp_flag = 1;
    +}
    +
    +function jsonp_chunk(doc) {
    + T(jsonp_flag == 0);
    + T(doc.total_rows == 1);
    + jsonp_flag = 1;
    +}
    +
    +// Do some jsonp tests.
    +couchTests.jsonp = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var doc = {_id:"0",a:0,b:0};
    + T(db.save(doc).ok);
    +
    + // callback param is ignored unless jsonp is configured
    + var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_not_configured");
    + JSON.parse(xhr.responseText);
    +
    + run_on_modified_server(
    + [{section: "httpd",
    + key: "allow_jsonp",
    + value: "true"}],
    + function() {
    +
    + // Test unchunked callbacks.
    + var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_no_chunk");
    + TEquals("application/javascript", xhr.getResponseHeader("Content-Type"));
    + T(xhr.status == 200);
    + jsonp_flag = 0;
    + eval(xhr.responseText);
    + T(jsonp_flag == 1);
    + xhr = CouchDB.request("GET", "/test_suite_db/0?callback=foo\"");
    + T(xhr.status == 400);
    +
    + // Test chunked responses
    + var doc = {_id:"1",a:1,b:1};
    + T(db.save(doc).ok);
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + all_docs: {map: "function(doc) {if(doc.a) emit(null, doc.a);}"}
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + var url = "/test_suite_db/_design/test/_view/all_docs?callback=jsonp_chunk";
    + xhr = CouchDB.request("GET", url);
    + TEquals("application/javascript", xhr.getResponseHeader("Content-Type"));
    + T(xhr.status == 200);
    + jsonp_flag = 0;
    + eval(xhr.responseText);
    + T(jsonp_flag == 1);
    + xhr = CouchDB.request("GET", url + "\'");
    + T(xhr.status == 400);
    + });
    +
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/large_docs.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/large_docs.js b/test/javascript/tests/large_docs.js
    new file mode 100644
    index 0000000..b84648b
    --- /dev/null
    +++ b/test/javascript/tests/large_docs.js
    @@ -0,0 +1,33 @@
    +// 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.
    +
    +couchTests.large_docs = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var longtext = "0123456789\n";
    +
    + for (var i=0; i<10; i++) {
    + longtext = longtext + longtext
    + }
    + T(db.save({"longtest":longtext}).ok);
    + T(db.save({"longtest":longtext}).ok);
    + T(db.save({"longtest":longtext}).ok);
    + T(db.save({"longtest":longtext}).ok);
    +
    + // query all documents, and return the doc.foo member as a key.
    + results = db.query(function(doc){
    + emit(null, doc.longtest);
    + });
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/list_views.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/list_views.js b/test/javascript/tests/list_views.js
    new file mode 100644
    index 0000000..ece81ea
    --- /dev/null
    +++ b/test/javascript/tests/list_views.js
    @@ -0,0 +1,492 @@
    +// 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.
    +
    +couchTests.list_views = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var designDoc = {
    + _id:"_design/lists",
    + language: "javascript",
    + views : {
    + basicView : {
    + map : stringFun(function(doc) {
    + emit(doc.integer, doc.string);
    + })
    + },
    + withReduce : {
    + map : stringFun(function(doc) {
    + emit(doc.integer, doc.string);
    + }),
    + reduce : stringFun(function(keys, values, rereduce) {
    + if (rereduce) {
    + return sum(values);
    + } else {
    + return values.length;
    + }
    + })
    + }
    + },
    + lists: {
    + basicBasic : stringFun(function(head, req) {
    + send("head");
    + var row;
    + while(row = getRow()) {
    + log("row: "+toJSON(row));
    + send(row.key);
    + };
    + return "tail";
    + }),
    + basicJSON : stringFun(function(head, req) {
    + start({"headers":{"Content-Type" : "application/json"}});
    + send('{"head":'+toJSON(head)+', ');
    + send('"req":'+toJSON(req)+', ');
    + send('"rows":[');
    + var row, sep = '';
    + while (row = getRow()) {
    + send(sep + toJSON(row));
    + sep = ', ';
    + }
    + return "]}";
    + }),
    + simpleForm: stringFun(function(head, req) {
    + log("simpleForm");
    + send('<ul>');
    + var row, row_number = 0, prevKey, firstKey = null;
    + while (row = getRow()) {
    + row_number += 1;
    + if (!firstKey) firstKey = row.key;
    + prevKey = row.key;
    + send('\n<li>Key: '+row.key
    + +' Value: '+row.value
    + +' LineNo: '+row_number+'</li>');
    + }
    + return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
    + }),
    + acceptSwitch: stringFun(function(head, req) {
    + // respondWith takes care of setting the proper headers
    + provides("html", function() {
    + send("HTML <ul>");
    +
    + var row, num = 0;
    + while (row = getRow()) {
    + num ++;
    + send('\n<li>Key: '
    + +row.key+' Value: '+row.value
    + +' LineNo: '+num+'</li>');
    + }
    +
    + // tail
    + return '</ul>';
    + });
    + }),
    + qsParams: stringFun(function(head, req) {
    + return toJSON(req.query) + "\n";
    + }),
    + stopIter: stringFun(function(req) {
    + send("head");
    + var row, row_number = 0;
    + while(row = getRow()) {
    + if(row_number > 2) break;
    + send(" " + row_number);
    + row_number += 1;
    + };
    + return " tail";
    + }),
    + stopIter2: stringFun(function(head, req) {
    + provides("html", function() {
    + send("head");
    + var row, row_number = 0;
    + while(row = getRow()) {
    + if(row_number > 2) break;
    + send(" " + row_number);
    + row_number += 1;
    + };
    + return " tail";
    + });
    + }),
    + tooManyGetRows : stringFun(function() {
    + send("head");
    + var row;
    + while(row = getRow()) {
    + send(row.key);
    + };
    + getRow();
    + getRow();
    + getRow();
    + row = getRow();
    + return "after row: "+toJSON(row);
    + }),
    + emptyList: stringFun(function() {
    + return " ";
    + }),
    + rowError : stringFun(function(head, req) {
    + send("head");
    + var row = getRow();
    + send(fooBarBam); // intentional error
    + return "tail";
    + }),
    + docReference : stringFun(function(head, req) {
    + send("head");
    + var row = getRow();
    + send(row.doc.integer);
    + return "tail";
    + }),
    + secObj: stringFun(function(head, req) {
    + return toJSON(req.secObj);
    + }),
    + setHeaderAfterGotRow: stringFun(function(head, req) {
    + getRow();
    + start({
    + code: 400,
    + headers: {
    + "X-My-Header": "MyHeader"
    + }
    + });
    + send("bad request");
    + }),
    + allDocs: stringFun(function(head, req){
    + start({'headers': {'Content-Type': 'application/json'}});
    + var resp = head;
    + var rows = [];
    + while(row=getRow()){
    + rows.push(row);
    + }
    + resp.rows = rows;
    + return toJSON(resp);
    + })
    + }
    + };
    + var viewOnlyDesignDoc = {
    + _id:"_design/views",
    + language: "javascript",
    + views : {
    + basicView : {
    + map : stringFun(function(doc) {
    + emit(-doc.integer, doc.string);
    + })
    + }
    + }
    + };
    + var erlListDoc = {
    + _id: "_design/erlang",
    + language: "erlang",
    + lists: {
    + simple:
    + 'fun(Head, {Req}) -> ' +
    + ' Send(<<"[">>), ' +
    + ' Fun = fun({Row}, Sep) -> ' +
    + ' Val = couch_util:get_value(<<"key">>, Row, 23), ' +
    + ' Send(list_to_binary(Sep ++ integer_to_list(Val))), ' +
    + ' {ok, ","} ' +
    + ' end, ' +
    + ' {ok, _} = FoldRows(Fun, ""), ' +
    + ' Send(<<"]">>) ' +
    + 'end.'
    + }
    + };
    +
    + T(db.save(designDoc).ok);
    +
    + var docs = makeDocs(0, 10);
    + db.bulkSave(docs);
    +
    + var view = db.view('lists/basicView');
    + T(view.total_rows == 10);
    +
    + // standard get
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
    + T(xhr.status == 200, "standard get should be 200");
    + T(/head0123456789tail/.test(xhr.responseText));
    +
    + // standard options
    + var xhr = CouchDB.request("OPTIONS", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
    + T(xhr.status == 200, "standard get should be 200");
    + T(/head0123456789tail/.test(xhr.responseText));
    +
    + // test that etags are available
    + var etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // confirm ETag changes with different POST bodies
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
    + {body: JSON.stringify({keys:[1]})}
    + );
    + var etag1 = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
    + {body: JSON.stringify({keys:[2]})}
    + );
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2, "POST to map _list generates key-depdendent ETags");
    +
    + // test the richness of the arguments
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView?update_seq=true");
    + T(xhr.status == 200, "standard get should be 200");
    + var resp = JSON.parse(xhr.responseText);
    + TEquals(10, resp.head.total_rows);
    + TEquals(0, resp.head.offset);
    + TEquals(11, resp.head.update_seq);
    +
    + T(resp.rows.length == 10);
    + TEquals(resp.rows[0], {"id": "0","key": 0,"value": "0"});
    +
    + TEquals(resp.req.info.db_name, "test_suite_db");
    + TEquals(resp.req.method, "GET");
    + TEquals(resp.req.path, [
    + "test_suite_db",
    + "_design",
    + "lists",
    + "_list",
    + "basicJSON",
    + "basicView"
    + ]);
    + T(resp.req.headers.Accept);
    + T(resp.req.headers.Host);
    + T(resp.req.headers["User-Agent"]);
    + T(resp.req.cookie);
    + TEquals("/test_suite_db/_design/lists/_list/basicJSON/basicView?update_seq=true",
    + resp.req.raw_path, "should include raw path");
    +
    + // get with query params
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3&endkey=8");
    + T(xhr.status == 200, "with query params");
    + T(!(/Key: 1/.test(xhr.responseText)));
    + T(/FirstKey: 3/.test(xhr.responseText));
    + T(/LastKey: 8/.test(xhr.responseText));
    +
    + // with 0 rows
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30");
    + T(xhr.status == 200, "0 rows");
    + T(/<\/ul>/.test(xhr.responseText));
    +
    + //too many Get Rows
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/tooManyGetRows/basicView");
    + T(xhr.status == 200, "tooManyGetRows");
    + T(/9after row: null/.test(xhr.responseText));
    +
    +
    + // reduce with 0 rows
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?startkey=30");
    + T(xhr.status == 200, "reduce 0 rows");
    + T(/LastKey: undefined/.test(xhr.responseText));
    +
    + // when there is a reduce present, but not used
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false");
    + T(xhr.status == 200, "reduce false");
    + T(/Key: 1/.test(xhr.responseText));
    +
    +
    + // when there is a reduce present, and used
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true");
    + T(xhr.status == 200, "group reduce");
    + T(/Key: 1/.test(xhr.responseText));
    +
    + // there should be etags on reduce as well
    + var etag = xhr.getResponseHeader("etag");
    + T(etag, "Etags should be served with reduce lists");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // confirm ETag changes with different POST bodies
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true",
    + {body: JSON.stringify({keys:[1]})}
    + );
    + var etag1 = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true",
    + {body: JSON.stringify({keys:[2]})}
    + );
    + var etag2 = xhr.getResponseHeader("etag");
    + T(etag1 != etag2, "POST to reduce _list generates key-depdendent ETags");
    +
    + // verify the etags expire correctly
    + var docs = makeDocs(11, 12);
    + db.bulkSave(docs);
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 200, "reduce etag");
    +
    + // empty list
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/basicView");
    + T(xhr.responseText.match(/^ $/));
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/withReduce?group=true");
    + T(xhr.responseText.match(/^ $/));
    +
    + // multi-key fetch
    + var xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/basicView", {
    + body: '{"keys":[2,4,5,7]}'
    + });
    + T(xhr.status == 200, "multi key");
    + T(!(/Key: 1 /.test(xhr.responseText)));
    + T(/Key: 2/.test(xhr.responseText));
    + T(/FirstKey: 2/.test(xhr.responseText));
    + T(/LastKey: 7/.test(xhr.responseText));
    +
    + // multi-key fetch with GET
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView" +
    + "?keys=[2,4,5,7]");
    +
    + T(xhr.status == 200, "multi key");
    + T(!(/Key: 1 /.test(xhr.responseText)));
    + T(/Key: 2/.test(xhr.responseText));
    + T(/FirstKey: 2/.test(xhr.responseText));
    + T(/LastKey: 7/.test(xhr.responseText));
    +
    + // no multi-key fetch allowed when group=false
    + xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=false", {
    + body: '{"keys":[2,4,5,7]}'
    + });
    + T(xhr.status == 400);
    + T(/query_parse_error/.test(xhr.responseText));
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/rowError/basicView");
    + T(/ReferenceError/.test(xhr.responseText));
    +
    +
    + // with include_docs and a reference to the doc.
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/docReference/basicView?include_docs=true");
    + T(xhr.responseText.match(/head0tail/));
    +
    + // now with extra qs params
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/qsParams/basicView?foo=blam");
    + T(xhr.responseText.match(/blam/));
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/basicView");
    + // T(xhr.getResponseHeader("Content-Type") == "text/plain");
    + T(xhr.responseText.match(/^head 0 1 2 tail$/) && "basic stop");
    +
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView", {
    + headers : {
    + "Accept" : "text/html"
    + }
    + });
    + T(xhr.responseText.match(/^head 0 1 2 tail$/) && "stop 2");
    +
    + // aborting iteration with reduce
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true");
    + T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop");
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true", {
    + headers : {
    + "Accept" : "text/html"
    + }
    + });
    + T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop 2");
    +
    + // with accept headers for HTML
    + xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/acceptSwitch/basicView", {
    + headers: {
    + "Accept": 'text/html'
    + }
    + });
    + T(xhr.getResponseHeader("Content-Type") == "text/html; charset=utf-8");
    + T(xhr.responseText.match(/HTML/));
    + T(xhr.responseText.match(/Value/));
    +
    + // Test we can run lists and views from separate docs.
    + T(db.save(viewOnlyDesignDoc).ok);
    + var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView" +
    + "?startkey=-3";
    + xhr = CouchDB.request("GET", url);
    + T(xhr.status == 200, "multiple design docs.");
    + T(!(/Key: -4/.test(xhr.responseText)));
    + T(/FirstKey: -3/.test(xhr.responseText));
    + T(/LastKey: 0/.test(xhr.responseText));
    +
    + // Test we do multi-key requests on lists and views in separate docs.
    + var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView";
    + xhr = CouchDB.request("POST", url, {
    + body: '{"keys":[-2,-4,-5,-7]}'
    + });
    +
    + T(xhr.status == 200, "multi key separate docs");
    + T(!(/Key: -3/.test(xhr.responseText)));
    + T(/Key: -7/.test(xhr.responseText));
    + T(/FirstKey: -2/.test(xhr.responseText));
    + T(/LastKey: -7/.test(xhr.responseText));
    +
    + // Test if secObj is available
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/secObj/basicView");
    + T(xhr.status == 200, "standard get should be 200");
    + var resp = JSON.parse(xhr.responseText);
    + T(typeof(resp) == "object");
    +
    + var erlViewTest = function() {
    + T(db.save(erlListDoc).ok);
    + var url = "/test_suite_db/_design/erlang/_list/simple/views/basicView" +
    + "?startkey=-3";
    + xhr = CouchDB.request("GET", url);
    + T(xhr.status == 200, "multiple languages in design docs.");
    + var list = JSON.parse(xhr.responseText);
    + T(list.length == 4);
    + for(var i = 0; i < list.length; i++)
    + {
    + T(list[i] + 3 == i);
    + }
    + };
    +
    + run_on_modified_server([{
    + section: "native_query_servers",
    + key: "erlang",
    + value: "{couch_native_process, start_link, []}"
    + }], erlViewTest);
    +
    + // COUCHDB-1113
    + var ddoc = {
    + _id: "_design/test",
    + views: {
    + me: {
    + map: (function(doc) { emit(null,null)}).toString()
    + }
    + },
    + lists: {
    + you: (function(head, req) {
    + var row;
    + while(row = getRow()) {
    + send(row);
    + }
    + }).toString()
    + }
    + };
    + db.save(ddoc);
    +
    + var resp = CouchDB.request("GET", "/" + db.name + "/_design/test/_list/you/me", {
    + headers: {
    + "Content-Type": "application/x-www-form-urlencoded"
    + }
    + });
    + TEquals(200, resp.status, "should return a 200 response");
    +
    + // TEST HTTP header response set after getRow() called in _list function.
    + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/setHeaderAfterGotRow/basicView");
    + T(xhr.status == 400);
    + T(xhr.getResponseHeader("X-My-Header") == "MyHeader");
    + T(xhr.responseText.match(/^bad request$/));
    +
    + // test handling _all_docs by _list functions. the result should be equal
    + var xhr_lAllDocs = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/allDocs/_all_docs");
    + T(xhr_lAllDocs.status == 200, "standard get should be 200");
    + var xhr_allDocs = CouchDB.request("GET", "/test_suite_db/_all_docs");
    + var allDocs = JSON.parse(xhr_allDocs.responseText);
    + var lAllDocs = JSON.parse(xhr_lAllDocs.responseText);
    + TEquals(allDocs.total_rows, lAllDocs.total_rows, "total_rows mismatch");
    + TEquals(allDocs.offset, lAllDocs.offset, "offset mismatch");
    + TEquals(allDocs.rows.length, lAllDocs.rows.length, "amount of rows mismatch");
    + TEquals(allDocs.rows, lAllDocs.rows, "rows mismatch");
    +};
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/purge.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/purge.js b/share/www/script/test/purge.js
    deleted file mode 100644
    index 2968913..0000000
    --- a/share/www/script/test/purge.js
    +++ /dev/null
    @@ -1,145 +0,0 @@
    -// 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.
    -
    -couchTests.purge = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - /*
    - purge is not to be confused with a document deletion. It removes the
    - document and all edit history from the local instance of the database.
    - */
    -
    - var numDocs = 10;
    -
    - var designDoc = {
    - _id:"_design/test",
    - language: "javascript",
    - views: {
    - all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
    - single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"}
    - }
    - };
    -
    - T(db.save(designDoc).ok);
    -
    - db.bulkSave(makeDocs(1, numDocs + 1));
    -
    - // go ahead and validate the views before purging
    - var rows = db.view("test/all_docs_twice").rows;
    - for (var i = 0; i < numDocs; i++) {
    - T(rows[2*i].key == i+1);
    - T(rows[(2*i)+1].key == i+1);
    - }
    - T(db.view("test/single_doc").total_rows == 1);
    -
    - var info = db.info();
    - var doc1 = db.open("1");
    - var doc2 = db.open("2");
    -
    - // purge the documents
    - var xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    - body: JSON.stringify({"1":[doc1._rev], "2":[doc2._rev]})
    - });
    - T(xhr.status == 200);
    -
    - var result = JSON.parse(xhr.responseText);
    - var newInfo = db.info();
    -
    - // purging increments the update sequence
    - T(info.update_seq+1 == newInfo.update_seq);
    - // and it increments the purge_seq
    - T(info.purge_seq+1 == newInfo.purge_seq);
    - T(result.purge_seq == newInfo.purge_seq);
    -
    - T(result.purged["1"][0] == doc1._rev);
    - T(result.purged["2"][0] == doc2._rev);
    -
    - T(db.open("1") == null);
    - T(db.open("2") == null);
    -
    - var rows = db.view("test/all_docs_twice").rows;
    - for (var i = 2; i < numDocs; i++) {
    - T(rows[2*(i-2)].key == i+1);
    - T(rows[(2*(i-2))+1].key == i+1);
    - }
    - T(db.view("test/single_doc").total_rows == 0);
    -
    - // purge sequences are preserved after compaction (COUCHDB-1021)
    - T(db.compact().ok);
    - T(db.last_req.status == 202);
    - // compaction isn't instantaneous, loop until done
    - while (db.info().compact_running) {};
    - var compactInfo = db.info();
    - T(compactInfo.purge_seq == newInfo.purge_seq);
    -
    - // purge documents twice in a row without loading views
    - // (causes full view rebuilds)
    -
    - var doc3 = db.open("3");
    - var doc4 = db.open("4");
    -
    - xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    - body: JSON.stringify({"3":[doc3._rev]})
    - });
    -
    - T(xhr.status == 200);
    -
    - xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
    - body: JSON.stringify({"4":[doc4._rev]})
    - });
    -
    - T(xhr.status == 200);
    - result = JSON.parse(xhr.responseText);
    - T(result.purge_seq == db.info().purge_seq);
    -
    - var rows = db.view("test/all_docs_twice").rows;
    - for (var i = 4; i < numDocs; i++) {
    - T(rows[2*(i-4)].key == i+1);
    - T(rows[(2*(i-4))+1].key == i+1);
    - }
    - T(db.view("test/single_doc").total_rows == 0);
    -
    - // COUCHDB-1065
    - var dbA = new CouchDB("test_suite_db_a");
    - var dbB = new CouchDB("test_suite_db_b");
    - dbA.deleteDb();
    - dbA.createDb();
    - dbB.deleteDb();
    - dbB.createDb();
    - var docA = {_id:"test", a:1};
    - var docB = {_id:"test", a:2};
    - dbA.save(docA);
    - dbB.save(docB);
    - CouchDB.replicate(dbA.name, dbB.name);
    - var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", {
    - body: JSON.stringify({"test":[docA._rev]})
    - });
    - TEquals(200, xhr.status, "single rev purge after replication succeeds");
    -
    - var xhr = CouchDB.request("GET", "/" + dbB.name + "/test?rev=" + docA._rev);
    - TEquals(404, xhr.status, "single rev purge removes revision");
    -
    - var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", {
    - body: JSON.stringify({"test":[docB._rev]})
    - });
    - TEquals(200, xhr.status, "single rev purge after replication succeeds");
    - var xhr = CouchDB.request("GET", "/" + dbB.name + "/test?rev=" + docB._rev);
    - TEquals(404, xhr.status, "single rev purge removes revision");
    -
    - var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", {
    - body: JSON.stringify({"test":[docA._rev, docB._rev]})
    - });
    - TEquals(200, xhr.status, "all rev purge after replication succeeds");
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/reader_acl.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/reader_acl.js b/share/www/script/test/reader_acl.js
    deleted file mode 100644
    index ff770c7..0000000
    --- a/share/www/script/test/reader_acl.js
    +++ /dev/null
    @@ -1,219 +0,0 @@
    -// 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.
    -
    -couchTests.reader_acl = function(debug) {
    - // this tests read access control
    -
    - var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
    - var secretDb = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - function testFun() {
    - try {
    - usersDb.deleteDb();
    - try {
    - usersDb.createDb();
    - } catch(e) {
    - if(usersDb.last_req.status != 412) {
    - throw e;
    - }
    - }
    - secretDb.deleteDb();
    - secretDb.createDb();
    -
    - // create a user with top-secret-clearance
    - var jchrisUserDoc = CouchDB.prepareUserDoc({
    - name: "jchris@apache.org",
    - roles : ["top-secret"]
    - }, "funnybone");
    - T(usersDb.save(jchrisUserDoc).ok);
    - usersDb.ensureFullCommit();
    -
    - T(CouchDB.session().userCtx.name == null);
    -
    - // set secret db to be read controlled
    - T(secretDb.save({_id:"baz",foo:"bar"}).ok);
    - T(secretDb.open("baz").foo == "bar");
    -
    - T(secretDb.setSecObj({
    - "members" : {
    - roles : ["super-secret-club"],
    - names : ["joe","barb"]
    - }
    - }).ok);
    - } finally {
    - CouchDB.logout();
    - }
    - }
    -
    - // split into 2 funs so we can test restart behavior
    - function testFun2() {
    - try {
    - // can't read it as jchris b/c he's missing the needed role
    - T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    - T(CouchDB.session().userCtx.name == "jchris@apache.org");
    -
    - try {
    - secretDb.open("baz");
    - T(false && "can't open a doc from a secret db") ;
    - } catch(e) {
    - T(true)
    - }
    -
    - CouchDB.logout();
    -
    - // make anyone with the top-secret role an admin
    - // db admins are automatically members
    - T(secretDb.setSecObj({
    - "admins" : {
    - roles : ["top-secret"],
    - names : []
    - },
    - "members" : {
    - roles : ["super-secret-club"],
    - names : ["joe","barb"]
    - }
    - }).ok);
    -
    -
    - T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    -
    - // db admin can read
    - T(secretDb.open("baz").foo == "bar");
    -
    - // and run temp views
    - TEquals(secretDb.query(function(doc) {
    - emit(null, null)
    - }).total_rows, 1);
    -
    - CouchDB.logout();
    - T(CouchDB.session().userCtx.roles.indexOf("_admin") != -1);
    -
    - // admin now adds the top-secret role to the db's members
    - // and removes db-admins
    - T(secretDb.setSecObj({
    - "admins" : {
    - roles : [],
    - names : []
    - },
    - "members" : {
    - roles : ["super-secret-club", "top-secret"],
    - names : ["joe","barb"]
    - }
    - }).ok);
    -
    - // server _admin can always read
    - T(secretDb.open("baz").foo == "bar");
    -
    - // and run temp views
    - TEquals(secretDb.query(function(doc) {
    - emit(null, null)
    - }).total_rows, 1);
    -
    - T(secretDb.save({
    - "_id" : "_design/foo",
    - views : {
    - bar : {
    - map : "function(doc){emit(null, null)}"
    - }
    - }
    - }).ok)
    -
    - // now top-secret users can read too
    - T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    - T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
    - T(secretDb.open("baz").foo == "bar");
    - // members can query stored views
    - T(secretDb.view("foo/bar").total_rows == 1);
    -
    - // members can't do temp views
    - try {
    - var results = secretDb.query(function(doc) {
    - emit(null, null);
    - });
    - T(false && "temp view should be admin only");
    - } catch (e) {
    - T(true && "temp view is admin only");
    - }
    -
    - CouchDB.logout();
    -
    - // works with readers (backwards compat with 1.0)
    - T(secretDb.setSecObj({
    - "admins" : {
    - roles : [],
    - names : []
    - },
    - "readers" : {
    - roles : ["super-secret-club", "top-secret"],
    - names : ["joe","barb"]
    - }
    - }).ok);
    -
    - T(CouchDB.login("jchris@apache.org", "funnybone").ok);
    - T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
    - T(secretDb.open("baz").foo == "bar");
    -
    - // can't set non string reader names or roles
    - try {
    - secretDb.setSecObj({
    - "members" : {
    - roles : ["super-secret-club", {"top-secret":"awesome"}],
    - names : ["joe","barb"]
    - }
    - })
    - T(false && "only string roles");
    - } catch (e) {}
    -
    - try {
    - secretDb.setSecObj({
    - "members" : {
    - roles : ["super-secret-club", {"top-secret":"awesome"}],
    - names : ["joe",22]
    - }
    - });
    - T(false && "only string names");
    - } catch (e) {}
    -
    - try {
    - secretDb.setSecObj({
    - "members" : {
    - roles : ["super-secret-club", {"top-secret":"awesome"}],
    - names : "joe"
    - }
    - });
    - T(false && "only lists of names");
    - } catch (e) {}
    - } finally {
    - CouchDB.logout();
    - }
    - };
    -
    - run_on_modified_server(
    - [{section: "httpd",
    - key: "authentication_handlers",
    - value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
    - {section: "couch_httpd_auth",
    - key: "authentication_db", value: "test_suite_users"}],
    - testFun
    - );
    -
    - // security changes will always commit synchronously
    - restartServer();
    -
    - run_on_modified_server(
    - [{section: "httpd",
    - key: "authentication_handlers",
    - value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
    - {section: "couch_httpd_auth",
    - key: "authentication_db", value: "test_suite_users"}],
    - testFun2
    - );
    -}

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/recreate_doc.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/recreate_doc.js b/share/www/script/test/recreate_doc.js
    deleted file mode 100644
    index f972379..0000000
    --- a/share/www/script/test/recreate_doc.js
    +++ /dev/null
    @@ -1,145 +0,0 @@
    -// 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.
    -
    -couchTests.recreate_doc = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - // First create a new document with the ID "foo", and delete it again
    - var doc = {_id: "foo", a: "bar", b: 42};
    - var result = db.save(doc);
    - T(result.ok);
    - var firstRev = result.rev;
    - T(db.deleteDoc(doc).ok);
    -
    - // Now create a new document with the same ID, save it, and then modify it
    - for (var i = 0; i < 10; i++) {
    - doc = {_id: "foo"};
    - T(db.save(doc).ok);
    - doc = db.open("foo");
    - doc.a = "baz";
    - T(db.save(doc).ok);
    - T(db.deleteDoc(doc).rev != undefined);
    - }
    -
    - try {
    - // COUCHDB-292 now attempt to save the document with a prev that's since
    - // been deleted and this should generate a conflict exception
    - db.save({_id:"foo", _rev:firstRev, bar:1});
    - T("no save conflict 1" && false); // we shouldn't hit here
    - } catch (e) {
    - T(e.error == "conflict");
    - }
    -
    - var binAttDoc = {
    - _id: "foo",
    - _rev:firstRev,
    - _attachments:{
    - "foo.txt": {
    - content_type:"text/plain",
    - data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    - }
    - }
    - };
    - try {
    - // same as before, but with binary
    - db.save(binAttDoc);
    - T("no save conflict 2" && false); // we shouldn't hit here
    - } catch (e) {
    - T(e.error == "conflict");
    - }
    -
    -
    - try {
    - // random non-existant prev rev
    - db.save({_id:"foo", _rev:"1-asfafasdf", bar:1});
    - T("no save conflict 3" && false); // we shouldn't hit here
    - } catch (e) {
    - T(e.error == "conflict");
    - }
    -
    - try {
    - // random non-existant prev rev with bin
    - binAttDoc._rev = "1-aasasfasdf";
    - db.save(binAttDoc);
    - T("no save conflict 4" && false); // we shouldn't hit here
    - } catch (e) {
    - T(e.error == "conflict");
    - }
    -
    - db.deleteDb();
    - db.createDb();
    -
    - // Helper function to create a doc with multiple revisions
    - // that are compacted away to ?REV_MISSING.
    -
    - var createDoc = function(docid) {
    - var ret = [{_id: docid, count: 0}];
    - T(db.save(ret[0]).ok);
    - for(var i = 0; i < 2; i++) {
    - ret[ret.length] = {
    - _id: docid,
    - _rev: ret[ret.length-1]._rev,
    - count: ret[ret.length-1].count+1
    - };
    - T(db.save(ret[ret.length-1]).ok);
    - }
    - db.compact();
    - while(db.info().compact_running) {}
    - return ret;
    - }
    -
    - // Helper function to check that there are no duplicates
    - // in the changes feed and that it has proper update
    - // sequence ordering.
    -
    - var checkChanges = function() {
    - // Assert that there are no duplicates in _changes.
    - var req = CouchDB.request("GET", "/test_suite_db/_changes");
    - var resp = JSON.parse(req.responseText);
    - var docids = {};
    - var prev_seq = -1;
    - for(var i = 0; i < resp.results.length; i++) {
    - row = resp.results[i];
    - T(row.seq > prev_seq, "Unordered _changes feed.");
    - T(docids[row.id] === undefined, "Duplicates in _changes feed.");
    - prev_seq = row.seq;
    - docids[row.id] = true;
    - }
    - };
    -
    - // COUCHDB-1265 - Check that the changes feed remains proper
    - // after we try and break the update_seq tree.
    -
    - // This first case is the one originally reported and "fixed"
    - // in COUCHDB-1265. Reinserting an old revision into the
    - // revision tree causes duplicates in the update_seq tree.
    -
    - var revs = createDoc("a");
    - T(db.save(revs[1], {new_edits: false}).ok);
    - T(db.save(revs[revs.length-1]).ok);
    - checkChanges();
    -
    - // The original fix for COUCHDB-1265 is not entirely correct
    - // as it didn't consider the possibility that a compaction
    - // might run after the original tree screw up.
    -
    - revs = createDoc("b");
    - T(db.save(revs[1], {new_edits: false}).ok);
    - db.compact();
    - while(db.info().compact_running) {}
    - T(db.save(revs[revs.length-1]).ok);
    - checkChanges();
    -
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/reduce.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/reduce.js b/share/www/script/test/reduce.js
    deleted file mode 100644
    index 36e5cb7..0000000
    --- a/share/www/script/test/reduce.js
    +++ /dev/null
    @@ -1,414 +0,0 @@
    -// 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.
    -
    -couchTests.reduce = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    - var numDocs = 500;
    - var docs = makeDocs(1,numDocs + 1);
    - db.bulkSave(docs);
    - var summate = function(N) {return (N+1)*N/2;};
    -
    - var map = function (doc) {
    - emit(doc.integer, doc.integer);
    - emit(doc.integer, doc.integer);
    - };
    - var reduce = function (keys, values) { return sum(values); };
    - var result = db.query(map, reduce);
    - T(result.rows[0].value == 2*summate(numDocs));
    -
    - result = db.query(map, reduce, {startkey: 4, endkey: 4});
    - T(result.rows[0].value == 8);
    -
    - result = db.query(map, reduce, {startkey: 4, endkey: 5});
    - T(result.rows[0].value == 18);
    -
    - result = db.query(map, reduce, {startkey: 4, endkey: 6});
    - T(result.rows[0].value == 30);
    -
    - result = db.query(map, reduce, {group:true, limit:3});
    - T(result.rows[0].value == 2);
    - T(result.rows[1].value == 4);
    - T(result.rows[2].value == 6);
    -
    - for(var i=1; i<numDocs/2; i+=30) {
    - result = db.query(map, reduce, {startkey: i, endkey: numDocs - i});
    - T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
    - }
    -
    - db.deleteDb();
    - db.createDb();
    -
    - for(var i=1; i <= 5; i++) {
    -
    - for(var j=0; j < 10; j++) {
    - // these docs are in the order of the keys collation, for clarity
    - var docs = [];
    - docs.push({keys:["a"]});
    - docs.push({keys:["a"]});
    - docs.push({keys:["a", "b"]});
    - docs.push({keys:["a", "b"]});
    - docs.push({keys:["a", "b", "c"]});
    - docs.push({keys:["a", "b", "d"]});
    - docs.push({keys:["a", "c", "d"]});
    - docs.push({keys:["d"]});
    - docs.push({keys:["d", "a"]});
    - docs.push({keys:["d", "b"]});
    - docs.push({keys:["d", "c"]});
    - db.bulkSave(docs);
    - T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
    - }
    -
    - map = function (doc) { emit(doc.keys, 1); };
    - reduce = function (keys, values) { return sum(values); };
    -
    - var results = db.query(map, reduce, {group:true});
    -
    - //group by exact key match
    - T(equals(results.rows[0], {key:["a"],value:20*i}));
    - T(equals(results.rows[1], {key:["a","b"],value:20*i}));
    - T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i}));
    - T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i}));
    -
    - // test to make sure group reduce and limit params provide valid json
    - var results = db.query(map, reduce, {group: true, limit: 2});
    - T(equals(results.rows[0], {key: ["a"], value: 20*i}));
    - T(equals(results.rows.length, 2));
    -
    - //group by the first element in the key array
    - var results = db.query(map, reduce, {group_level:1});
    - T(equals(results.rows[0], {key:["a"],value:70*i}));
    - T(equals(results.rows[1], {key:["d"],value:40*i}));
    -
    - //group by the first 2 elements in the key array
    - var results = db.query(map, reduce, {group_level:2});
    - T(equals(results.rows[0], {key:["a"],value:20*i}));
    - T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    - T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    - T(equals(results.rows[3], {key:["d"],value:10*i}));
    - T(equals(results.rows[4], {key:["d","a"],value:10*i}));
    - T(equals(results.rows[5], {key:["d","b"],value:10*i}));
    - T(equals(results.rows[6], {key:["d","c"],value:10*i}));
    -
    - // endkey test with inclusive_end=true
    - var results = db.query(map, reduce, {group_level:2,endkey:["d"],inclusive_end:true});
    - T(equals(results.rows[0], {key:["a"],value:20*i}));
    - T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    - T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    - T(equals(results.rows[3], {key:["d"],value:10*i}));
    - TEquals(4, results.rows.length);
    -
    - // endkey test with inclusive_end=false
    - var results = db.query(map, reduce, {group_level:2,endkey:["d"],inclusive_end:false});
    - T(equals(results.rows[0], {key:["a"],value:20*i}));
    - T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    - T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    - TEquals(3, results.rows.length);
    - }
    -
    - // now test out more complex reductions that need to use the combine option.
    -
    - db.deleteDb();
    - db.createDb();
    -
    -
    - var map = function (doc) { emit(doc.val, doc.val); };
    - var reduceCombine = function (keys, values, rereduce) {
    - // This computes the standard deviation of the mapped results
    - var stdDeviation=0.0;
    - var count=0;
    - var total=0.0;
    - var sqrTotal=0.0;
    -
    - if (!rereduce) {
    - // This is the reduce phase, we are reducing over emitted values from
    - // the map functions.
    - for(var i in values) {
    - total = total + values[i];
    - sqrTotal = sqrTotal + (values[i] * values[i]);
    - }
    - count = values.length;
    - }
    - else {
    - // This is the rereduce phase, we are re-reducing previosuly
    - // reduced values.
    - for(var i in values) {
    - count = count + values[i].count;
    - total = total + values[i].total;
    - sqrTotal = sqrTotal + values[i].sqrTotal;
    - }
    - }
    -
    - var variance = (sqrTotal - ((total * total)/count)) / count;
    - stdDeviation = Math.sqrt(variance);
    -
    - // the reduce result. It contains enough information to be rereduced
    - // with other reduce results.
    - return {"stdDeviation":stdDeviation,"count":count,
    - "total":total,"sqrTotal":sqrTotal};
    - };
    -
    - // Save a bunch a docs.
    -
    - for(var i=0; i < 10; i++) {
    - var docs = [];
    - docs.push({val:10});
    - docs.push({val:20});
    - docs.push({val:30});
    - docs.push({val:40});
    - docs.push({val:50});
    - docs.push({val:60});
    - docs.push({val:70});
    - docs.push({val:80});
    - docs.push({val:90});
    - docs.push({val:100});
    - db.bulkSave(docs);
    - }
    -
    - var results = db.query(map, reduceCombine);
    -
    - var difference = results.rows[0].value.stdDeviation - 28.722813232690143;
    - // account for floating point rounding error
    - T(Math.abs(difference) < 0.0000000001);
    -
    - function testReducePagination() {
    - var ddoc = {
    - "_id": "_design/test",
    - "language": "javascript",
    - "views": {
    - "view1": {
    - "map": "function(doc) {" +
    - "emit(doc.int, doc._id);" +
    - "emit(doc.int + 1, doc._id);" +
    - "emit(doc.int + 2, doc._id);" +
    - "}",
    - "reduce": "_count"
    - }
    - }
    - };
    - var result, docs = [];
    -
    - function randVal() {
    - return Math.random() * 100000000;
    - }
    -
    - db.deleteDb();
    - db.createDb();
    -
    - for (var i = 0; i < 1123; i++) {
    - docs.push({"_id": String(i), "int": i});
    - }
    - db.bulkSave(docs.concat([ddoc]));
    -
    - // ?group=false tests
    - result = db.view('test/view1', {startkey: 400, endkey: 402, foobar: randVal()});
    - TEquals(9, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 402, endkey: 400, descending: true,
    - foobar: randVal()});
    - TEquals(9, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 400, endkey: 402, inclusive_end: false,
    - foobar: randVal()});
    - TEquals(6, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 402, endkey: 400, inclusive_end: false,
    - descending: true, foobar: randVal()});
    - TEquals(6, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "400",
    - foobar: randVal()});
    - TEquals(7, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "400",
    - inclusive_end: false, foobar: randVal()});
    - TEquals(6, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "401",
    - foobar: randVal()});
    - TEquals(8, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "401",
    - inclusive_end: false, foobar: randVal()});
    - TEquals(7, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "402",
    - foobar: randVal()});
    - TEquals(9, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "402",
    - inclusive_end: false, foobar: randVal()});
    - TEquals(8, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "398",
    - descending: true, foobar: randVal()});
    - TEquals(9, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "398",
    - descending: true, inclusive_end: false, foobar: randVal()}),
    - TEquals(8, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "399",
    - descending: true, foobar: randVal()});
    - TEquals(8, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "399",
    - descending: true, inclusive_end: false, foobar: randVal()}),
    - TEquals(7, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "400",
    - descending: true, foobar: randVal()}),
    - TEquals(7, result.rows[0].value);
    - result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "400",
    - descending: true, inclusive_end: false, foobar: randVal()}),
    - TEquals(6, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 402, startkey_docid: "400", endkey: 400,
    - descending: true, foobar: randVal()});
    - TEquals(7, result.rows[0].value);
    -
    - result = db.view('test/view1', {startkey: 402, startkey_docid: "401", endkey: 400,
    - descending: true, inclusive_end: false, foobar: randVal()});
    - TEquals(5, result.rows[0].value);
    -
    - // ?group=true tests
    - result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    - foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(400, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(402, result.rows[2].key);
    - TEquals(3, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    - descending: true, foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(400, result.rows[2].key);
    - TEquals(3, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    - inclusive_end: false, foobar: randVal()});
    - TEquals(2, result.rows.length);
    - TEquals(400, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    - descending: true, inclusive_end: false, foobar: randVal()});
    - TEquals(2, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    - endkey_docid: "401", foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(400, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(402, result.rows[2].key);
    - TEquals(2, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    - endkey_docid: "400", foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(400, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(402, result.rows[2].key);
    - TEquals(1, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "401",
    - endkey: 400, descending: true, foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(2, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(400, result.rows[2].key);
    - TEquals(3, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "400",
    - endkey: 400, descending: true, foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(1, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(400, result.rows[2].key);
    - TEquals(3, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "401",
    - endkey: 400, descending: true, inclusive_end: false, foobar: randVal()});
    - TEquals(2, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(2, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "400",
    - endkey: 400, descending: true, inclusive_end: false, foobar: randVal()});
    - TEquals(2, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(1, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    - endkey_docid: "398", descending: true, inclusive_end: true, foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(400, result.rows[2].key);
    - TEquals(3, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    - endkey_docid: "399", descending: true, inclusive_end: true, foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(400, result.rows[2].key);
    - TEquals(2, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    - endkey_docid: "399", descending: true, inclusive_end: false, foobar: randVal()});
    - TEquals(3, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    - TEquals(400, result.rows[2].key);
    - TEquals(1, result.rows[2].value);
    -
    - result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    - endkey_docid: "400", descending: true, inclusive_end: false, foobar: randVal()});
    - TEquals(2, result.rows.length);
    - TEquals(402, result.rows[0].key);
    - TEquals(3, result.rows[0].value);
    - TEquals(401, result.rows[1].key);
    - TEquals(3, result.rows[1].value);
    -
    - db.deleteDb();
    - }
    -
    - testReducePagination();
    -
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/reduce_builtin.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/reduce_builtin.js b/share/www/script/test/reduce_builtin.js
    deleted file mode 100644
    index b3cc3cc..0000000
    --- a/share/www/script/test/reduce_builtin.js
    +++ /dev/null
    @@ -1,179 +0,0 @@
    -// 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.
    -
    -couchTests.reduce_builtin = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - var numDocs = 500;
    - var docs = makeDocs(1,numDocs + 1);
    - db.bulkSave(docs);
    -
    - var summate = function(N) {return (N+1)*N/2;};
    -
    - var sumsqr = function(N) {
    - var acc = 0;
    - for (var i=1; i<=N; ++i) {
    - acc += i*i;
    - }
    - return acc;
    - };
    -
    - // this is the same test as the reduce.js test
    - // only we'll let CouchDB run reduce in Erlang
    - var map = function (doc) {
    - emit(doc.integer, doc.integer);
    - emit(doc.integer, doc.integer);
    - };
    -
    - var result = db.query(map, "_sum");
    - T(result.rows[0].value == 2*summate(numDocs));
    - result = db.query(map, "_count");
    - T(result.rows[0].value == 1000);
    - result = db.query(map, "_stats");
    - T(result.rows[0].value.sum == 2*summate(numDocs));
    - T(result.rows[0].value.count == 1000);
    - T(result.rows[0].value.min == 1);
    - T(result.rows[0].value.max == 500);
    - T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
    -
    - result = db.query(map, "_sum", {startkey: 4, endkey: 4});
    - T(result.rows[0].value == 8);
    - result = db.query(map, "_count", {startkey: 4, endkey: 4});
    - T(result.rows[0].value == 2);
    -
    - result = db.query(map, "_sum", {startkey: 4, endkey: 5});
    - T(result.rows[0].value == 18);
    - result = db.query(map, "_count", {startkey: 4, endkey: 5});
    - T(result.rows[0].value == 4);
    -
    - result = db.query(map, "_sum", {startkey: 4, endkey: 6});
    - T(result.rows[0].value == 30);
    - result = db.query(map, "_count", {startkey: 4, endkey: 6});
    - T(result.rows[0].value == 6);
    -
    - result = db.query(map, "_sum", {group:true, limit:3});
    - T(result.rows[0].value == 2);
    - T(result.rows[1].value == 4);
    - T(result.rows[2].value == 6);
    -
    - for(var i=1; i<numDocs/2; i+=30) {
    - result = db.query(map, "_sum", {startkey: i, endkey: numDocs - i});
    - T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
    - }
    -
    - // test for trailing characters after builtin functions, desired behaviour
    - // is to disregard any trailing characters
    - // I think the behavior should be a prefix test, so that even "_statsorama"
    - // or "_stats\nare\awesome" should work just as "_stats" does. - JChris
    -
    - var trailing = ["\u000a", "orama", "\nare\nawesome", " ", " \n "];
    -
    - for(var i=0; i < trailing.length; i++) {
    - result = db.query(map, "_sum" + trailing[i]);
    - T(result.rows[0].value == 2*summate(numDocs));
    - result = db.query(map, "_count" + trailing[i]);
    - T(result.rows[0].value == 1000);
    - result = db.query(map, "_stats" + trailing[i]);
    - T(result.rows[0].value.sum == 2*summate(numDocs));
    - T(result.rows[0].value.count == 1000);
    - T(result.rows[0].value.min == 1);
    - T(result.rows[0].value.max == 500);
    - T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
    - }
    -
    - db.deleteDb();
    - db.createDb();
    -
    - for(var i=1; i <= 5; i++) {
    -
    - for(var j=0; j < 10; j++) {
    - // these docs are in the order of the keys collation, for clarity
    - var docs = [];
    - docs.push({keys:["a"]});
    - docs.push({keys:["a"]});
    - docs.push({keys:["a", "b"]});
    - docs.push({keys:["a", "b"]});
    - docs.push({keys:["a", "b", "c"]});
    - docs.push({keys:["a", "b", "d"]});
    - docs.push({keys:["a", "c", "d"]});
    - docs.push({keys:["d"]});
    - docs.push({keys:["d", "a"]});
    - docs.push({keys:["d", "b"]});
    - docs.push({keys:["d", "c"]});
    - db.bulkSave(docs);
    - T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
    - }
    -
    - map = function (doc) { emit(doc.keys, 1); };
    - // with emitted values being 1, count should be the same as sum
    - var builtins = ["_sum", "_count"];
    -
    - for (var b=0; b < builtins.length; b++) {
    - var fun = builtins[b];
    - var results = db.query(map, fun, {group:true});
    -
    - //group by exact key match
    - T(equals(results.rows[0], {key:["a"],value:20*i}));
    - T(equals(results.rows[1], {key:["a","b"],value:20*i}));
    - T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i}));
    - T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i}));
    -
    - // test to make sure group reduce and limit params provide valid json
    - var results = db.query(map, fun, {group: true, limit: 2});
    - T(equals(results.rows[0], {key: ["a"], value: 20*i}));
    - T(equals(results.rows.length, 2));
    -
    - //group by the first element in the key array
    - var results = db.query(map, fun, {group_level:1});
    - T(equals(results.rows[0], {key:["a"],value:70*i}));
    - T(equals(results.rows[1], {key:["d"],value:40*i}));
    -
    - //group by the first 2 elements in the key array
    - var results = db.query(map, fun, {group_level:2});
    - T(equals(results.rows[0], {key:["a"],value:20*i}));
    - T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    - T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    - T(equals(results.rows[3], {key:["d"],value:10*i}));
    - T(equals(results.rows[4], {key:["d","a"],value:10*i}));
    - T(equals(results.rows[5], {key:["d","b"],value:10*i}));
    - T(equals(results.rows[6], {key:["d","c"],value:10*i}));
    - };
    -
    - map = function (doc) { emit(doc.keys, [1, 1]); };
    -
    - var results = db.query(map, "_sum", {group:true});
    - T(equals(results.rows[0], {key:["a"],value:[20*i,20*i]}));
    - T(equals(results.rows[1], {key:["a","b"],value:[20*i,20*i]}));
    - T(equals(results.rows[2], {key:["a", "b", "c"],value:[10*i,10*i]}));
    - T(equals(results.rows[3], {key:["a", "b", "d"],value:[10*i,10*i]}));
    -
    - var results = db.query(map, "_sum", {group: true, limit: 2});
    - T(equals(results.rows[0], {key: ["a"], value: [20*i,20*i]}));
    - T(equals(results.rows.length, 2));
    -
    - var results = db.query(map, "_sum", {group_level:1});
    - T(equals(results.rows[0], {key:["a"],value:[70*i,70*i]}));
    - T(equals(results.rows[1], {key:["d"],value:[40*i,40*i]}));
    -
    - var results = db.query(map, "_sum", {group_level:2});
    - T(equals(results.rows[0], {key:["a"],value:[20*i,20*i]}));
    - T(equals(results.rows[1], {key:["a","b"],value:[40*i,40*i]}));
    - T(equals(results.rows[2], {key:["a","c"],value:[10*i,10*i]}));
    - T(equals(results.rows[3], {key:["d"],value:[10*i,10*i]}));
    - T(equals(results.rows[4], {key:["d","a"],value:[10*i,10*i]}));
    - T(equals(results.rows[5], {key:["d","b"],value:[10*i,10*i]}));
    - T(equals(results.rows[6], {key:["d","c"],value:[10*i,10*i]}));
    - }
    -}

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/reduce_false.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/reduce_false.js b/share/www/script/test/reduce_false.js
    deleted file mode 100644
    index 699b258..0000000
    --- a/share/www/script/test/reduce_false.js
    +++ /dev/null
    @@ -1,44 +0,0 @@
    -// 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.
    -
    -couchTests.reduce_false = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - var numDocs = 5;
    - var docs = makeDocs(1,numDocs + 1);
    - db.bulkSave(docs);
    - var summate = function(N) {return (N+1)*N/2;};
    -
    - var designDoc = {
    - _id:"_design/test",
    - language: "javascript",
    - views: {
    - summate: {map:"function (doc) { emit(doc.integer, doc.integer); }",
    - reduce:"function (keys, values) { return sum(values); }"},
    - }
    - };
    - T(db.save(designDoc).ok);
    -
    - // Test that the reduce works
    - var res = db.view('test/summate');
    - T(res.rows.length == 1 && res.rows[0].value == summate(5));
    -
    - //Test that we get our docs back
    - res = db.view('test/summate', {reduce: false});
    - T(res.rows.length == 5);
    - for(var i=0; i<5; i++) {
    - T(res.rows[i].value == i+1);
    - }
    -};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/share/www/script/test/reduce_false_temp.js
    ----------------------------------------------------------------------
    diff --git a/share/www/script/test/reduce_false_temp.js b/share/www/script/test/reduce_false_temp.js
    deleted file mode 100644
    index d45f05b..0000000
    --- a/share/www/script/test/reduce_false_temp.js
    +++ /dev/null
    @@ -1,37 +0,0 @@
    -// 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.
    -
    -couchTests.reduce_false_temp = function(debug) {
    - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    - db.deleteDb();
    - db.createDb();
    - if (debug) debugger;
    -
    - var numDocs = 5;
    - var docs = makeDocs(1,numDocs + 1);
    - db.bulkSave(docs);
    - var summate = function(N) {return (N+1)*N/2;};
    -
    - var mapFun = "function (doc) { emit(doc.integer, doc.integer); }";
    - var reduceFun = "function (keys, values) { return sum(values); }";
    -
    - // Test that the reduce works
    - var res = db.query(mapFun, reduceFun);
    - T(res.rows.length == 1 && res.rows[0].value == summate(5));
    -
    - //Test that we get our docs back
    - res = db.query(mapFun, reduceFun, {reduce: false});
    - T(res.rows.length == 5);
    - for(var i=0; i<5; i++) {
    - T(res.rows[i].value == i+1);
    - }
    -};
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/json2.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/json2.js b/test/javascript/json2.js
    new file mode 100644
    index 0000000..a1a3b17
    --- /dev/null
    +++ b/test/javascript/json2.js
    @@ -0,0 +1,482 @@
    +/*
    + http://www.JSON.org/json2.js
    + 2010-03-20
    +
    + Public Domain.
    +
    + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
    +
    + See http://www.JSON.org/js.html
    +
    +
    + This code should be minified before deployment.
    + See http://javascript.crockford.com/jsmin.html
    +
    + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    + NOT CONTROL.
    +
    +
    + This file creates a global JSON object containing two methods: stringify
    + and parse.
    +
    + JSON.stringify(value, replacer, space)
    + value any JavaScript value, usually an object or array.
    +
    + replacer an optional parameter that determines how object
    + values are stringified for objects. It can be a
    + function or an array of strings.
    +
    + space an optional parameter that specifies the indentation
    + of nested structures. If it is omitted, the text will
    + be packed without extra whitespace. If it is a number,
    + it will specify the number of spaces to indent at each
    + level. If it is a string (such as '\t' or '&nbsp;'),
    + it contains the characters used to indent at each level.
    +
    + This method produces a JSON text from a JavaScript value.
    +
    + When an object value is found, if the object contains a toJSON
    + method, its toJSON method will be called and the result will be
    + stringified. A toJSON method does not serialize: it returns the
    + value represented by the name/value pair that should be serialized,
    + or undefined if nothing should be serialized. The toJSON method
    + will be passed the key associated with the value, and this will be
    + bound to the value
    +
    + For example, this would serialize Dates as ISO strings.
    +
    + Date.prototype.toJSON = function (key) {
    + function f(n) {
    + // Format integers to have at least two digits.
    + return n < 10 ? '0' + n : n;
    + }
    +
    + return this.getUTCFullYear() + '-' +
    + f(this.getUTCMonth() + 1) + '-' +
    + f(this.getUTCDate()) + 'T' +
    + f(this.getUTCHours()) + ':' +
    + f(this.getUTCMinutes()) + ':' +
    + f(this.getUTCSeconds()) + 'Z';
    + };
    +
    + You can provide an optional replacer method. It will be passed the
    + key and value of each member, with this bound to the containing
    + object. The value that is returned from your method will be
    + serialized. If your method returns undefined, then the member will
    + be excluded from the serialization.
    +
    + If the replacer parameter is an array of strings, then it will be
    + used to select the members to be serialized. It filters the results
    + such that only members with keys listed in the replacer array are
    + stringified.
    +
    + Values that do not have JSON representations, such as undefined or
    + functions, will not be serialized. Such values in objects will be
    + dropped; in arrays they will be replaced with null. You can use
    + a replacer function to replace those with JSON values.
    + JSON.stringify(undefined) returns undefined.
    +
    + The optional space parameter produces a stringification of the
    + value that is filled with line breaks and indentation to make it
    + easier to read.
    +
    + If the space parameter is a non-empty string, then that string will
    + be used for indentation. If the space parameter is a number, then
    + the indentation will be that many spaces.
    +
    + Example:
    +
    + text = JSON.stringify(['e', {pluribus: 'unum'}]);
    + // text is '["e",{"pluribus":"unum"}]'
    +
    +
    + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
    + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
    +
    + text = JSON.stringify([new Date()], function (key, value) {
    + return this[key] instanceof Date ?
    + 'Date(' + this[key] + ')' : value;
    + });
    + // text is '["Date(---current time---)"]'
    +
    +
    + JSON.parse(text, reviver)
    + This method parses a JSON text to produce an object or array.
    + It can throw a SyntaxError exception.
    +
    + The optional reviver parameter is a function that can filter and
    + transform the results. It receives each of the keys and values,
    + and its return value is used instead of the original value.
    + If it returns what it received, then the structure is not modified.
    + If it returns undefined then the member is deleted.
    +
    + Example:
    +
    + // Parse the text. Values that look like ISO date strings will
    + // be converted to Date objects.
    +
    + myData = JSON.parse(text, function (key, value) {
    + var a;
    + if (typeof value === 'string') {
    + a =
    +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
    + if (a) {
    + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
    + +a[5], +a[6]));
    + }
    + }
    + return value;
    + });
    +
    + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
    + var d;
    + if (typeof value === 'string' &&
    + value.slice(0, 5) === 'Date(' &&
    + value.slice(-1) === ')') {
    + d = new Date(value.slice(5, -1));
    + if (d) {
    + return d;
    + }
    + }
    + return value;
    + });
    +
    +
    + This is a reference implementation. You are free to copy, modify, or
    + redistribute.
    +*/
    +
    +/*jslint evil: true, strict: false */
    +
    +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    + lastIndex, length, parse, prototype, push, replace, slice, stringify,
    + test, toJSON, toString, valueOf
    +*/
    +
    +
    +// Create a JSON object only if one does not already exist. We create the
    +// methods in a closure to avoid creating global variables.
    +
    +if (!this.JSON) {
    + this.JSON = {};
    +}
    +
    +(function () {
    +
    + function f(n) {
    + // Format integers to have at least two digits.
    + 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 = { // table of character substitutions
    + '\b': '\\b',
    + '\t': '\\t',
    + '\n': '\\n',
    + '\f': '\\f',
    + '\r': '\\r',
    + '"' : '\\"',
    + '\\': '\\\\'
    + },
    + rep;
    +
    +
    + function quote(string) {
    +
    +// If the string contains no control characters, no quote characters, and no
    +// backslash characters, then we can safely slap some quotes around it.
    +// Otherwise we must also replace the offending characters with safe escape
    +// sequences.
    +
    + 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) {
    +
    +// Produce a string from holder[key].
    +
    + var i, // The loop counter.
    + k, // The member key.
    + v, // The member value.
    + length,
    + mind = gap,
    + partial,
    + value = holder[key];
    +
    +// If the value has a toJSON method, call it to obtain a replacement value.
    +
    + if (value && typeof value === 'object' &&
    + typeof value.toJSON === 'function') {
    + value = value.toJSON(key);
    + }
    +
    +// If we were called with a replacer function, then call the replacer to
    +// obtain a replacement value.
    +
    + if (typeof rep === 'function') {
    + value = rep.call(holder, key, value);
    + }
    +
    +// What happens next depends on the value's type.
    +
    + switch (typeof value) {
    + case 'string':
    + return quote(value);
    +
    + case 'number':
    +
    +// JSON numbers must be finite. Encode non-finite numbers as null.
    +
    + return isFinite(value) ? String(value) : 'null';
    +
    + case 'boolean':
    + case 'null':
    +
    +// If the value is a boolean or null, convert it to a string. Note:
    +// typeof null does not produce 'null'. The case is included here in
    +// the remote chance that this gets fixed someday.
    +
    + return String(value);
    +
    +// If the type is 'object', we might be dealing with an object or an array or
    +// null.
    +
    + case 'object':
    +
    +// Due to a specification blunder in ECMAScript, typeof null is 'object',
    +// so watch out for that case.
    +
    + if (!value) {
    + return 'null';
    + }
    +
    +// Make an array to hold the partial results of stringifying this object value.
    +
    + gap += indent;
    + partial = [];
    +
    +// Is the value an array?
    +
    + if (Object.prototype.toString.apply(value) === '[object Array]') {
    +
    +// The value is an array. Stringify every element. Use null as a placeholder
    +// for non-JSON values.
    +
    + length = value.length;
    + for (i = 0; i < length; i += 1) {
    + partial[i] = str(i, value) || 'null';
    + }
    +
    +// Join all of the elements together, separated with commas, and wrap them in
    +// brackets.
    +
    + v = partial.length === 0 ? '[]' :
    + gap ? '[\n' + gap +
    + partial.join(',\n' + gap) + '\n' +
    + mind + ']' :
    + '[' + partial.join(',') + ']';
    + gap = mind;
    + return v;
    + }
    +
    +// If the replacer is an array, use it to select the members to be stringified.
    +
    + if (rep && typeof rep === 'object') {
    + length = rep.length;
    + for (i = 0; i < length; i += 1) {
    + k = rep[i];
    + if (typeof k === 'string') {
    + v = str(k, value);
    + if (v) {
    + partial.push(quote(k) + (gap ? ': ' : ':') + v);
    + }
    + }
    + }
    + } else {
    +
    +// Otherwise, iterate through all of the keys in the object.
    +
    + for (k in value) {
    + if (Object.hasOwnProperty.call(value, k)) {
    + v = str(k, value);
    + if (v) {
    + partial.push(quote(k) + (gap ? ': ' : ':') + v);
    + }
    + }
    + }
    + }
    +
    +// Join all of the member texts together, separated with commas,
    +// and wrap them in braces.
    +
    + v = partial.length === 0 ? '{}' :
    + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
    + mind + '}' : '{' + partial.join(',') + '}';
    + gap = mind;
    + return v;
    + }
    + }
    +
    +// If the JSON object does not yet have a stringify method, give it one.
    +
    + if (typeof JSON.stringify !== 'function') {
    + JSON.stringify = function (value, replacer, space) {
    +
    +// The stringify method takes a value and an optional replacer, and an optional
    +// space parameter, and returns a JSON text. The replacer can be a function
    +// that can replace values, or an array of strings that will select the keys.
    +// A default replacer method can be provided. Use of the space parameter can
    +// produce text that is more easily readable.
    +
    + var i;
    + gap = '';
    + indent = '';
    +
    +// If the space parameter is a number, make an indent string containing that
    +// many spaces.
    +
    + if (typeof space === 'number') {
    + for (i = 0; i < space; i += 1) {
    + indent += ' ';
    + }
    +
    +// If the space parameter is a string, it will be used as the indent string.
    +
    + } else if (typeof space === 'string') {
    + indent = space;
    + }
    +
    +// If there is a replacer, it must be a function or an array.
    +// Otherwise, throw an error.
    +
    + rep = replacer;
    + if (replacer && typeof replacer !== 'function' &&
    + (typeof replacer !== 'object' ||
    + typeof replacer.length !== 'number')) {
    + throw new Error('JSON.stringify');
    + }
    +
    +// Make a fake root object containing our value under the key of ''.
    +// Return the result of stringifying the value.
    +
    + return str('', {'': value});
    + };
    + }
    +
    +
    +// If the JSON object does not yet have a parse method, give it one.
    +
    + if (typeof JSON.parse !== 'function') {
    + JSON.parse = function (text, reviver) {
    +
    +// The parse method takes a text and an optional reviver function, and returns
    +// a JavaScript value if the text is a valid JSON text.
    +
    + var j;
    +
    + function walk(holder, key) {
    +
    +// The walk method is used to recursively walk the resulting structure so
    +// that modifications can be made.
    +
    + var k, v, value = holder[key];
    + if (value && typeof value === 'object') {
    + for (k in value) {
    + if (Object.hasOwnProperty.call(value, k)) {
    + v = walk(value, k);
    + if (v !== undefined) {
    + value[k] = v;
    + } else {
    + delete value[k];
    + }
    + }
    + }
    + }
    + return reviver.call(holder, key, value);
    + }
    +
    +
    +// Parsing happens in four stages. In the first stage, we replace certain
    +// Unicode characters with escape sequences. JavaScript handles many characters
    +// incorrectly, either silently deleting them, or treating them as line endings.
    +
    + 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);
    + });
    + }
    +
    +// In the second stage, we run the text against regular expressions that look
    +// for non-JSON patterns. We are especially concerned with '()' and 'new'
    +// because they can cause invocation, and '=' because it can cause mutation.
    +// But just to be safe, we want to reject all unexpected forms.
    +
    +// We split the second stage into 4 regexp operations in order to work around
    +// crippling inefficiencies in IE's and Safari's regexp engines. First we
    +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
    +// replace all simple value tokens with ']' characters. Third, we delete all
    +// open brackets that follow a colon or comma or that begin the text. Finally,
    +// we look to see that the remaining characters are only whitespace or ']' or
    +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
    +
    + 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, ''))) {
    +
    +// In the third stage we use the eval function to compile the text into a
    +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
    +// in JavaScript: it can begin a block or an object literal. We wrap the text
    +// in parens to eliminate the ambiguity.
    +
    + j = eval('(' + text + ')');
    +
    +// In the optional fourth stage, we recursively walk the new structure, passing
    +// each name/value pair to a reviver function for possible transformation.
    +
    + return typeof reviver === 'function' ?
    + walk({'': j}, '') : j;
    + }
    +
    +// If the text is not JSON parseable, then a SyntaxError is thrown.
    +
    + throw new SyntaxError('JSON.parse');
    + };
    + }
    +}());

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/oauth.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/oauth.js b/test/javascript/oauth.js
    new file mode 100644
    index 0000000..ada00a2
    --- /dev/null
    +++ b/test/javascript/oauth.js
    @@ -0,0 +1,511 @@
    +/*
    + * Copyright 2008 Netflix, 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.
    + */
    +
    +/* Here's some JavaScript software for implementing OAuth.
    +
    + This isn't as useful as you might hope. OAuth is based around
    + allowing tools and websites to talk to each other. However,
    + JavaScript running in web browsers is hampered by security
    + restrictions that prevent code running on one website from
    + accessing data stored or served on another.
    +
    + Before you start hacking, make sure you understand the limitations
    + posed by cross-domain XMLHttpRequest.
    +
    + On the bright side, some platforms use JavaScript as their
    + language, but enable the programmer to access other web sites.
    + Examples include Google Gadgets, and Microsoft Vista Sidebar.
    + For those platforms, this library should come in handy.
    +*/
    +
    +// The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by
    +// http://pajhome.org.uk/crypt/md5/sha1.js
    +
    +/* An OAuth message is represented as an object like this:
    + {method: "GET", action: "http://server.com/path", parameters: ...}
    +
    + The parameters may be either a map {name: value, name2: value2}
    + or an Array of name-value pairs [[name, value], [name2, value2]].
    + The latter representation is more powerful: it supports parameters
    + in a specific sequence, or several parameters with the same name;
    + for example [["a", 1], ["b", 2], ["a", 3]].
    +
    + Parameter names and values are NOT percent-encoded in an object.
    + They must be encoded before transmission and decoded after reception.
    + For example, this message object:
    + {method: "GET", action: "http://server/path", parameters: {p: "x y"}}
    + ... can be transmitted as an HTTP request that begins:
    + GET /path?p=x%20y HTTP/1.0
    + (This isn't a valid OAuth request, since it lacks a signature etc.)
    + Note that the object "x y" is transmitted as x%20y. To encode
    + parameters, you can call OAuth.addToURL, OAuth.formEncode or
    + OAuth.getAuthorization.
    +
    + This message object model harmonizes with the browser object model for
    + input elements of an form, whose value property isn't percent encoded.
    + The browser encodes each value before transmitting it. For example,
    + see consumer.setInputs in example/consumer.js.
    + */
    +var OAuth; if (OAuth == null) OAuth = {};
    +
    +OAuth.setProperties = function setProperties(into, from) {
    + if (into != null && from != null) {
    + for (var key in from) {
    + into[key] = from[key];
    + }
    + }
    + return into;
    +}
    +
    +OAuth.setProperties(OAuth, // utility functions
    +{
    + percentEncode: function percentEncode(s) {
    + if (s == null) {
    + return "";
    + }
    + if (s instanceof Array) {
    + var e = "";
    + for (var i = 0; i < s.length; ++i) {
    + if (e != "") e += '&';
    + e += percentEncode(s[i]);
    + }
    + return e;
    + }
    + s = encodeURIComponent(s);
    + // Now replace the values which encodeURIComponent doesn't do
    + // encodeURIComponent ignores: - _ . ! ~ * ' ( )
    + // OAuth dictates the only ones you can ignore are: - _ . ~
    + // Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent
    + s = s.replace(/\!/g, "%21");
    + s = s.replace(/\*/g, "%2A");
    + s = s.replace(/\'/g, "%27");
    + s = s.replace(/\(/g, "%28");
    + s = s.replace(/\)/g, "%29");
    + return s;
    + }
    +,
    + decodePercent: function decodePercent(s) {
    + if (s != null) {
    + // Handle application/x-www-form-urlencoded, which is defined by
    + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
    + s = s.replace(/\+/g, " ");
    + }
    + return decodeURIComponent(s);
    + }
    +,
    + /** Convert the given parameters to an Array of name-value pairs. */
    + getParameterList: function getParameterList(parameters) {
    + if (parameters == null) {
    + return [];
    + }
    + if (typeof parameters != "object") {
    + return decodeForm(parameters + "");
    + }
    + if (parameters instanceof Array) {
    + return parameters;
    + }
    + var list = [];
    + for (var p in parameters) {
    + list.push([p, parameters[p]]);
    + }
    + return list;
    + }
    +,
    + /** Convert the given parameters to a map from name to value. */
    + getParameterMap: function getParameterMap(parameters) {
    + if (parameters == null) {
    + return {};
    + }
    + if (typeof parameters != "object") {
    + return getParameterMap(decodeForm(parameters + ""));
    + }
    + if (parameters instanceof Array) {
    + var map = {};
    + for (var p = 0; p < parameters.length; ++p) {
    + var key = parameters[p][0];
    + if (map[key] === undefined) { // first value wins
    + map[key] = parameters[p][1];
    + }
    + }
    + return map;
    + }
    + return parameters;
    + }
    +,
    + getParameter: function getParameter(parameters, name) {
    + if (parameters instanceof Array) {
    + for (var p = 0; p < parameters.length; ++p) {
    + if (parameters[p][0] == name) {
    + return parameters[p][1]; // first value wins
    + }
    + }
    + } else {
    + return OAuth.getParameterMap(parameters)[name];
    + }
    + return null;
    + }
    +,
    + formEncode: function formEncode(parameters) {
    + var form = "";
    + var list = OAuth.getParameterList(parameters);
    + for (var p = 0; p < list.length; ++p) {
    + var value = list[p][1];
    + if (value == null) value = "";
    + if (form != "") form += '&';
    + form += OAuth.percentEncode(list[p][0])
    + +'='+ OAuth.percentEncode(value);
    + }
    + return form;
    + }
    +,
    + decodeForm: function decodeForm(form) {
    + var list = [];
    + var nvps = form.split('&');
    + for (var n = 0; n < nvps.length; ++n) {
    + var nvp = nvps[n];
    + if (nvp == "") {
    + continue;
    + }
    + var equals = nvp.indexOf('=');
    + var name;
    + var value;
    + if (equals < 0) {
    + name = OAuth.decodePercent(nvp);
    + value = null;
    + } else {
    + name = OAuth.decodePercent(nvp.substring(0, equals));
    + value = OAuth.decodePercent(nvp.substring(equals + 1));
    + }
    + list.push([name, value]);
    + }
    + return list;
    + }
    +,
    + setParameter: function setParameter(message, name, value) {
    + var parameters = message.parameters;
    + if (parameters instanceof Array) {
    + for (var p = 0; p < parameters.length; ++p) {
    + if (parameters[p][0] == name) {
    + if (value === undefined) {
    + parameters.splice(p, 1);
    + } else {
    + parameters[p][1] = value;
    + value = undefined;
    + }
    + }
    + }
    + if (value !== undefined) {
    + parameters.push([name, value]);
    + }
    + } else {
    + parameters = OAuth.getParameterMap(parameters);
    + parameters[name] = value;
    + message.parameters = parameters;
    + }
    + }
    +,
    + setParameters: function setParameters(message, parameters) {
    + var list = OAuth.getParameterList(parameters);
    + for (var i = 0; i < list.length; ++i) {
    + OAuth.setParameter(message, list[i][0], list[i][1]);
    + }
    + }
    +,
    + /** Fill in parameters to help construct a request message.
    + This function doesn't fill in every parameter.
    + The accessor object should be like:
    + {consumerKey:'foo', consumerSecret:'bar', accessorSecret:'nurn', token:'krelm', tokenSecret:'blah'}
    + The accessorSecret property is optional.
    + */
    + completeRequest: function completeRequest(message, accessor) {
    + if (message.method == null) {
    + message.method = "GET";
    + }
    + var map = OAuth.getParameterMap(message.parameters);
    + if (map.oauth_consumer_key == null) {
    + OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || "");
    + }
    + if (map.oauth_token == null && accessor.token != null) {
    + OAuth.setParameter(message, "oauth_token", accessor.token);
    + }
    + if (map.oauth_version == null) {
    + OAuth.setParameter(message, "oauth_version", "1.0");
    + }
    + if (map.oauth_timestamp == null) {
    + OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
    + }
    + if (map.oauth_nonce == null) {
    + OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6));
    + }
    + OAuth.SignatureMethod.sign(message, accessor);
    + }
    +,
    + setTimestampAndNonce: function setTimestampAndNonce(message) {
    + OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
    + OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6));
    + }
    +,
    + addToURL: function addToURL(url, parameters) {
    + newURL = url;
    + if (parameters != null) {
    + var toAdd = OAuth.formEncode(parameters);
    + if (toAdd.length > 0) {
    + var q = url.indexOf('?');
    + if (q < 0) newURL += '?';
    + else newURL += '&';
    + newURL += toAdd;
    + }
    + }
    + return newURL;
    + }
    +,
    + /** Construct the value of the Authorization header for an HTTP request. */
    + getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) {
    + var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"';
    + var list = OAuth.getParameterList(parameters);
    + for (var p = 0; p < list.length; ++p) {
    + var parameter = list[p];
    + var name = parameter[0];
    + if (name.indexOf("oauth_") == 0) {
    + header += ',' + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"';
    + }
    + }
    + return header;
    + }
    +,
    + timestamp: function timestamp() {
    + var d = new Date();
    + return Math.floor(d.getTime()/1000);
    + }
    +,
    + nonce: function nonce(length) {
    + var chars = OAuth.nonce.CHARS;
    + var result = "";
    + for (var i = 0; i < length; ++i) {
    + var rnum = Math.floor(Math.random() * chars.length);
    + result += chars.substring(rnum, rnum+1);
    + }
    + return result;
    + }
    +});
    +
    +OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
    +
    +/** Define a constructor function,
    + without causing trouble to anyone who was using it as a namespace.
    + That is, if parent[name] already existed and had properties,
    + copy those properties into the new constructor.
    + */
    +OAuth.declareClass = function declareClass(parent, name, newConstructor) {
    + var previous = parent[name];
    + parent[name] = newConstructor;
    + if (newConstructor != null && previous != null) {
    + for (var key in previous) {
    + if (key != "prototype") {
    + newConstructor[key] = previous[key];
    + }
    + }
    + }
    + return newConstructor;
    +}
    +
    +/** An abstract algorithm for signing messages. */
    +OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod(){});
    +
    +OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members
    +{
    + /** Add a signature to the message. */
    + sign: function sign(message) {
    + var baseString = OAuth.SignatureMethod.getBaseString(message);
    + var signature = this.getSignature(baseString);
    + OAuth.setParameter(message, "oauth_signature", signature);
    + return signature; // just in case someone's interested
    + }
    +,
    + /** Set the key string for signing. */
    + initialize: function initialize(name, accessor) {
    + var consumerSecret;
    + if (accessor.accessorSecret != null
    + && name.length > 9
    + && name.substring(name.length-9) == "-Accessor")
    + {
    + consumerSecret = accessor.accessorSecret;
    + } else {
    + consumerSecret = accessor.consumerSecret;
    + }
    + this.key = OAuth.percentEncode(consumerSecret)
    + +"&"+ OAuth.percentEncode(accessor.tokenSecret);
    + }
    +});
    +
    +/* SignatureMethod expects an accessor object to be like this:
    + {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."}
    + The accessorSecret property is optional.
    + */
    +// Class members:
    +OAuth.setProperties(OAuth.SignatureMethod, // class members
    +{
    + sign: function sign(message, accessor) {
    + var name = OAuth.getParameterMap(message.parameters).oauth_signature_method;
    + if (name == null || name == "") {
    + name = "HMAC-SHA1";
    + OAuth.setParameter(message, "oauth_signature_method", name);
    + }
    + OAuth.SignatureMethod.newMethod(name, accessor).sign(message);
    + }
    +,
    + /** Instantiate a SignatureMethod for the given method name. */
    + newMethod: function newMethod(name, accessor) {
    + var impl = OAuth.SignatureMethod.REGISTERED[name];
    + if (impl != null) {
    + var method = new impl();
    + method.initialize(name, accessor);
    + return method;
    + }
    + var err = new Error("signature_method_rejected");
    + var acceptable = "";
    + for (var r in OAuth.SignatureMethod.REGISTERED) {
    + if (acceptable != "") acceptable += '&';
    + acceptable += OAuth.percentEncode(r);
    + }
    + err.oauth_acceptable_signature_methods = acceptable;
    + throw err;
    + }
    +,
    + /** A map from signature method name to constructor. */
    + REGISTERED : {}
    +,
    + /** Subsequently, the given constructor will be used for the named methods.
    + The constructor will be called with no parameters.
    + The resulting object should usually implement getSignature(baseString).
    + You can easily define such a constructor by calling makeSubclass, below.
    + */
    + registerMethodClass: function registerMethodClass(names, classConstructor) {
    + for (var n = 0; n < names.length; ++n) {
    + OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor;
    + }
    + }
    +,
    + /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */
    + makeSubclass: function makeSubclass(getSignatureFunction) {
    + var superClass = OAuth.SignatureMethod;
    + var subClass = function() {
    + superClass.call(this);
    + };
    + subClass.prototype = new superClass();
    + // Delete instance variables from prototype:
    + // delete subclass.prototype... There aren't any.
    + subClass.prototype.getSignature = getSignatureFunction;
    + subClass.prototype.constructor = subClass;
    + return subClass;
    + }
    +,
    + getBaseString: function getBaseString(message) {
    + var URL = message.action;
    + var q = URL.indexOf('?');
    + var parameters;
    + if (q < 0) {
    + parameters = message.parameters;
    + } else {
    + // Combine the URL query string with the other parameters:
    + parameters = OAuth.decodeForm(URL.substring(q + 1));
    + var toAdd = OAuth.getParameterList(message.parameters);
    + for (var a = 0; a < toAdd.length; ++a) {
    + parameters.push(toAdd[a]);
    + }
    + }
    + return OAuth.percentEncode(message.method.toUpperCase())
    + +'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL))
    + +'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters));
    + }
    +,
    + normalizeUrl: function normalizeUrl(url) {
    + var uri = OAuth.SignatureMethod.parseUri(url);
    + var scheme = uri.protocol.toLowerCase();
    + var authority = uri.authority.toLowerCase();
    + var dropPort = (scheme == "http" && uri.port == 80)
    + || (scheme == "https" && uri.port == 443);
    + if (dropPort) {
    + // find the last : in the authority
    + var index = authority.lastIndexOf(":");
    + if (index >= 0) {
    + authority = authority.substring(0, index);
    + }
    + }
    + var path = uri.path;
    + if (!path) {
    + path = "/"; // conforms to RFC 2616 section 3.2.2
    + }
    + // we know that there is no query and no fragment here.
    + return scheme + "://" + authority + path;
    + }
    +,
    + parseUri: function parseUri (str) {
    + /* This function was adapted from parseUri 1.2.1
    + http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
    + */
    + var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
    + parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }};
    + var m = o.parser.strict.exec(str);
    + var uri = {};
    + var i = 14;
    + while (i--) uri[o.key[i]] = m[i] || "";
    + return uri;
    + }
    +,
    + normalizeParameters: function normalizeParameters(parameters) {
    + if (parameters == null) {
    + return "";
    + }
    + var list = OAuth.getParameterList(parameters);
    + var sortable = [];
    + for (var p = 0; p < list.length; ++p) {
    + var nvp = list[p];
    + if (nvp[0] != "oauth_signature") {
    + sortable.push([ OAuth.percentEncode(nvp[0])
    + + " " // because it comes before any character that can appear in a percentEncoded string.
    + + OAuth.percentEncode(nvp[1])
    + , nvp]);
    + }
    + }
    + sortable.sort(function(a,b) {
    + if (a[0] < b[0]) return -1;
    + if (a[0] > b[0]) return 1;
    + return 0;
    + });
    + var sorted = [];
    + for (var s = 0; s < sortable.length; ++s) {
    + sorted.push(sortable[s][1]);
    + }
    + return OAuth.formEncode(sorted);
    + }
    +});
    +
    +OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"],
    + OAuth.SignatureMethod.makeSubclass(
    + function getSignature(baseString) {
    + return this.key;
    + }
    + ));
    +
    +OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"],
    + OAuth.SignatureMethod.makeSubclass(
    + function getSignature(baseString) {
    + b64pad = '=';
    + var signature = b64_hmac_sha1(this.key, baseString);
    + return signature;
    + }
    + ));

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/replicator_db_inc.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/replicator_db_inc.js b/test/javascript/replicator_db_inc.js
    new file mode 100644
    index 0000000..23f8587
    --- /dev/null
    +++ b/test/javascript/replicator_db_inc.js
    @@ -0,0 +1,96 @@
    +// 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.
    +
    +var replicator_db = {};
    +replicator_db.wait_rep_doc = 500; // number of millisecs to wait after saving a Rep Doc
    +replicator_db.dbA = new CouchDB("test_suite_rep_db_a", {"X-Couch-Full-Commit":"false"});
    +replicator_db.dbB = new CouchDB("test_suite_rep_db_b", {"X-Couch-Full-Commit":"false"});
    +replicator_db.repDb = new CouchDB("test_suite_rep_db", {"X-Couch-Full-Commit":"false"});
    +replicator_db.usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
    +
    +replicator_db.docs1 = [
    + {
    + _id: "foo1",
    + value: 11
    + },
    + {
    + _id: "foo2",
    + value: 22
    + },
    + {
    + _id: "foo3",
    + value: 33
    + }
    +];
    +
    +replicator_db.waitForRep = function waitForSeq(repDb, repDoc, state) {
    + var newRep,
    + t0 = new Date(),
    + t1,
    + ms = 3000;
    +
    + do {
    + newRep = repDb.open(repDoc._id);
    + t1 = new Date();
    + } while (((t1 - t0) <= ms) && newRep._replication_state !== state);
    +}
    +
    +replicator_db.waitForSeq = function waitForSeq(sourceDb, targetDb) {
    + var targetSeq,
    + sourceSeq = sourceDb.info().update_seq,
    + t0 = new Date(),
    + t1,
    + ms = 3000;
    +
    + do {
    + targetSeq = targetDb.info().update_seq;
    + t1 = new Date();
    + } while (((t1 - t0) <= ms) && targetSeq < sourceSeq);
    +}
    +
    +replicator_db.waitForDocPos = function waitForDocPos(db, docId, pos) {
    + var doc, curPos, t0, t1,
    + maxWait = 3000;
    +
    + doc = db.open(docId);
    + curPos = Number(doc._rev.split("-", 1));
    + t0 = t1 = new Date();
    +
    + while ((curPos < pos) && ((t1 - t0) <= maxWait)) {
    + doc = db.open(docId);
    + curPos = Number(doc._rev.split("-", 1));
    + t1 = new Date();
    + }
    +
    + return doc;
    +}
    +
    +replicator_db.wait = function wait(ms) {
    + var t0 = new Date(), t1;
    + do {
    + CouchDB.request("GET", "/");
    + t1 = new Date();
    + } while ((t1 - t0) <= ms);
    +}
    +
    +
    +replicator_db.populate_db = function populate_db(db, docs) {
    + if (db.name !== replicator_db.usersDb.name) {
    + db.deleteDb();
    + db.createDb();
    + }
    + for (var i = 0; i < docs.length; i++) {
    + var d = docs[i];
    + delete d._rev;
    + T(db.save(d).ok);
    + }
    +}
    \ No newline at end of file

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/run
    ----------------------------------------------------------------------
    diff --git a/test/javascript/run b/test/javascript/run
    index ab145b1..4c71204 100755
    --- a/test/javascript/run
    +++ b/test/javascript/run
    @@ -27,12 +27,12 @@ N = 3
      COUCHJS = "src/couch/priv/couchjs"

      SCRIPTS = """
    - share/www/script/json2.js
    - share/www/script/sha1.js
    - share/www/script/oauth.js
    - share/www/script/couch.js
    - share/www/script/replicator_db_inc.js
    - share/www/script/couch_test_runner.js
    + test/javascript/json2.js
    + test/javascript/sha1.js
    + test/javascript/oauth.js
    + test/javascript/couch.js
    + test/javascript/replicator_db_inc.js
    + test/javascript/couch_test_runner.js
          test/javascript/couch_http.js
          test/javascript/test_setup.js
      """.split()
    @@ -106,14 +106,14 @@ def main():

          tests = []
          if not len(args):
    - args = ["share/www/script/test"]
    + args = ["test/javascript/tests"]
          for name in args:
              if os.path.isdir(name):
                  tests.extend(glob.glob(os.path.join(name, "*.js")))
              elif os.path.isfile(name):
                  tests.append(name)
              else:
    - pname = os.path.join("share/www/script/test", name)
    + pname = os.path.join("test/javascript/tests", name)
                  if os.path.isfile(pname):
                      tests.append(pname)
                  elif os.path.isfile(pname + ".js"):

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/sha1.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/sha1.js b/test/javascript/sha1.js
    new file mode 100644
    index 0000000..ee73a63
    --- /dev/null
    +++ b/test/javascript/sha1.js
    @@ -0,0 +1,202 @@
    +/*
    + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
    + * in FIPS PUB 180-1
    + * Version 2.1a Copyright Paul Johnston 2000 - 2002.
    + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
    + * Distributed under the BSD License
    + * See http://pajhome.org.uk/crypt/md5 for details.
    + */
    +
    +/*
    + * Configurable variables. You may need to tweak these to be compatible with
    + * the server-side, but the defaults work in most cases.
    + */
    +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
    +var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */
    +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
    +
    +/*
    + * These are the functions you'll usually want to call
    + * They take string arguments and return either hex or base-64 encoded strings
    + */
    +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
    +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
    +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
    +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
    +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
    +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
    +
    +/*
    + * Perform a simple self-test to see if the VM is working
    + */
    +function sha1_vm_test()
    +{
    + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
    +}
    +
    +/*
    + * Calculate the SHA-1 of an array of big-endian words, and a bit length
    + */
    +function core_sha1(x, len)
    +{
    + /* append padding */
    + x[len >> 5] |= 0x80 << (24 - len % 32);
    + x[((len + 64 >> 9) << 4) + 15] = len;
    +
    + var w = Array(80);
    + var a = 1732584193;
    + var b = -271733879;
    + var c = -1732584194;
    + var d = 271733878;
    + var e = -1009589776;
    +
    + for(var i = 0; i < x.length; i += 16)
    + {
    + var olda = a;
    + var oldb = b;
    + var oldc = c;
    + var oldd = d;
    + var olde = e;
    +
    + for(var j = 0; j < 80; j++)
    + {
    + if(j < 16) w[j] = x[i + j];
    + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
    + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
    + safe_add(safe_add(e, w[j]), sha1_kt(j)));
    + e = d;
    + d = c;
    + c = rol(b, 30);
    + b = a;
    + a = t;
    + }
    +
    + a = safe_add(a, olda);
    + b = safe_add(b, oldb);
    + c = safe_add(c, oldc);
    + d = safe_add(d, oldd);
    + e = safe_add(e, olde);
    + }
    + return Array(a, b, c, d, e);
    +
    +}
    +
    +/*
    + * Perform the appropriate triplet combination function for the current
    + * iteration
    + */
    +function sha1_ft(t, b, c, d)
    +{
    + if(t < 20) return (b & c) | ((~b) & d);
    + if(t < 40) return b ^ c ^ d;
    + if(t < 60) return (b & c) | (b & d) | (c & d);
    + return b ^ c ^ d;
    +}
    +
    +/*
    + * Determine the appropriate additive constant for the current iteration
    + */
    +function sha1_kt(t)
    +{
    + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
    + (t < 60) ? -1894007588 : -899497514;
    +}
    +
    +/*
    + * Calculate the HMAC-SHA1 of a key and some data
    + */
    +function core_hmac_sha1(key, data)
    +{
    + var bkey = str2binb(key);
    + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
    +
    + var ipad = Array(16), opad = Array(16);
    + for(var i = 0; i < 16; i++)
    + {
    + ipad[i] = bkey[i] ^ 0x36363636;
    + opad[i] = bkey[i] ^ 0x5C5C5C5C;
    + }
    +
    + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
    + return core_sha1(opad.concat(hash), 512 + 160);
    +}
    +
    +/*
    + * Add integers, wrapping at 2^32. This uses 16-bit operations internally
    + * to work around bugs in some JS interpreters.
    + */
    +function safe_add(x, y)
    +{
    + var lsw = (x & 0xFFFF) + (y & 0xFFFF);
    + var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
    + return (msw << 16) | (lsw & 0xFFFF);
    +}
    +
    +/*
    + * Bitwise rotate a 32-bit number to the left.
    + */
    +function rol(num, cnt)
    +{
    + return (num << cnt) | (num >>> (32 - cnt));
    +}
    +
    +/*
    + * Convert an 8-bit or 16-bit string to an array of big-endian words
    + * In 8-bit function, characters >255 have their hi-byte silently ignored.
    + */
    +function str2binb(str)
    +{
    + var bin = Array();
    + var mask = (1 << chrsz) - 1;
    + for(var i = 0; i < str.length * chrsz; i += chrsz)
    + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
    + return bin;
    +}
    +
    +/*
    + * Convert an array of big-endian words to a string
    + */
    +function binb2str(bin)
    +{
    + var str = "";
    + var mask = (1 << chrsz) - 1;
    + for(var i = 0; i < bin.length * 32; i += chrsz)
    + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
    + return str;
    +}
    +
    +/*
    + * Convert an array of big-endian words to a hex string.
    + */
    +function binb2hex(binarray)
    +{
    + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
    + var str = "";
    + for(var i = 0; i < binarray.length * 4; i++)
    + {
    + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
    + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
    + }
    + return str;
    +}
    +
    +/*
    + * Convert an array of big-endian words to a base-64 string
    + */
    +function binb2b64(binarray)
    +{
    + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    + var str = "";
    + for(var i = 0; i < binarray.length * 4; i += 3)
    + {
    + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
    + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
    + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
    + for(var j = 0; j < 4; j++)
    + {
    + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
    + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    + }
    + }
    + return str;
    +}

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/all_docs.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/all_docs.js b/test/javascript/tests/all_docs.js
    new file mode 100644
    index 0000000..66ad880
    --- /dev/null
    +++ b/test/javascript/tests/all_docs.js
    @@ -0,0 +1,142 @@
    +// 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.
    +
    +couchTests.all_docs = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // Create some more documents.
    + // Notice the use of the ok member on the return result.
    + T(db.save({_id:"0",a:1,b:1}).ok);
    + T(db.save({_id:"3",a:4,b:16}).ok);
    + T(db.save({_id:"1",a:2,b:4}).ok);
    + T(db.save({_id:"2",a:3,b:9}).ok);
    +
    + // Check the all docs
    + var results = db.allDocs();
    + var rows = results.rows;
    +
    + T(results.total_rows == results.rows.length);
    +
    + for(var i=0; i < rows.length; i++) {
    + T(rows[i].id >= "0" && rows[i].id <= "4");
    + }
    +
    + // Check _all_docs with descending=true
    + var desc = db.allDocs({descending:true});
    + T(desc.total_rows == desc.rows.length);
    +
    + // Check _all_docs offset
    + var all = db.allDocs({startkey:"2"});
    + T(all.offset == 2);
    +
    + // Confirm that queries may assume raw collation.
    + var raw = db.allDocs({ startkey: "org.couchdb.user:",
    + endkey : "org.couchdb.user;"
    + });
    + TEquals(0, raw.rows.length);
    +
    + // check that the docs show up in the seq view in the order they were created
    + var changes = db.changes();
    + var ids = ["0","3","1","2"];
    + for (var i=0; i < changes.results.length; i++) {
    + var row = changes.results[i];
    + T(row.id == ids[i], "seq order");
    + };
    +
    + // it should work in reverse as well
    + changes = db.changes({descending:true});
    + ids = ["2","1","3","0"];
    + for (var i=0; i < changes.results.length; i++) {
    + var row = changes.results[i];
    + T(row.id == ids[i], "descending=true");
    + };
    +
    + // check that deletions also show up right
    + var doc1 = db.open("1");
    + var deleted = db.deleteDoc(doc1);
    + T(deleted.ok);
    + changes = db.changes();
    + // the deletion should make doc id 1 have the last seq num
    + T(changes.results.length == 4);
    + T(changes.results[3].id == "1");
    + T(changes.results[3].deleted);
    +
    + // do an update
    + var doc2 = db.open("3");
    + doc2.updated = "totally";
    + db.save(doc2);
    + changes = db.changes();
    +
    + // the update should make doc id 3 have the last seq num
    + T(changes.results.length == 4);
    + T(changes.results[3].id == "3");
    +
    + // ok now lets see what happens with include docs
    + changes = db.changes({include_docs: true});
    + T(changes.results.length == 4);
    + T(changes.results[3].id == "3");
    + T(changes.results[3].doc.updated == "totally");
    +
    + T(changes.results[2].doc);
    + T(changes.results[2].doc._deleted);
    +
    + rows = db.allDocs({include_docs: true}, ["1"]).rows;
    + TEquals(1, rows.length);
    + TEquals("1", rows[0].key);
    + TEquals("1", rows[0].id);
    + TEquals(true, rows[0].value.deleted);
    + TEquals(null, rows[0].doc);
    +
    + // add conflicts
    + var conflictDoc1 = {
    + _id: "3", _rev: "2-aa01552213fafa022e6167113ed01087", value: "X"
    + };
    + var conflictDoc2 = {
    + _id: "3", _rev: "2-ff01552213fafa022e6167113ed01087", value: "Z"
    + };
    + T(db.save(conflictDoc1, {new_edits: false}));
    + T(db.save(conflictDoc2, {new_edits: false}));
    +
    + var winRev = db.open("3");
    +
    + changes = db.changes({include_docs: true, conflicts: true, style: "all_docs"});
    + TEquals("3", changes.results[3].id);
    + TEquals(3, changes.results[3].changes.length);
    + TEquals(winRev._rev, changes.results[3].changes[0].rev);
    + TEquals("3", changes.results[3].doc._id);
    + TEquals(winRev._rev, changes.results[3].doc._rev);
    + TEquals(true, changes.results[3].doc._conflicts instanceof Array);
    + TEquals(2, changes.results[3].doc._conflicts.length);
    +
    + rows = db.allDocs({include_docs: true, conflicts: true}).rows;
    + TEquals(3, rows.length);
    + TEquals("3", rows[2].key);
    + TEquals("3", rows[2].id);
    + TEquals(winRev._rev, rows[2].value.rev);
    + TEquals(winRev._rev, rows[2].doc._rev);
    + TEquals("3", rows[2].doc._id);
    + TEquals(true, rows[2].doc._conflicts instanceof Array);
    + TEquals(2, rows[2].doc._conflicts.length);
    +
    + // test the all docs collates sanely
    + db.save({_id: "Z", foo: "Z"});
    + db.save({_id: "a", foo: "a"});
    +
    + var rows = db.allDocs({startkey: "Z", endkey: "Z"}).rows;
    + T(rows.length == 1);
    +
    + // cleanup
    + db.deleteDb();
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/attachment_names.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/attachment_names.js b/test/javascript/tests/attachment_names.js
    new file mode 100644
    index 0000000..c9a5fcc
    --- /dev/null
    +++ b/test/javascript/tests/attachment_names.js
    @@ -0,0 +1,96 @@
    +// 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.
    +
    +couchTests.attachment_names = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var goodDoc = {
    + _id: "good_doc",
    + _attachments: {
    + "�š�¾�»Ñ��½.txt": {
    + content_type:"application/octet-stream",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    +
    + var save_response = db.save(goodDoc);
    + T(save_response.ok);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/good_doc/�š�¾�»Ñ��½.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.getResponseHeader("Content-Type") == "application/octet-stream");
    + TEquals("\"aEI7pOYCRBLTRQvvqYrrJQ==\"", xhr.getResponseHeader("Etag"));
    +
    + var binAttDoc = {
    + _id: "bin_doc",
    + _attachments:{
    + "foo\x80txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    +
    + // inline attachments
    + resp = db.save(binAttDoc);
    + TEquals(true, resp.ok, "attachment_name: inline attachment");
    +
    +
    + // standalone docs
    + var bin_data = "JHAPDO*AU£PN ){(3u[d 93DQ9¡€])} ææøo'∂ƒæ≤çæ�€�€â€¢Â¥âˆ«Â¶Â®#†�€Â¶Â®Â¥�€â‚¬ÂªÂ®Ë™�€8np";
    +
    +
    + var xhr = (CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment\x80txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:bin_data
    + }));
    +
    + var resp = JSON.parse(xhr.responseText);
    + TEquals(201, xhr.status, "attachment_name: standalone API");
    + TEquals(true, resp.ok, "attachment_name: standalone API");
    +
    + // bulk docs
    + var docs = { docs: [binAttDoc] };
    +
    + var xhr = CouchDB.request("POST", "/test_suite_db/_bulk_docs", {
    + body: JSON.stringify(docs)
    + });
    +
    + TEquals(201, xhr.status, "attachment_name: bulk docs");
    +
    +
    + // leading underscores
    + var binAttDoc = {
    + _id: "bin_doc2",
    + _attachments:{
    + "_foo.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    +
    + try {
    + db.save(binAttDoc);
    + TEquals(1, 2, "Attachment name with leading underscore saved. Should never show!");
    + } catch (e) {
    + TEquals("bad_request", e.error, "attachment_name: leading underscore");
    + TEquals("Attachment name can't start with '_'", e.reason, "attachment_name: leading underscore");
    + }
    +
    + // todo: form uploads, waiting for cmlenz' test case for form uploads
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/attachment_paths.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/attachment_paths.js b/test/javascript/tests/attachment_paths.js
    new file mode 100644
    index 0000000..3f6ffb7
    --- /dev/null
    +++ b/test/javascript/tests/attachment_paths.js
    @@ -0,0 +1,153 @@
    +// 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.
    +
    +couchTests.attachment_paths = function(debug) {
    + if (debug) debugger;
    + var dbNames = ["test_suite_db", "test_suite_db/with_slashes"];
    + for (var i=0; i < dbNames.length; i++) {
    + var db = new CouchDB(dbNames[i]);
    + var dbName = encodeURIComponent(dbNames[i]);
    + db.deleteDb();
    + db.createDb();
    +
    + // first just save a regular doc with an attachment that has a slash in the url.
    + // (also gonna run an encoding check case)
    + var binAttDoc = {
    + _id: "bin_doc",
    + _attachments:{
    + "foo/bar.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + },
    + "foo%2Fbaz.txt": {
    + content_type:"text/plain",
    + data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg=="
    + }
    + }
    + };
    +
    + T(db.save(binAttDoc).ok);
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo/bar.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.getResponseHeader("Content-Type") == "text/plain");
    +
    + // lets try it with an escaped attachment id...
    + // weird that it's at two urls
    + var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo%2Fbar.txt");
    + T(xhr.status == 200);
    + // xhr.responseText == "This is a base64 encoded text"
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo/baz.txt");
    + T(xhr.status == 404);
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo%252Fbaz.txt");
    + T(xhr.status == 200);
    + T(xhr.responseText == "We like percent two F.");
    +
    + // require a _rev to PUT
    + var xhr = CouchDB.request("PUT", "/"+dbName+"/bin_doc/foo/attachment.txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:"Just some text"
    + });
    + T(xhr.status == 409);
    +
    + var xhr = CouchDB.request("PUT", "/"+dbName+"/bin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, {
    + body:"This is no base64 encoded text",
    + headers:{"Content-Type": "text/plain;charset=utf-8"}
    + });
    + T(xhr.status == 201);
    + var rev = JSON.parse(xhr.responseText).rev;
    +
    + binAttDoc = db.open("bin_doc");
    +
    + T(binAttDoc._attachments["foo/bar.txt"] !== undefined);
    + T(binAttDoc._attachments["foo%2Fbaz.txt"] !== undefined);
    + T(binAttDoc._attachments["foo/bar2.txt"] !== undefined);
    + TEquals("text/plain;charset=utf-8", // thank you Safari
    + binAttDoc._attachments["foo/bar2.txt"].content_type.toLowerCase(),
    + "correct content-type"
    + );
    + T(binAttDoc._attachments["foo/bar2.txt"].length == 30);
    +
    + //// now repeat the while thing with a design doc
    +
    + // first just save a regular doc with an attachment that has a slash in the url.
    + // (also gonna run an encoding check case)
    + var binAttDoc = {
    + _id: "_design/bin_doc",
    + _attachments:{
    + "foo/bar.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + },
    + "foo%2Fbaz.txt": {
    + content_type:"text/plain",
    + data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg=="
    + }
    + }
    + };
    +
    + T(db.save(binAttDoc).ok);
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo/bar.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.getResponseHeader("Content-Type") == "text/plain");
    +
    + // lets try it with an escaped attachment id...
    + // weird that it's at two urls
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo%2Fbar.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.status == 200);
    +
    + // err, 3 urls
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design/bin_doc/foo%2Fbar.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.status == 200);
    +
    + // I mean um, 4 urls
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design/bin_doc/foo/bar.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.status == 200);
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo/baz.txt");
    + T(xhr.status == 404);
    +
    + var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo%252Fbaz.txt");
    + T(xhr.status == 200);
    + T(xhr.responseText == "We like percent two F.");
    +
    + // require a _rev to PUT
    + var xhr = CouchDB.request("PUT", "/"+dbName+"/_design%2Fbin_doc/foo/attachment.txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:"Just some text"
    + });
    + T(xhr.status == 409);
    +
    + var xhr = CouchDB.request("PUT", "/"+dbName+"/_design%2Fbin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, {
    + body:"This is no base64 encoded text",
    + headers:{"Content-Type": "text/plain;charset=utf-8"}
    + });
    + T(xhr.status == 201);
    + var rev = JSON.parse(xhr.responseText).rev;
    +
    + binAttDoc = db.open("_design/bin_doc");
    +
    + T(binAttDoc._attachments["foo/bar.txt"] !== undefined);
    + T(binAttDoc._attachments["foo/bar2.txt"] !== undefined);
    + TEquals("text/plain;charset=utf-8", // thank you Safari
    + binAttDoc._attachments["foo/bar2.txt"].content_type.toLowerCase(),
    + "correct content-type"
    + );
    + T(binAttDoc._attachments["foo/bar2.txt"].length == 30);
    + }
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/attachment_ranges.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/attachment_ranges.js b/test/javascript/tests/attachment_ranges.js
    new file mode 100644
    index 0000000..7d9afb5
    --- /dev/null
    +++ b/test/javascript/tests/attachment_ranges.js
    @@ -0,0 +1,160 @@
    +// 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 cacheBust() {
    + return "?anti-cache=" + String(Math.round(Math.random() * 1000000));
    +};
    +
    +couchTests.attachment_ranges = function(debug) {
    + var db = new CouchDB("test_suite_db", {
    + "X-Couch-Full-Commit": "false"
    + });
    + db.deleteDb();
    + db.createDb();
    +
    + if (debug) debugger;
    +
    + if((typeof window != "undefined") && window.navigator.userAgent.match(/Chrome/)) {
    + // Chrome is broken.
    + return;
    + }
    +
    + var binAttDoc = {
    + _id: "bin_doc",
    + _attachments: {
    + "foo.txt": {
    + content_type: "application/octet-stream",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    +
    + var save_response = db.save(binAttDoc);
    + T(save_response.ok);
    +
    + // Fetching the whole entity is a 206.
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=0-28"
    + }
    + });
    + TEquals(206, xhr.status, "fetch 0-28");
    + TEquals("This is a base64 encoded text", xhr.responseText);
    + TEquals("bytes 0-28/29", xhr.getResponseHeader("Content-Range"));
    + TEquals("29", xhr.getResponseHeader("Content-Length"));
    +
    + // Fetch the whole entity without an end offset is a 200.
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=0-"
    + }
    + });
    + TEquals(200, xhr.status, "fetch 0-");
    + TEquals("This is a base64 encoded text", xhr.responseText);
    + TEquals(null, xhr.getResponseHeader("Content-Range"));
    + TEquals("29", xhr.getResponseHeader("Content-Length"));
    +
    + // Even if you ask multiple times.
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=0-,0-,0-"
    + }
    + });
    + TEquals(200, xhr.status, "multiple 0-'s");
    +
    + // Badly formed range header is a 200.
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes:0-"
    + }
    + });
    + TEquals(200, xhr.status, "fetch with bad range header");
    +
    + // Fetch the end of an entity without an end offset is a 206.
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=2-"
    + }
    + });
    + TEquals(206, xhr.status, "fetch 2-");
    + TEquals("is is a base64 encoded text", xhr.responseText);
    + TEquals("bytes 2-28/29", xhr.getResponseHeader("Content-Range"));
    + TEquals("27", xhr.getResponseHeader("Content-Length"));
    +
    + // Fetch past the end of the entity is a 206
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=0-29"
    + }
    + });
    + TEquals(206, xhr.status, "fetch 0-29");
    + TEquals("bytes 0-28/29", xhr.getResponseHeader("Content-Range"));
    + TEquals("29", xhr.getResponseHeader("Content-Length"));
    +
    + // Fetch first part of entity is a 206
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=0-3"
    + }
    + });
    + TEquals(206, xhr.status, "fetch 0-3");
    + TEquals("This", xhr.responseText);
    + TEquals("4", xhr.getResponseHeader("Content-Length"));
    + TEquals("bytes 0-3/29", xhr.getResponseHeader("Content-Range"));
    +
    + // Fetch middle of entity is also a 206
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=10-15"
    + }
    + });
    + TEquals(206, xhr.status, "fetch 10-15");
    + TEquals("base64", xhr.responseText);
    + TEquals("6", xhr.getResponseHeader("Content-Length"));
    + TEquals("bytes 10-15/29", xhr.getResponseHeader("Content-Range"));
    +
    + // Fetch end of entity is also a 206
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=-3"
    + }
    + });
    + TEquals(206, xhr.status, "fetch -3");
    + TEquals("ext", xhr.responseText);
    + TEquals("3", xhr.getResponseHeader("Content-Length"));
    + TEquals("bytes 26-28/29", xhr.getResponseHeader("Content-Range"));
    +
    + // backward range is 416
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=5-3"
    + }
    + });
    + TEquals(416, xhr.status, "fetch 5-3");
    +
    + // range completely outside of entity is 416
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=300-310"
    + }
    + });
    + TEquals(416, xhr.status, "fetch 300-310");
    +
    + // We ignore a Range header with too many ranges
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt" + cacheBust(), {
    + headers: {
    + "Range": "bytes=0-1,0-1,0-1,0-1,0-1,0-1,0-1,0-1,0-1,0-1"
    + }
    + });
    + TEquals(200, xhr.status, "too many ranges");
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/attachment_views.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/attachment_views.js b/test/javascript/tests/attachment_views.js
    new file mode 100644
    index 0000000..b55aabe
    --- /dev/null
    +++ b/test/javascript/tests/attachment_views.js
    @@ -0,0 +1,140 @@
    +// 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.
    +
    +couchTests.attachment_views= function(debug) {
    +
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // count attachments in a view
    +
    + var attachmentData = "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=";
    +
    + db.bulkSave(makeDocs(0, 10));
    +
    + db.bulkSave(makeDocs(10, 20, {
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: attachmentData
    + }
    + }
    + }));
    +
    + db.bulkSave(makeDocs(20, 30, {
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: attachmentData
    + },
    + "bar.txt": {
    + content_type:"text/plain",
    + data: attachmentData
    + }
    + }
    + }));
    +
    + db.bulkSave(makeDocs(30, 40, {
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: attachmentData
    + },
    + "bar.txt": {
    + content_type:"text/plain",
    + data: attachmentData
    + },
    + "baz.txt": {
    + content_type:"text/plain",
    + data: attachmentData
    + }
    + }
    + }));
    +
    + var mapFunction = function(doc) {
    + var count = 0;
    +
    + for(var idx in doc._attachments) {
    + count = count + 1;
    + }
    +
    + emit(parseInt(doc._id), count);
    + };
    +
    + var reduceFunction = function(key, values) {
    + return sum(values);
    + };
    +
    + var result = db.query(mapFunction, reduceFunction);
    +
    + T(result.rows.length == 1);
    + T(result.rows[0].value == 60);
    +
    + var result = db.query(mapFunction, reduceFunction, {
    + startkey:10,
    + endkey:19
    + });
    +
    + T(result.rows.length == 1);
    + T(result.rows[0].value == 10);
    +
    + var result = db.query(mapFunction, reduceFunction, {
    + startkey:20,
    + endkey:29
    + });
    +
    + T(result.rows.length == 1);
    + T(result.rows[0].value == 20);
    +
    + var result = db.query(mapFunction, null, {
    + startkey: 30,
    + endkey: 39,
    + include_docs: true
    + });
    +
    + T(result.rows.length == 10);
    + T(result.rows[0].value == 3);
    + T(result.rows[0].doc._attachments['baz.txt'].stub === true);
    + T(result.rows[0].doc._attachments['baz.txt'].data === undefined);
    + T(result.rows[0].doc._attachments['baz.txt'].encoding === undefined);
    + T(result.rows[0].doc._attachments['baz.txt'].encoded_length === undefined);
    +
    + var result = db.query(mapFunction, null, {
    + startkey: 30,
    + endkey: 39,
    + include_docs: true,
    + attachments: true
    + });
    +
    + T(result.rows.length == 10);
    + T(result.rows[0].value == 3);
    + T(result.rows[0].doc._attachments['baz.txt'].data === attachmentData);
    + T(result.rows[0].doc._attachments['baz.txt'].stub === undefined);
    + T(result.rows[0].doc._attachments['baz.txt'].encoding === undefined);
    + T(result.rows[0].doc._attachments['baz.txt'].encoded_length === undefined);
    +
    + var result = db.query(mapFunction, null, {
    + startkey: 30,
    + endkey: 39,
    + include_docs: true,
    + att_encoding_info: true
    + });
    +
    + T(result.rows.length == 10);
    + T(result.rows[0].value == 3);
    + T(result.rows[0].doc._attachments['baz.txt'].data === undefined);
    + T(result.rows[0].doc._attachments['baz.txt'].stub === true);
    + T(result.rows[0].doc._attachments['baz.txt'].encoding === "gzip");
    + T(result.rows[0].doc._attachments['baz.txt'].encoded_length === 47);
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/attachments.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/attachments.js b/test/javascript/tests/attachments.js
    new file mode 100644
    index 0000000..2fa08ee
    --- /dev/null
    +++ b/test/javascript/tests/attachments.js
    @@ -0,0 +1,328 @@
    +// 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.
    +
    +couchTests.attachments= function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    +
    + // MD5 Digests of compressible attachments and therefore Etags
    + // will vary depending on platform gzip implementation.
    + // These MIME types are defined in [attachments] compressible_types
    + var binAttDoc = {
    + _id: "bin_doc",
    + _attachments:{
    + "foo.txt": {
    + content_type:"application/octet-stream",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    +
    + var save_response = db.save(binAttDoc);
    + T(save_response.ok);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt");
    + T(xhr.responseText == "This is a base64 encoded text");
    + T(xhr.getResponseHeader("Content-Type") == "application/octet-stream");
    + TEquals("\"aEI7pOYCRBLTRQvvqYrrJQ==\"", xhr.getResponseHeader("Etag"));
    +
    + // empty attachment
    + var binAttDoc2 = {
    + _id: "bin_doc2",
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: ""
    + }
    + }
    + }
    +
    + T(db.save(binAttDoc2).ok);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo.txt");
    + T(xhr.responseText.length == 0);
    + T(xhr.getResponseHeader("Content-Type") == "text/plain");
    +
    + // test RESTful doc API
    +
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc2/foo2.txt?rev=" + binAttDoc2._rev, {
    + body:"This is no base64 encoded text",
    + headers:{"Content-Type": "text/plain;charset=utf-8"}
    + });
    + T(xhr.status == 201);
    + TEquals("/bin_doc2/foo2.txt",
    + xhr.getResponseHeader("Location").substr(-18),
    + "should return Location header to newly created or updated attachment");
    +
    + var rev = JSON.parse(xhr.responseText).rev;
    +
    + binAttDoc2 = db.open("bin_doc2");
    +
    + T(binAttDoc2._attachments["foo.txt"] !== undefined);
    + T(binAttDoc2._attachments["foo2.txt"] !== undefined);
    + TEqualsIgnoreCase("text/plain;charset=utf-8", binAttDoc2._attachments["foo2.txt"].content_type);
    + T(binAttDoc2._attachments["foo2.txt"].length == 30);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo2.txt");
    + T(xhr.responseText == "This is no base64 encoded text");
    + TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
    +
    + // test without rev, should fail
    + var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt");
    + T(xhr.status == 409);
    +
    + // test with rev, should not fail
    + var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt?rev=" + rev);
    + T(xhr.status == 200);
    + TEquals(null, xhr.getResponseHeader("Location"),
    + "should not return Location header on DELETE request");
    +
    + // test binary data
    + var bin_data = "JHAPDO*AU£PN ){(3u[d 93DQ9¡€])} ææøo'∂ƒæ≤çæ�€�€â€¢Â¥âˆ«Â¶Â®#†�€Â¶Â®Â¥�€â‚¬ÂªÂ®Ë™�€8np";
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:bin_data
    + });
    + T(xhr.status == 201);
    + var rev = JSON.parse(xhr.responseText).rev;
    + TEquals('"' + rev + '"', xhr.getResponseHeader("Etag"));
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt");
    + T(xhr.responseText == bin_data);
    + TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
    +
    + // without rev
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:bin_data
    + });
    + T(xhr.status == 409);
    +
    + // with nonexistent rev
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt" + "?rev=1-adae8575ecea588919bd08eb020c708e", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:bin_data
    + });
    + T(xhr.status == 409);
    +
    + // with current rev
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev, {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:bin_data
    + });
    + T(xhr.status == 201);
    + var rev = JSON.parse(xhr.responseText).rev;
    + TEquals('"' + rev + '"', xhr.getResponseHeader("Etag"));
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt");
    + T(xhr.responseText == bin_data);
    + TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev);
    + T(xhr.responseText == bin_data);
    + TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
    +
    + var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev);
    + T(xhr.status == 200);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt");
    + T(xhr.status == 404);
    +
    + // deleted attachment is still accessible with revision
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev);
    + T(xhr.status == 200);
    + T(xhr.responseText == bin_data);
    + TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
    +
    + // empty attachments
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:""
    + });
    + T(xhr.status == 201);
    + var rev = JSON.parse(xhr.responseText).rev;
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc4/attachment.txt");
    + T(xhr.status == 200);
    + T(xhr.responseText.length == 0);
    +
    + // overwrite previsously empty attachment
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt?rev=" + rev, {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:"This is a string"
    + });
    + T(xhr.status == 201);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc4/attachment.txt");
    + T(xhr.status == 200);
    + T(xhr.responseText == "This is a string");
    +
    + // Attachment sparseness COUCHDB-220
    +
    + var docs = [];
    + for (var i = 0; i < 5; i++) {
    + var doc = {
    + _id: (i).toString(),
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    + docs.push(doc);
    + }
    +
    + var saved = db.bulkSave(docs);
    + // now delete the docs, and while we are looping over them, remove the
    + // '_rev' field so we can re-create after deletion.
    + var to_up = [];
    + for (i=0;i<saved.length;i++) {
    + to_up.push({'_id': saved[i]['id'], '_rev': saved[i]['rev'], '_deleted': true});
    + delete docs[i]._rev;
    + }
    + // delete them.
    + var saved2 = db.bulkSave(to_up);
    + // re-create them
    + var saved3 = db.bulkSave(docs);
    +
    + var before = db.info().disk_size;
    +
    + // Compact it.
    + T(db.compact().ok);
    + T(db.last_req.status == 202);
    + // compaction isn't instantaneous, loop until done
    + while (db.info().compact_running) {};
    +
    + var after = db.info().disk_size;
    +
    + // Compaction should reduce the database slightly, but not
    + // orders of magnitude (unless attachments introduce sparseness)
    + T(after > before * 0.1, "before: " + before + " after: " + after);
    +
    +
    + // test large attachments - COUCHDB-366
    + var lorem = CouchDB.request("GET", "/_utils/script/test/lorem.txt").responseText;
    +
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc5/lorem.txt", {
    + headers:{"Content-Type":"text/plain;charset=utf-8"},
    + body:lorem
    + });
    + T(xhr.status == 201);
    + var rev = JSON.parse(xhr.responseText).rev;
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc5/lorem.txt");
    + T(xhr.responseText == lorem);
    + TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
    +
    + // test large inline attachment too
    + var lorem_b64 = CouchDB.request("GET", "/_utils/script/test/lorem_b64.txt").responseText;
    + var doc = db.open("bin_doc5", {attachments:true});
    + T(doc._attachments["lorem.txt"].data == lorem_b64);
    +
    + // test etags for attachments.
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc5/lorem.txt");
    + T(xhr.status == 200);
    + var etag = xhr.getResponseHeader("etag");
    + xhr = CouchDB.request("GET", "/test_suite_db/bin_doc5/lorem.txt", {
    + headers: {"if-none-match": etag}
    + });
    + T(xhr.status == 304);
    +
    + // test COUCHDB-497 - empty attachments
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc5/empty.txt?rev="+rev, {
    + headers:{"Content-Type":"text/plain;charset=utf-8", "Content-Length": "0"},
    + body:""
    + });
    + TEquals(201, xhr.status, "should send 201 Accepted");
    + var rev = JSON.parse(xhr.responseText).rev;
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc5/empty.txt?rev="+rev, {
    + headers:{"Content-Type":"text/plain;charset=utf-8"}
    + });
    + TEquals(201, xhr.status, "should send 201 Accepted");
    +
    + // implicit doc creation allows creating docs with a reserved id. COUCHDB-565
    + var xhr = CouchDB.request("PUT", "/test_suite_db/_nonexistant/attachment.txt", {
    + headers: {"Content-Type":"text/plain;charset=utf-8"},
    + body: "THIS IS AN ATTACHMENT. BOOYA!"
    + });
    + TEquals(400, xhr.status, "should return error code 400 Bad Request");
    +
    + // test COUCHDB-809 - stubs should only require the 'stub' field
    + var bin_doc6 = {
    + _id: "bin_doc6",
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    + T(db.save(bin_doc6).ok);
    + // stub out the attachment
    + bin_doc6._attachments["foo.txt"] = { stub: true };
    + T(db.save(bin_doc6).ok == true);
    +
    + // wrong rev pos specified
    +
    + // stub out the attachment with the wrong revpos
    + bin_doc6._attachments["foo.txt"] = { stub: true, revpos: 10};
    + try {
    + T(db.save(bin_doc6).ok == true);
    + T(false && "Shouldn't get here!");
    + } catch (e) {
    + T(e.error == "missing_stub");
    + }
    +
    + // test MD5 header
    + var bin_data = "foo bar"
    + var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc7/attachment.txt", {
    + headers:{"Content-Type":"application/octet-stream",
    + "Content-MD5":"MntvB0NYESObxH4VRDUycw=="},
    + body:bin_data
    + });
    + TEquals(201, xhr.status);
    +
    + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc7/attachment.txt");
    + TEquals('MntvB0NYESObxH4VRDUycw==', xhr.getResponseHeader("Content-MD5"));
    +
    + // test attachment via multipart/form-data
    + var bin_doc8 = {
    + _id: "bin_doc8"
    + };
    + T(db.save(bin_doc8).ok);
    + var doc = db.open("bin_doc8");
    + var body = "------TF\r\n" +
    + "Content-Disposition: form-data; name=\"_rev\"\r\n\r\n" +
    + doc._rev + "\r\n" +
    + "------TF\r\n" +
    + "Content-Disposition: form-data; name=\"_attachments\"; filename=\"file.txt\"\r\n" +
    + "Content-Type: text/plain\r\n\r\n" +
    + "contents of file.txt\r\n\r\n" +
    + "------TF--"
    + xhr = CouchDB.request("POST", "/test_suite_db/bin_doc8", {
    + headers: {
    + "Content-Type": "multipart/form-data; boundary=----TF",
    + "Content-Length": body.length
    + },
    + body: body
    + });
    + TEquals(201, xhr.status);
    + TEquals(true, JSON.parse(xhr.responseText).ok);
    + var doc = db.open("bin_doc8");
    + T(doc._attachments);
    + T(doc._attachments['file.txt']);
    +
    +};
  • Kxepal at Dec 10, 2014 at 11:08 am
    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/recreate_doc.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/recreate_doc.js b/test/javascript/tests/recreate_doc.js
    new file mode 100644
    index 0000000..f972379
    --- /dev/null
    +++ b/test/javascript/tests/recreate_doc.js
    @@ -0,0 +1,145 @@
    +// 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.
    +
    +couchTests.recreate_doc = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + // First create a new document with the ID "foo", and delete it again
    + var doc = {_id: "foo", a: "bar", b: 42};
    + var result = db.save(doc);
    + T(result.ok);
    + var firstRev = result.rev;
    + T(db.deleteDoc(doc).ok);
    +
    + // Now create a new document with the same ID, save it, and then modify it
    + for (var i = 0; i < 10; i++) {
    + doc = {_id: "foo"};
    + T(db.save(doc).ok);
    + doc = db.open("foo");
    + doc.a = "baz";
    + T(db.save(doc).ok);
    + T(db.deleteDoc(doc).rev != undefined);
    + }
    +
    + try {
    + // COUCHDB-292 now attempt to save the document with a prev that's since
    + // been deleted and this should generate a conflict exception
    + db.save({_id:"foo", _rev:firstRev, bar:1});
    + T("no save conflict 1" && false); // we shouldn't hit here
    + } catch (e) {
    + T(e.error == "conflict");
    + }
    +
    + var binAttDoc = {
    + _id: "foo",
    + _rev:firstRev,
    + _attachments:{
    + "foo.txt": {
    + content_type:"text/plain",
    + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
    + }
    + }
    + };
    + try {
    + // same as before, but with binary
    + db.save(binAttDoc);
    + T("no save conflict 2" && false); // we shouldn't hit here
    + } catch (e) {
    + T(e.error == "conflict");
    + }
    +
    +
    + try {
    + // random non-existant prev rev
    + db.save({_id:"foo", _rev:"1-asfafasdf", bar:1});
    + T("no save conflict 3" && false); // we shouldn't hit here
    + } catch (e) {
    + T(e.error == "conflict");
    + }
    +
    + try {
    + // random non-existant prev rev with bin
    + binAttDoc._rev = "1-aasasfasdf";
    + db.save(binAttDoc);
    + T("no save conflict 4" && false); // we shouldn't hit here
    + } catch (e) {
    + T(e.error == "conflict");
    + }
    +
    + db.deleteDb();
    + db.createDb();
    +
    + // Helper function to create a doc with multiple revisions
    + // that are compacted away to ?REV_MISSING.
    +
    + var createDoc = function(docid) {
    + var ret = [{_id: docid, count: 0}];
    + T(db.save(ret[0]).ok);
    + for(var i = 0; i < 2; i++) {
    + ret[ret.length] = {
    + _id: docid,
    + _rev: ret[ret.length-1]._rev,
    + count: ret[ret.length-1].count+1
    + };
    + T(db.save(ret[ret.length-1]).ok);
    + }
    + db.compact();
    + while(db.info().compact_running) {}
    + return ret;
    + }
    +
    + // Helper function to check that there are no duplicates
    + // in the changes feed and that it has proper update
    + // sequence ordering.
    +
    + var checkChanges = function() {
    + // Assert that there are no duplicates in _changes.
    + var req = CouchDB.request("GET", "/test_suite_db/_changes");
    + var resp = JSON.parse(req.responseText);
    + var docids = {};
    + var prev_seq = -1;
    + for(var i = 0; i < resp.results.length; i++) {
    + row = resp.results[i];
    + T(row.seq > prev_seq, "Unordered _changes feed.");
    + T(docids[row.id] === undefined, "Duplicates in _changes feed.");
    + prev_seq = row.seq;
    + docids[row.id] = true;
    + }
    + };
    +
    + // COUCHDB-1265 - Check that the changes feed remains proper
    + // after we try and break the update_seq tree.
    +
    + // This first case is the one originally reported and "fixed"
    + // in COUCHDB-1265. Reinserting an old revision into the
    + // revision tree causes duplicates in the update_seq tree.
    +
    + var revs = createDoc("a");
    + T(db.save(revs[1], {new_edits: false}).ok);
    + T(db.save(revs[revs.length-1]).ok);
    + checkChanges();
    +
    + // The original fix for COUCHDB-1265 is not entirely correct
    + // as it didn't consider the possibility that a compaction
    + // might run after the original tree screw up.
    +
    + revs = createDoc("b");
    + T(db.save(revs[1], {new_edits: false}).ok);
    + db.compact();
    + while(db.info().compact_running) {}
    + T(db.save(revs[revs.length-1]).ok);
    + checkChanges();
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/reduce.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/reduce.js b/test/javascript/tests/reduce.js
    new file mode 100644
    index 0000000..36e5cb7
    --- /dev/null
    +++ b/test/javascript/tests/reduce.js
    @@ -0,0 +1,414 @@
    +// 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.
    +
    +couchTests.reduce = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    + var numDocs = 500;
    + var docs = makeDocs(1,numDocs + 1);
    + db.bulkSave(docs);
    + var summate = function(N) {return (N+1)*N/2;};
    +
    + var map = function (doc) {
    + emit(doc.integer, doc.integer);
    + emit(doc.integer, doc.integer);
    + };
    + var reduce = function (keys, values) { return sum(values); };
    + var result = db.query(map, reduce);
    + T(result.rows[0].value == 2*summate(numDocs));
    +
    + result = db.query(map, reduce, {startkey: 4, endkey: 4});
    + T(result.rows[0].value == 8);
    +
    + result = db.query(map, reduce, {startkey: 4, endkey: 5});
    + T(result.rows[0].value == 18);
    +
    + result = db.query(map, reduce, {startkey: 4, endkey: 6});
    + T(result.rows[0].value == 30);
    +
    + result = db.query(map, reduce, {group:true, limit:3});
    + T(result.rows[0].value == 2);
    + T(result.rows[1].value == 4);
    + T(result.rows[2].value == 6);
    +
    + for(var i=1; i<numDocs/2; i+=30) {
    + result = db.query(map, reduce, {startkey: i, endkey: numDocs - i});
    + T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
    + }
    +
    + db.deleteDb();
    + db.createDb();
    +
    + for(var i=1; i <= 5; i++) {
    +
    + for(var j=0; j < 10; j++) {
    + // these docs are in the order of the keys collation, for clarity
    + var docs = [];
    + docs.push({keys:["a"]});
    + docs.push({keys:["a"]});
    + docs.push({keys:["a", "b"]});
    + docs.push({keys:["a", "b"]});
    + docs.push({keys:["a", "b", "c"]});
    + docs.push({keys:["a", "b", "d"]});
    + docs.push({keys:["a", "c", "d"]});
    + docs.push({keys:["d"]});
    + docs.push({keys:["d", "a"]});
    + docs.push({keys:["d", "b"]});
    + docs.push({keys:["d", "c"]});
    + db.bulkSave(docs);
    + T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
    + }
    +
    + map = function (doc) { emit(doc.keys, 1); };
    + reduce = function (keys, values) { return sum(values); };
    +
    + var results = db.query(map, reduce, {group:true});
    +
    + //group by exact key match
    + T(equals(results.rows[0], {key:["a"],value:20*i}));
    + T(equals(results.rows[1], {key:["a","b"],value:20*i}));
    + T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i}));
    + T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i}));
    +
    + // test to make sure group reduce and limit params provide valid json
    + var results = db.query(map, reduce, {group: true, limit: 2});
    + T(equals(results.rows[0], {key: ["a"], value: 20*i}));
    + T(equals(results.rows.length, 2));
    +
    + //group by the first element in the key array
    + var results = db.query(map, reduce, {group_level:1});
    + T(equals(results.rows[0], {key:["a"],value:70*i}));
    + T(equals(results.rows[1], {key:["d"],value:40*i}));
    +
    + //group by the first 2 elements in the key array
    + var results = db.query(map, reduce, {group_level:2});
    + T(equals(results.rows[0], {key:["a"],value:20*i}));
    + T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    + T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    + T(equals(results.rows[3], {key:["d"],value:10*i}));
    + T(equals(results.rows[4], {key:["d","a"],value:10*i}));
    + T(equals(results.rows[5], {key:["d","b"],value:10*i}));
    + T(equals(results.rows[6], {key:["d","c"],value:10*i}));
    +
    + // endkey test with inclusive_end=true
    + var results = db.query(map, reduce, {group_level:2,endkey:["d"],inclusive_end:true});
    + T(equals(results.rows[0], {key:["a"],value:20*i}));
    + T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    + T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    + T(equals(results.rows[3], {key:["d"],value:10*i}));
    + TEquals(4, results.rows.length);
    +
    + // endkey test with inclusive_end=false
    + var results = db.query(map, reduce, {group_level:2,endkey:["d"],inclusive_end:false});
    + T(equals(results.rows[0], {key:["a"],value:20*i}));
    + T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    + T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    + TEquals(3, results.rows.length);
    + }
    +
    + // now test out more complex reductions that need to use the combine option.
    +
    + db.deleteDb();
    + db.createDb();
    +
    +
    + var map = function (doc) { emit(doc.val, doc.val); };
    + var reduceCombine = function (keys, values, rereduce) {
    + // This computes the standard deviation of the mapped results
    + var stdDeviation=0.0;
    + var count=0;
    + var total=0.0;
    + var sqrTotal=0.0;
    +
    + if (!rereduce) {
    + // This is the reduce phase, we are reducing over emitted values from
    + // the map functions.
    + for(var i in values) {
    + total = total + values[i];
    + sqrTotal = sqrTotal + (values[i] * values[i]);
    + }
    + count = values.length;
    + }
    + else {
    + // This is the rereduce phase, we are re-reducing previosuly
    + // reduced values.
    + for(var i in values) {
    + count = count + values[i].count;
    + total = total + values[i].total;
    + sqrTotal = sqrTotal + values[i].sqrTotal;
    + }
    + }
    +
    + var variance = (sqrTotal - ((total * total)/count)) / count;
    + stdDeviation = Math.sqrt(variance);
    +
    + // the reduce result. It contains enough information to be rereduced
    + // with other reduce results.
    + return {"stdDeviation":stdDeviation,"count":count,
    + "total":total,"sqrTotal":sqrTotal};
    + };
    +
    + // Save a bunch a docs.
    +
    + for(var i=0; i < 10; i++) {
    + var docs = [];
    + docs.push({val:10});
    + docs.push({val:20});
    + docs.push({val:30});
    + docs.push({val:40});
    + docs.push({val:50});
    + docs.push({val:60});
    + docs.push({val:70});
    + docs.push({val:80});
    + docs.push({val:90});
    + docs.push({val:100});
    + db.bulkSave(docs);
    + }
    +
    + var results = db.query(map, reduceCombine);
    +
    + var difference = results.rows[0].value.stdDeviation - 28.722813232690143;
    + // account for floating point rounding error
    + T(Math.abs(difference) < 0.0000000001);
    +
    + function testReducePagination() {
    + var ddoc = {
    + "_id": "_design/test",
    + "language": "javascript",
    + "views": {
    + "view1": {
    + "map": "function(doc) {" +
    + "emit(doc.int, doc._id);" +
    + "emit(doc.int + 1, doc._id);" +
    + "emit(doc.int + 2, doc._id);" +
    + "}",
    + "reduce": "_count"
    + }
    + }
    + };
    + var result, docs = [];
    +
    + function randVal() {
    + return Math.random() * 100000000;
    + }
    +
    + db.deleteDb();
    + db.createDb();
    +
    + for (var i = 0; i < 1123; i++) {
    + docs.push({"_id": String(i), "int": i});
    + }
    + db.bulkSave(docs.concat([ddoc]));
    +
    + // ?group=false tests
    + result = db.view('test/view1', {startkey: 400, endkey: 402, foobar: randVal()});
    + TEquals(9, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 402, endkey: 400, descending: true,
    + foobar: randVal()});
    + TEquals(9, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 400, endkey: 402, inclusive_end: false,
    + foobar: randVal()});
    + TEquals(6, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 402, endkey: 400, inclusive_end: false,
    + descending: true, foobar: randVal()});
    + TEquals(6, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "400",
    + foobar: randVal()});
    + TEquals(7, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "400",
    + inclusive_end: false, foobar: randVal()});
    + TEquals(6, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "401",
    + foobar: randVal()});
    + TEquals(8, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "401",
    + inclusive_end: false, foobar: randVal()});
    + TEquals(7, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "402",
    + foobar: randVal()});
    + TEquals(9, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 400, endkey: 402, endkey_docid: "402",
    + inclusive_end: false, foobar: randVal()});
    + TEquals(8, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "398",
    + descending: true, foobar: randVal()});
    + TEquals(9, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "398",
    + descending: true, inclusive_end: false, foobar: randVal()}),
    + TEquals(8, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "399",
    + descending: true, foobar: randVal()});
    + TEquals(8, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "399",
    + descending: true, inclusive_end: false, foobar: randVal()}),
    + TEquals(7, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "400",
    + descending: true, foobar: randVal()}),
    + TEquals(7, result.rows[0].value);
    + result = db.view('test/view1', {startkey: 402, endkey: 400, endkey_docid: "400",
    + descending: true, inclusive_end: false, foobar: randVal()}),
    + TEquals(6, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 402, startkey_docid: "400", endkey: 400,
    + descending: true, foobar: randVal()});
    + TEquals(7, result.rows[0].value);
    +
    + result = db.view('test/view1', {startkey: 402, startkey_docid: "401", endkey: 400,
    + descending: true, inclusive_end: false, foobar: randVal()});
    + TEquals(5, result.rows[0].value);
    +
    + // ?group=true tests
    + result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    + foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(400, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(402, result.rows[2].key);
    + TEquals(3, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    + descending: true, foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(400, result.rows[2].key);
    + TEquals(3, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    + inclusive_end: false, foobar: randVal()});
    + TEquals(2, result.rows.length);
    + TEquals(400, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    + descending: true, inclusive_end: false, foobar: randVal()});
    + TEquals(2, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    + endkey_docid: "401", foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(400, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(402, result.rows[2].key);
    + TEquals(2, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 400, endkey: 402,
    + endkey_docid: "400", foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(400, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(402, result.rows[2].key);
    + TEquals(1, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "401",
    + endkey: 400, descending: true, foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(2, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(400, result.rows[2].key);
    + TEquals(3, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "400",
    + endkey: 400, descending: true, foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(1, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(400, result.rows[2].key);
    + TEquals(3, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "401",
    + endkey: 400, descending: true, inclusive_end: false, foobar: randVal()});
    + TEquals(2, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(2, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, startkey_docid: "400",
    + endkey: 400, descending: true, inclusive_end: false, foobar: randVal()});
    + TEquals(2, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(1, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    + endkey_docid: "398", descending: true, inclusive_end: true, foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(400, result.rows[2].key);
    + TEquals(3, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    + endkey_docid: "399", descending: true, inclusive_end: true, foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(400, result.rows[2].key);
    + TEquals(2, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    + endkey_docid: "399", descending: true, inclusive_end: false, foobar: randVal()});
    + TEquals(3, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    + TEquals(400, result.rows[2].key);
    + TEquals(1, result.rows[2].value);
    +
    + result = db.view('test/view1', {group: true, startkey: 402, endkey: 400,
    + endkey_docid: "400", descending: true, inclusive_end: false, foobar: randVal()});
    + TEquals(2, result.rows.length);
    + TEquals(402, result.rows[0].key);
    + TEquals(3, result.rows[0].value);
    + TEquals(401, result.rows[1].key);
    + TEquals(3, result.rows[1].value);
    +
    + db.deleteDb();
    + }
    +
    + testReducePagination();
    +
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/reduce_builtin.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/reduce_builtin.js b/test/javascript/tests/reduce_builtin.js
    new file mode 100644
    index 0000000..b3cc3cc
    --- /dev/null
    +++ b/test/javascript/tests/reduce_builtin.js
    @@ -0,0 +1,179 @@
    +// 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.
    +
    +couchTests.reduce_builtin = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var numDocs = 500;
    + var docs = makeDocs(1,numDocs + 1);
    + db.bulkSave(docs);
    +
    + var summate = function(N) {return (N+1)*N/2;};
    +
    + var sumsqr = function(N) {
    + var acc = 0;
    + for (var i=1; i<=N; ++i) {
    + acc += i*i;
    + }
    + return acc;
    + };
    +
    + // this is the same test as the reduce.js test
    + // only we'll let CouchDB run reduce in Erlang
    + var map = function (doc) {
    + emit(doc.integer, doc.integer);
    + emit(doc.integer, doc.integer);
    + };
    +
    + var result = db.query(map, "_sum");
    + T(result.rows[0].value == 2*summate(numDocs));
    + result = db.query(map, "_count");
    + T(result.rows[0].value == 1000);
    + result = db.query(map, "_stats");
    + T(result.rows[0].value.sum == 2*summate(numDocs));
    + T(result.rows[0].value.count == 1000);
    + T(result.rows[0].value.min == 1);
    + T(result.rows[0].value.max == 500);
    + T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
    +
    + result = db.query(map, "_sum", {startkey: 4, endkey: 4});
    + T(result.rows[0].value == 8);
    + result = db.query(map, "_count", {startkey: 4, endkey: 4});
    + T(result.rows[0].value == 2);
    +
    + result = db.query(map, "_sum", {startkey: 4, endkey: 5});
    + T(result.rows[0].value == 18);
    + result = db.query(map, "_count", {startkey: 4, endkey: 5});
    + T(result.rows[0].value == 4);
    +
    + result = db.query(map, "_sum", {startkey: 4, endkey: 6});
    + T(result.rows[0].value == 30);
    + result = db.query(map, "_count", {startkey: 4, endkey: 6});
    + T(result.rows[0].value == 6);
    +
    + result = db.query(map, "_sum", {group:true, limit:3});
    + T(result.rows[0].value == 2);
    + T(result.rows[1].value == 4);
    + T(result.rows[2].value == 6);
    +
    + for(var i=1; i<numDocs/2; i+=30) {
    + result = db.query(map, "_sum", {startkey: i, endkey: numDocs - i});
    + T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
    + }
    +
    + // test for trailing characters after builtin functions, desired behaviour
    + // is to disregard any trailing characters
    + // I think the behavior should be a prefix test, so that even "_statsorama"
    + // or "_stats\nare\awesome" should work just as "_stats" does. - JChris
    +
    + var trailing = ["\u000a", "orama", "\nare\nawesome", " ", " \n "];
    +
    + for(var i=0; i < trailing.length; i++) {
    + result = db.query(map, "_sum" + trailing[i]);
    + T(result.rows[0].value == 2*summate(numDocs));
    + result = db.query(map, "_count" + trailing[i]);
    + T(result.rows[0].value == 1000);
    + result = db.query(map, "_stats" + trailing[i]);
    + T(result.rows[0].value.sum == 2*summate(numDocs));
    + T(result.rows[0].value.count == 1000);
    + T(result.rows[0].value.min == 1);
    + T(result.rows[0].value.max == 500);
    + T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
    + }
    +
    + db.deleteDb();
    + db.createDb();
    +
    + for(var i=1; i <= 5; i++) {
    +
    + for(var j=0; j < 10; j++) {
    + // these docs are in the order of the keys collation, for clarity
    + var docs = [];
    + docs.push({keys:["a"]});
    + docs.push({keys:["a"]});
    + docs.push({keys:["a", "b"]});
    + docs.push({keys:["a", "b"]});
    + docs.push({keys:["a", "b", "c"]});
    + docs.push({keys:["a", "b", "d"]});
    + docs.push({keys:["a", "c", "d"]});
    + docs.push({keys:["d"]});
    + docs.push({keys:["d", "a"]});
    + docs.push({keys:["d", "b"]});
    + docs.push({keys:["d", "c"]});
    + db.bulkSave(docs);
    + T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
    + }
    +
    + map = function (doc) { emit(doc.keys, 1); };
    + // with emitted values being 1, count should be the same as sum
    + var builtins = ["_sum", "_count"];
    +
    + for (var b=0; b < builtins.length; b++) {
    + var fun = builtins[b];
    + var results = db.query(map, fun, {group:true});
    +
    + //group by exact key match
    + T(equals(results.rows[0], {key:["a"],value:20*i}));
    + T(equals(results.rows[1], {key:["a","b"],value:20*i}));
    + T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i}));
    + T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i}));
    +
    + // test to make sure group reduce and limit params provide valid json
    + var results = db.query(map, fun, {group: true, limit: 2});
    + T(equals(results.rows[0], {key: ["a"], value: 20*i}));
    + T(equals(results.rows.length, 2));
    +
    + //group by the first element in the key array
    + var results = db.query(map, fun, {group_level:1});
    + T(equals(results.rows[0], {key:["a"],value:70*i}));
    + T(equals(results.rows[1], {key:["d"],value:40*i}));
    +
    + //group by the first 2 elements in the key array
    + var results = db.query(map, fun, {group_level:2});
    + T(equals(results.rows[0], {key:["a"],value:20*i}));
    + T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    + T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    + T(equals(results.rows[3], {key:["d"],value:10*i}));
    + T(equals(results.rows[4], {key:["d","a"],value:10*i}));
    + T(equals(results.rows[5], {key:["d","b"],value:10*i}));
    + T(equals(results.rows[6], {key:["d","c"],value:10*i}));
    + };
    +
    + map = function (doc) { emit(doc.keys, [1, 1]); };
    +
    + var results = db.query(map, "_sum", {group:true});
    + T(equals(results.rows[0], {key:["a"],value:[20*i,20*i]}));
    + T(equals(results.rows[1], {key:["a","b"],value:[20*i,20*i]}));
    + T(equals(results.rows[2], {key:["a", "b", "c"],value:[10*i,10*i]}));
    + T(equals(results.rows[3], {key:["a", "b", "d"],value:[10*i,10*i]}));
    +
    + var results = db.query(map, "_sum", {group: true, limit: 2});
    + T(equals(results.rows[0], {key: ["a"], value: [20*i,20*i]}));
    + T(equals(results.rows.length, 2));
    +
    + var results = db.query(map, "_sum", {group_level:1});
    + T(equals(results.rows[0], {key:["a"],value:[70*i,70*i]}));
    + T(equals(results.rows[1], {key:["d"],value:[40*i,40*i]}));
    +
    + var results = db.query(map, "_sum", {group_level:2});
    + T(equals(results.rows[0], {key:["a"],value:[20*i,20*i]}));
    + T(equals(results.rows[1], {key:["a","b"],value:[40*i,40*i]}));
    + T(equals(results.rows[2], {key:["a","c"],value:[10*i,10*i]}));
    + T(equals(results.rows[3], {key:["d"],value:[10*i,10*i]}));
    + T(equals(results.rows[4], {key:["d","a"],value:[10*i,10*i]}));
    + T(equals(results.rows[5], {key:["d","b"],value:[10*i,10*i]}));
    + T(equals(results.rows[6], {key:["d","c"],value:[10*i,10*i]}));
    + }
    +}

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/reduce_false.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/reduce_false.js b/test/javascript/tests/reduce_false.js
    new file mode 100644
    index 0000000..699b258
    --- /dev/null
    +++ b/test/javascript/tests/reduce_false.js
    @@ -0,0 +1,44 @@
    +// 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.
    +
    +couchTests.reduce_false = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var numDocs = 5;
    + var docs = makeDocs(1,numDocs + 1);
    + db.bulkSave(docs);
    + var summate = function(N) {return (N+1)*N/2;};
    +
    + var designDoc = {
    + _id:"_design/test",
    + language: "javascript",
    + views: {
    + summate: {map:"function (doc) { emit(doc.integer, doc.integer); }",
    + reduce:"function (keys, values) { return sum(values); }"},
    + }
    + };
    + T(db.save(designDoc).ok);
    +
    + // Test that the reduce works
    + var res = db.view('test/summate');
    + T(res.rows.length == 1 && res.rows[0].value == summate(5));
    +
    + //Test that we get our docs back
    + res = db.view('test/summate', {reduce: false});
    + T(res.rows.length == 5);
    + for(var i=0; i<5; i++) {
    + T(res.rows[i].value == i+1);
    + }
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/reduce_false_temp.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/reduce_false_temp.js b/test/javascript/tests/reduce_false_temp.js
    new file mode 100644
    index 0000000..d45f05b
    --- /dev/null
    +++ b/test/javascript/tests/reduce_false_temp.js
    @@ -0,0 +1,37 @@
    +// 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.
    +
    +couchTests.reduce_false_temp = function(debug) {
    + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
    + db.deleteDb();
    + db.createDb();
    + if (debug) debugger;
    +
    + var numDocs = 5;
    + var docs = makeDocs(1,numDocs + 1);
    + db.bulkSave(docs);
    + var summate = function(N) {return (N+1)*N/2;};
    +
    + var mapFun = "function (doc) { emit(doc.integer, doc.integer); }";
    + var reduceFun = "function (keys, values) { return sum(values); }";
    +
    + // Test that the reduce works
    + var res = db.query(mapFun, reduceFun);
    + T(res.rows.length == 1 && res.rows[0].value == summate(5));
    +
    + //Test that we get our docs back
    + res = db.query(mapFun, reduceFun, {reduce: false});
    + T(res.rows.length == 5);
    + for(var i=0; i<5; i++) {
    + T(res.rows[i].value == i+1);
    + }
    +};

    http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/replication.js
    ----------------------------------------------------------------------
    diff --git a/test/javascript/tests/replication.js b/test/javascript/tests/replication.js
    new file mode 100644
    index 0000000..21a4304
    --- /dev/null
    +++ b/test/javascript/tests/replication.js
    @@ -0,0 +1,1795 @@
    +// 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.
    +
    +couchTests.replication = function(debug) {
    +
    + if (debug) debugger;
    +
    + var host = CouchDB.host;
    + var sourceDb = new CouchDB("test_suite_db_a",{"X-Couch-Full-Commit":"false"});
    + var targetDb = new CouchDB("test_suite_db_b",{"X-Couch-Full-Commit":"false"});
    +
    + var dbPairs = [
    + {
    + source: sourceDb.name,
    + target: targetDb.name
    + },
    + {
    + source: CouchDB.protocol + host + "/" + sourceDb.name,
    + target: targetDb.name
    + },
    + {
    + source: sourceDb.name,
    + target: CouchDB.protocol + host + "/" + targetDb.name
    + },
    + {
    + source: CouchDB.protocol + host + "/" + sourceDb.name,
    + target: CouchDB.protocol + host + "/" + targetDb.name
    + }
    + ];
    +
    + var att1_data = CouchDB.request("GET", "/_utils/script/test/lorem.txt");
    + att1_data = att1_data.responseText;
    +
    + var att2_data = CouchDB.request("GET", "/_utils/script/test/lorem_b64.txt");
    + att2_data = att2_data.responseText;
    +
    + var sourceInfo, targetInfo;
    + var docs, doc, copy;
    + var repResult;
    + var i, j, k;
    +
    +
    + function makeAttData(minSize) {
    + var data = att1_data;
    +
    + while (data.length < minSize) {
    + data = data + att1_data;
    + }
    + return data;
    + }
    +
    +
    + function enableAttCompression(level, types) {
    + var xhr = CouchDB.request(
    + "PUT",
    + "/_config/attachments/compression_level",
    + {
    + body: JSON.stringify(level),
    + headers: {"X-Couch-Persist": "false"}
    + }
    + );
    + T(xhr.status === 200);
    + xhr = CouchDB.request(
    + "PUT",
    + "/_config/attachments/compressible_types",
    + {
    + body: JSON.stringify(types),
    + headers: {"X-Couch-Persist": "false"}
    + }
    + );
    + T(xhr.status === 200);
    + }
    +
    +
    + function disableAttCompression() {
    + var xhr = CouchDB.request(
    + "PUT",
    + "/_config/attachments/compression_level",
    + {
    + body: JSON.stringify("0"),
    + headers: {"X-Couch-Persist": "false"}
    + }
    + );
    + T(xhr.status === 200);
    + }
    +
    +
    + function populateDb(db, docs, dontRecreateDb) {
    + if (dontRecreateDb !== true) {
    + db.deleteDb();
    + db.createDb();
    + }
    + for (var i = 0; i < docs.length; i++) {
    + var doc = docs[i];
    + delete doc._rev;
    + }
    + if (docs.length > 0) {
    + db.bulkSave(docs);
    + }
    + }
    +
    +
    + function addAtt(db, doc, attName, attData, type) {
    + var uri = "/" + db.name + "/" + encodeURIComponent(doc._id) + "/" + attName;
    +
    + if (doc._rev) {
    + uri += "?rev=" + doc._rev;
    + }
    +
    + var xhr = CouchDB.request("PUT", uri, {
    + headers: {
    + "Content-Type": type
    + },
    + body: attData
    + });
    +
    + T(xhr.status === 201);
    + doc._rev = JSON.parse(xhr.responseText).rev;
    + }
    +
    +
    + function compareObjects(o1, o2) {
    + for (var p in o1) {
    + if (o1[p] === null && o2[p] !== null) {
    + return false;
    + } else if (typeof o1[p] === "object") {
    + if ((typeof o2[p] !== "object") || o2[p] === null) {
    + return false;
    + }
    + if (!arguments.callee(o1[p], o2[p])) {
    + return false;
    + }
    + } else {
    + if (o1[p] !== o2[p]) {
    + return false;
    + }
    + }
    + }
    + return true;
    + }
    +
    +
    + function getTask(rep_id, delay) {
    + var t0 = new Date();
    + var t1;
    + do {
    + var xhr = CouchDB.request("GET", "/_active_tasks");
    + var tasks = JSON.parse(xhr.responseText);
    + for(var i = 0; i < tasks.length; i++) {
    + if(tasks[i].replication_id == repResult._local_id) {
    + return tasks[i];
    + }
    + }
    + t1 = new Date();
    + } while((t1 - t0) <= delay);
    +
    + return null;
    + }
    +
    +
    + function waitForSeq(sourceDb, targetDb, rep_id) {
    + var sourceSeq = sourceDb.info().update_seq,
    + t0 = new Date(),
    + t1,
    + ms = 3000;
    +
    + do {
    + var task = getTask(rep_id, 0);
    + if(task && task["through_seq"] == sourceSeq) {
    + return;
    + }
    + t1 = new Date();
    + } while (((t1 - t0) <= ms));
    + }
    +
    +
    + // test simple replications (not continuous, not filtered), including
    + // conflict creation
    + docs = makeDocs(1, 21);
    + docs.push({
    + _id: "_design/foo",
    + language: "javascript",
    + value: "ddoc"
    + });
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + // add some attachments
    + for (j = 10; j < 15; j++) {
    + addAtt(sourceDb, docs[j], "readme.txt", att1_data, "text/plain");
    + }
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + TEquals('string', typeof repResult.session_id);
    + TEquals(repResult.source_last_seq, sourceInfo.update_seq);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(1, repResult.history.length);
    + TEquals(repResult.history[0].session_id, repResult.session_id);
    + TEquals('string', typeof repResult.history[0].start_time);
    + TEquals('string', typeof repResult.history[0].end_time);
    + TEquals(0, repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(sourceInfo.doc_count, repResult.history[0].missing_checked);
    + TEquals(sourceInfo.doc_count, repResult.history[0].missing_found);
    + TEquals(sourceInfo.doc_count, repResult.history[0].docs_read);
    + TEquals(sourceInfo.doc_count, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    +
    + if (j >= 10 && j < 15) {
    + var atts = copy._attachments;
    + TEquals('object', typeof atts);
    + TEquals('object', typeof atts["readme.txt"]);
    + TEquals(2, atts["readme.txt"].revpos);
    + TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
    + TEquals(true, atts["readme.txt"].stub);
    +
    + var att_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
    + ).responseText;
    + TEquals(att1_data.length, att_copy.length);
    + TEquals(att1_data, att_copy);
    + }
    + }
    +
    +
    + // add one more doc to source, more attachments to some existing docs
    + // and replicate again
    + var newDoc = {
    + _id: "foo666",
    + value: "d"
    + };
    + TEquals(true, sourceDb.save(newDoc).ok);
    +
    + // add some more attachments
    + for (j = 10; j < 15; j++) {
    + addAtt(sourceDb, docs[j], "data.dat", att2_data, "application/binary");
    + }
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(targetInfo.doc_count, sourceInfo.doc_count);
    +
    + TEquals('string', typeof repResult.session_id);
    + TEquals(sourceInfo.update_seq, repResult.source_last_seq);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(2, repResult.history.length);
    + TEquals(repResult.history[0].session_id, repResult.session_id);
    + TEquals('string', typeof repResult.history[0].start_time);
    + TEquals('string', typeof repResult.history[0].end_time);
    + TEquals((sourceInfo.update_seq - 6), repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(6, repResult.history[0].missing_checked);
    + TEquals(6, repResult.history[0].missing_found);
    + TEquals(6, repResult.history[0].docs_read);
    + TEquals(6, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(newDoc._id);
    + T(copy !== null);
    + TEquals(newDoc._id, copy._id);
    + TEquals(newDoc.value, copy.value);
    +
    + for (j = 10; j < 15; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    +
    + var atts = copy._attachments;
    + TEquals('object', typeof atts);
    + TEquals('object', typeof atts["readme.txt"]);
    + TEquals(2, atts["readme.txt"].revpos);
    + TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
    + TEquals(true, atts["readme.txt"].stub);
    +
    + var att1_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
    + ).responseText;
    + TEquals(att1_data.length, att1_copy.length);
    + TEquals(att1_data, att1_copy);
    +
    + TEquals('object', typeof atts["data.dat"]);
    + TEquals(3, atts["data.dat"].revpos);
    + TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
    + TEquals(true, atts["data.dat"].stub);
    +
    + var att2_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
    + ).responseText;
    + TEquals(att2_data.length, att2_copy.length);
    + TEquals(att2_data, att2_copy);
    + }
    +
    + // test deletion is replicated
    + doc = sourceDb.open(docs[1]._id);
    + TEquals(true, sourceDb.deleteDoc(doc).ok);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(targetInfo.doc_count, sourceInfo.doc_count);
    + TEquals(targetInfo.doc_del_count, sourceInfo.doc_del_count);
    + TEquals(1, targetInfo.doc_del_count);
    +
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(3, repResult.history.length);
    + TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(1, repResult.history[0].missing_checked);
    + TEquals(1, repResult.history[0].missing_found);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(docs[1]._id);
    + TEquals(null, copy);
    +
    + var changes = targetDb.changes({since: 0});
    + var idx = changes.results.length - 1;
    + TEquals(docs[1]._id, changes.results[idx].id);
    + TEquals(true, changes.results[idx].deleted);
    +
    + // test conflict
    + doc = sourceDb.open(docs[0]._id);
    + doc.value = "white";
    + TEquals(true, sourceDb.save(doc).ok);
    +
    + copy = targetDb.open(docs[0]._id);
    + copy.value = "black";
    + TEquals(true, targetDb.save(copy).ok);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(4, repResult.history.length);
    + TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(1, repResult.history[0].missing_checked);
    + TEquals(1, repResult.history[0].missing_found);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(docs[0]._id, {conflicts: true});
    +
    + TEquals(0, copy._rev.indexOf("2-"));
    + TEquals(true, copy._conflicts instanceof Array);
    + TEquals(1, copy._conflicts.length);
    + TEquals(0, copy._conflicts[0].indexOf("2-"));
    +
    + // replicate again with conflict
    + doc.value = "yellow";
    + TEquals(true, sourceDb.save(doc).ok);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(5, repResult.history.length);
    + TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(1, repResult.history[0].missing_checked);
    + TEquals(1, repResult.history[0].missing_found);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(docs[0]._id, {conflicts: true});
    +
    + TEquals(0, copy._rev.indexOf("3-"));
    + TEquals(true, copy._conflicts instanceof Array);
    + TEquals(1, copy._conflicts.length);
    + TEquals(0, copy._conflicts[0].indexOf("2-"));
    +
    + // resolve the conflict
    + TEquals(true, targetDb.deleteDoc({_id: copy._id, _rev: copy._conflicts[0]}).ok);
    +
    + // replicate again, check there are no more conflicts
    + doc.value = "rainbow";
    + TEquals(true, sourceDb.save(doc).ok);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(6, repResult.history.length);
    + TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(1, repResult.history[0].missing_checked);
    + TEquals(1, repResult.history[0].missing_found);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(docs[0]._id, {conflicts: true});
    +
    + TEquals(0, copy._rev.indexOf("4-"));
    + TEquals('undefined', typeof copy._conflicts);
    +
    + // test that revisions already in a target are not copied
    + TEquals(true, sourceDb.save({_id: "foo1", value: 111}).ok);
    + TEquals(true, targetDb.save({_id: "foo1", value: 111}).ok);
    + TEquals(true, sourceDb.save({_id: "foo2", value: 222}).ok);
    + TEquals(true, sourceDb.save({_id: "foo3", value: 333}).ok);
    + TEquals(true, targetDb.save({_id: "foo3", value: 333}).ok);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + TEquals(sourceInfo.update_seq, repResult.source_last_seq);
    + TEquals(sourceInfo.update_seq - 3, repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(3, repResult.history[0].missing_checked);
    + TEquals(1, repResult.history[0].missing_found);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + TEquals(true, sourceDb.save({_id: "foo4", value: 444}).ok);
    + TEquals(true, targetDb.save({_id: "foo4", value: 444}).ok);
    + TEquals(true, sourceDb.save({_id: "foo5", value: 555}).ok);
    + TEquals(true, targetDb.save({_id: "foo5", value: 555}).ok);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + TEquals(sourceInfo.update_seq, repResult.source_last_seq);
    + TEquals(sourceInfo.update_seq - 2, repResult.history[0].start_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
    + TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
    + TEquals(2, repResult.history[0].missing_checked);
    + TEquals(0, repResult.history[0].missing_found);
    + TEquals(0, repResult.history[0].docs_read);
    + TEquals(0, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    + TEquals(true, repResult.no_changes);
    + sourceInfo = sourceDb.info();
    + TEquals(sourceInfo.update_seq, repResult.source_last_seq);
    + }
    +
    +
    + // test error when source database does not exist
    + try {
    + CouchDB.replicate("foobar", "test_suite_db");
    + T(false, "should have failed with db_not_found error");
    + } catch (x) {
    + TEquals("db_not_found", x.error);
    + }
    +
    + // validate COUCHDB-317
    + try {
    + CouchDB.replicate("/foobar", "test_suite_db");
    + T(false, "should have failed with db_not_found error");
    + } catch (x) {
    + TEquals("db_not_found", x.error);
    + }
    +
    + try {
    + CouchDB.replicate(CouchDB.protocol + host + "/foobar", "test_suite_db");
    + T(false, "should have failed with db_not_found error");
    + } catch (x) {
    + TEquals("db_not_found", x.error);
    + }
    +
    +
    + // test since_seq parameter
    + docs = makeDocs(1, 6);
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + var since_seq = 3;
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + var expected_ids = [];
    + var changes = sourceDb.changes({since: since_seq});
    + for (j = 0; j < changes.results.length; j++) {
    + expected_ids.push(changes.results[j].id);
    + }
    + TEquals(2, expected_ids.length, "2 documents since since_seq");
    +
    + // For OTP < R14B03, temporary child specs are kept in the supervisor
    + // after the child terminates, so cancel the replication to delete the
    + // child spec in those OTP releases, otherwise since_seq will have no
    + // effect.
    + try {
    + CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {body: {cancel: true}}
    + );
    + } catch (x) {
    + // OTP R14B03 onwards
    + TEquals("not found", x.error);
    + }
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {body: {since_seq: since_seq}}
    + );
    + // Same reason as before. But here we don't want since_seq to affect
    + // subsequent replications, so we need to delete the child spec from the
    + // supervisor (since_seq is not used to calculate the replication ID).
    + try {
    + CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {body: {cancel: true}}
    + );
    + } catch (x) {
    + // OTP R14B03 onwards
    + TEquals("not found", x.error);
    + }
    + TEquals(true, repResult.ok);
    + TEquals(2, repResult.history[0].missing_checked);
    + TEquals(2, repResult.history[0].missing_found);
    + TEquals(2, repResult.history[0].docs_read);
    + TEquals(2, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + if (expected_ids.indexOf(doc._id) === -1) {
    + T(copy === null);
    + } else {
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + }
    + }
    + }
    +
    +
    + // test errors due to doc validate_doc_update functions in the target endpoint
    + docs = makeDocs(1, 8);
    + docs[2]["_attachments"] = {
    + "hello.txt": {
    + "content_type": "text/plain",
    + "data": "aGVsbG8gd29ybGQ=" // base64:encode("hello world")
    + }
    + };
    + var ddoc = {
    + _id: "_design/test",
    + language: "javascript",
    + validate_doc_update: (function(newDoc, oldDoc, userCtx, secObj) {
    + if ((newDoc.integer % 2) !== 0) {
    + throw {forbidden: "I only like multiples of 2."};
    + }
    + }).toString()
    + };
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, [ddoc]);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target
    + );
    + TEquals(true, repResult.ok);
    + TEquals(7, repResult.history[0].missing_checked);
    + TEquals(7, repResult.history[0].missing_found);
    + TEquals(7, repResult.history[0].docs_read);
    + TEquals(3, repResult.history[0].docs_written);
    + TEquals(4, repResult.history[0].doc_write_failures);
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + if (doc.integer % 2 === 0) {
    + T(copy !== null);
    + TEquals(copy.integer, doc.integer);
    + } else {
    + T(copy === null);
    + }
    + }
    + }
    +
    +
    + // test create_target option
    + docs = makeDocs(1, 2);
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(sourceDb, docs);
    + targetDb.deleteDb();
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {body: {create_target: true}}
    + );
    + TEquals(true, repResult.ok);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    + TEquals(sourceInfo.update_seq, targetInfo.update_seq);
    + }
    +
    +
    + // test filtered replication
    + docs = makeDocs(1, 31);
    + docs.push({
    + _id: "_design/mydesign",
    + language: "javascript",
    + filters: {
    + myfilter: (function(doc, req) {
    + var modulus = Number(req.query.modulus);
    + var special = req.query.special;
    + return (doc.integer % modulus === 0) || (doc.string === special);
    + }).toString()
    + }
    + });
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + filter: "mydesign/myfilter",
    + query_params: {
    + modulus: "2",
    + special: "7"
    + }
    + }
    + }
    + );
    +
    + TEquals(true, repResult.ok);
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + if ((doc.integer && (doc.integer % 2 === 0)) || (doc.string === "7")) {
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + } else {
    + TEquals(null, copy);
    + }
    + }
    +
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(1, repResult.history.length);
    + // We (incorrectly) don't record update sequences for things
    + // that don't pass the changse feed filter. Historically the
    + // last document to pass was the second to last doc which has
    + // an update sequence of 30. Work that has been applied to avoid
    + // conflicts from duplicate IDs breaking _bulk_docs updates added
    + // a sort to the logic which changes this. Now the last document
    + // to pass has an doc id of "8" and is at update_seq 29 (because only
    + // "9" and the design doc are after it).
    + //
    + // In the future the fix ought to be that we record that update
    + // sequence of the database. BigCouch has some existing work on
    + // this in the clustered case because if you have very few documents
    + // that pass the filter then (given single node's behavior) you end
    + // up having to rescan a large portion of the database.
    + TEquals(29, repResult.source_last_seq);
    + TEquals(0, repResult.history[0].start_last_seq);
    + TEquals(29, repResult.history[0].end_last_seq);
    + TEquals(29, repResult.history[0].recorded_seq);
    + // 16 => 15 docs with even integer field + 1 doc with string field "7"
    + TEquals(16, repResult.history[0].missing_checked);
    + TEquals(16, repResult.history[0].missing_found);
    + TEquals(16, repResult.history[0].docs_read);
    + TEquals(16, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    +
    + // add new docs to source and resume the same replication
    + var newDocs = makeDocs(50, 56);
    + populateDb(sourceDb, newDocs, true);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + filter: "mydesign/myfilter",
    + query_params: {
    + modulus: "2",
    + special: "7"
    + }
    + }
    + }
    + );
    +
    + TEquals(true, repResult.ok);
    +
    + for (j = 0; j < newDocs.length; j++) {
    + doc = newDocs[j];
    + copy = targetDb.open(doc._id);
    +
    + if (doc.integer && (doc.integer % 2 === 0)) {
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + } else {
    + TEquals(null, copy);
    + }
    + }
    +
    + // last doc has even integer field, so last replicated seq is 36
    + TEquals(36, repResult.source_last_seq);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(2, repResult.history.length);
    + TEquals(29, repResult.history[0].start_last_seq);
    + TEquals(36, repResult.history[0].end_last_seq);
    + TEquals(36, repResult.history[0].recorded_seq);
    + TEquals(3, repResult.history[0].missing_checked);
    + TEquals(3, repResult.history[0].missing_found);
    + TEquals(3, repResult.history[0].docs_read);
    + TEquals(3, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    + }
    +
    +
    + // test filtered replication works as expected after changing the filter's
    + // code (ticket COUCHDB-892)
    + var filterFun1 = (function(doc, req) {
    + if (doc.value < Number(req.query.maxvalue)) {
    + return true;
    + } else {
    + return false;
    + }
    + }).toString();
    +
    + var filterFun2 = (function(doc, req) {
    + return true;
    + }).toString();
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(targetDb, []);
    + populateDb(sourceDb, []);
    +
    + TEquals(true, sourceDb.save({_id: "foo1", value: 1}).ok);
    + TEquals(true, sourceDb.save({_id: "foo2", value: 2}).ok);
    + TEquals(true, sourceDb.save({_id: "foo3", value: 3}).ok);
    + TEquals(true, sourceDb.save({_id: "foo4", value: 4}).ok);
    +
    + var ddoc = {
    + "_id": "_design/mydesign",
    + "language": "javascript",
    + "filters": {
    + "myfilter": filterFun1
    + }
    + };
    +
    + TEquals(true, sourceDb.save(ddoc).ok);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + filter: "mydesign/myfilter",
    + query_params: {
    + maxvalue: "3"
    + }
    + }
    + }
    + );
    +
    + TEquals(true, repResult.ok);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(1, repResult.history.length);
    + TEquals(2, repResult.history[0].docs_written);
    + TEquals(2, repResult.history[0].docs_read);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + var docFoo1 = targetDb.open("foo1");
    + T(docFoo1 !== null);
    + TEquals(1, docFoo1.value);
    +
    + var docFoo2 = targetDb.open("foo2");
    + T(docFoo2 !== null);
    + TEquals(2, docFoo2.value);
    +
    + var docFoo3 = targetDb.open("foo3");
    + TEquals(null, docFoo3);
    +
    + var docFoo4 = targetDb.open("foo4");
    + TEquals(null, docFoo4);
    +
    + // replication should start from scratch after the filter's code changed
    +
    + ddoc.filters.myfilter = filterFun2;
    + TEquals(true, sourceDb.save(ddoc).ok);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + filter: "mydesign/myfilter",
    + query_params : {
    + maxvalue: "3"
    + }
    + }
    + }
    + );
    +
    + TEquals(true, repResult.ok);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(1, repResult.history.length);
    + TEquals(3, repResult.history[0].docs_written);
    + TEquals(3, repResult.history[0].docs_read);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + docFoo1 = targetDb.open("foo1");
    + T(docFoo1 !== null);
    + TEquals(1, docFoo1.value);
    +
    + docFoo2 = targetDb.open("foo2");
    + T(docFoo2 !== null);
    + TEquals(2, docFoo2.value);
    +
    + docFoo3 = targetDb.open("foo3");
    + T(docFoo3 !== null);
    + TEquals(3, docFoo3.value);
    +
    + docFoo4 = targetDb.open("foo4");
    + T(docFoo4 !== null);
    + TEquals(4, docFoo4.value);
    +
    + T(targetDb.open("_design/mydesign") !== null);
    + }
    +
    +
    + // test replication by doc IDs
    + docs = makeDocs(1, 11);
    + docs.push({
    + _id: "_design/foo",
    + language: "javascript",
    + integer: 1
    + });
    +
    + var target_doc_ids = [
    + { initial: ["1", "2", "10"], after: [], conflict_id: "2" },
    + { initial: ["1", "2"], after: ["7"], conflict_id: "1" },
    + { initial: ["1", "foo_666", "10"], after: ["7"], conflict_id: "10" },
    + { initial: ["_design/foo", "8"], after: ["foo_5"], conflict_id: "8" },
    + { initial: ["_design%2Ffoo", "8"], after: ["foo_5"], conflict_id: "8" },
    + { initial: [], after: ["foo_1000", "_design/foo", "1"], conflict_id: "1" }
    + ];
    + var doc_ids, after_doc_ids;
    + var id, num_inexistent_docs, after_num_inexistent_docs;
    + var total, after_total;
    +
    + for (i = 0; i < dbPairs.length; i++) {
    +
    + for (j = 0; j < target_doc_ids.length; j++) {
    + doc_ids = target_doc_ids[j].initial;
    + num_inexistent_docs = 0;
    +
    + for (k = 0; k < doc_ids.length; k++) {
    + id = doc_ids[k];
    + if (id.indexOf("foo_") === 0) {
    + num_inexistent_docs += 1;
    + }
    + }
    +
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + doc_ids: doc_ids
    + }
    + }
    + );
    +
    + total = doc_ids.length - num_inexistent_docs;
    + TEquals(true, repResult.ok);
    + if (total === 0) {
    + TEquals(true, repResult.no_changes);
    + } else {
    + TEquals('string', typeof repResult.start_time);
    + TEquals('string', typeof repResult.end_time);
    + TEquals(total, repResult.docs_read);
    + TEquals(total, repResult.docs_written);
    + TEquals(0, repResult.doc_write_failures);
    + }
    +
    + for (k = 0; k < doc_ids.length; k++) {
    + id = decodeURIComponent(doc_ids[k]);
    + doc = sourceDb.open(id);
    + copy = targetDb.open(id);
    +
    + if (id.indexOf("foo_") === 0) {
    + TEquals(null, doc);
    + TEquals(null, copy);
    + } else {
    + T(doc !== null);
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + }
    + }
    +
    + // be absolutely sure that other docs were not replicated
    + for (k = 0; k < docs.length; k++) {
    + var base_id = docs[k]._id;
    + id = encodeURIComponent(base_id);
    + doc = targetDb.open(base_id);
    +
    + if ((doc_ids.indexOf(id) >= 0) || (doc_ids.indexOf(base_id) >= 0)) {
    + T(doc !== null);
    + } else {
    + TEquals(null, doc);
    + }
    + }
    +
    + targetInfo = targetDb.info();
    + TEquals(total, targetInfo.doc_count);
    +
    +
    + // add more docs throught replication by doc IDs
    + after_doc_ids = target_doc_ids[j].after;
    + after_num_inexistent_docs = 0;
    +
    + for (k = 0; k < after_doc_ids.length; k++) {
    + id = after_doc_ids[k];
    + if (id.indexOf("foo_") === 0) {
    + after_num_inexistent_docs += 1;
    + }
    + }
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + doc_ids: after_doc_ids
    + }
    + }
    + );
    +
    + after_total = after_doc_ids.length - after_num_inexistent_docs;
    + TEquals(true, repResult.ok);
    + if (after_total === 0) {
    + TEquals(true, repResult.no_changes);
    + } else {
    + TEquals('string', typeof repResult.start_time);
    + TEquals('string', typeof repResult.end_time);
    + TEquals(after_total, repResult.docs_read);
    + TEquals(after_total, repResult.docs_written);
    + TEquals(0, repResult.doc_write_failures);
    + }
    +
    + for (k = 0; k < after_doc_ids.length; k++) {
    + id = after_doc_ids[k];
    + doc = sourceDb.open(id);
    + copy = targetDb.open(id);
    +
    + if (id.indexOf("foo_") === 0) {
    + TEquals(null, doc);
    + TEquals(null, copy);
    + } else {
    + T(doc !== null);
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + }
    + }
    +
    + // be absolutely sure that other docs were not replicated
    + for (k = 0; k < docs.length; k++) {
    + var base_id = docs[k]._id;
    + id = encodeURIComponent(base_id);
    + doc = targetDb.open(base_id);
    +
    + if ((doc_ids.indexOf(id) >= 0) || (after_doc_ids.indexOf(id) >= 0) ||
    + (doc_ids.indexOf(base_id) >= 0) ||
    + (after_doc_ids.indexOf(base_id) >= 0)) {
    + T(doc !== null);
    + } else {
    + TEquals(null, doc);
    + }
    + }
    +
    + targetInfo = targetDb.info();
    + TEquals((total + after_total), targetInfo.doc_count);
    +
    +
    + // replicate again the same doc after updated on source (no conflict)
    + id = target_doc_ids[j].conflict_id;
    + doc = sourceDb.open(id);
    + T(doc !== null);
    + doc.integer = 666;
    + TEquals(true, sourceDb.save(doc).ok);
    + addAtt(sourceDb, doc, "readme.txt", att1_data, "text/plain");
    + addAtt(sourceDb, doc, "data.dat", att2_data, "application/binary");
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + doc_ids: [id]
    + }
    + }
    + );
    +
    + TEquals(true, repResult.ok);
    + TEquals(1, repResult.docs_read);
    + TEquals(1, repResult.docs_written);
    + TEquals(0, repResult.doc_write_failures);
    +
    + copy = targetDb.open(id, {conflicts: true});
    +
    + TEquals(666, copy.integer);
    + TEquals(0, copy._rev.indexOf("4-"));
    + TEquals('undefined', typeof copy._conflicts);
    +
    + var atts = copy._attachments;
    + TEquals('object', typeof atts);
    + TEquals('object', typeof atts["readme.txt"]);
    + TEquals(3, atts["readme.txt"].revpos);
    + TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
    + TEquals(true, atts["readme.txt"].stub);
    +
    + var att1_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
    + ).responseText;
    + TEquals(att1_data.length, att1_copy.length);
    + TEquals(att1_data, att1_copy);
    +
    + TEquals('object', typeof atts["data.dat"]);
    + TEquals(4, atts["data.dat"].revpos);
    + TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
    + TEquals(true, atts["data.dat"].stub);
    +
    + var att2_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
    + ).responseText;
    + TEquals(att2_data.length, att2_copy.length);
    + TEquals(att2_data, att2_copy);
    +
    +
    + // generate a conflict throught replication by doc IDs
    + id = target_doc_ids[j].conflict_id;
    + doc = sourceDb.open(id);
    + copy = targetDb.open(id);
    + T(doc !== null);
    + T(copy !== null);
    + doc.integer += 100;
    + copy.integer += 1;
    + TEquals(true, sourceDb.save(doc).ok);
    + TEquals(true, targetDb.save(copy).ok);
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + doc_ids: [id]
    + }
    + }
    + );
    +
    + TEquals(true, repResult.ok);
    + TEquals(1, repResult.docs_read);
    + TEquals(1, repResult.docs_written);
    + TEquals(0, repResult.doc_write_failures);
    +
    + copy = targetDb.open(id, {conflicts: true});
    +
    + TEquals(0, copy._rev.indexOf("5-"));
    + TEquals(true, copy._conflicts instanceof Array);
    + TEquals(1, copy._conflicts.length);
    + TEquals(0, copy._conflicts[0].indexOf("5-"));
    + }
    + }
    +
    +
    + docs = makeDocs(1, 25);
    + docs.push({
    + _id: "_design/foo",
    + language: "javascript",
    + filters: {
    + myfilter: (function(doc, req) { return true; }).toString()
    + }
    + });
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + // add some attachments
    + for (j = 10; j < 15; j++) {
    + addAtt(sourceDb, docs[j], "readme.txt", att1_data, "text/plain");
    + }
    +
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + continuous: true
    + }
    + }
    + );
    + TEquals(true, repResult.ok);
    + TEquals('string', typeof repResult._local_id);
    +
    + var rep_id = repResult._local_id;
    +
    + waitForSeq(sourceDb, targetDb, rep_id);
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    +
    + if (j >= 10 && j < 15) {
    + var atts = copy._attachments;
    + TEquals('object', typeof atts);
    + TEquals('object', typeof atts["readme.txt"]);
    + TEquals(2, atts["readme.txt"].revpos);
    + TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
    + TEquals(true, atts["readme.txt"].stub);
    +
    + var att_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
    + ).responseText;
    + TEquals(att1_data.length, att_copy.length);
    + TEquals(att1_data, att_copy);
    + }
    + }
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + // add attachments to docs in source
    + for (j = 10; j < 15; j++) {
    + addAtt(sourceDb, docs[j], "data.dat", att2_data, "application/binary");
    + }
    +
    + var ddoc = docs[docs.length - 1]; // design doc
    + addAtt(sourceDb, ddoc, "readme.txt", att1_data, "text/plain");
    +
    + waitForSeq(sourceDb, targetDb, rep_id);
    +
    + var modifDocs = docs.slice(10, 15).concat([ddoc]);
    + for (j = 0; j < modifDocs.length; j++) {
    + doc = modifDocs[j];
    + copy = targetDb.open(doc._id);
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    +
    + var atts = copy._attachments;
    + TEquals('object', typeof atts);
    + TEquals('object', typeof atts["readme.txt"]);
    + TEquals(2, atts["readme.txt"].revpos);
    + TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
    + TEquals(true, atts["readme.txt"].stub);
    +
    + var att1_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
    + ).responseText;
    + TEquals(att1_data.length, att1_copy.length);
    + TEquals(att1_data, att1_copy);
    +
    + if (doc._id.indexOf("_design/") === -1) {
    + TEquals('object', typeof atts["data.dat"]);
    + TEquals(3, atts["data.dat"].revpos);
    + TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
    + TEquals(true, atts["data.dat"].stub);
    +
    + var att2_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
    + ).responseText;
    + TEquals(att2_data.length, att2_copy.length);
    + TEquals(att2_data, att2_copy);
    + }
    + }
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + // add another attachment to the ddoc on source
    + addAtt(sourceDb, ddoc, "data.dat", att2_data, "application/binary");
    +
    + waitForSeq(sourceDb, targetDb, rep_id);
    +
    + copy = targetDb.open(ddoc._id);
    + var atts = copy._attachments;
    + TEquals('object', typeof atts);
    + TEquals('object', typeof atts["readme.txt"]);
    + TEquals(2, atts["readme.txt"].revpos);
    + TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
    + TEquals(true, atts["readme.txt"].stub);
    +
    + var att1_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
    + ).responseText;
    + TEquals(att1_data.length, att1_copy.length);
    + TEquals(att1_data, att1_copy);
    +
    + TEquals('object', typeof atts["data.dat"]);
    + TEquals(3, atts["data.dat"].revpos);
    + TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
    + TEquals(true, atts["data.dat"].stub);
    +
    + var att2_copy = CouchDB.request(
    + "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
    + ).responseText;
    + TEquals(att2_data.length, att2_copy.length);
    + TEquals(att2_data, att2_copy);
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    +
    + // add more docs to source
    + var newDocs = makeDocs(25, 35);
    + populateDb(sourceDb, newDocs, true);
    +
    + waitForSeq(sourceDb, targetDb, rep_id);
    +
    + for (j = 0; j < newDocs.length; j++) {
    + doc = newDocs[j];
    + copy = targetDb.open(doc._id);
    +
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + }
    +
    + sourceInfo = sourceDb.info();
    + targetInfo = targetDb.info();
    +
    + TEquals(sourceInfo.doc_count, targetInfo.doc_count);
    +
    + // delete docs from source
    + TEquals(true, sourceDb.deleteDoc(newDocs[0]).ok);
    + TEquals(true, sourceDb.deleteDoc(newDocs[6]).ok);
    +
    + waitForSeq(sourceDb, targetDb, rep_id);
    +
    + copy = targetDb.open(newDocs[0]._id);
    + TEquals(null, copy);
    + copy = targetDb.open(newDocs[6]._id);
    + TEquals(null, copy);
    +
    + var changes = targetDb.changes({since: targetInfo.update_seq});
    + var line1 = changes.results[changes.results.length - 2];
    + var line2 = changes.results[changes.results.length - 1];
    + TEquals(newDocs[0]._id, line1.id);
    + TEquals(true, line1.deleted);
    + TEquals(newDocs[6]._id, line2.id);
    + TEquals(true, line2.deleted);
    +
    + // cancel the replication
    + repResult = CouchDB.replicate(
    + dbPairs[i].source,
    + dbPairs[i].target,
    + {
    + body: {
    + continuous: true,
    + cancel: true
    + }
    + }
    + );
    + TEquals(true, repResult.ok);
    + TEquals(rep_id, repResult._local_id);
    +
    + doc = {
    + _id: 'foobar',
    + value: 666
    + };
    + TEquals(true, sourceDb.save(doc).ok);
    +
    + waitForSeq(sourceDb, targetDb, rep_id);
    + copy = targetDb.open(doc._id);
    + TEquals(null, copy);
    + }
    +
    + // COUCHDB-1093 - filtered and continuous _changes feed dies when the
    + // database is compacted
    + docs = makeDocs(1, 10);
    + docs.push({
    + _id: "_design/foo",
    + language: "javascript",
    + filters: {
    + myfilter: (function(doc, req) { return true; }).toString()
    + }
    + });
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + repResult = CouchDB.replicate(
    + CouchDB.protocol + host + "/" + sourceDb.name,
    + targetDb.name,
    + {
    + body: {
    + continuous: true,
    + filter: "foo/myfilter"
    + }
    + }
    + );
    + TEquals(true, repResult.ok);
    + TEquals('string', typeof repResult._local_id);
    +
    + TEquals(true, sourceDb.compact().ok);
    + while (sourceDb.info().compact_running) {};
    +
    + TEquals(true, sourceDb.save(makeDocs(30, 31)[0]).ok);
    +
    + var task = getTask(repResult._local_id, 1000);
    + T(task != null);
    +
    + waitForSeq(sourceDb, targetDb, repResult._local_id);
    + T(sourceDb.open("30") !== null);
    +
    + // cancel replication
    + repResult = CouchDB.replicate(
    + CouchDB.protocol + host + "/" + sourceDb.name,
    + targetDb.name,
    + {
    + body: {
    + continuous: true,
    + filter: "foo/myfilter",
    + cancel: true
    + }
    + }
    + );
    + TEquals(true, repResult.ok);
    + TEquals('string', typeof repResult._local_id);
    +
    +
    + //
    + // test replication of compressed attachments
    + //
    + doc = {
    + _id: "foobar"
    + };
    + var bigTextAtt = makeAttData(128 * 1024);
    + var attName = "readme.txt";
    + var xhr = CouchDB.request("GET", "/_config/attachments/compression_level");
    + var compressionLevel = JSON.parse(xhr.responseText);
    + xhr = CouchDB.request("GET", "/_config/attachments/compressible_types");
    + var compressibleTypes = JSON.parse(xhr.responseText);
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + populateDb(sourceDb, [doc]);
    + populateDb(targetDb, []);
    +
    + // enable compression of text types
    + enableAttCompression("8", "text/*");
    +
    + // add text attachment to foobar doc
    + xhr = CouchDB.request(
    + "PUT",
    + "/" + sourceDb.name + "/" + doc._id + "/" + attName + "?rev=" + doc._rev,
    + {
    + body: bigTextAtt,
    + headers: {"Content-Type": "text/plain"}
    + }
    + );
    + TEquals(201, xhr.status);
    +
    + // disable compression and replicate
    + disableAttCompression();
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + TEquals(true, repResult.ok);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(1, repResult.history.length);
    + TEquals(1, repResult.history[0].missing_checked);
    + TEquals(1, repResult.history[0].missing_found);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(
    + doc._id,
    + {att_encoding_info: true, bypass_cache: Math.round(Math.random() * 1000)}
    + );
    + T(copy !== null);
    + T(attName in copy._attachments);
    + TEquals("gzip", copy._attachments[attName].encoding);
    + TEquals("number", typeof copy._attachments[attName].length);
    + TEquals("number", typeof copy._attachments[attName].encoded_length);
    + T(copy._attachments[attName].encoded_length < copy._attachments[attName].length);
    + }
    +
    + delete bigTextAtt;
    + // restore original settings
    + enableAttCompression(compressionLevel, compressibleTypes);
    +
    +
    + //
    + // test replication triggered by non admins
    + //
    +
    + // case 1) user triggering the replication is not a DB admin of the target DB
    + var joeUserDoc = CouchDB.prepareUserDoc({
    + name: "joe",
    + roles: ["erlanger"]
    + }, "erly");
    + var usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
    + var server_config = [
    + {
    + section: "couch_httpd_auth",
    + key: "authentication_db",
    + value: usersDb.name
    + }
    + ];
    +
    + docs = makeDocs(1, 6);
    + docs.push({
    + _id: "_design/foo",
    + language: "javascript"
    + });
    +
    + dbPairs = [
    + {
    + source: sourceDb.name,
    + target: targetDb.name
    + },
    + {
    + source: CouchDB.protocol + host + "/" + sourceDb.name,
    + target: targetDb.name
    + },
    + {
    + source: sourceDb.name,
    + target: CouchDB.protocol + "joe:erly@" + host + "/" + targetDb.name
    + },
    + {
    + source: CouchDB.protocol + host + "/" + sourceDb.name,
    + target: CouchDB.protocol + "joe:erly@" + host + "/" + targetDb.name
    + }
    + ];
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + usersDb.deleteDb();
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + TEquals(true, targetDb.setSecObj({
    + admins: {
    + names: ["superman"],
    + roles: ["god"]
    + }
    + }).ok);
    +
    + run_on_modified_server(server_config, function() {
    + delete joeUserDoc._rev;
    + TEquals(true, usersDb.save(joeUserDoc).ok);
    +
    + TEquals(true, CouchDB.login("joe", "erly").ok);
    + TEquals('joe', CouchDB.session().userCtx.name);
    +
    + repResult = CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    +
    + TEquals(true, CouchDB.logout().ok);
    +
    + TEquals(true, repResult.ok);
    + TEquals(docs.length, repResult.history[0].docs_read);
    + TEquals((docs.length - 1), repResult.history[0].docs_written); // 1 ddoc
    + TEquals(1, repResult.history[0].doc_write_failures);
    + });
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    +
    + if (doc._id.indexOf("_design/") === 0) {
    + TEquals(null, copy);
    + } else {
    + T(copy !== null);
    + TEquals(true, compareObjects(doc, copy));
    + }
    + }
    + }
    +
    + // case 2) user triggering the replication is not a reader (nor admin) of the
    + // source DB
    + dbPairs = [
    + {
    + source: sourceDb.name,
    + target: targetDb.name
    + },
    + {
    + source: CouchDB.protocol + "joe:erly@" + host + "/" + sourceDb.name,
    + target: targetDb.name
    + },
    + {
    + source: sourceDb.name,
    + target: CouchDB.protocol + host + "/" + targetDb.name
    + },
    + {
    + source: CouchDB.protocol + "joe:erly@" + host + "/" + sourceDb.name,
    + target: CouchDB.protocol + host + "/" + targetDb.name
    + }
    + ];
    +
    + for (i = 0; i < dbPairs.length; i++) {
    + usersDb.deleteDb();
    + populateDb(sourceDb, docs);
    + populateDb(targetDb, []);
    +
    + TEquals(true, sourceDb.setSecObj({
    + admins: {
    + names: ["superman"],
    + roles: ["god"]
    + },
    + readers: {
    + names: ["john"],
    + roles: ["secret"]
    + }
    + }).ok);
    +
    + run_on_modified_server(server_config, function() {
    + delete joeUserDoc._rev;
    + TEquals(true, usersDb.save(joeUserDoc).ok);
    +
    + TEquals(true, CouchDB.login("joe", "erly").ok);
    + TEquals('joe', CouchDB.session().userCtx.name);
    +
    + try {
    + CouchDB.replicate(dbPairs[i].source, dbPairs[i].target);
    + T(false, "should have raised an exception");
    + } catch (x) {
    + TEquals("unauthorized", x.error);
    + }
    +
    + TEquals(true, CouchDB.logout().ok);
    + });
    +
    + for (j = 0; j < docs.length; j++) {
    + doc = docs[j];
    + copy = targetDb.open(doc._id);
    + TEquals(null, copy);
    + }
    + }
    +
    +
    + // COUCHDB-885 - push replication of a doc with attachment causes a
    + // conflict in the target.
    + sourceDb = new CouchDB("test_suite_db_a");
    + targetDb = new CouchDB("test_suite_db_b");
    +
    + sourceDb.deleteDb();
    + sourceDb.createDb();
    + targetDb.deleteDb();
    + targetDb.createDb();
    +
    + doc = {
    + _id: "doc1"
    + };
    + TEquals(true, sourceDb.save(doc).ok);
    +
    + repResult = CouchDB.replicate(
    + sourceDb.name,
    + CouchDB.protocol + host + "/" + targetDb.name
    + );
    + TEquals(true, repResult.ok);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(1, repResult.history.length);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + doc["_attachments"] = {
    + "hello.txt": {
    + "content_type": "text/plain",
    + "data": "aGVsbG8gd29ybGQ=" // base64:encode("hello world")
    + },
    + "foo.dat": {
    + "content_type": "not/compressible",
    + "data": "aSBhbSBub3QgZ3ppcGVk" // base64:encode("i am not gziped")
    + }
    + };
    +
    + TEquals(true, sourceDb.save(doc).ok);
    + repResult = CouchDB.replicate(
    + sourceDb.name,
    + CouchDB.protocol + host + "/" + targetDb.name
    + );
    + TEquals(true, repResult.ok);
    + TEquals(true, repResult.history instanceof Array);
    + TEquals(2, repResult.history.length);
    + TEquals(1, repResult.history[0].docs_written);
    + TEquals(1, repResult.history[0].docs_read);
    + TEquals(0, repResult.history[0].doc_write_failures);
    +
    + copy = targetDb.open(doc._id, {
    + conflicts: true, deleted_conflicts: true,
    + attachments: true, att_encoding_info: true});
    + T(copy !== null);
    + TEquals("undefined", typeof copy._conflicts);
    + TEquals("undefined", typeof copy._deleted_conflicts);
    + TEquals("text/plain", copy._attachments["hello.txt"]["content_type"]);
    + TEquals("aGVsbG8gd29ybGQ=", copy._attachments["hello.txt"]["data"]);
    + TEquals("gzip", copy._attachments["hello.txt"]["encoding"]);
    + TEquals("not/compressible", copy._attachments["foo.dat"]["content_type"]);
    + TEquals("aSBhbSBub3QgZ3ppcGVk", copy._attachments["foo.dat"]["data"]);
    + TEquals("undefined", typeof copy._attachments["foo.dat"]["encoding"]);
    + // end of test for COUCHDB-885
    +
    + // Test for COUCHDB-1242 (reject non-string query_params)
    + try {
    + CouchDB.replicate(sourceDb, targetDb, {
    + body: {
    + filter : "mydesign/myfilter",
    + query_params : {
    + "maxvalue": 4
    + }
    + }
    + });
    + } catch (e) {
    + TEquals("bad_request", e.error);
    + }
    +
    +
    + // Test that we can cancel a replication just by POSTing an object
    + // like {"replication_id": Id, "cancel": true}. The replication ID
    + // can be obtained from a continuous replication request response
    + // (_local_id field), from _active_tasks or from the log
    + populateDb(sourceDb, makeDocs(1, 6));
    + populateDb(targetDb, []);
    +
    + repResult = CouchDB.replicate(
    + CouchDB.protocol + host + "/" + sourceDb.name,
    + targetDb.name,
    + {
    + body: {
    + continuous: true,
    + create_target: true
    + }
    + }
    + );
    + TEquals(true, repResult.ok);
    + TEquals('string', typeof repResult._local_id);
    + var repId = repResult._local_id;
    +
    + var task = getTask(repId, 3000);
    + T(task != null);
    +
    + TEquals(task["replication_id"], repId, "Replication found in _active_tasks");
    + xhr = CouchDB.request(
    + "POST", "/_replicate", {
    + body: JSON.stringify({"replication_id": repId, "cancel": true}),
    + headers: {"Content-Type": "application/json"}
    + });
    + TEquals(200, xhr.status, "Replication cancel request success");
    +
    + task = getTask(repId);
    + TEquals(null, task, "Replication was canceled");
    +
    + xhr = CouchDB.request(
    + "POST", "/_replicate", {
    + body: JSON.stringify({"replication_id": repId, "cancel": true}),
    + headers: {"Content-Type": "application/json"}
    + });
    + TEquals(404, xhr.status, "2nd replication cancel failed");
    +
    + // Non-admin user can not cancel replications triggered by other users
    + var userDoc = CouchDB.prepareUserDoc({
    + name: "tony",
    + roles: ["mafia"]
    + }, "soprano");
    + usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
    + server_config = [
    + {
    + section: "couch_httpd_auth",
    + key: "authentication_db",
    + value: usersDb.name
    + }
    + ];
    +
    + run_on_modified_server(server_config, function() {
    + populateDb(sourceDb, makeDocs(1, 6));
    + populateDb(targetDb, []);
    + TEquals(true, usersDb.save(userDoc).ok);
    +
    + repResult = CouchDB.replicate(
    + CouchDB.protocol + host + "/" + sourceDb.name,
    + targetDb.name,
    + {
    + body: {
    + continuous: true
    + }
    + }
    + );
    + TEquals(true, repResult.ok);
    + TEquals('string', typeof repResult._local_id);
    +
    + TEquals(true, CouchDB.login("tony", "soprano").ok);
    + TEquals('tony', CouchDB.session().userCtx.name);
    +
    + xhr = CouchDB.request(
    + "POST", "/_replicate", {
    + body: JSON.stringify({"replication_id": repResult._local_id, "cancel": true}),
    + headers: {"Content-Type": "application/json"}
    + });
    + TEquals(401, xhr.status, "Unauthorized to cancel replication");
    + TEquals("unauthorized", JSON.parse(xhr.responseText).error);
    +
    + TEquals(true, CouchDB.logout().ok);
    +
    + xhr = CouchDB.request(
    + "POST", "/_replicate", {
    + body: JSON.stringify({"replication_id": repResult._local_id, "cancel": true}),
    + headers: {"Content-Type": "application/json"}
    + });
    + TEquals(200, xhr.status, "Authorized to cancel replication");
    + });
    +
    + // cleanup
    + usersDb.deleteDb();
    + sourceDb.deleteDb();
    + targetDb.deleteDb();
    +};
  • Kxepal at Dec 10, 2014 at 11:08 am
    Move JS tests to test/javascript/tests


    Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
    Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/6a4893aa
    Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/6a4893aa
    Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/6a4893aa

    Branch: refs/heads/master
    Commit: 6a4893aabdb26eb957a4e57be91ff8d0152161f8
    Parents: 9c53cff
    Author: Jan Lehnardt <jan@apache.org>
    Authored: Fri Oct 10 20:46:08 2014 +0200
    Committer: Alexander Shorin <kxepal@apache.org>
    Committed: Wed Dec 10 14:07:01 2014 +0300

    ----------------------------------------------------------------------
      share/www/script/test/all_docs.js | 142 --
      share/www/script/test/attachment_names.js | 96 -
      share/www/script/test/attachment_paths.js | 153 --
      share/www/script/test/attachment_ranges.js | 160 --
      share/www/script/test/attachment_views.js | 140 --
      share/www/script/test/attachments.js | 328 ----
      share/www/script/test/attachments_multipart.js | 416 ----
      share/www/script/test/auth_cache.js | 269 ---
      share/www/script/test/basics.js | 290 ---
      share/www/script/test/batch_save.js | 48 -
      share/www/script/test/bulk_docs.js | 124 --
      share/www/script/test/changes.js | 738 -------
      share/www/script/test/coffee.js | 67 -
      share/www/script/test/compact.js | 65 -
      share/www/script/test/config.js | 211 --
      share/www/script/test/conflicts.js | 119 --
      share/www/script/test/content_negotiation.js | 39 -
      share/www/script/test/cookie_auth.js | 288 ---
      share/www/script/test/copy_doc.js | 65 -
      share/www/script/test/delayed_commits.js | 154 --
      share/www/script/test/design_docs.js | 466 -----
      share/www/script/test/design_options.js | 74 -
      share/www/script/test/design_paths.js | 72 -
      share/www/script/test/erlang_views.js | 136 --
      share/www/script/test/etags_head.js | 78 -
      share/www/script/test/etags_views.js | 220 ---
      share/www/script/test/form_submit.js | 25 -
      share/www/script/test/http.js | 79 -
      share/www/script/test/invalid_docids.js | 77 -
      share/www/script/test/jsonp.js | 84 -
      share/www/script/test/large_docs.js | 33 -
      share/www/script/test/list_views.js | 492 -----
      share/www/script/test/lorem.txt | 103 -
      share/www/script/test/lorem_b64.txt | 1 -
      share/www/script/test/lots_of_docs.js | 55 -
      share/www/script/test/method_override.js | 40 -
      share/www/script/test/multiple_rows.js | 80 -
      share/www/script/test/oauth.js | 294 ---
      share/www/script/test/oauth_users_db.js | 161 --
      share/www/script/test/proxyauth.js | 130 --
      share/www/script/test/purge.js | 145 --
      share/www/script/test/reader_acl.js | 219 ---
      share/www/script/test/recreate_doc.js | 145 --
      share/www/script/test/reduce.js | 414 ----
      share/www/script/test/reduce_builtin.js | 179 --
      share/www/script/test/reduce_false.js | 44 -
      share/www/script/test/reduce_false_temp.js | 37 -
      share/www/script/test/replication.js | 1795 ------------------
      .../www/script/test/replicator_db_bad_rep_id.js | 77 -
      .../www/script/test/replicator_db_by_doc_id.js | 99 -
      .../script/test/replicator_db_compact_rep_db.js | 119 --
      .../www/script/test/replicator_db_continuous.js | 137 --
      .../test/replicator_db_credential_delegation.js | 149 --
      .../test/replicator_db_field_validation.js | 178 --
      share/www/script/test/replicator_db_filtered.js | 105 -
      .../www/script/test/replicator_db_identical.js | 87 -
      .../test/replicator_db_identical_continuous.js | 139 --
      .../script/test/replicator_db_invalid_filter.js | 119 --
      share/www/script/test/replicator_db_security.js | 399 ----
      share/www/script/test/replicator_db_simple.js | 114 --
      .../www/script/test/replicator_db_successive.js | 127 --
      share/www/script/test/replicator_db_survives.js | 126 --
      .../script/test/replicator_db_swap_rep_db.js | 170 --
      .../test/replicator_db_update_security.js | 92 -
      share/www/script/test/replicator_db_user_ctx.js | 272 ---
      .../www/script/test/replicator_db_write_auth.js | 102 -
      share/www/script/test/rev_stemming.js | 110 --
      share/www/script/test/rewrite.js | 505 -----
      share/www/script/test/security_validation.js | 338 ----
      share/www/script/test/show_documents.js | 420 ----
      share/www/script/test/stats.js | 348 ----
      share/www/script/test/update_documents.js | 235 ---
      share/www/script/test/users_db.js | 173 --
      share/www/script/test/users_db_security.js | 423 -----
      share/www/script/test/utf8.js | 42 -
      share/www/script/test/uuids.js | 149 --
      share/www/script/test/view_collation.js | 116 --
      share/www/script/test/view_collation_raw.js | 130 --
      share/www/script/test/view_compaction.js | 110 --
      share/www/script/test/view_conflicts.js | 49 -
      share/www/script/test/view_errors.js | 189 --
      share/www/script/test/view_include_docs.js | 192 --
      .../www/script/test/view_multi_key_all_docs.js | 95 -
      share/www/script/test/view_multi_key_design.js | 220 ---
      share/www/script/test/view_multi_key_temp.js | 40 -
      share/www/script/test/view_offsets.js | 108 --
      share/www/script/test/view_pagination.js | 147 --
      share/www/script/test/view_sandboxing.js | 140 --
      share/www/script/test/view_update_seq.js | 106 --
      test/javascript/couch.js | 520 +++++
      test/javascript/couch_test_runner.js | 465 +++++
      test/javascript/json2.js | 482 +++++
      test/javascript/oauth.js | 511 +++++
      test/javascript/replicator_db_inc.js | 96 +
      test/javascript/run | 16 +-
      test/javascript/sha1.js | 202 ++
      test/javascript/tests/all_docs.js | 142 ++
      test/javascript/tests/attachment_names.js | 96 +
      test/